【AI大模型】PyTorch Autograd 实战

发布于:2025-07-10 ⋅ 阅读:(18) ⋅ 点赞:(0)

 PyTorch Autograd 实战指南,帮助你深入理解自动微分机制。内容涵盖从基础概念到高级应用,包含代码示例、可视化解释和常见陷阱分析。


Autograd 核心概念图解


基础实战:Autograd 工作流程

import torch

# 1. 创建需要梯度的张量
x = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(3.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)

# 2. 前向传播构建计算图
y = w * x + b  # 线性计算
z = y**2       # 非线性变换

# 3. 反向传播计算梯度
z.backward()   # 自动计算 dz/dx, dz/dw, dz/db

# 4. 查看梯度
print(f'dz/dx = {x.grad}')  # dz/dx = 2*y*w = 2*(6+1)*3 = 42
print(f'dz/dw = {w.grad}')  # dz/dw = 2*y*x = 2*7*2 = 28
print(f'dz/db = {b.grad}')  # dz/db = 2*y*1 = 2*7 = 14

关键机制详解

1. 计算图构建
# 查看计算图节点
print(z.grad_fn)           # <PowBackward0 at 0x7f8b0c0b5f70>
print(z.grad_fn.next_functions)  # ((<AddBackward0 at 0x7f8b0c0b5e20>, 0),)
print(y.grad_fn)           # <AddBackward0 at 0x7f8b0c0b5e20>
2. 梯度累积机制
# 梯度会累积,不清零会导致错误
x = torch.tensor([1., 2.], requires_grad=True)

# 第一次计算
y = x.sum()
y.backward()
print(x.grad)  # tensor([1., 1.])

# 第二次计算(不清零)
y = x.sum()
y.backward()
print(x.grad)  # tensor([2., 2.]) 梯度翻倍!

# 正确做法:计算前清零
x.grad.zero_()
y = x.sum()
y.backward()
print(x.grad)  # tensor([1., 1.])
3. 向量-Jacobian 乘积 (VJP)
# 当输出是向量时,需要提供grad_tensors
x = torch.tensor([1., 2.], requires_grad=True)
y = torch.stack([x[0]**2, x[1]**3])  # [1, 8]

# 计算 dy/dx
v = torch.tensor([1., 0.5])  # 指定每个输出的权重
y.backward(gradient=v)

# 梯度 = [2*x0*v0, 3*x1^2*v1]
print(x.grad)  # tensor([2.0000, 6.0000]) 
# 解释: [2*1*1, 3*4*0.5] = [2, 6]

高级应用:自定义 Autograd 函数

实现自定义 ReLU 函数
class CustomReLU(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., 2., 0.5], requires_grad=True)
y = CustomReLU.apply(x)
y.backward(torch.tensor([1., 1., 1.]))
print(x.grad)  # tensor([0., 1., 1.])
实现二次函数 f(x) = ax² + bx + c
class QuadraticFunction(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, a, b, c):
        ctx.save_for_backward(x, a, b, c)
        return a*x**2 + b*x + c
    
    @staticmethod
    def backward(ctx, grad_output):
        x, a, b, c = ctx.saved_tensors
        grad_x = grad_output * (2*a*x + b)
        grad_a = grad_output * x**2
        grad_b = grad_output * x
        grad_c = grad_output * 1
        return grad_x, grad_a, grad_b, grad_c

# 使用示例
x = torch.tensor(3.0, requires_grad=True)
a = torch.tensor(2.0, requires_grad=True)
b = torch.tensor(1.0, requires_grad=True)
c = torch.tensor(4.0, requires_grad=True)

y = QuadraticFunction.apply(x, a, b, c)
y.backward()

print(f'dy/dx = {x.grad}')  # 2ax+b = 12+1=13
print(f'dy/da = {a.grad}')  # x² = 9
print(f'dy/db = {b.grad}')  # x = 3
print(f'dy/dc = {c.grad}')  # 1

Autograd 性能优化技巧

  1. 禁用梯度计算

# 推理时禁用梯度
with torch.no_grad():
    output = model(input_data)

# 临时张量不需要梯度
x = torch.randn(100, device='cuda', requires_grad=False)
  1. 梯度检查点 (Gradient Checkpointing)

from torch.utils.checkpoint import checkpoint

# 对大模型分段计算
def segment1(x):
    return layer1(x)

def segment2(x):
    return layer2(x)

x = torch.rand(10, requires_grad=True)
y = checkpoint(segment1, x)
z = checkpoint(segment2, y)
  1. 梯度累积

# 小批量训练时模拟大批量
for i, (inputs, labels) in enumerate(train_loader):
    outputs = model(inputs)
    loss = criterion(outputs, labels)
    loss = loss / accumulation_steps  # 缩放损失
    loss.backward()  # 累积梯度
    
    if (i+1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()  # 累积后清零

常见错误与调试技巧

错误1:忘记 requires_grad
# 错误:权重张量未设置梯度
weights = torch.randn(10, 10)  # 缺少 requires_grad=True
loss = model(inputs, weights)
loss.backward()  # RuntimeError: element 0 of tensors does not require grad...
错误2:in-place 操作破坏计算图
x = torch.tensor([1., 2.], requires_grad=True)
y = x**2

# 错误:in-place 操作
x.add_(1)  # 修改原始张量

y.backward()  # RuntimeError: a leaf Variable that requires grad...
错误3:计算图未释放
# 循环中未释放计算图
for data in dataset:
    output = model(data)
    loss = criterion(output, target)
    loss.backward()  # 计算图未释放
    
# 正确做法:使用小批量或detach()
loss.backward(retain_graph=False)  # 默认已释放
调试工具
# 1. 检查梯度是否存在
print(torch.is_grad_enabled())  # True/False

# 2. 可视化计算图
from torchviz import make_dot
make_dot(z, params={'x': x, 'w': w, 'b': b}).render("graph", format="png")

# 3. 梯度检查
torch.autograd.gradcheck(QuadraticFunction.apply, 
                        (x, a, b, c), eps=1e-6)

Autograd 内部机制剖析

  1. 动态计算图构建

    • 前向传播时动态构建图

    • 每个张量记录创建它的操作 (grad_fn)

    • 叶子节点记录梯度 (grad)

  2. 反向传播过程

    • 从输出张量开始

    • 遍历计算图的反向边

    • 调用每个操作的 backward() 方法

    • 链式法则计算梯度

  3. 梯度计算优化

    • 延迟执行:仅当调用 backward() 时计算

    • 内存优化:前向后立即释放中间结果

    • 并行计算:异步梯度计算


实战练习:实现简单的神经网络训练

import torch
import torch.nn as nn
import torch.optim as optim

# 手动实现线性层(不使用 nn.Module)
class ManualLinear:
    def __init__(self, in_features, out_features):
        self.weight = torch.randn(out_features, in_features, requires_grad=True)
        self.bias = torch.randn(out_features, requires_grad=True)
    
    def forward(self, x):
        return x @ self.weight.t() + self.bias

# 创建模型
model = ManualLinear(2, 1)
optimizer = optim.SGD([model.weight, model.bias], lr=0.01)

# 训练数据
X = torch.tensor([[0,0], [0,1], [1,0], [1,1]], dtype=torch.float32)
y = torch.tensor([[0], [1], [1], [0]], dtype=torch.float32)

# 训练循环
for epoch in range(1000):
    # 前向传播
    pred = model.forward(X)
    
    # 计算损失
    loss = ((pred - y)**2).mean()
    
    # 反向传播
    loss.backward()
    
    # 手动更新参数
    with torch.no_grad():
        model.weight -= 0.01 * model.weight.grad
        model.bias -= 0.01 * model.bias.grad
        
        # 清零梯度
        model.weight.grad.zero_()
        model.bias.grad.zero_()
    
    if epoch % 100 == 0:
        print(f'Epoch {epoch}, Loss: {loss.item()}')

# 测试
with torch.no_grad():
    test_pred = model.forward(X)
    print("Predictions:", test_pred)

通过这个完整的 Autograd 实战指南,你应该能够:

  1. 理解 PyTorch 自动微分的核心机制

  2. 实现自定义的 Autograd 函数

  3. 避免常见的梯度计算错误

  4. 优化梯度计算性能

  5. 深入调试复杂的计算图

Autograd 是 PyTorch 的核心优势之一,掌握它将使你能够更灵活地构建和调试深度学习模型。


网站公告

今日签到

点亮在社区的每一天
去签到