PyTorch自动微分知识点讲解

知识导图

PyTorch自动微分
├── 基础认知
│   ├── 自动微分的核心概念
│   └── autograd模块的作用
├── 梯度计算
│   ├── 梯度计算的规则
│   └── backward与grad的使用
└── 实战案例
    ├── 单参数的更新
    └── 多参数的更新

核心名词解释

  • 自动微分 (AD):PyTorch 的核心功能,用来自动计算函数的梯度,不需要手动求导,简化了神经网络的训练。

  • autograd:PyTorch 的自动微分模块,实现了自动梯度计算,支持反向传播。

  • 反向传播:神经网络的训练算法,通过损失函数的梯度来更新模型的参数,autograd 模块实现了这个过程的自动化。

  • requires_grad:张量的属性,用来标记是否需要对这个张量计算梯度,开启自动微分。

  • backward():反向传播的方法,用来自动计算梯度,调用后会自动计算所有需要梯度的张量的梯度。

  • grad:张量的属性,用来存储计算得到的梯度值,每次调用 backward 后会累加。

  • 梯度清零:因为 grad 会累加,所以每次训练前需要把梯度清零,避免上一次的梯度影响本次的计算。


一、自动微分基础认知

本章节学习目标:

  • 掌握自动微分模块的使用

1.1 什么是自动微分

自动微分(Automatic Differentiation, AD) 是 PyTorch 的核心功能之一,它通过 autograd 模块实现,无需手动计算梯度,极大简化了神经网络训练等依赖梯度优化的任务。

在这里插入图片描述

1.2 autograd 模块的作用

训练神经网络时,最常用的算法就是反向传播。在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整。

PyTorch 内置的名为 torch\.autograd 的微分模块,它支持自动梯度计算,我们可以通过 backward 方法、grad 属性来实现梯度的计算和访问。

在这里插入图片描述


二、梯度的基本计算

梯度计算的规则

PyTorch 的梯度计算有以下规则:

  1. PyTorch 不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导

  2. 如果 x 是张量,那么 loss 必须是标量(一个值)才可以进行求导

  3. 计算梯度:调用 loss\.backward\(\),其中 loss 是一个标量张量

  4. 获取 x 点的梯度值:x\.grad,注意它会累加上一次的梯度值,所以经常要提前梯度清零


三、自动微分实战案例

3.1 单参数的更新

我们先来看单参数的自动微分更新,只更新权重 w:

*""""""*
"""
一次神经网络训练的全流程:初始化参数→前向算损失→反向算梯度→梯度下降更新参数。
"""
*# 导包*
import torch
*# todo 1 模拟初始化权重参数*
w = torch.tensor(data=2.0, requires_grad=True)
*# 只有浮点型的张量才能跟踪梯度,这也是为什么我们用2.0而不是2*

*# todo 2 模拟损失函数,计算损失*
loss = 2*w**2
print("损失值:", loss)
*# 输出: tensor(8., grad_fn=<MulBackward0>)*
*# 这里的`grad_fn=<MulBackward0>`就是autograd的跟踪标记:*
*# 它记录了这个loss是怎么算出来的,反向传播的时候就靠这个算梯度*

*# todo 3 反向传播:自动计算梯度*
loss.backward()
*# 这一步就是我们之前聊的自动微分+反向传播,算完的梯度会自动存到w.grad里*
print("w的梯度:", w.grad)
*# 输出: tensor(8.)*

*# 手动验证一下:损失函数是 loss=2w²,它的导数是 dloss/dw = 4w*
*# w初始是2,所以导数就是 4*2=8,和autograd自动算的完全一致!*

*# --------------------------*
*# todo 4 参数更新:梯度下降*
*# --------------------------*
lr = 0.01 *# 学习率*
print(f'更新前的w: {w.data}') *# 输出: 更新前的w: 2.0*

*# 正确的更新公式:w = w - 学习率 * 梯度*
w.data = w.data - lr * w.grad

print(f'更新后的w: {w.data}') *# 输出: 更新后的w: 1.92*

"""
补充两个实际开发的注意点
为什么更新参数要用w.data?
因为如果直接写w = w - lr * w.grad,会新建一个普通的张量,把原来带requires_grad的参数张量覆盖掉,之后就没法跟踪梯度了。用.data可以直接修改张量的数值,同时保留它的梯度跟踪属性。
梯度要清零!
如果你要跑第二次训练,一定要把上一次的梯度清零,不然梯度会累加:比如你再跑一次loss.backward(),w.grad 会变成 16,而不是重新计算。
实际开发中,优化器会自动帮你做optimizer.zero_grad(),不用你手动清。
"""
*""""""*
"""
一次神经网络训练的全流程:初始化参数→前向算损失→反向算梯度→梯度下降更新参数。
"""
*# 导包*
import torch
*# todo 1 模拟初始化权重参数*
w = torch.tensor(data=[2.0,4.0], requires_grad=True)
*# 只有浮点型的张量才能跟踪梯度,这也是为什么我们用2.0而不是2*

*# todo 2 模拟损失函数,计算损失*
loss = 2*w**2
print("损失值:", loss)
*# 输出: tensor([ 8., 32.], grad_fn=<MulBackward0>)*
*# 这里的`grad_fn=<MulBackward0>`就是autograd的跟踪标记:*
*# 它记录了这个loss是怎么算出来的,反向传播的时候就靠这个算梯度*
*# todo loss 必须是标量张量,否则用它的均值*
*# todo 3 反向传播:自动计算梯度*
loss.mean().backward()  *# sum、mean*
*# 这一步就是我们之前聊的自动微分+反向传播,算完的梯度会自动存到w.grad里*
print("w的梯度:", w.grad)
*# 输出:w的梯度: tensor([4., 8.])*

*# 手动验证一下:损失函数是 loss=2w²,它的导数是 dloss/dw = 4w*
*# w初始是2,所以导数就是 4*2=8,和autograd自动算的完全一致!*

*# --------------------------*
*# todo 4 参数更新:梯度下降*
*# --------------------------*
lr = 0.01 *# 学习率*
print(f'更新前的w: {w.data}') *# 输出: 更新前的w: 2.0*

*# 正确的更新公式:w = w - 学习率 * 梯度*
w.data = w.data - lr * w.grad
*# =2-0.01*4*
*# =4-0.01*8*
print(f'更新后的w: {w.data}') *# 输出: 更新后的w: 1.92*
* # 更新后的w: tensor([1.9600, 3.9200])*
"""
补充两个实际开发的注意点
为什么更新参数要用w.data?
因为如果直接写w = w - lr * w.grad,会新建一个普通的张量,把原来带requires_grad的参数张量覆盖掉,之后就没法跟踪梯度了。用.data可以直接修改张量的数值,同时保留它的梯度跟踪属性。
梯度要清零!
如果你要跑第二次训练,一定要把上一次的梯度清零,不然梯度会累加:比如你再跑一次loss.backward(),w.grad 会变成 16,而不是重新计算。
实际开发中,优化器会自动帮你做optimizer.zero_grad(),不用你手动清。
"""

3.2 多参数的更新

接下来我们看同时更新权重 w 和偏置 b 的案例:

import torch
torch.manual_seed(6)
*# todo 1 初始化参数*
*# 1)模拟x数据 --真实特征*
x = torch.ones(size = (5,3))
*# 2)模拟y 数据*
y = torch.zeros(size=(2,3))
*# 3)模拟初始化w数据*
w = torch.randn(size=(2,5),requires_grad=True)
*# 4)模拟初始化b数据*
b = torch.randn(3,requires_grad=True)
*# 5)计算z标签*
z = w@x + b
print(z.shape) *# torch.Size([2, 3]) ->y*
*# 6)损失函数*
loss_fn = torch.nn.MSELoss()
*# 7) 学习率*
lr = 0.01
*# todo 2  前向算损失*
loss = loss_fn(z,y)
print(f'损失值:{loss}') *# loss= 13.9481782913208*

*# todo 3 反向算梯度*
loss.backward() *# ->w.grad*
print(f'w的梯度:{w.grad}')
print(f'b的梯度:{b.grad}')
*# todo 4 梯度下降更新参数 新 = 旧  - 学习率*梯度*
print(f'更新前的w:{w}')
w = w - 0.01*w.grad
print(f'更新后前的w:{w}')
print('-------------------------')
print(f'更新前的b:{b}')
b = b - 0.01*b.grad
print(f'更新后前的b:{b}')

四、知识点小结

本小节主要讲解了 PyTorch 中非常重要的自动微分模块的使用和理解。
我们对需要计算梯度的张量需要设置 requires\_grad=True 属性,这样 autograd 模块就会自动跟踪这个张量的运算,在反向传播的时候自动计算梯度。

在这里插入图片描述


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐