在使用pytorch进行网络训练的时候,有时候不可避免的使用迁移学习(trainsfer learning),即使用已经训练好的模型(如resnet、inception等),固定其已经训练好的网络层参数,然后进行finetune。
以下代码是以resnet-101为例使用pytorch进行finetune的操作:
#导入必要模块
import torch
import torch.nn as nn
from torchvision import models
#读取pytorch自带的resnet-101模型,因为使用了预训练模型,所以会自动下载模型参数
model=models.resnet101(pretrained=True)
#对于模型的每个权重,使其不进行反向传播,即固定参数
for param in model.parameters():
param.requires_grad = False
#但是参数全部固定了,也没法进行学习,所以我们不固定最后一层,即全连接层fc
for param in model.fc.parameters():
param.requires_grad = True
如果想修改最后一层的话,可以这么修改:
class_num = 200 #假设要分类数目是200
channel_in = model.fc.in_features#获取fc层的输入通道数
#然后把resnet-101的fc层替换成300类别的fc层
model.fc = nn.Linear(channel_in,class_num)
也可以删除最后一层或者几层
#这里[:-1]代表删除最后一层
new_model = nn.Sequential(*list(model.children())[:-1])
#或删除最后两层
new_model = nn.Sequential(*list(model.children())[:-2])
当然删除了最后几层,可能还要添加,可以直接把以上代码写入新的网络里,然后再在forward代码块中添加,这个比较基础,就不详说了。
这个时候是如果按常规训练模型的方法直接使用optimizer的话会出错误的,如:
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
会报错:ValueError: optimizing a parameter that doesn't require gradients
Traceback (most recent call last):
File "main.py", line 1, in <module>
main()
File "main.py", line 20, in main
optimizer = torch.optim.SGD(model.parameters(),lr=0.1)
File "C:\Anaconda3\lib\site-packages\torch\optim\sgd.py", line 64, in __init__
super(SGD, self).__init__(params, defaults)
File "C:\Anaconda3\lib\site-packages\torch\optim\optimizer.py", line 43, in __init__
self.add_param_group(param_group)
File "C:\Anaconda3\lib\site-packages\torch\optim\optimizer.py", line 193, in add_param_group
raise ValueError("optimizing a parameter that doesn't require gradients")
ValueError: optimizing a parameter that doesn't require gradients
这是因为optimizer的输入参数parameters必须都是可以修改、反向传播的,即requires_grad=True
,但是我们刚才已经固定了除了最后一层的所有参数,所以会出错。
解决方法是optimizer中只输入需要反向传播的参数:
#filter()函数过滤掉parameters中requires_grad=Fasle的参数
optimizer = torch.optim.SGD(
filter(lambda p: p.requires_grad, model.parameters()),#重要的是这一句
lr=0.1)
这样就可以进行正常的训练了。
这里引申一下:接下来的代码是如何输出网络模型的卷积方式以及权重数值
for child in model.children():
print(child)#打印网络模型的卷积方式
for param in child.parameters():#打印权重数值
print(param)
部分结果(第一层卷积层):
Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
Parameter containing:
tensor([[[[ 2.0222e-02, -4.3927e-03, -1.8274e-02, ..., -1.5180e-02,
-1.5794e-03, 9.3115e-03],
[-4.0752e-03, 3.2116e-03, -1.5956e-02, ..., -8.4465e-02,
-7.4997e-02, -4.0676e-02],
[ 3.5039e-03, 2.6746e-02, 5.0813e-02, ..., 3.3407e-02,
1.3659e-02, 2.7821e-02],
...,
[-3.6174e-02, -1.2986e-01, -3.0369e-01, ..., -3.7412e-01,
-1.3025e-01, 4.2633e-02],
[ 1.5479e-02, 2.3444e-02, 6.5222e-03, ..., -1.6439e-01,
-1.8245e-01, -9.7434e-02],
[-3.0444e-02, -1.1357e-02, 4.9984e-02, ..., 1.6412e-01,
1.0419e-01, -1.2681e-02]],
[[ 8.7115e-03, -5.8911e-03, -1.2204e-02, ..., -1.3515e-02,
1.5212e-02, 1.9115e-02],
[-6.8970e-03,