Pytorch: 自定义网络层实例
自定义Autograd函数
对于浅层的网络,我们可以手动的书写前向传播和反向传播过程。但是当网络变得很大时,特别是在做深度学习时,网络结构变得复杂。前向传播和反向传播也随之变得复杂,手动书写这两个过程就会存在很大的困难。幸运地是在pytorch中存在了自动微分的包,可以用来解决该问题。在使用自动求导的时候,网络的前向传播会定义一个计算图(computationalgraph),图中的节点是张量(tensor),两个节点之间的边对应了两个张量之间变换关系的函数。有了计算图的存在,张量的梯度计算也变得容易了些。例如,x是一个张量,其属性x.requires_grad=True,那么x.grad就是一个保存这个张量x的梯度的一些标量值。
最基础的自动求导操作在底层就是作用在两个张量上。前向传播函数是从输入张量到输出张量的计算过程;反向传播是输入输出张量的梯度(一些标量)并输出输入张量的梯度(一些标量)。在pytorch中我们可以很容易地定义自己的自动求导操作,通过继承torch.autograd.Function并定义forward和backward函数。
forward():前向传播操作。可以输入任意多的参数,任意的python对象都可以。
backward():反向传播(梯度公式)。输出的梯度个数需要与所使用的张量个数保持一致,且返回的顺序也要对应起来。
#InheritfromFunction classLinearFunction(Function): #Notethatbothforwardandbackwardare@staticmethods @staticmethod #biasisanoptionalargument defforward(ctx,input,weight,bias=None): #ctx在这里类似self,ctx的属性可以在backward中调用 ctx.save_for_backward(input,weight,bias) output=input.mm(weight.t()) ifbiasisnotNone: output+=bias.unsqueeze(0).expand_as(output) returnoutput #Thisfunctionhasonlyasingleoutput,soitgetsonlyonegradient @staticmethod defbackward(ctx,grad_output): #Thisisapatternthatisveryconvenient-atthetopofbackward #unpacksaved_tensorsandinitializeallgradientsw.r.t.inputsto #None.ThankstothefactthatadditionaltrailingNonesare #ignored,thereturnstatementissimpleevenwhenthefunctionhas #optionalinputs. input,weight,bias=ctx.saved_tensors grad_input=grad_weight=grad_bias=None #Theseneeds_input_gradchecksareoptionalandthereonlyto #improveefficiency.Ifyouwanttomakeyourcodesimpler,youcan #skipthem.Returninggradientsforinputsthatdon'trequireitis #notanerror. ifctx.needs_input_grad[0]: grad_input=grad_output.mm(weight) ifctx.needs_input_grad[1]: grad_weight=grad_output.t().mm(input) ifbiasisnotNoneandctx.needs_input_grad[2]: grad_bias=grad_output.sum(0).squeeze(0) returngrad_input,grad_weight,grad_bias #调用自定义的自动求导函数 linear=LinearFunction.apply(*args)#前向传播 linear.backward()#反向传播 linear.grad_fn.apply(*args)#反向传播
对于非参数化的张量(权重是常量,不需要更新),此时可以定义为:
classMulConstant(Function): @staticmethod defforward(ctx,tensor,constant): #ctxisacontextobjectthatcanbeusedtostashinformation #forbackwardcomputation ctx.constant=constant returntensor*constant @staticmethod defbackward(ctx,grad_output): #Wereturnasmanyinputgradientsastherewerearguments. #Gradientsofnon-TensorargumentstoforwardmustbeNone. returngrad_output*ctx.constant,None
高阶导数
grad_x=t.autograd.grad(y,x,create_graph=True) grad_grad_x=t.autograd.grad(grad_x[0],x)
自定义Module
计算图和自动求导在定义复杂网络和求梯度的时候非常好用,但对于大型的网络,这个还是有点偏底层。在我们构建网络的时候,经常希望将计算限制在每个层之内(参数更新分层更新)。而且在TensorFlow等其他深度学习框架中都提供了高级抽象结构。因此,在pytorch中也提供了类似的包nn,它定义了一组等价于层(layer)的模块(Modules)。一个Module接受输入张量并得到输出张量,同时也会包含可学习的参数。
有时候,我们希望运用一些新的且nn包中不存在的Module。此时就需要定义自己的Module了。自定义的Module需要继承nn.Module且自定义forward函数。其中forward函数可以接受输入张量并利用其它模型或者其他自动求导操作来产生输出张量。但并不需要重写backward函数,因此nn使用了autograd。这也就意味着,需要自定义Module,都必须有对应的autograd函数以调用其中的backward。
classLinear(nn.Module): def__init__(self,input_features,output_features,bias=True): super(Linear,self).__init__() self.input_features=input_features self.output_features=output_features #nn.ParameterisaspecialkindofTensor,thatwillget #automaticallyregisteredasModule'sparameteronceit'sassigned #asanattribute.Parametersandbuffersneedtoberegistered,or #theywon'tappearin.parameters()(doesn'tapplytobuffers),and #won'tbeconvertedwhene.g..cuda()iscalled.Youcanuse #.register_buffer()toregisterbuffers. #(很重要!!!参数一定需要梯度!)nn.Parametersrequiregradientsbydefault. self.weight=nn.Parameter(torch.Tensor(output_features,input_features)) ifbias: self.bias=nn.Parameter(torch.Tensor(output_features)) else: #Youshouldalwaysregisterallpossibleparameters,butthe #optionalonescanbeNoneifyouwant. self.register_parameter('bias',None) #Notaverysmartwaytoinitializeweights self.weight.data.uniform_(-0.1,0.1) ifbiasisnotNone: self.bias.data.uniform_(-0.1,0.1) defforward(self,input): #Seetheautogradsectionforexplanationofwhathappenshere. returnLinearFunction.apply(input,self.weight,self.bias) defextra_repr(self): #(Optional)Settheextrainformationaboutthismodule.Youcantest #itbyprintinganobjectofthisclass. return'in_features={},out_features={},bias={}'.format( self.in_features,self.out_features,self.biasisnotNone
Function与Module的异同
Function与Module都可以对pytorch进行自定义拓展,使其满足网络的需求,但这两者还是有十分重要的不同:
Function一般只定义一个操作,因为其无法保存参数,因此适用于激活函数、pooling等操作;Module是保存了参数,因此适合于定义一层,如线性层,卷积层,也适用于定义一个网络
Function需要定义三个方法:init,forward,backward(需要自己写求导公式);Module:只需定义init和forward,而backward的计算由自动求导机制构成
可以不严谨的认为,Module是由一系列Function组成,因此其在forward的过程中,Function和Variable组成了计算图,在backward时,只需调用Function的backward就得到结果,因此Module不需要再定义backward。
Module不仅包括了Function,还包括了对应的参数,以及其他函数与变量,这是Function所不具备的。
module是pytorch组织神经网络的基本方式。Module包含了模型的参数以及计算逻辑。Function承载了实际的功能,定义了前向和后向的计算逻辑。
Module是任何神经网络的基类,pytorch中所有模型都必需是Module的子类。Module可以套嵌,构成树状结构。一个Module可以通过将其他Module做为属性的方式,完成套嵌。
Function是pytorch自动求导机制的核心类。Function是无参数或者说无状态的,它只负责接收输入,返回相应的输出;对于反向,它接收输出相应的梯度,返回输入相应的梯度。
在调用loss.backward()时,使用的是Function子类中定义的backward()函数。
以上这篇Pytorch:自定义网络层实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。