4.nn.functional
nn中还有一个很常用的模块:nn.functional
。nn
中大多数layer在functional
中都有一个与之对应的函数。
nn.functional
中的函数和nn.Module
的主要区别在于,用nn.Module
实现的layers是一个特殊的类,都是由class Layer(nn.Module)
定义,会自动提取可学习的参数;
而nn.functional
中的函数更像是纯函数,由def function(input)
定义。
input=V(t.randn(2,3))
model=nn.Linear(3,4)
output1=model(input)
output2=nn.functional.linear(input, model.weight, model.bias)
output1==output2
运行结果:
tensor([[ 1, 1, 1, 1],
[ 1, 1, 1, 1]], dtype=torch.uint8)
b=nn.functional.relu(input)
b2=nn.ReLU()(input)
b==b2
结果:
tensor([[ 1, 1, 1],
[ 1, 1, 1]], dtype=torch.uint8)
设么时候用nn.Module,什么时候用nn.functional呢??
1)如果模型有可学习的参数,最好用nn.Module,否则既可以用nn.functional也可以用nn.Module,二者在性能上没有太大的差异。
2)由于激活函数(ReLU,sigmoid、tanh),池化(MaxPool)等层没有可学习参数,可以使用对应的functional函数代替。
3)卷积、全连接等具有可学习参数的网络建议使用nn.Module.
4) 虽然dropout操作也没有可学习参数,但建议还是使用nn.Dropout而不是nn.functional.dropout, 因为dropout在训练和测试两个阶段的行为有所差别,使用nn.Module对象能够通过model.eval操作加以区分。
from torch.nn import functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init()
self.conv1=nn.Conv2d(3,6,5)
self.conv2=nn.Conv2d(6,16,5)
self.fc1=nn.Linear(16*5*5, 120)
self.fc2=nn.Linear(120,84)
self.fc3=nn.Linear(84,10)
def forward(self,x):
x=F.pool(F.relu(self.conv1(x)),2) #卷积1、激活、池化 三个操作
x=F.pool(F.relu(self.conv2(x)),2) #卷积2、激活、池化 三个操作
x=x.view(-1,16*5*5)
x=F.relu(self.fc1(x)) #全连接1、激活
x=F.relu(self.fc2(x)) #全连接2、激活
x=self.fc3(x) #全连接3
return x
不具备可学习参数的层(激活、池化层等),将他们用函数代替,这样可以不用放置在构造函数__init__()
中。
有可学习参数的模块,也可以用functional代替,只不过实现起来较麻烦,需要手动定义参数parameter,如前面实现自定义的全连接层,就可将weight和bias两个参数单独拿出来,在构造函数中初始化为parameter.
class MyLinear(nn.Module):
def __init__(self):
super(MyLinear,self).__init__()
self.weight=nn.Parameter(t.randn(3,4))
self.bias=nn.Parameter(t.zeros(3))
def forward(self):
return F.linear(input,weight,bias)
5. 初始化策略
Pytorch中的nn.Module
的模块都采取了较合理的初始化策略,一般不用我们考虑。当然也可以用自定义的初始化策略替代系统的默认初始化。
当我们使用Parameter
时,自定义初始化尤为重要,因为t.Tensor()
返回的是内存中的随机数,很可能会有极大值,这在实际训练网络中会造成溢出或者梯度消失。
Pytorch中的nn.init
模块专门为初始化设计,实现了常用的 初始化策略。如果某种初始化策略n.init
不提供,用户也可以自己直接初始化。
#利用nn.init 初始化
from torch.nn import init
linear=nn.Linear(3,4)
t.manual_seed(1)
#等价于linear.weight.data.normal_(0,std)
init.xavier_normal_(linear.weight)
结果:
Parameter containing:
tensor([[ 0.3535, 0.1427, 0.0330],
[ 0.3321, -0.2416, -0.0888],
[-0.8140, 0.2040, -0.5493],
[-0.3010, -0.4769, -0.0311]])
#直接初始化
import math
t.manual_seed(1)
std=math.sqrt(2)/math.sqrt(7.)
linear.weight.data.normal_(0,std)
结果:
tensor([[ 0.3535, 0.1427, 0.0330],
[ 0.3321, -0.2416, -0.0888],
[-0.8140, 0.2040, -0.5493],
[-0.3010, -0.4769, -0.0311]])
#对模型的所有参数进行初始化
for neme,params in net.named_parameters():
if name.find('linear')!=-1:
#init linear
params[0] #weight
params[1] #bias
elif name.find('conv')!=-1:
pass
elif neme.find('norm')!=-1:
pass