深度学习基本功2:网络训练小技巧之使用预训练权重冻结训练和断点恢复(代码片段)

Convolution@ Convolution@     2023-03-01     188

关键词:

本篇博客将介绍神经网络训练过程中的三个必备技能:使用预训练权重、冻结训练和断点恢复,巧妙运用这三个技巧可以很有效地提高网络的训练效率和效果。


一、引言

If I have seen further, it is by standing on the shoulders of giants.

迁移学习在计算机视觉领域中是一种很流行的方法,因为它可以建立精确的模型,耗时更短。利用迁移学习,不是从零开始学习,而是从之前解决各种问题时学到的模式开始。这样,我们就可以利用以前的学习成果,避免从零开始。

二、使用预训练权重

在计算机视觉领域中,迁移学习通常是通过使用预训练模型来表示的。预训练模型是在大型基准数据集上训练的模型,用于解决相似的问题。由于训练这种模型的计算成本较高,因此,导入已发布的成果并使用相应的模型是比较常见的做法。例如,在目标检测任务中,首先要利用主干神经网络进行特征提取,这里使用的backbone一般就是VGG、ResNet等神经网络,因此在训练一个目标检测模型时,可以使用这些神经网络的预训练权重来将backbone的参数初始化,这样在一开始就能提取到比较有效的特征。

可能大家会有疑问,预训练权重是针对他们数据集训练得到的,如果是训练自己的数据集还能用吗?预训练权重对于不同的数据集是通用的,因为特征是通用的。一般来讲,从0开始训练效果会很差,因为权值太过随机,特征提取效果不明显。对于目标检测模型来说,一般不从0开始训练,至少会使用主干部分的权值,虽然有些论文提到了可以不用预训练,但这主要是因为他们的数据集比较大而且他们的调参能力很强。如果从0开始训练,网络在前几个epoch的Loss可能会非常大,并且多次训练得到的训练结果可能相差很大,因为权重初始化太过随机。

PyTorch提供了state_dict()和load_state_dict()两个方法用来保存和加载模型参数,前者将模型参数保存为字典形式,后者将字典形式的模型参数载入到模型当中。下面是使用预训练权重(加载预训练模型)的代码,其中model_path就是预训练权重文件的路径:

# 第一步:读取当前模型参数
model_dict = model.state_dict()
# 第二步:读取预训练模型
pretrained_dict = torch.load(model_path, map_location = device)
pretrained_dict = k: v for k, v in pretrained_dict.items() if np.shape(model_dict[k]) == np.shape(v)
# 第三步:使用预训练的模型更新当前模型参数
model_dict.update(pretrained_dict)
# 第四步:加载模型参数
model.load_state_dict(model_dict)

但是,使用load_state_dict()加载模型参数时,要求保存的模型参数键值类型和模型完全一致,一旦我们对模型结构做了些许修改,就会出现类似unexpected key module.xxx.weight问题。比如在目标检测模型中,如果修改了主干特征提取网络,只要不是直接替换为现有的其它神经网络,基本上预训练权重是不能用的,要么就自己判断权值里卷积核的shape然后去匹配,要么就只能利用这个主干网络在诸如ImageNet这样的数据集上训练一个自己的预训练模型;如果修改的是后面的neck或者是head的话,前面的backbone的预训练权重还是可以用的。下面是权值匹配的示例代码,把不匹配的直接pass了:

model_dict = model.state_dict()
pretrained_dict = torch.load(model_path, map_location=device)
temp = 
for k, v in pretrained_dict.items():
    try:    
        if np.shape(model_dict[k]) == np.shape(v):
            temp[k]=v
    except:
        pass
model_dict.update(temp)
model.load_state_dict(model_dict)

三、冻结训练

冻结训练其实也是迁移学习的思想,在目标检测任务中用得十分广泛。因为目标检测模型里,主干特征提取部分所提取到的特征是通用的,把backbone冻结起来训练可以加快训练效率,也可以防止权值被破坏。在冻结阶段,模型的主干被冻结了,特征提取网络不发生改变,占用的显存较小,仅对网络进行微调。在解冻阶段,模型的主干不被冻结了,特征提取网络会发生改变,占用的显存较大,网络所有的参数都会发生改变。举个例子,如果在解冻阶段设置batch_size为4,那么在冻结阶段有可能可以把batch_size设置到8。下面是进行冻结训练的示例代码,假设前50个epoch冻结,后50个epoch解冻:

# 冻结阶段训练参数,learning_rate和batch_size可以设置大一点
Init_Epoch          = 0
Freeze_Epoch        = 50
Freeze_batch_size   = 8
Freeze_lr           = 1e-3
# 解冻阶段训练参数,learning_rate和batch_size设置小一点
UnFreeze_Epoch      = 100
Unfreeze_batch_size = 4
Unfreeze_lr         = 1e-4
# 可以加一个变量控制是否进行冻结训练
Freeze_Train        = True
# 冻结一部分进行训练
batch_size  = Freeze_batch_size
lr          = Freeze_lr
start_epoch = Init_Epoch
end_epoch   = Freeze_Epoch
if Freeze_Train:
	for param in model.backbone.parameters():
	param.requires_grad = False
# 解冻后训练
batch_size  = Unfreeze_batch_size
lr          = Unfreeze_lr
start_epoch = Freeze_Epoch
end_epoch   = UnFreeze_Epoch
if Freeze_Train:
	for param in model.backbone.parameters():
	param.requires_grad = True

如果不进行冻结训练,一定要注意参数设置,注意上述代码中冻结阶段和解冻阶段的learning_rate和batch_size是不一样的,另外起始epoch和结束epoch也要重新调整一下。如果是从0开始训练模型(不使用预训练权重),那么一定不能进行冻结训练

四、断点恢复

在上面冻结训练和解冻训练的代码里设置了不同的batch_size,前者是8后者是4,有可能冻结训练的时候显存是够用的,结果解冻后显存不足了,这个时候需要重新把解冻训练阶段的batch_size调得更小一点。但是网络才训练了冻结阶段的50个epoch,backbone参数还是用的预训练权重呢,网络效果肯定不够好。难道要前功尽弃重新开始训练?这时候就要使用断点恢复技术了。其实断点恢复的思想很简单,就是把网络初始设置的model_path改为出错前保存好的权值文件,然后调整一下起始epoch和终止epoch即可,比如在前面提到的这种情况里,在第51个epoch报了错,那么可以把model_path修改为第50个epoch训练结束后保存的权值文件,然后把起始epoch调整成50就可以了。

断点恢复的应用范围非常非常广。最常见的情况就是代码跑到一半因为某些原因中断了(比如电脑突然死机重启这种不可抗力因素),又不想从头重新跑,那么就可以利用断点恢复训练的方法,这样可以节省不少时间。再比如,一个非常常见的情况,假如一开始设置了100个epoch,结果模型训练结束时,Loss还呈现下降的趋势,也就是模型还没有收敛,这种现象有可能就是epoch设置小了,所以可以把第100个epoch训练得到的权值文件当做初始权值文件再训练几个epoch看看,避免重新设置epoch从头训练。

当然,想要执行断点恢复首先需要把每个epoch得到的权值文件保存起来,这样才能修改model_path重新加载。断点恢复和常规的模型保存加载的区别其实就是epoch也要修改一下而已。保存权重可以用以下方法:

torch.save(model.state_dict(), "你要保存到的路径")

五、预训练和微调

最后再来总结一下预训练和微调,这是两个非常重要的概念,其实也很好理解。举个栗子是最能直观理解的。

假如我们现在要搭建一个网络模型来完成一个图像分类的任务,首先我们需要把网络的参数进行初始化,然后在训练网络的过程中不断对参数进行调整,直到网络的损失越来越小。在训练过程中,一开始初始化的参数会不断变化,如果结果已经满意了,那我们就可以把训练好的模型参数保存下来,以便训练好的模型可以在下次执行类似任务的时候获得比较好的效果。这个过程就是预训练(Pre-Training)

假如在完成上面的模型训练后,我们又接到另一个类似的图像分类任务,这时我们就可以直接使用之前保存下来的模型参数作为这一次任务的初始化参数,然后在训练过程中依据结果不断进行修改,这个过程就是微调(Fine-Tuning)

我们使用的神经网络越深,就需要越多的样本来进行训练,否则就很容易出现过拟合现象。比如我们想训练一个识别猫的模型,但是自己标注数据精力有限只标了100张,这时就可以考虑ImageNet数据集,可以在ImageNet上训练一个模型,然后使用该模型作为类似任务的初始化或特征提取器,这样既节省了时间和计算资源,又能很快地达到较好的效果。当然,采用预训练+微调也不是绝对有效的,上面识别猫的例子可以这样做是因为ImageNet里有猫的图像,所以可以认为是一个类似的数据集,如果是识别癌细胞的话,效果可能就不是那么好了。关于预训练和微调是有很多策略的,经验也很重要。

深度学习基本功1:网络训练小技巧之理解batchsizeiterations和epochs

训练网络之前有很多参数要设置,不了解各个参数的含义就没法合理地设置参数值,训练效果也会因此大受影响。本篇博客记录一下网络训练里的BatchSize、Iterations和Epochs怎么理解,也方便后续理解模型微调和冻结训... 查看详情

深度学习fine-tune技巧总结

深度学习中需要大量的数据和计算资源(乞丐版都需要12G显存的GPU--)且需花费大量时间来训练模型,但在实际中难以满足这些需求,而使用迁移学习则能有效降低数据量、计算量和计算时间,并能定制在新场景的业务需求,可... 查看详情

深度解析预训练权重的本质和作用:你真的了解它们吗?(代码片段)

文章目录🌟写在前面🌟预训练权重的本质是什么?🌟为什么要使用预训练权重?🌟预训练权重会影响模型的性能吗?🌟改进自定义模型是否需要使用预训练权重?改变了网络结构后,预... 查看详情

深度学习训练技巧---权重初始化

...的随机化方法了。然而这种偷懒的初始化方法非常不适合深度学习,因为这种初始化方法没有打破神经元之间的对称性,将导致收敛速度很慢甚至训练失败。常量初始化(constant)    把权值或者偏置初始化为一个常... 查看详情

为啥预训练的深度学习模型的性能会下降?

】为啥预训练的深度学习模型的性能会下降?【英文标题】:Whyisthereadecreaseintheperformanceofpre-trainedDeepLearningmodels?为什么预训练的深度学习模型的性能会下降?【发布时间】:2017-10-0223:13:28【问题描述】:使用来自Keras的模型和权... 查看详情

从头开始训练 Resnet 深度神经网络

】从头开始训练Resnet深度神经网络【英文标题】:TrainingResnetdeepneuralnetworkfromscratch【发布时间】:2018-06-1114:38:45【问题描述】:我需要了解一些关于深度神经网络的知识。对于“ResNet”非常深的神经网络,我们可以使用迁移学习... 查看详情

《python深度学习》第五章-3(预训练)读书笔记(代码片段)

5.3 使用预训练的卷积神经网络预训练网络(pretrainednetwork):是一个之前已在大型数据集(通常是大规模图像分类任务)上训练好、保存好的网络\\colorred训练好、保存好的网络训练好、保存好的网络。预训... 查看详情

dl:深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码(代码片段)

DL:深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码目录深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码深度学习模型优化之模型训练技巧总结之适时自动调整学习率实现代码defsc... 查看详情

pytorch训练深度学习小技巧收集(代码片段)

1、对不同的网络层配置不同的学习率importtorchoptimizer=torch.optim.Adam([dict(params=model.conv1.parameters(),weight_decay=5e-4),dict(params=model.conv2.parameters(),weight_decay=0)],lr=args.lr)#Onlyperformweight-decayonfirstconvolution. 查看详情

六pytorch进阶训练技巧(代码片段)

...修改超参数7.4.总结参考资料1.自定义损失函数损失函数是深度学习过程中需要定义的 查看详情

为啥我需要在迁移学习中预训练权重

】为啥我需要在迁移学习中预训练权重【英文标题】:WhyIneedpre-trainedweightintransferlearning为什么我需要在迁移学习中预训练权重【发布时间】:2019-06-1518:39:02【问题描述】:我正在学习使用一些预训练模型(vgg16、vgg19、...)的迁... 查看详情

深度学习目标检测---使用yolov5训练自己的数据集模型(windows系统)(代码片段)

...v5代码1.1yolov5网络project克隆1.2项目代码结构的整体介绍1.3深度学习环境的配置和安装yolov5所需要的库2、数据集和预训练权重的准备2.1利用labelimg对数据进行标注和划分2.2下载预训练权重 3、训练自己的模型3.1修改一些文件配置3.2... 查看详情

使用keras训练神经网络备忘录

使用Keras训练神经网络备忘录小书匠 深度学习 文章太长,放个目录:1.优化函数的选择2.损失函数的选择2.2常用的损失函数2.2自定义函数2.1实践2.2将损失函数自定义为网络层3.模型的保存3.1同时保持结构和权重3.2模型结构的... 查看详情

[深度学习][pytorch]pytorch实现一个简单得线性回归模型并训练

一个典型的神经网络的训练过程如下:定义具有学习参数(或权重)的神经网络迭代输入数据集根据神经网络对输入数据集进行运算计算损失(输出与真实结果的距离,损失越小说明模型越准确)将梯度反... 查看详情

如何在 Keras 中使用来自现有 CNN 模型的预训练权重进行迁移学习?

...如何在Keras中使用来自现有CNN模型的预训练权重进行迁移学习?【英文标题】:HowcanIusepre-trainedweightsfromanexistingCNNmodelfortransferlearninginKeras?【发布时间】:2018-07-1307:09:54【问题描述】:我正在通过Keras中的卷积神经网络(CNN)研究基... 查看详情

如何构建深度学习预训练模型?

keras提供多种预训练的深度学习模型,可供迁移学习使用。如果我想要根据自己的数据集与训练模型,如何让去构建一个预训练模型用于后续的迁移学习?参考技术A可以直接先找到自己需要的训练模型,一般来说都可以找到的 查看详情

手把手写深度学习(14):如何利用官方预训练模型做微调/迁移学习?(以resnet50提取图像特征为例)

前言:对于transformer、resnet等网络较深、很难训练的模型,使用官方提供的预训练模型,然后在自己的数据集/应用场景下微调,同样能取得非常棒的效果,也是最省时省力的方法。本文以训练一个resnet50网络来提取图像特征为例... 查看详情

深度学习与cv教程|神经网络训练技巧(下)

本文讲解训练神经网络的核心方法:优化方式(SGD、动量更新、Nesterov动量、Adagrad、RMSProp、Adam等),正则化(L2、Dropout),迁移学习,模型集成等【对应CS231nLecture7】本文讲解训练神经网络的核心方法:优化方式(SGD、动量更... 查看详情