pytorch学习笔记第三篇———自动梯度(torch.autograd)

Posted 非晚非晚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch学习笔记第三篇———自动梯度(torch.autograd)相关的知识,希望对你有一定的参考价值。


训练神经网路(NN, neural network)分为 正向传播和反向传播

  • 在正向传播中,NN 对正确的输出进行最佳猜测。 它通过其每个函数运行输入数据以进行猜测。
  • 在反向传播中,NN 根据其猜测中的误差调整其参数。 它通过从输出向后遍历,收集有关函数参数(梯度)的误差导数并使用梯度下降来优化参数来实现。

1. 一个神经网络例子

本例神经网络过程如下图

下面是一个完整的神经网络例子,包括前向传播与反向传播,具体过程见注释。

import torch, torchvision

model = torchvision.models.resnet18(pretrained=True) #从官网下载已经训练的过模型
data = torch.rand(1, 3, 64, 64) #随机生成4维数据:单个图像、3通道、高度和宽度
labels = torch.rand(1, 1000) #labels随机值

prediction = model(data) # 正向传播:对输入的数据进行预测

loss = (prediction - labels).sum() # 计算误差
loss.backward() # 反向传播:Autograd会为每个模型参数计算梯度,并将参数存储在.grad属性中

# 学习率:0.01,动量:0.9,。优化器中注册模型的所有参数
optim = torch.optim.SGD(model.parameters(), lr=1e-2, momentum=0.9)

# 启动梯度下降。优化器通过.grad中存储的梯度来调整每个参数
optim.step() #gradient descent

2. Autograd的微分

我们看看Autograd怎么自动求梯度。我们定义 Q = 3 a 2 − b 2 Q=3a^2-b^2 Q=3a2b2,我们知道
∂ Q ∂ a = 9 a 2 ; ∂ Q ∂ b = − 2 b 2 \\textstyle\\frac{\\partial Q}{\\partial a}=9a^2 ;\\textstyle\\frac{\\partial Q}{\\partial b}=-2b^2 aQ=9a2bQ=2b2
下面使用程序对它们进行验证。

import torch

# 定义两个张亮
a = torch.tensor([2., 3.], requires_grad=True)
b = torch.tensor([6., 4.], requires_grad=True)

# 定义Q张量
Q = 3*a**3 - b**2

# 反向传播计算梯度,Autograd将计算结果保存在.grad中
external_grad = torch.tensor([1., 1.]) # Q本身的梯度(Q对Q求导)
Q.backward(gradient=external_grad)

# 查看梯度结果
print(9*a**2 == a.grad)
print(-2*b == b.grad)

输出:

tensor([True, True])
tensor([True, True])

可以看到自动梯度与手动测算一致。

3. 计算图

从概念上讲,Autograd 在由函数对象组成的有向无环图(DAG,Directed acyclic graph)中记录数据(张量)和所有已执行的操作(以及由此产生的新张量)。 在此 DAG 中,叶子是输入张量,根是输出张量。 通过从根到叶跟踪此图,可以使用链式规则自动计算梯度。

在正向传播中,Autograd 同时执行两项操作:

  • 运行请求的操作以计算结果张量
  • 在 DAG 中维护操作的梯度函数。

当在 DAG 根目录上调用.backward()时,后退通道开始。 autograd然后:

  • 从每个.grad_fn计算梯度,
  • 将它们累积在各自的张量的.grad属性中,然后
  • 使用链式规则,一直传播到叶子张量。

下面是我们示例中 DAG 的直观表示。 在图中,箭头指向前进的方向。 节点代表正向传播中每个操作的反向函数。 蓝色的叶节点代表我们的叶张量a和b


注意:DAG 在 PyTorch 中是动态的。要注意的重要一点是,图是从头开始重新创建的; 在每个.backward()调用之后,Autograd 开始填充新图。 这正是允许您在模型中使用控制流语句的原因。 您可以根据需要在每次迭代中更改形状,大小和操作。

4. 从 DAG 中排除

torch.autograd跟踪所有将其requires_grad标志设置为True的张量的操作。 对于不需要梯度的张量,将此属性设置为False会将其从梯度计算 DAG 中排除。

注意:在运算中,即使只有一个输入张量具有requires_grad=True,操作的输出张量也将需要梯度。例如:

import torch

x = torch.rand(5, 5) # 默认不需要
y = torch.rand(5, 5) # 默认不需要
z = torch.rand((5, 5), requires_grad=True) # 设置为需要

a = x + y #x、y为不需要,所以a为不需要
print(f"Does `a` require gradients? : {a.requires_grad}")
b = x + z # z为需要,所以b为需要
print(f"Does `b` require gradients?: {b.requires_grad}")

输出:

Does `a` require gradients? : False
Does `b` require gradients?: True

在 NN 中,不计算梯度的参数通常称为冻结参数。 如果事先知道您不需要这些参数的梯度,则“冻结”模型的一部分很有用(通过减少自动梯度计算,这会带来一些表现优势)。

从 DAG 中排除很重要的另一个常见用例是调整预训练网络。如下例中,先冻结所有的模型,然后给model.fc赋予新的线性层,只需要计算它的权重与偏差

from torch import nn, optim

model = torchvision.models.resnet18(pretrained=True)

# Freeze all the parameters in the network
# 先冻结所有参数
for param in model.parameters():
    param.requires_grad = False

# 给model.fc赋予新的参数,计算梯度的唯一参数为model.fc,计算它的权重和偏差
model.fc = nn.Linear(512, 10)

# Optimize only the classifier
optimizer = optim.SGD(model.fc.parameters(), lr=1e-2, momentum=0.9)

以上是关于pytorch学习笔记第三篇———自动梯度(torch.autograd)的主要内容,如果未能解决你的问题,请参考以下文章

pytorch学习笔记第四篇——神经网络

Pytorch深度学习50篇·······第三篇:非监督学习

pytorch学习笔记:梯度下降

PyTorch入门学习:Autogard之自动求梯度

PyTorch进阶之路:使用logistic回归实现图像分类

linux学习笔记 第三篇