范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Pytorch人工智能AI大模型微调代码示例!

  随着深度学习的发展,在大模型的训练上都是在一些较大数据集上进行训练的,比如Imagenet-1k,Imagenet-11k,甚至是ImageNet-21k等。但我们在实际应用中,我们自己的数据集可能比较小,只有几千张照片,这时从头训练具有几千万参数的大型神经网络是不现实的,因为越大的模型对数据量的要求越高,过拟合无法避免。
  因为适用于ImageNet数据集的复杂模型,在一些小的数据集上可能会过拟合,同时因为数据量有限,最终训练得到的模型的精度也可能达不到实用要求。
  解决上述问题的方法:
  收集更多数据集,当然这对于研究成本会大大增加
  应用迁移学习(transfer learning):从源数据集中学到知识迁移到目标数据集上。
  1、模型微调(fine-tune)
  微调(fine-tune)通过使用在大数据上得到的预训练好的模型来初始化自己的模型权重,从而提升精度。这就要求预训练模型质量要有保证。微调通常速度更快、精度更高。当然,自己训练好的模型也可以当做预训练模型,然后再在自己的数据集上进行训练,来使模型适用于自己的场景、自己的任务。
  先引入迁移学习(Transfer Learning)的概念:
  当我们训练好了一个模型之后,如果想应用到其他任务中,可以在这个模型的基础上进行训练,来作微调网络。这也是迁移学习的概念,可以节省训练的资源以及训练的时间。
  迁移学习的一大应用场景就是模型微调,简单的来说就是把在别人训练好的基础上,换成自己的数据集继续训练,来调整参数。Pytorch中提供很多预训练模型,学习如何进行模型微调,可以大大提升自己任务的质量和速度。
  假设我们要识别的图片类别是椅子,尽管ImageNet数据集中的大多数图像与椅子无关,但在ImageNet数据集上训练的模型可能会提取更通用的图像特征,这有助于识别边缘、纹理、形状和对象组合。 这些类似的特征对于识别椅子也可能同样有效。
  2.1、为什么要微调
  因为预训练模型用了大量数据做训练,已经具备了提取浅层基础特征和深层抽象特征的能力。
  对于图片来说,我们CNN的前几层学习到的都是低级的特征,比如,点、线、面,这些低级的特征对于任何图片来说都是可以抽象出来的,所以我们将他作为通用数据,只微调这些低级特征组合起来的高级特征即可,例如,这些点、线、面,组成的是园还是椭圆,还是正方形,这些代表的含义是我们需要后面训练出来的。
  如果我们自己的数据不够多,泛化性不够强,那么可能存在模型不收敛,准确率低,模型泛化能力差,过拟合等问题,所以这时就需要使用预训练模型来做微调了。注意的是,进行微调时,应该使用较小的学习率。因为预训练模型的权重相对于随机初始化的权重来说已经很不错了,所以不希望使用太大的学习率来破坏原本的权重。通常用于微调的初始学习率会比从头开始训练的学习率小10倍。
  总结:对于不同的层可以设置不同的学习率,一般情况下建议,对于使用的原始数据做初始化的层设置的学习率要小于(一般可设置小于10倍)初始化的学习率,这样保证对于已经初始化的数据不会扭曲的过快,而使用初始化学习率的新层可以快速的收敛。
  2.2、需要微调的情况
  其中微调的方法又要根据自身数据集和预训练模型数据集的相似程度,以及自己数据集的大小来抉择。
  不同情况下的微调:
  数据少,数据类似程度高:可以只修改最后几层或者最后一层进行微调。
  数据少,数据类似程度低:冻结预训练模型的前几层,训练剩余的层。因为数据集之间的相似度较低,所以根据自身的数据集对较高层进行重新训练会比较有效。
  数据多,数据类似程度高:这是最理想的情况。使用预训练的权重来初始化模型,然后重新训练整个模型。这也是最简单的微调方式,因为不涉及修改、冻结模型的层。
  数据多,数据类似程度低:微调的效果估计不好,可以考虑直接重新训练整个模型。如果你用的预训练模型的数据集是ImageNet,而你要做的是文字识别,那么预训练模型自然不会起到太大作用,因为它们的场景特征相差太大了。
  注意:
  如果自己的模型中有fc层,则新数据集的大小一定要与原始数据集相同,比如CNN中输入的图片大小一定要相同,才不会报错。
  如果包含fc层但是数据集大小不同的话,可以在最后的fc层之前添加卷积或者pool层,使得最后的输出与fc层一致,但这样会导致准确度大幅下降,所以不建议这样做
  2.3、 模型微调的流程
  微调的步骤有很多,看你自身数据和计算资源的情况而定。虽然各有不同,但是总体的流程大同小异。
  步骤示例1:
  1、在源数据集(如ImageNet数据集)上预训练一个神经网络模型,即源模型。
  2、创建一个新的神经网络模型,即目标模型。它复制了源模型上除了输出层外的所有模型设计及其参数。
  我们假设这些模型参数包含了源数据集上学习到的知识,且这些知识同样适用于目标数据集。
  我们还假设源模型的输出层跟源数据集的标签紧密相关,因此在目标模型中不予采用。
  3、为目标模型添加一个输出大小为目标数据集类别个数的输出层,并随机初始化该层的模型参数。
  4、在目标数据集(如椅子数据集)上训练目标模型。可以从头训练输出层,而其余层的参数都是基于源模型的参数微调得到的。
  步骤示例2:
  在已经训练好的网络上进行修改;
  冻结网络的原来那一部分;
  训练新添加的部分;
  解冻原来网络的部分层;
  联合训练解冻的层和新添加的部分。
  2.4、参数冻结---指定训练模型的部分层
  我们所提到的冻结模型、冻结部分层,其实归根结底都是对参数进行冻结。冻结训练可以加快训练速度。在这里,有两种方式:全程冻结与非全程冻结。
  非全程冻结比全程冻结多了一个步骤:解冻,因此这里就讲解非全程冻结。看完非全程冻结之后,就明白全程冻结是如何进行的了。
  非全程冻结训练分为两个阶段,分别是冻结阶段和解冻阶段。当处于冻结阶段时,被冻结的参数就不会被更新,在这个阶段,可以看做是全程冻结;而处于解冻阶段时,就和普通的训练一样了,所有参数都会被更新。
  当进行冻结训练时,占用的显存较小,因为仅对部分网络进行微调。如果计算资源不够,也可以通过冻结训练的方式来减少训练时资源的占用。
  因为一般需要保留Features Extractor的结构和参数,提出了两种训练方法:
  固定预训练的参数:requires_grad = False 或者 lr = 0,即不更新参数;
  将Features Extractor部分设置很小的学习率,这里用到参数组(params_group)的概念,分组设置优化器的参数。
  2.5、参数冻结的方式
  我们经常提到的模型,就是一个可遍历的字典。既然是字典,又是可遍历的,那么就有两种方式进行索引:一是通过数字,二是通过名字。
  其实使用冻结很简单,没有太高深的魔法,只用设置模型的参数requires_grad为False就可以了。
  2.5.1、冻结方式1
  在默认情况下,参数的属性​​.requires_grad = True​​​,如果我们从头开始训练或微调不需要注意这里。但如果我们正在提取特征并且只想为新初始化的层计算梯度,其他参数不进行改变。那我们就需要通过设置​​requires_grad = False​​来冻结部分层。在PyTorch官方中提供了这样一个例程。
  def set_parameter_requires_grad(model, feature_extracting):
  if feature_extracting:
  for param in model.parameters():
  param.requires_grad = False
  在下面我们使用​​resnet18​​为例的将1000类改为4类,但是仅改变最后一层的模型参数,不改变特征提取的模型参数;
  注意我们先冻结模型参数的梯度;
  再对模型输出部分的全连接层进行修改,这样修改后的全连接层的参数就是可计算梯度的。
  在训练过程中,model仍会进行梯度回传,但是参数更新则只会发生在fc层。通过设定参数的​​requires_grad​​属性,我们完成了指定训练模型的特定层的目标,这对实现模型微调非常重要。
  import torchvision.models as models
  # 冻结参数的梯度
  feature_extract = True
  model = models.resnet18(pretrained=True)
  set_parameter_requires_grad(model, feature_extract)
  # 修改模型, 输出通道4, 此时,fc层就被随机初始化了,但是其他层依然保存着预训练得到的参数。
  model.fc = nn.Linear(in_features=512, out_features=4, bias=True)
  我们直接拿​​torchvision.models.resnet50 ​​模型微调,首先冻结预训练模型中的所有参数,然后替换掉最后两层的网络(替换2层池化层,还有fc层改为dropout,正则,线性,激活等部分),最后返回模型:
  # 8 更改池化层
  class AdaptiveConcatPool2d(nn.Module):
  def __init__(self, size=None):
  super().__init__()
  size = size or (1, 1) # 池化层的卷积核大小,默认值为(1,1)
  self.pool_one = nn.AdaptiveAvgPool2d(size) # 池化层1
  self.pool_two = nn.AdaptiveAvgPool2d(size) # 池化层2
  def forward(self, x):
  return torch.cat([self.pool_one(x), self.pool_two(x), 1]) # 连接两个池化层
  # 7 迁移学习:拿到一个成熟的模型,进行模型微调
  def get_model():
  model_pre = models.resnet50(pretrained=True) # 获取预训练模型
  # 冻结预训练模型中所有的参数
  for param in model_pre.parameters():
  param.requires_grad = False
  # 微调模型:替换ResNet最后的两层网络,返回一个新的模型
  model_pre.avgpool = AdaptiveConcatPool2d() # 池化层替换
  model_pre.fc = nn.Sequential(
  nn.Flatten(), # 所有维度拉平
  nn.BatchNorm1d(4096), # 256 x 6 x 6 ——> 4096
  nn.Dropout(0.5), # 丢掉一些神经元
  nn.Linear(4096, 512), # 线性层的处理
  nn.ReLU(), # 激活层
  nn.BatchNorm1d(512), # 正则化处理
  nn.Linear(512,2),
  nn.LogSoftmax(dim=1), # 损失函数
  )
  return
  2.5.2、冻结方式2
  因为ImageNet有1000个类别,所以提供的ImageNet预训练模型也是1000分类。如果我需要训练一个10分类模型,理论上来说只需要修改最后一层的全连接层即可。
  如果前面的参数不冻结就表示所有特征提取的层会使用预训练模型的参数来进行参数初始化,而最后一层的参数还是保持某种初始化的方式来进行初始化。
  在模型中,每一层的参数前面都有前缀,比如conv1、conv2、fc3、backbone等等,我们可以通过这个前缀来进行判断,也就是通过名字来判断,如:if "backbone" in param.name,最终选择需要冻结与不需要冻结的层。最后需要将训练的参数传入优化器进行配置。
  if freeze_layers:
  for name, param in model.named_parameters():
  # 除最后的全连接层外,其他权重全部冻结
  if "fc" not in name:
  param.requires_grad_(False)
  pg = [p for p in model.parameters() if p.requires_grad]
  optimizer = optim.SGD(pg, lr=0.01, momentum=0.9, weight_decay=4E-5)
  或者判断该参数位于模型的哪些模块层中,如param in model.backbone.parameters(),然后对于该模块层的全部参数进行批量设置,将requires_grad置为False。
  if Freeze_Train:
  for param in model.backbone.parameters():
  param.requires_grad = False
  2.5.2、冻结方式3
  通过数字来遍历模型中的层的参数,冻结所指定的若干个参数, 这种方式用的少
  count = 0
  for layer in model.children():
  count = count + 1
  if count < 10:
  for param in layer.parameters():
  param.requires_grad = False
  # 然后将需要训练的参数传入优化器,也就是过滤掉被冻结的参数。
  optimizer = torch.optim.Adam(filter(lambda p: p.requires_grad, model.parameters()), lr=LR)
  2.6、修改模型参数
  前面说道,冻结模型就是冻结参数,那么这里的修改模型参数更多的是修改模型参数的名称。
  值得一提的是,由于训练方式(单卡、多卡训练)、模型定义的方式不同,参数的名称也会有所区别,但是此时模型的结构是一样的,依旧可以加载预训练模型。不过却无法直接载入预训练模型的参数,因为名称不同,会出现KeyError的错误,所以载入前可能需要修改参数的名称。
  比如说,使用多卡训练时,保存的时候每个参数前面多会多出"module."这几个字符,那么当使用单卡载入时,可能就会报错了。
  通过以下方式,就可以使用"conv1"来替代"module.conv1"这个key的方式来将更新后的key和原来的value相匹配,再载入自己定义的模型中。
  model_dict = pretrained_model.state_dict()
  pretrained_dict={k: v for k, v in pretrained_dict.items() if k[7:] in model_dict}
  model_dict.update(pretrained_dict)
  2.7、修改模型结构
  import torch.nn as nn
  import torch
  class AlexNet(nn.Module):
  def __init__(self):
  super(AlexNet, self).__init__()
  self.features=nn.Sequential(
  nn.Conv2d(3, 64, kernel_size=11, stride=4, padding=2), # 使用卷积层,输入为3,输出为64,核大小为11,步长为4
  nn.ReLU(inplace=True), # 使用激活函数
  nn.MaxPool2d(kernel_size=3, stride=2), # 使用最大池化,这里的大小为3,步长为2
  nn.Conv2d(64, 192, kernel_size=5, padding=2), # 使用卷积层,输入为64,输出为192,核大小为5,步长为2
  nn.ReLU(inplace=True),# 使用激活函数
  nn.MaxPool2d(kernel_size=3, stride=2), # 使用最大池化,这里的大小为3,步长为2
  nn.Conv2d(192, 384, kernel_size=3, padding=1), # 使用卷积层,输入为192,输出为384,核大小为3,步长为1
  nn.ReLU(inplace=True),# 使用激活函数
  nn.Conv2d(384, 256, kernel_size=3, padding=1),# 使用卷积层,输入为384,输出为256,核大小为3,步长为1
  nn.ReLU(inplace=True),# 使用激活函数
  nn.Conv2d(256, 256, kernel_size=3, padding=1),# 使用卷积层,输入为256,输出为256,核大小为3,步长为1
  nn.ReLU(inplace=True),# 使用激活函数
  nn.MaxPool2d(kernel_size=3, stride=2), # 使用最大池化,这里的大小为3,步长为2
  )
  self.avgpool=nn.AdaptiveAvgPool2d((6, 6))
  self.classifier=nn.Sequential(
  nn.Dropout(),# 使用Dropout来减缓过拟合
  nn.Linear(256 * 6 * 6, 4096), # 全连接,输出为4096
  nn.ReLU(inplace=True),# 使用激活函数
  nn.Dropout(),# 使用Dropout来减缓过拟合
  nn.Linear(4096, 4096), # 维度不变,因为后面引入了激活函数,从而引入非线性
  nn.ReLU(inplace=True), # 使用激活函数
  nn.Linear(4096, 1000), #ImageNet默认为1000个类别,所以这里进行1000个类别分类
  )
  def forward(self, x):
  x=self.features(x)
  x=self.avgpool(x)
  x=torch.flatten(x, 1)
  x=self.classifier(x)
  return x
  def alexnet(num_classes, device, pretrained_weights=""):
  net=AlexNet() # 定义AlexNet
  if pretrained_weights: # 判断预训练模型路径是否为空,如果不为空则加载
  net.load_state_dict(torch.load(pretrained_weights,map_location=device))
  num_fc=net.classifier[6].in_features # 获取输入到全连接层的输入维度信息
  net.classifier[6]=torch.nn.Linear(in_features=num_fc, out_features=num_classes) # 根据数据集的类别数来指定最后输出的out_features数目
  return net
  在上述代码中,我是先将权重载入全部网络结构中。此时,模型的最后一层大小并不是我想要的,因此我获取了输入到最后一层全连接层之前的维度大小,然后根据数据集的类别数来指定最后输出的out_features数目,以此代替原来的全连接层。
  你也可以先定义好具有指定全连接大小的网络结构,然后除了最后一层全连接层之外,全部层都载入预训练模型;你也可以先将权重载入全部网络结构中,然后删掉最后一层全连接层,最后再加入一层指定大小的全连接层

头部农商行陆续下调存款利率,中长期存款挂牌利率大幅降低国有大行大部分股份行相继下调存款挂牌利率后,券中社记者注意到,上海农商行深圳农商行重庆农商行北京农商行等多家头部农商行已先后发布人民币存款挂牌利率调整公告,除活期储蓄存款利率全部下我为什么一直看好国内旅游市场众所周知,新冠疫情截止到即将到来的国庆,离满三年还差两个月的时期。旅游行业一直在被疫情影响受损的最前沿,也是影响最重的行业。很多人讲,旅游业与之影响的直接是旅游行业从业人员,我认为ColorOS13正式版来了!FindX5系列首发升级,实用性与设计感齐飞自从8月底的OPPO开发者大会开展以来,应该有不少OPPO用户都在期盼着ColorOS13。0Android13正式版的到来吧,毕竟无论是柔和简约的水生设计,还是极具实用性的智慧息中国集装箱航运市场疲软海运价格暴跌九成9月财经新势力受疫情等因素的影响,中国集装箱航运市场表现疲软,海运价格持续下跌,曾经一箱难求,如今却是一货难求。9月23日,上海出口集装箱运价指数(SCFI)跌至2072。04点,美媒全球债券市场73年来最糟来源环球时报环球时报报道记者倪浩全球债券市场正经历自1949年以来最为糟糕的一年。彭博社25日报道称,包括美联储在内的多国央行激进加息,导致全球债券市场持续下跌,债券泡沫破裂。美国王者荣耀看似陷阱,实则版本答案!专精三烧流刘邦预定T0边路大家好,我是秋豆。本期内容想和大家聊聊专精装刘邦。S29赛季鲁班七号专精装未上线,刘邦成为唯一的新专精装获得者。单看刘邦新赛季的数据,胜率大跌,巅峰赛胜率从53跌落到45,足足下降TODampamp39S2023春夏系列时装秀TODS2023春夏系列时装秀以灵感设计,呈现有关TodsItalianFlair的无限遐想。本季时装秀于HangarBicocca艺术中心举办,将意大利风格与极简主义风格相融合。击败三星苹果,联想摩托罗拉成墨西哥最大智能手机厂商IT之家9月26日消息,通常而言,三星是全球最大的智能手机制造商。然而,它并非在每个市场都领先于所有竞争对手。在某些市场,三星是遥遥领先的品牌。而在墨西哥等其他国家地区,三星还需要mate50逆风翻盘,华为赢了,除了苹果,还有谁是输家?谁也没想到,华为用一款4G手机,实现了逆风翻盘,让苹果瑟瑟发抖,竟然还传出富士康拆除iPhone14生产线。这个最新的mate50系列到底有什么魔力?让消费者在5G时代,争相抢购这柬埔寨洪森总理安全返回柬埔寨据柬媒消息,经过从9月21日至28日分别出席联合国大会第77届会议对古巴进行正式访问,并出席日本前首相安倍晋三国葬后,柬埔寨总理洪森于9月28日下午率高级代表团安全返回柬埔寨。洪森外交部中方要求美方对恶意网络攻击作出解释,但美方一直沉默来源北京日报客户端9月28日,外交部发言人汪文斌主持例行记者会。有记者提问,昨天,中国国家计算机病毒应急处理中心发布了西北工业大学遭美国国家安全局网络攻击事件调查报告(之二),详细
河南省唯一的飞地石槽沟村石槽沟村,隶属于河南省淅川县荆紫关镇,位于湖北省十堰市郧阳区白浪镇的腹心,为河南省唯一的省级飞地。石槽沟村四周被白浪镇的会沟东沟杨沟寺沟所包围,村民要离开本村,必经湖北地界。离荆紫野味十足石窝长城头条带你乐享河北河北文旅看图识景这么近那么美周末到河北长城是一个神奇的存在,两千年前战国时期各国分筑长城,抵御着他国兵戎相见和北方少数民族的入侵,巩固边境的平安。历朝历代都有修筑长克莱硬件升级!勇士万事俱备冲击第二个王朝,库里有望超越詹姆斯说到上赛季联盟最大的赢家,非金州勇士莫属,他们蛰伏两年,在盛行抱团的风气下杀出重围,以最快的速度从低谷回到巅峰,库里更是首次斩获FMVP,弥补了生涯唯一的缺憾。即便如此,每当外界提愿意叫一声岳父吗?瓜迪奥拉女儿成为泳装模特,身材苗条嘴唇性感曼城主帅瓜迪奥拉在国际足坛声名显赫,大家都专注于他在足球场上的战术革新,很少关注他的家庭和私生活。其实,瓜迪奥拉有一个长相出众的女儿,而且即将成为一名泳装模特。今年51岁的瓜迪奥拉训练必须戴护腿板用餐老队员先盛饭,上任20天李玮锋立规立行记者王伟报道9月18日是李玮锋接任广州城队主教练满20天的日子。在过去的这20天里,李玮锋为广州城队带来了哪些改变?时间拨回到20天前,8月29日下午的训练,是当时广州城队代理主教输球后幸运晋级,U19国青教练组疯狂庆祝遭球迷怒批邵佳一能出线已不容易北京时间9月19日凌晨2点,U20亚洲杯预选赛A组最后一轮展开较量,U19国青上01不敌实力强劲的沙特,不过仍以成绩最好的小组第二晋级亚洲杯正赛,比赛结束后,国青教练团队相拥庆祝的库娃久违上热搜!丈夫当众狂亲粉丝,生仨娃的俄罗斯美少女被同情安娜库尔尼科娃(AnnaKournikova),曾经是世界网坛上的最耀眼的明星之一,也是众多俄罗斯网球美少女娃中的佼佼者,库娃在球场内外的表现,都能让她占据热搜。据悉,在巅峰时期库西班牙胜法国,一路逆袭夺冠!上演哈登奇迹,火箭要给他机会啊永远不要低估一颗总冠军的心啊!欧锦赛今日凌晨落下帷幕,传统强队西班牙确实有些意外的,以8876轻取热门法国队,拿下欧锦赛冠军,登上欧洲之巅!没想到啊,加鲁巴竟然是这支火箭队内第一个保罗公开表达不满恐引发球队内讧!或被交易太阳队提前面临重建太阳队老板萨沃尔因为2021年的种族歧视问题被联盟调查,并且在近日联盟对萨沃尔做了禁止一年内不能参与球队事务和罚款1000万的处罚。处罚结果一出来,引起来联盟的热议,其中詹姆斯和保2022卡塔尔世界杯巡礼英格兰B组英格兰1。2018年世界杯2018年俄罗斯世界杯,英格兰进入4强,最终取得第4。时任主帅就是南门索斯盖特。小组赛首轮英格兰21补时绝杀突尼斯。第2轮61血洗巴拿马凯恩上演帽子戏女排全锦赛上海3比2险胜天津,卫冕冠军首秀告捷,云南爆冷胜辽宁北京时间9月19日消息,2022年全国女子排球锦标赛在福建漳州进入到第二天。小组循环赛,卫冕冠军上海女排经过五局苦战以3比2击败天津女排,首秀取得胜利云南女排以3比1爆冷战胜辽宁女