当前位置: 首页 > news >正文

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

文章目录

    • 自动微分
      • 一、核心概念
      • 二、梯度计算
        • 标量对向量的梯度
        • 向量对向量的梯度
        • 矩阵对向量的梯度
      • 三、梯度上下文控制
        • 1. 控制梯度计算(torch.no_grad())
        • 2. 累计梯度
        • 3. 梯度清零(zero_())
        • 【求函数最小值】
        • 【函数参数求解】

自动微分

一、核心概念

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

    例如:
    z=x∗yloss=z.sum()z = x * y\\loss = z.sum() z=xyloss=z.sum()
    在上述代码中,x 和 y 是输入张量,即叶子节点,z 是中间结果,loss 是最终输出。每一步操作都会记录依赖关系:

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

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

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

    	  x       y\     /\   /\ /z||vloss
    
  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_leafTrueFalse
    梯度保留默认保留(用于参数更新)反向传播后自动释放(节省内存)
    典型示例输入数据、模型参数(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]))。

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)))。

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))。

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临时失效),适用于推理、验证阶段。
  • 原理:禁用计算图构建,减少内存占用,加快前向计算。
import torchx = 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 = Falseprint(y.requires_grad)  # True(上下文外正常跟踪)
print(z.requires_grad)  # False(上下文内禁用)
2. 累计梯度
  • 功能:多次调用backward()时,梯度会自动累加(grad属性累加新梯度),适用于 “大批次拆分小批次” 训练(如显存不足时)。
  • 原理:梯度本质是张量的grad属性,每次反向传播会将新梯度加到该属性上,而非覆盖。
import torchx = 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,重置梯度状态。
import torchx = 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.])(新梯度,未受之前影响)
【求函数最小值】
import torch
def gradient_descent():x = torch.tensor(2.0,requires_grad=True,dtype=torch.float64)# 迭代次数epochs = 100# 学习率lr = 0.01for 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.gradprint(f'epoch:{epoch},x:{x}')if __name__ == '__main__':gradient_descent()
【函数参数求解】
import torch
def test01():# 定义x,yx = 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.01for 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.gradb -= lr * b.gradprint(f'epoch:{epoch},w:{w},b:{b},loss:{loss}')if __name__ == '__main__':test01()
http://www.dtcms.com/a/319785.html

相关文章:

  • Ethereum: 像Uniswap V3贡献者一样开发,克隆、编译与测试v3-core
  • 通过减少回表和增加冗余字段,优化SQL查询效率
  • LSTM 单变量时序预测—pytorch
  • vscode+latex本地英文期刊环境配置
  • VScode使用jupyter notebook,配置内核报错没有torch解决
  • 如何委托第三方检测机构做软件测试?
  • 鸿蒙 - 分享功能
  • 直播预告|鸿蒙生态下的 Flutter 开发实战
  • 非化学冷却塔水处理解决方案:绿色工业时代的革新引擎
  • Elasticsearch 文档分词器
  • 神经网络入门指南:从零理解 PyTorch 的核心思想
  • 2025 五大商旅平台管控力解析:合规要求下的商旅管理新范式
  • Flutter 布局控件使用详解
  • 【java基础|第十六篇】面向对象(六)——抽象和接口
  • Java-JVM探析
  • 参考平面与返回电流
  • BMS保护板测试仪:电池安全管理的“质检卫士”|深圳鑫达能
  • Java爬虫性能优化:多线程抓取JSP动态数据实践
  • 键盘+系统+软件等快捷键大全
  • RK3568笔记九十八:使用Qt实现RTMP拉流显示
  • FluentUI-main的详解
  • MyBatis联合查询
  • windows有一个企业微信安装包,脚本执行并安装到d盘。
  • 我的世界Java版1.21.4的Fabric模组开发教程(十七)自定义维度
  • PCL提取平面上的圆形凸台特征
  • WindowsLinux系统 安装 CUDA 和 cuDNN
  • 从库存一盘货到全域智能铺货:巨益科技全渠道平台助力品牌业财一体化升级
  • 电子基石:硬件工程师的器件手册 (九) - DC-DC拓扑:电能转换的魔术师
  • 线上业务突然流量掉 0 ?一次 DNS 污染排查与自救实录
  • Qt中类提升后不显示问题