PyTorch基础知识讲解(三)自动微分与模型微调

马肤
这是懒羊羊

文章目录

  • autograd
    • 1. 模型训练步骤摘要
    • 2. 自动微分与计算图
    • 3. 模型参数冻结

      autograd

      pytorch 中 tensor 特有的自动微分机制,tensor可以保留在参与运算时的微分(required_grad=True),并用于反向传播。

      通常,在进行推理时,我们不需要保存微分,此时可以使用with torch.no_grad():来指定一个无微分的上下文管理器,在该环境中进行推理的步骤。

      另外,在对已经预训练好的模型,可以对模型的参数进行冻结(required_grad=False),随后使用少量的线性层用于下游任务训练。

      (图片来源网络,侵删)

      准。

      1. 模型训练步骤摘要

      这里我们回顾了在单次训练(不考虑迭代轮次等细节,只关注核心代码)时需要做的主要步骤,同时假设我们已经有预训练好的模型。

      1. 加载 resnet18 预训练模型,加载训练数据及标签
      2. 前向传播,计算损失
      3. 反向传播,计算梯度
      4. 依据梯度更新参数权重
      import torch, torchvision
      from torchvision.models import ResNet18_Weights
      # data: 输入, 表示1个 batch 中包含一个样本, 该样本包含3个通道(C), 大小为 64x64
      # labels: 输出, 表示1个输出, 表示输入属于这 1000 个类的概率
      torch.random.seed = 0
      model = torchvision.models.resnet18(weights=ResNet18_Weights.IMAGENET1K_V1)
      data = torch.rand((1, 3, 64, 64))
      labels = torch.rand(1, 1000)
      data.shape, labels.shape
      
      (torch.Size([1, 3, 64, 64]), torch.Size([1, 1000]))
      
      # 1. 通过模型的每一层来运行输入数据,进行预测。这就是前向传播
      y = model(data)
      y.shape
      
      torch.Size([1, 1000])
      
      # 2. 使用模型的预测和相应的标签来计算误差(损失)。下一步是通过网络反向传播这个误差。
      #    当我们在误差tensor上调用.backward()时,后向传播就开始了。然后,Autograd计算
      #    并将每个模型参数的梯度存储在参数的.grad属性中。
      loss = ((labels-y)**2).sum()
      loss.backward()
      loss
      
      tensor(1056.4602, grad_fn=)
      
      # 3. 加载一个优化器,例如SGD,设置超参数:学习率为0.01,动量为0.9。
      #    同时,在优化器中注册模型的所有参数。
      sgd = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)
      sgd
      
      SGD (
      Parameter Group 0
          dampening: 0
          foreach: None
          lr: 0.01
          maximize: False
          momentum: 0.9
          nesterov: False
          weight_decay: 0
      )
      
      # 4. 调用.step()来启动梯度下降。优化器通过存储在.grad中的梯度调整每个参数
      sgd.step()
      

      2. 自动微分与计算图

      # 默认情况下, 一个 tensor 的保留微分这一属性是 False
      a = torch.tensor([1., 2.])
      a.requires_grad
      
      False
      
      a = torch.tensor([1., 2.], requires_grad=True)
      b = torch.tensor([2.4, 3.6], requires_grad=True)
      Y = a*a + a*b + b*b
      Y, Y.requires_grad
      
      (tensor([ 9.1600, 24.1600], grad_fn=), True)
      
      # 计算Y关于 a, b的微分
      Y.sum().backward()
      a.grad == 2*a+b, b.grad== 2*b+a
      
      (tensor([True, True]), tensor([True, True]))
      

      从概念上讲,autograd在一个由Function对象组成的有向无环图(DAG)中保存了数据(tensor)和所有执行的操作(以及产生的新tensor)的记录。在这个DAG中,叶子是输入tensor,根部是输出tensor。通过追踪这个图从根到叶,你可以使用链式规则自动计算梯度。

      在一个前向传递中,autograd同时做两件事:

      运行请求的操作,计算出结果的tensor,以及

      在DAG中维护该操作的梯度函数。

      当在DAG根上调用.backward()时,后向传递开始了。

      计算每个.grad_fn的梯度。

      将它们累积到各自的tensor的 .grad 属性中,并且

      使用链规则,一直传播到叶子tensor。

      3. 模型参数冻结

      在NN中,不计算梯度的参数通常被称为冻结参数。如果你事先知道你不需要这些参数的梯度,那么 "冻结 "你的模型的一部分是很有用的(这通过减少自动梯度计算提供了一些性能上的好处)。

      从DAG中排除的另一个常见情况是对预训练的网络进行微调。

      在微调中,我们冻结了大部分模型,通常只修改分类器层以对新标签进行预测。

      # 仍然以 resnet18 为例, 如下冻结了模型所有参数
      for para in model.parameters():
          para.requires_grad = False
      
      # fc 是 resnet 的最后一个线性层
      model.fc
      
      Linear(in_features=512, out_features=1000, bias=True)
      
      # 简单的将其进行替换, 例如假设需要训练一个 10 类别的分类器
      from torch import nn
      model.fc = nn.Linear(512, 10)
      
      # 随后即可在这个冻结大部分参数的预训练模型上进行微调训练
      

文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复:表情:
评论列表 (暂无评论,0人围观)

还没有评论,来说两句吧...

目录[+]

取消
微信二维码
微信二维码
支付宝二维码