2深度学习Pytorch-自动微分--梯度计算、梯度上下文控制(累计梯度、梯度清零)

文章目录

自动微分

一、核心概念

  1. 计算图(Computation Graph) 动态构建的有向图,节点表示张量,边表示运算操作(如加减乘除、激活函数等)。用于记录张量的计算路径,为反向传播提供依据。

    例如:
    z = x ∗ y l o s s = z . s u m ( ) z = x * y\\loss = z.sum() z=x∗yloss=z.sum()

    在上述代码中,x 和 y 是输入张量,即叶子节点,z 是中间结果,loss 是最终输出。每一步操作都会记录依赖关系:

    z = x * y:z 依赖于 x 和 y。

    loss = z.sum():loss 依赖于 z。

    这些依赖关系形成了一个动态计算图,如下所示:

    复制代码
    	  x       y
           \     /
            \   /
             \ /
              z
              |
              |
              v
            loss
  2. requires_grad 张量的属性(布尔值),默认为False。若设为True,PyTorch 会跟踪该张量的所有运算,以便后续计算梯度。

  3. grad 属性 存储通过反向传播计算出的梯度值(如x.grad表示损失函数对x的梯度)。梯度会累积,多次反向传播前需用zero_()清零。

  4. 反向传播(backward ()) 从标量(如损失函数值)出发,沿计算图反向遍历,根据链式法则计算所有requires_grad=True的张量的梯度,结果存入grad。例如:调用 loss.backward() 从输出节点 loss 开始,沿着计算图反向传播,计算每个节点的梯度。

  5. 叶子节点 :叶子节点是用户直接创建requires_grad=True的张量(如输入数据、模型参数),其is_leaf属性为True,梯度会被保留以用于参数更新;非叶子节点则是通过张量运算生成的中间变量,is_leafFalse,梯度在反向传播后自动释放,以节省内存。

    特征 叶子节点(Leaf Node) 非叶子节点(Non-leaf Node)
    创建方式 用户直接定义(非运算生成) 通过张量运算生成(中间变量)
    is_leaf True False
    梯度保留 默认保留(用于参数更新) 反向传播后自动释放(节省内存)
    典型示例 输入数据、模型参数(requires_grad=True 卷积 / 线性层输出、激活函数结果等
    梯度调试 无需额外操作 需调用retain_grad()强制保留梯度

二、梯度计算

使用tensor.backward()方法执行反向传播,从而计算张量的梯度

标量对向量的梯度

数学原理

  • 对于标量输出 L 和参数矩阵 W,梯度 ∂L/∂W 与 W 同形
  • 每个元素 wij 的梯度表示:∂L/∂wij = lim(Δ→0) [L(wij+Δ) - L(wij)]/Δ

标量是形状为()的张量(如单个损失值),可直接调用backward()计算梯度。

计算 (y = x_1^2 + x_2^2 + x_3^2) 对 (x = [x_1, x_2, x_3]) 的梯度(理论梯度为 ([2x_1, 2x_2, 2x_3]))。

python 复制代码
import torch

# 定义输入张量,设置requires_grad=True以跟踪梯度
x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)

# 构建计算图:y是标量(x的平方和)
y = torch.sum(x **2)  # y = 1² + 2² + 3² = 14.0

# 反向传播:计算y对所有requires_grad=True的张量的梯度
y.backward()

# 查看梯度(结果存储在x.grad中)
print("x的梯度:", x.grad)  # 输出:tensor([2., 4., 6.])(与理论一致)

# 梯度会累积,再次计算前需清零
x.grad.zero_()
print("清零后x的梯度:", x.grad)  # 输出:tensor([0., 0., 0.])
向量对向量的梯度

当输出为张量(非标量)时,需用雅可比矩阵 描述梯度关系。PyTorch 提供torch.autograd.functional.jacobian实现。

数学定义

  • 对于函数 f: ℝⁿ → ℝᵐ,雅可比矩阵 J 是 m×n 矩阵:
    J = [∂f₁/∂x₁ ... ∂f₁/∂xₙ]

    ... ...

    ∂fₘ/∂x₁ ... ∂fₘ/∂xₙ

计算 (y = [2x_1, 3x_2, 4x_3]) 对 (x = [x_1, x_2, x_3]) 的梯度(理论雅可比矩阵为对角矩阵 (diag(2, 3, 4)))。

python 复制代码
import torch
from torch.autograd import functional

# 定义输入向量
x = torch.tensor([1.0, 2.0, 3.0])

# 定义函数:输入x(向量),输出y(向量)
def func(x):
    return torch.tensor([2*x[0], 3*x[1], 4*x[2]])

# 计算雅可比矩阵:形状为 (y的长度, x的长度)
jacobian = functional.jacobian(func, x)

print("雅可比矩阵:\n", jacobian)
# 输出:
# tensor([[2., 0., 0.],
#         [0., 3., 0.],
#         [0., 0., 4.]])
矩阵对向量的梯度

计算 (y = W \cdot x)(W 为 3×2 矩阵,x 为 2 维向量)对 x 的梯度(理论梯度为 (W^T))。

python 复制代码
import torch
from torch.autograd import functional

# 定义矩阵W和向量x
W = torch.tensor([[1.0, 2.0],
                  [3.0, 4.0],
                  [5.0, 6.0]])  # 3×2矩阵
x = torch.tensor([7.0, 8.0])  # 2维向量

# 定义函数:y = W与x的矩阵乘法(结果为3维向量)
def func(x):
    return torch.matmul(W, x)

# 计算雅可比矩阵(y对x的梯度)
jacobian = functional.jacobian(func, x)

print("雅可比矩阵(y对x的梯度):\n", jacobian)
print("理论梯度(W的转置):\n", W.T)  # 两者完全一致

三、梯度上下文控制

用于推理、参数冻结等场景,避免不必要的梯度计算,节省内存。

1. 控制梯度计算(torch.no_grad())
  • 功能 :创建上下文环境,内部所有张量运算不跟踪梯度(requires_grad临时失效),适用于推理、验证阶段。
  • 原理:禁用计算图构建,减少内存占用,加快前向计算。
python 复制代码
import torch

x = torch.tensor([1.0, 2.0], requires_grad=True)

# 正常计算:跟踪梯度
y = x **2  # y.requires_grad = True

# 上下文内:不跟踪梯度
with torch.no_grad():
    z = x** 2  # z.requires_grad = False

print(y.requires_grad)  # True(上下文外正常跟踪)
print(z.requires_grad)  # False(上下文内禁用)
2. 累计梯度
  • 功能 :多次调用backward()时,梯度会自动累加(grad属性累加新梯度),适用于 "大批次拆分小批次" 训练(如显存不足时)。
  • 原理 :梯度本质是张量的grad属性,每次反向传播会将新梯度加到该属性上,而非覆盖。
python 复制代码
import torch

x = torch.tensor([1.0], requires_grad=True)

# 第一次反向传播
y1 = x **2
y1.backward()
print(x.grad)  # tensor([2.])(首次梯度)

# 第二次反向传播(未清零,梯度累加)
y2 = x** 2
y2.backward()
print(x.grad)  # tensor([4.])(2+2,累计结果)
3. 梯度清零(zero_())
  • 功能 :清除张量的grad属性值(置为 0),避免累计梯度干扰下一次计算,是训练中每次参数更新前的必要操作。
  • 原理 :通过 in-place 操作将grad张量填充为 0,重置梯度状态。
py 复制代码
import torch

x = torch.tensor([1.0], requires_grad=True)

# 第一次反向传播
y = x **2
y.backward()
print(x.grad)  # tensor([2.])

# 清零梯度
x.grad.zero_()
print(x.grad)  # tensor([0.])(已清零)

# 第二次反向传播(梯度重新计算,不累计)
y = x** 2
y.backward()
print(x.grad)  # tensor([2.])(新梯度,未受之前影响)
【求函数最小值】
py 复制代码
import torch
def gradient_descent():
    x = torch.tensor(2.0,requires_grad=True,dtype=torch.float64)
    # 迭代次数
    epochs = 100
    # 学习率
    lr = 0.01

    for epoch in range(epochs):
        y = x ** 2
        # 梯度清零
        if x.grad is not None:
            x.grad.zero_()

        # 反向传播
        y.backward()

        # 修改x:不参与梯度运算
        with torch.no_grad():
            x -= lr * x.grad
        print(f'epoch:{epoch},x:{x}')

if __name__ == '__main__':
    gradient_descent()
【函数参数求解】
py 复制代码
import torch
def test01():
    # 定义x,y
    x = torch.tensor([1,2,3],requires_grad=True,dtype=torch.float64)
    y = torch.tensor([4,5,6],requires_grad=True,dtype=torch.float64)
    # 定义模型参数
    w = torch.tensor([1.0],requires_grad=True)
    b = torch.tensor([1.0],requires_grad=True)

    # 迭代次数
    epochs = 100
    # 学习率
    lr = 0.01

    for epoch in range(epochs):
        # 前向传播:计算预测值
        y_pred = w * x + b
        # 损失函数
        loss = ((y_pred - y)**2).mean()
        # 梯度清零
        if w.grad is not None and b.grad is not None:
            w.grad.zero_()
            b.grad.zero_()
        # 反向传播
        loss.backward()

        # 修改参数
        with torch.no_grad():
            w -= lr * w.grad
            b -= lr * b.grad
        print(f'epoch:{epoch},w:{w},b:{b},loss:{loss}')

if __name__ == '__main__':
    test01()
相关推荐
机器之心6 分钟前
刚刚,奥特曼发布 GPT-5!人人免费用「博士级」智能,基准图错误遭全网吐槽
人工智能·openai
aneasystone本尊11 分钟前
实战 Coze Studio 知识库使用
人工智能
新智元17 分钟前
GPT-5,AI的「登月时刻」来了!奥特曼现场发布,三位一体博士级智能体
人工智能·openai
递归尽头是星辰22 分钟前
大模型与Spring AI的无缝对接:从原理到实践
人工智能·大模型·spring ai·deepseek
机器之心28 分钟前
北大、字节跳动联手发布SWE-Swiss:一把修复代码Bug的「瑞士军刀」,完整配方直指开源SOTA
人工智能·openai
双向331 小时前
Agent配置最佳实践:Prompt工程与参数调优
人工智能
双向331 小时前
协同进化:AIGC、Agent和MCP如何相互促进共同发展
人工智能
葡萄城技术团队1 小时前
什么是上下文窗口
人工智能
Ly2020Wj1 小时前
pytorch入门3:使用pytorch进行多输出手写数据集模型预测
人工智能·pytorch·python
Baihai_IDP1 小时前
AI 智能体记忆机制详解
人工智能·面试·llm