PyTorch 的 Autograd 系统进行一次全面且深入的详解

PyTorch 的 Autograd 系统进行一次全面且深入的详解。Autograd 是 PyTorch 的核心,它实现了自动微分,让神经网络训练中的反向传播变得自动化。

一、 Autograd 是什么?

Autograd 是 PyTorch 的自动微分引擎,它的核心功能是:
自动计算张量操作的导数/梯度

在深度学习中,我们需要计算损失函数相对于模型参数的梯度,然后用这些梯度来更新参数。Autograd 使得我们只需要定义前向传播,它可以自动构建计算图并计算所有必要的梯度。


二、 核心概念与基础

1. 计算图

Autograd 的核心是一个动态计算图,它是一个有向无环图:

  • 节点:表示张量或操作(函数)
  • :表示张量之间的依赖关系
import torch

# 简单的计算图示例
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2 + 3 * x + 1
# 计算图: x -> (x^2, 3*x) -> (x^2 + 3*x + 1) -> y

2. requires_grad 属性

这个布尔值标记决定是否跟踪对该张量的操作并构建计算图。

x = torch.tensor(2.0, requires_grad=True)  # 跟踪梯度
y = torch.tensor(3.0)                      # 不跟踪梯度

print(x.requires_grad)  # True
print(y.requires_grad)  # False

3. grad_fn 属性

每个非叶子张量都有一个 grad_fn 属性,它指向创建该张量的 Function 对象。

x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
print(y.grad_fn)  # <PowBackward0 object at ...>

三、 Autograd 的工作流程

1. 前向传播:构建计算图

当你在 requires_grad=True 的张量上执行操作时,PyTorch 会:

  1. 执行计算得到结果张量
  2. 记录执行的操作(在 grad_fn 中)
  3. 构建计算图
# 构建计算图的过程
a = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(2.0, requires_grad=True)
c = a * b                    # grad_fn: <MulBackward0>
d = c + 1                    # grad_fn: <AddBackward0>
e = d ** 2                   # grad_fn: <PowBackward0>

2. 反向传播:计算梯度

调用 .backward() 方法时,Autograd 会:

  1. 从该张量开始,反向遍历计算图
  2. 对每个节点计算梯度
  3. 将梯度累积到叶子节点的 .grad 属性中
# 继续上面的例子
e.backward()  # 触发反向传播

print(a.grad)  # d(e)/d(a) = 2*(a*b+1)*b = 2*(1*2+1)*2 = 12
print(b.grad)  # d(e)/d(b) = 2*(a*b+1)*a = 2*(1*2+1)*1 = 6

四、 叶子张量与非叶子张量

1. 定义

  • 叶子张量:用户直接创建的张量,不是通过操作产生的
  • 非叶子张量:通过对叶子张量进行操作得到的张量
x = torch.tensor(2.0, requires_grad=True)  # 叶子张量
y = x ** 2                                 # 非叶子张量
z = y + 1                                  # 非叶子张量

2. 梯度保留策略

默认情况下:

  • 叶子张量:梯度保存在 .grad 属性中
  • 非叶子张量:梯度不保留(为了节省内存)
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
y.backward()

print(x.grad)        # 有值: 4.0
print(y.grad)        # None (非叶子张量)

要保留非叶子张量的梯度:

y.retain_grad()      # 显式要求保留梯度
y.backward()
print(y.grad)        # 现在有值: 1.0

五、 .backward() 方法详解

1. 标量张量的反向传播

对于标量张量(0维),直接调用 .backward()

x = torch.tensor(2.0, requires_grad=True)
y = x ** 2
y.backward()        # 等价于 y.backward(torch.tensor(1.0))
print(x.grad)       # 4.0

2. 非标量张量的反向传播

对于非标量张量,需要提供 gradient 参数(通常称为 grad_tensors):

x = torch.tensor([1.0, 2.0], requires_grad=True)
y = x ** 2                          # y = [1, 4]

# 假设我们有 l = sum(v * y),其中 v 是权重向量
v = torch.tensor([0.1, 0.2])
y.backward(gradient=v)              # 计算 d(l)/d(x)

print(x.grad)  # d(l)/d(x) = 2 * x * v = [0.2, 0.8]

理解 gradient 参数

  • 它表示上游梯度
  • 对于标量损失,默认为 1.0(因为 d(loss)/d(loss) = 1
  • 对于向量,它定义了如何将向量输出"缩减"为标量

六、 梯度累积与清零

1. 梯度累积

PyTorch 默认会累加梯度,这在某些场景下有用(如梯度累积):

x = torch.tensor(2.0, requires_grad=True)

for _ in range(3):
    y = x ** 2
    y.backward()
    print(f"Grad after step {_}: {x.grad}")
# 输出: 4.0, 8.0, 12.0 (梯度不断累加)

2. 梯度清零

在大多数训练场景中,我们需要在每个 batch 前清零梯度:

# 标准训练循环
optimizer = torch.optim.SGD([x], lr=0.1)

for epoch in range(num_epochs):
    optimizer.zero_grad()   # 清零梯度
    
    output = model(data)
    loss = criterion(output, target)
    loss.backward()         # 计算梯度
    
    optimizer.step()        # 更新参数

七、 控制梯度跟踪

1. torch.no_grad() 上下文管理器

禁用梯度跟踪,用于推理或中间计算:

x = torch.tensor(2.0, requires_grad=True)

with torch.no_grad():
    y = x ** 2              # y.requires_grad = False
    z = y + 1               # 不会跟踪梯度

print(y.requires_grad)      # False

2. detach() 方法

从计算图中分离张量,创建不跟踪梯度的新张量:

x = torch.tensor(2.0, requires_grad=True)
y = x ** 2

y_detached = y.detach()     # 创建新张量,不跟踪梯度
print(y_detached.requires_grad)  # False

# 原张量不受影响
print(y.requires_grad)      # True

3. requires_grad_() 方法

原地修改张量的 requires_grad 属性:

x = torch.tensor(2.0)
x.requires_grad_(True)      # 启用梯度跟踪
print(x.requires_grad)      # True

八、 高级 Autograd 特性

1. 高阶梯度计算

Autograd 支持计算高阶导数(需要设置 create_graph=True):

x = torch.tensor(2.0, requires_grad=True)
y = x ** 3

# 计算一阶导
dy_dx = torch.autograd.grad(y, x, create_graph=True)[0]
print(dy_dx)  # 3*x^2 = 12.0

# 计算二阶导
d2y_dx2 = torch.autograd.grad(dy_dx, x)[0]
print(d2y_dx2)  # 6*x = 12.0

2. 自定义 Autograd Function

你可以创建自定义的自动微分函数:

class MyReLU(torch.autograd.Function):
    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)  # 保存输入供反向传播使用
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0      # ReLU的导数
        return grad_input

# 使用自定义函数
x = torch.tensor([-1.0, 2.0], requires_grad=True)
y = MyReLU.apply(x)                    # 使用 .apply() 调用
y.backward(torch.tensor([1.0, 1.0]))
print(x.grad)  # [0., 1.]

3. torch.autograd.grad()

另一种计算梯度的方法,不修改 .grad 属性:

x = torch.tensor(2.0, requires_grad=True)
y = x ** 2

# 计算梯度,但不累积到 x.grad
dy_dx = torch.autograd.grad(y, x)[0]
print(dy_dx)        # 4.0
print(x.grad)       # None (未被修改)

九、 内存管理与性能优化

1. retain_graph 参数

默认情况下,.backward() 后会释放计算图。如果需要多次反向传播:

x = torch.tensor(2.0, requires_grad=True)
y1 = x ** 2
y2 = x ** 3

# 第一次反向传播
y1.backward(retain_graph=True)
print(f"First grad: {x.grad}")  # 2x = 4.0

# 第二次反向传播(需要 retain_graph=True)
y2.backward()
print(f"Second grad: {x.grad}") # 4.0 + 3x^2 = 4.0 + 12.0 = 16.0

2. 梯度检查点

对于大模型,使用梯度检查点来节省内存:

from torch.utils.checkpoint import checkpoint

def custom_forward(x):
    # 复杂的计算
    return x ** 2

x = torch.tensor(2.0, requires_grad=True)
y = checkpoint(custom_forward, x)  # 节省内存但增加计算时间

十、 调试技巧

1. 检查梯度

# 注册钩子来检查梯度
def grad_hook(grad):
    print(f"Gradient: {grad}")

x = torch.tensor(2.0, requires_grad=True)
x.register_hook(grad_hook)  # 在反向传播时打印梯度

y = x ** 2
y.backward()

2. 检测数值问题

# 检测梯度爆炸/消失
torch.autograd.set_detect_anomaly(True)

try:
    # 你的训练代码
    loss.backward()
except RuntimeError as e:
    print("Autograd异常:", e)

十一、 总结

PyTorch 的 Autograd 系统是一个强大而灵活的自动微分引擎,其核心特性包括:

  1. 动态计算图:根据需要构建,支持动态网络结构
  2. 延迟执行:只在需要时计算梯度
  3. 灵活控制:可以精细控制梯度跟踪和计算
  4. 高性能:底层由 C++ 实现,优化良好

理解 Autograd 的工作原理对于:

  • 调试梯度相关问题
  • 实现复杂模型结构
  • 优化训练性能
  • 开发自定义层和损失函数

都至关重要。通过掌握这些概念,你就能更好地利用 PyTorch 进行深度学习研究和开发。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小赖同学啊

感谢上帝的投喂

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值