4.2 PyTorch自动反向传播

Posted 王小小小草

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了4.2 PyTorch自动反向传播相关的知识,希望对你有一定的参考价值。

欢迎订阅本专栏:《PyTorch深度学习实践》
订阅地址:https://blog.csdn.net/sinat_33761963/category_9720080.html

  • 第二章:认识Tensor的类型、创建、存储、api等,打好Tensor的基础,是进行PyTorch深度学习实践的重中之重的基础。
  • 第三章:学习PyTorch如何读入各种外部数据
  • 第四章:利用PyTorch从头到尾创建、训练、评估一个模型,理解与熟悉PyTorch实现模型的每个步骤,用到的模块与方法。
  • 第五章:学习如何利用PyTorch提供的3种方法去创建各种模型结构。
  • 第六章:利用PyTorch实现简单与经典的模型全过程:简单二分类、手写字体识别、词向量的实现、自编码器实现。
  • 第七章利用PyTorch实现复杂模型:翻译机(nlp领域)、生成对抗网络(GAN)、强化学习(RL)、风格迁移(cv领域)。
  • 第八章:PyTorch的其他高级用法:模型在不同框架之间的迁移、可视化、多个GPU并行计算。

4.1节中介绍了全手工进行模型构建与训练的过程,因为一元线性回归模型只涉及2个参数w,b,所以写起来倒也不费劲,但真实业务场景中自然会复杂很多,可能会涉及百万的参数量,因此手工求每个参数的梯度简直难以想象。

不用担心,pytorch提供了自动反向计算的机制,一行代码搞定你的烦恼。

还是按照原来的步骤,先进行前向计算:

import torch

# 构建模型函数
def model(x, w, b):
    return w*x + b

# 构建损失函数
def loss_fn(y_p, y):
    squared_diffs = (y_p - y) ** 2
    return squared_diffs.mean()

# x与y
x = [0.5, 14.0, 15.0, 28.0, 11.0, 8.0, 3.0, -4.0, 6.0, 13.0, 21.0]
y = [35.7, 55.9, 58.2, 81.9, 56.3, 48.9, 33.9, 21.8, 48.4, 60.4, 68.4]
x = torch.tensor(x)
y = torch.tensor(y)
x = 0.1 * x

# 初始化参数
params = torch.tensor([1.0, 0.0], requires_grad=True)  # 注意这里

# 前向计算
y_p = model(x, *params)
loss = loss_fn(y_p, y)

注意,在创建初始化参数的时候,requires_grad=True表示告诉pytorch需要跟踪在params上的所有操作与tensor,并记录下param tensor的梯度。通常情况下, 所有的Pytorch tensor都回有一个属性.grad, 默认是None值,如下:

print(params.grad)
None

现在,我们来对params计算损失的梯度:使用pytorch提供的自动反向计算功能来计算params的梯度:

loss = loss_fn(model(x, *params), y)
loss.backward()
print(params.grad)
tensor([-989.5273,  -82.6000])

上面代码,首先对loss调用了.backward()方法,会在loss上对叶子节点上的变量(params)进行求导并累加,也就是说被设置了requires_grad=True的params张量上的grad属性被赋值了,再打印params.grad就会有如上结果,即梯度。

要注意的是调用.backward()是将本次梯度“累加”导节点原有的梯度上,而不是“替换”,若进行下一次迭代时,梯度需要重新置零才行。

if params.grad is not None:
    params.grad.zero_()

好啦,上面讲述了一次迭代计算梯度的过程,若有多次迭代,我们整合如下:

def training_loop(n_epochs, learning_rate, params, x, y):
    for epoch in range(1, n_epochs+1):
        if params.grad is not None:
            params.grad.zero_()
        y_p = model(x, *params)
        loss = loss_fn(y_p, y)
        loss.backward()
        
        params = (params - learning_rate * params.grad).detach().requires_grad_()  # 注意这里,需要设置心params的requires_grad属性
        
        if epoch % 100 == 0:
            print('Epoch %d, Loss %f' % (epoch, float(loss)))
        
    return params

training_loop(
    n_epochs = 1000,
    learning_rate = 1e-2,
    params = torch.tensor([1.0, 0.0], requires_grad=True),
    x = x,
    y = y
)
Epoch 100, Loss 39.538250
Epoch 200, Loss 18.696901
Epoch 300, Loss 12.456582
Epoch 400, Loss 10.581183
Epoch 500, Loss 10.017575
Epoch 600, Loss 9.848182
Epoch 700, Loss 9.797276
Epoch 800, Loss 9.781981
Epoch 900, Loss 9.777378
Epoch 1000, Loss 9.776002





tensor([17.9473, 32.9443], requires_grad=True)

总结:

  • 设置要记录梯度的变量属性 requires_grad=True
  • .backward()进行反向计算
  • .grad 获取梯度
  • 梯度的计算时累加的,需要及时置零:.grad.zero_()

以上是关于4.2 PyTorch自动反向传播的主要内容,如果未能解决你的问题,请参考以下文章

PyTorch自动求导:反向传播的一切

pytorch前向传播和反向传播

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

Pytorch Note13 反向传播算法

pytorch 如何通过 argmax 反向传播?

如何使用 Pytorch 中的截断反向传播(闪电)在很长的序列上运行 LSTM?