pytorch模型微调(Finetune)

2022-10-25 13:09:30

Transfer Learning & Model Finetune

模型微调

**Transfer Learning:**机器学习分支,研究源域(source domain)的知识如何应用到目标域(target domain)。
迁移学习是一个很大的概念,它主要研究一系列源域的知识应用到目标域中,如何理解?
来自论文《A Survey on Transfer Learning》
上图左边是一个传统的机器学习任务的学习过程,传统的机器学习任务中对不同的任务分别进行训练和学习,得到称之为Learning System的模型,这里三个不同的任务就会得到三个不同的Learning System的模型;
而右边展示了迁移学习,它是怎么进行的呢?它就有不同的任务了,这个任务会划分为称之为Source Tasks(源任务)、Target Task(目标任务),这两个任务之间有一定的关联。首先,看右边的左边,先对Source Tasks进行学习,这里学习到的称之为Knowledge,而在右边的Target Task中,会利用在源任务中学习到的Knowledge进行学习进行训练,得到Learning System,这个过程叫做迁移学习。这个模型的训练不仅用到了Target Task的任务,以及还用到了Source Targets中的Knowledge,这就是迁移学习所要研究的问题。我们将源任务所学习到的知识用到目标任务当中,用来提升在目标任务里模型的性能。
深度学习模型训练当中的模型微调与迁移学习之间的关系?
训练一个模型就是不断更新它的权值,而整个模型当中最重要的东西也就是它的权值,这里的权值可以称之为知识,
可以把这些权值神经网络在特定任务当中学习到的知识,而这些知识是可以进行迁移的,将这些知识迁移到新任务当中,这样就完成了Transfer Learning,这就是模型微调。
在这里插入图片描述
为什么要采用Model Finetune这个trick呢?
这是由于在新任务中,数据量较小,不足以去训练一个较大的模型,因此就可以采用Model Finetune的方式来辅助我们在新任务当中去训练一个较好的模型,让我们的训练过程更快。这就类比于一个人如果学会了骑自行车,再去学骑电动车,这样就学的比较快。
神经网络该如何去迁移呢?
下面看一个卷积神经网络的示意图,卷积神经网络通常会划分为两个部分,将前面一系列的卷积、池化看作是features extractor,即特征提取,通过这些卷积、池化得到特征,后面会接上一系列的全连接层,将后面的全连接层称之为classifier,即分类器,这样就将一个卷积神经网络剖析成两个部分:特征提取、分类器。
对里面的一系列参数进行分析,哪些参数是有共性的,哪些参数又需要改变呢?会对卷积神经网络中的参数进行一个划分,将特征提取的部分认为是非常有共性的地方,可以原封不动的进行的进行迁移,而分类器的参数会与具体的任务有关,通常是需要进行改变的。
在这里插入图片描述

模型微调步骤

  • 1获取预训练模型参数(可以认为是源任务中学习到知识)
  • 2加载模型(load_state_dict)
  • 3修改输出层 ,以适应新的任务

模型微调训练方法

  • 1 固定预训练的参数(requires_grad = False;lr = 0)
    比如,图中的卷积神经网络,我们有时候会固定特征提取的部分,也就是这一系列卷积层的参数,固定它们不进行训练,这是因为有的时候,新任务的数据量比较小,我们不足以去训练那么多的参数,同时,我们也认为前面的特征提取的部分,它们的参数是非常有共性的,所以可以固定这些参数,让这些参数不更新,而这个具体的操作在pytorch中有两种方法:①可以设置requires_grad = False,也就是说这些参数不需要计算梯度,即不会再进行更新;②设置学习率lr为0,即更新的步伐为0,所以也不会去更新。这就完成了参数的固定。
  • 2 Features Extractor较小学习率(params_group)
    将特征提取这部分设置较小的学习率,这时就需要用到params_group(在优化器那部分介绍过),优化器可以对不同的参数组设置不同的超参数,这里就可以对不同的参数设置不同的学习率,让特征提取部分的学习率较小,而全连接层部分的学习率较大,这就实现了不同的参数设置不同的学习率。

【举例】
如何在pytorch中实现模型的finetune呢?下面的例子采用Resnet-18进行Finetune,用一个在image net训练好的Resnet-18,然后应用到新任务中,我们的目标任务(即新任务)是一个蚂蚁蜜蜂二分类任务。
蚂蚁蜜蜂二分类数据:

  • 训练集:各120~张
  • 验证集:各70~张
    可以看出这是一个数据量非常小的任务,将用一个Resnet-18进行Finetune。
    在这里插入图片描述
    查看Resnet-18的模型结构:
    在这里插入图片描述
    分析一下模型结构,然后要观察要在哪里改动。
    一般认为子模块‘conv1’、‘bn1’、‘relu’、‘maxpool’对图像进行初步的特征提取;‘layer1’、‘layer2’、‘layer3’、‘layer4’进行一系列的特征提取;‘avgpool’是对特征图进行一个池化操作;此处的‘fc’层是一个有1000个神经元的全连接层,1000类的分类任务。而新任务是二分类任务,所以需要将最后的fc层修改为一个只有两个神经元的全连接层。
    在这里插入图片描述
    采用model finetune与不采用model finetune,它们之间的训练情况
    不采用model finetune,即随机初始化一个Resnet-18,然后进行训练的情况:

    在这里插入图片描述
    代码实现如何进行Model Finetue?
#step 2/5 模型# 1/3 构建模型
resnet18_ft= models.resnet18()# 2/3 加载参数
path_pretrained_model= os.path.join(BASEDIR,"..","..","data/resnet18-5c106cde.pth")
state_dict_load= torch.load(path_pretrained_model)
resnet18_ft.load_state_dict(state_dict_load)# 法1:冻结卷积层for paramin resnet18_ft.parameters():
	param.requires_grad=Falseprint("conv1.weights[0, 0, ...]".format(resnet18_ft.conv1.weight[0,0,...]))# 3/3 替换fc层(以适应新任务)
num_ftrs= resnet18_ft.fc.in_features
resnet18_ft.fc= nn.Linear(num_ftrs, classes)# step 4/5 优化器#优化器中可以管理不同的参数组,为不同的参数组设置不同的超参数# 法2:conv 小学习率
flag=0# flag = 1if flag:#列表fc_params_id中的每一个元素对应fc层中的参数的地址
	fc_params_id=list(map(id, resnet18_ft.fc.parameters()))#返回的是parameters的内存地址# 将resnet18_ft中所有的参数过滤掉fc层,过滤条件就是采用内存地址,得到前面卷积层的参数
	base_params=filter(lambda p:id(p)notin fc_params_id, resnet18_ft.parameters())# 优化器设置不同的参数组
	optimizer= optim.SGD([{'params': base_params,'lr': LR*0.1},#0{'params': resnet18_ft.fc.parameters(),'lr': LR}], momentum=0.9)else:
	optimizer= optim.SGD(resnet18_ft.parameters(), lr= LR, momentum=0.9)#选择优化器

scheduler= torch.optim.lr_scheduler.StepLR(optimizer, step_size=lr_decay_step, gamma=0.1)#设置学习率

下图为使用Model Finetune的情况:
在这里插入图片描述

  • 作者:尧景
  • 原文链接:https://blog.csdn.net/Ying_M/article/details/117932055
    更新时间:2022-10-25 13:09:30