1.实际使用中,最常见的做法是继承nn.Module,撰写自己的网络层。下面将介绍如何使用nn.Module实现自己的全连接层。
全连接层,又称仿射层,输入y和x满足y=Wx+b,W,b是可学习参数。
import torch as t import torch.nn as nn import torch.nn.functional as F from torch.autograd import Variable as V class Linear(nn.Module):#继承nn.Module def __init__(self,in_features,outfeatures): super(Linear,self).__init__()#等价于nn.Module.__init__(self) self.w=nn.Parameter(t.randn(in_features,outfeatures)) self.b=nn.Parameter(t.randn(outfeatures)) def forward(self, x): x=x.mm(self.w) return x+self.b.expand_as(x) # expand_as (a) # 这是tensor变量的一个内置方法,如果使用b.expand_as (a) # 就是将b进行扩充,扩充到a的维度,需要说明的是a的低维度需要比b大,例如b的shape是3 * 1,如果a的shape是3 * 2 # 不会出错,但是是2 * 2 就会报错了 layer=Linear(4,3) input=V(t.randn(2,4)) output =layer(input) print(output)
输出:
tensor([[-3.0932, 1.7944, -0.4888],
[-1.8203, 1.4885, -0.2306]], grad_fn=
可见,全连接层实现简单,但需要注意以下几点:
1)自定义层Linear必须继承nn.Module,并且在其构造函数中调用nn.Module的构造函数,即super(Linear,self)__init__(self)。
2)在构造函数__init__中必须自己定义可学习的参数,并封装成Parameter,如在本例中我们把w,b封装成Parameter。它是一种特殊的Variable,但其默认需要求导(required_grad=True)。
3)forward函数实现前向传播过程,其输入可以是一个或者多个variable,对x的任何一个操作也必须是variable支持的操作。
4)无须写反向传播函数,因其前向传播都是对variable进行操作,nn.Module能够利用autograd自动实现反向传播,这一点比Function简单许多。
2.Module能够自动检测到自己的parameter,并且作为学习参数。除了parameter,Module还包含子Module,主Module能够递归查找子Module中的parameter。
多层感知机,由两个全连接层组成,采用sigmoid函数作为激活函数。
class Perceptron(nn.Module): def __init__(self,in_features,hidden_features,out_features): nn.Module.__init__(self) #super (Perceptron, self).__init__ () self.layer1=Linear(in_features,hidden_features) self.layer2 = Linear (hidden_features, out_features) def forward(self, x): x=self.layer1(x) x=t.sigmoid(x) return self.layer2(x) perceptron =Perceptron(3,4,1) for name,parameter in perceptron.named_parameters(): print(name,parameter.size())#w,b
输出:
layer1.w torch.Size([3, 4])
layer1.b torch.Size([4])
layer2.w torch.Size([4, 1])
layer2.b torch.Size([1])
1)构造函数__init__中,可利用前面自定义的Linear层(Module)作为当前Module的对象的一个子Module,他的可学习参数,也会成为当前Module的可学习参数。
2)在前向传播函数中,我们有意识地将输出变量都命名成x,是为了能让Python回收一些中间层的输出,从而节省内存。
3)Module中parameter的全局命名规范如下:
①Parameter直接命名。例如self.param_name=nn.Parameter(t.randn(3,4)),命名为param_name。
②子Module中的parameter,会在其名字之前加入当前Module的名字。例如:self.sub_module=SubModel(),SubModel中有一个parameter的名字也叫作param_name,那么两者拼起来的parameter name就是sub_module.param_name。
4)构造函数的参数,如nn.Linear(in_feature,out_feature,bias),需要关注这三个参数的作用。
属性、可学习参数和子Module。如nn.Linear中有weight和bias两个可学习参数,不包含子Module。
输入输出的形状,如nn.linear的输入形状是(N,input_features),输出为(N,output_features),N是batch_size。
注意:自定义的layer对于输入形状都有假设:输入的不是单个数据,而是一个batch。若想输入一个数据,必须调用unsqueeze(0)函数伪装成batch_size=1的batch。