文章目录
- autograd
- 1. 模型训练步骤摘要
- 2. 自动微分与计算图
- 3. 模型参数冻结
autograd
pytorch 中 tensor 特有的自动微分机制,tensor可以保留在参与运算时的微分(required_grad=True),并用于反向传播。
(图片来源网络,侵删)通常,在进行推理时,我们不需要保存微分,此时可以使用with torch.no_grad():来指定一个无微分的上下文管理器,在该环境中进行推理的步骤。
另外,在对已经预训练好的模型,可以对模型的参数进行冻结(required_grad=False),随后使用少量的线性层用于下游任务训练。
(图片来源网络,侵删)准。
1. 模型训练步骤摘要
这里我们回顾了在单次训练(不考虑迭代轮次等细节,只关注核心代码)时需要做的主要步骤,同时假设我们已经有预训练好的模型。
- 加载 resnet18 预训练模型,加载训练数据及标签
- 前向传播,计算损失
- 反向传播,计算梯度
- 依据梯度更新参数权重
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)
# 随后即可在这个冻结大部分参数的预训练模型上进行微调训练
还没有评论,来说两句吧...