1.nn.functional
今天学nn中一个很常用的模块:nn.functional,nn中的大多数layer,在functional中都有一个对应的函数。
nn.functional中的函数和nn.Module的主要区别在于:
用nn.Module实现的layer是一个特殊的类,都是由calss layer(nn.Module)定义的,会自动提取可学习的参数。
用nn.functional中的函数更像是纯函数,由def function(input)定义,我们来看看functional的使用方法:
线性层
input = t.randn(2,3)
model = nn.Linear(3,4)
output = model(input)#Module
output2 = nn.functional.linear(input,model.weight,model.bias)#function
output == output2
->tensor([[True, True, True, True],
[True, True, True, True]])
ReLU
b = nn.functional.relu(input)
b2 = nn.ReLU()(input)
b == b2
->tensor([[True, True, True],
[True, True, True]])
那到底什么时候用nn.Module,什么适合用nn.functional呢?
其实很简单,如果模型内有可学习的参数,最好用nn.Module,其实两者在性能上没有太大差异,具体使用取决于个人的喜好
比如激活函数(ReLU,sigmoid,tanh),池化(MaxPool)等没有u儿媳参数的,可用functional函数代替,对于卷积、全连接等具有可学习参数的网络,使用nn.Module
注意虽然dropout操作也没有可学习的操作,但建议还是使用nn.Dropout,因为drpout在训练和测试两个阶段的行为有差别,使用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)#2*2卷积和的最大池化
x = F.poo(F.relu(self.conv2(x)),2)
x = x.view(1,16*5*5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
这个例子就是昨天学的Lenet网络
# 首先定义一个LeNet网络
class Net1(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 6, 5),#1张图像,3个颜色通道,5*5卷积核
nn.ReLU(),#将负值变成0 ReLU
nn.MaxPool2d(2,2),#最大池化使用2*2的池化窗口,步幅为2 池化会在2*2的区域中选择内部的最大值
nn.Conv2d(6, 16, 5),
nn.ReLU(),
nn.MaxPool2d(2,2)
)
self.classifier = nn.Sequential(
nn.Linear(16 * 5 * 5, 120),
nn.ReLU(),
nn.Linear(120, 84),
nn.ReLU(),
nn.Linear(84, 10)
)
def forward(self, x):
x = self.features(x)
x = x.view(-1, 16 * 5 * 5)
x = self.classifier(x)
return x
net = Net1()
只是在计算不可学习参数的层(激活层、池化层),将它们用函数代题,这样则可不用单独放置在构造函数__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)
2.初始化策略
在深度学习中,参数的初始化十分重要,良好的初始化能让模型更快收敛,并达到更高的水平。二糟糕的初始化会使得模型迅速瘫痪
pytorch中nn.Module的模块都采用了较合理的初始化策略,因此一般不用担心初始化问题
当然我们也可以自定义初始化去替换默认的初始化
注意,当我们使用Parameter时,自定义初始化尤为重要。因t.Tensor()返回的是内存中的随机数,很可能有极大值
这在实际训练网络中会造成溢出或梯度消失
pytorch中的nn.init模块就是专门为初始化为设计的:
#利用nn.init进行初始化
from torch.nn import init#常见的初始化权重共组 比如xavier_normal_, kaiming_normal_,或者 uniform_
linear = nn.Linear(3,4)#Linear会默认创建两个参数W(weight 权重矩阵) 和 B(偏置项)->weight形状[4,3],bias形状[4]
t.manual_seed(1)
init.xavier_normal_(linear.weight)#正态分布初始化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]], requires_grad=True)
由于该层输入特征数是(3),输出特征数为(4)
根据正态分布:
计算权重矩阵[4,3]的标准差
我们也可以直接计算并替换权重:
# 直接初始化
import math
t.manual_seed(1)
# xavier初始化的计算公式
std = math.sqrt(2)/math.sqrt(7.)#计算了标准差,这里的2是计算的个数,sqrt则是3+4=7,输入和输出特征数的和
linear.weight.data.normal_(0,std)#这里直接用均值0和标准差std对linear的权重进行了替换
->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 name, 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 name.find('norm') != -1:
pass
遍历模型中的所有参数,将'Linear','conv','norm'的函数层进行初始化
代码只对linear进行了修改,其他跳过
标签:初始化,nn,1218,functional,学习,init,深度,True,self From: https://blog.csdn.net/m0_65881366/article/details/144569535