模型的定义
首先,必须继承 nn.Module 类。
其次,在__init__(self)中设置好需要的“组件"(如 conv、 pooling、 Linear、 BatchNorm等)。
最后,在 forward(self, x)中用定义好的“组件”进行组装。
class Net(nn.Module):
def __init__(self): # 初始化,定义组件
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool1 = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.pool2 = nn.MaxPool2d(2, 2)
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 = self.pool1(F.relu(self.conv1(x))) # x 经过 conv1,然后经过激活函数 relu,再经过 pool1 操作
x = self.pool2(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5) # 将 x 进行 reshape,为了后面做为全连接层的输入
x = F.relu(self.fc1(x)) # 先经过全连接层 fc,然后经过 relu
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
至此,一个模型定义完毕,接着就可以在后面进行使用。例如,实例化一个模型
net = Net()
,然后把输入 inputs 扔进去,
outputs = net(inputs)
,就可以得到输出 outputs。
torch.nn.Sequential 容器,将一系列操作按先后顺序给包起来,方便重复使用。
权值初始化
第一步,先设定什么层用什么初始化方法,初始化方法在 torch.nn.init 中给出;
第二步,实例化一个模型之后,执行该函数,即可完成初始化。
def initialize_weights(self):
for m in self.modules(): # 遍历每一层
if isinstance(m, nn.Conv2d): # 判断各层属于什么类型
torch.nn.init.xavier_normal_(m.weight.data) # 设定不同的权值初始化方法
if m.bias is not None: # 判断是否存在偏置,若存在,初始化为全0
m.bias.data.zero_()
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
elif isinstance(m, nn.Linear):
torch.nn.init.normal_(m.weight.data, 0, 0.01)
m.bias.data.zero_()
常用初始化方法
Xavier 均匀分布
torch.nn.init.xavier_uniform_(tensor, gain=1)
Xavier 正态分布
torch.nn.init.xavier_normal_(tensor, gain=1)
kaiming 均匀分布
torch.nn.init.kaiming_uniform_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
kaiming 正态分布
torch.nn.init.kaiming_normal_(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
均匀分布初始化
torch.nn.init.uniform_(tensor, a=0, b=1)
正态分布初始化
torch.nn.init.normal_(tensor, mean=0, std=1)
常数初始化
torch.nn.init.constant_(tensor, val)
单位矩阵初始化
torch.nn.init.eye_(tensor)
正交初始化
torch.nn.init.orthogonal_(tensor, gain=1)
稀疏初始化
torch.nn.init.sparse_(tensor, sparsity, std=0.01)
计算增益
torch.nn.init.calculate_gain(nonlinearity, param=None)
PyTorch默认初始化
在创建网络实例的过程中, 一旦调用 nn.Conv2d 的时候就会有对权值进行初始化。采用的是均匀分布,其中-stdv 与 kernel 的 size 有关。在 PyTorch1.0 版本中,这里改用了 kaiming_uniform_()进行初始化。
模型finetune(迁移学习)
在实际应用中,我们通常采用一个已经训练模型的模型的权值参数作为我们模型的初始化参数,也称之为 Finetune, 更宽泛的称之为迁移学习。
net = Net() # 创建一个网络
# ================================ #
# finetune 权值初始化
# ================================ #
# load params
pretrained_dict = torch.load('net_params.pkl')
# 获取当前网络的dict
net_state_dict = net.state_dict()
# 剔除不匹配的权值参数
pretrained_dict_1 = {k: v for k, v in pretrained_dict.items() if k in net_state_dict}
# 更新新模型参数字典
net_state_dict.update(pretrained_dict_1)
# 将包含预训练模型参数的字典"放"到新模型中
net.load_state_dict(net_state_dict)
流程如下:
第一步:保存模型,拥有一个预训练模型;
net = Net()
torch.save(net.state_dict(), 'net_params.pkl')
第二步:加载模型,把预训练模型中的权值取出来;
pretrained_dict = torch.load('net_params.pkl')
第三步:初始化,将权值对应的“放”到新模型中
首先我们创建新模型,并且获取新模型的参数字典
net_state_dict = net.state_dict()
接着将 pretrained_dict 里不属于 net_state_dict 的键剔除掉
pretrained_dict_1 = {k: v for k, v in pretrained_dict.items() if k in net_state_dict}
然后,用预训练模型的参数字典 对 新模型的参数字典 net_state_dict 进行更新
net_state_dict.update(pretrained_dict_1)
最后,将更新了参数的字典 “放”回到网络中
net.load_state_dict(net_state_dict)
不同层设置不同的学习率
为不同层设置不同的学习率,只需要将原始的参数组,划分成两个,甚至更多的参数组,然后分别进行设置学习率。
ignored_params = list(map(id, net.fc3.parameters())) # 返回的是 parameters 的 内存地址
base_params = filter(lambda p: id(p) not in ignored_params, net.parameters()) # 剥离了 fc3 层的参数的其余参数
# base_params 中的层,用 0.001, momentum=0.9,weight_decay=1e-4, fc3 层设定学习率为: 0.001*10
optimizer = optim.SGD([
{'params': base_params}, # base_params 是一个 list,每个元素是一个 Parameter 类
{'params': net.fc3.parameters(), 'lr': 0.001*10}], 0.001, momentum=0.9, weight_decay=1e-4)
摘自《Pytorch模型训练实用教程》。详情见:https://github.com/tensor-yu/PyTorch_Tutorial