神经网络中的反向传播实例解读

部署运行你感兴趣的模型镜像

前提条件:

已经安装好了python及torch环境。

示例代码:

inp = torch.eye(4, 5, requires_grad=True)
out = (inp+1).pow(2).t()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"First call\n{inp.grad}")
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nSecond call\n{inp.grad}")
inp.grad.zero_()
out.backward(torch.ones_like(out), retain_graph=True)
print(f"\nCall after zeroing gradients\n{inp.grad}")

代码详细解读:

这段代码展示了PyTorch中自动求导(autograd)的基本原理,特别是梯度计算、累积和清零的过程。我们逐步解析:

  1. 创建输入张量

    inp = torch.eye(4, 5, requires_grad=True)
    
    • 创建一个4行5列的单位矩阵(对角线上为1,其余为0)
    • requires_grad=True 表示需要跟踪该张量的计算历史并计算梯度
  2. 定义计算过程

    out = (inp+1).pow(2).t()
    

    对输入张量进行一系列操作:

    • inp+1:每个元素加1(单位矩阵变成对角线上为2,其余为1)
    • .pow(2):每个元素平方(对角线上为4,其余为1)
    • .t():矩阵转置(变成5行4列)
  3. 第一次反向传播

    out.backward(torch.ones_like(out), retain_graph=True)
    
    • torch.ones_like(out) 是梯度的初始值(外部梯度),形状与out相同的全1矩阵
    • retain_graph=True 表示保留计算图,以便后续再次反向传播
    • 计算图会自动计算 outinp 的梯度,并存储在 inp.grad

    根据链式法则,求导结果为:d(out)/d(inp) = 2*(inp+1)
    所以梯度矩阵中,原单位矩阵对角线位置为 2*(1+1)=4,其余位置为 2*(0+1)=2

  4. 第二次反向传播

    out.backward(torch.ones_like(out), retain_graph=True)
    
    • 再次计算梯度时,PyTorch会累积梯度(将新计算的梯度加到已有梯度上)
    • 此时 inp.grad 中的值是第一次梯度的2倍(对角线8,其余4)
  5. 梯度清零后再次传播

    inp.grad.zero_()  # 清零梯度
    out.backward(torch.ones_like(out), retain_graph=True)
    
    • zero_() 方法将梯度重置为0
    • 再次传播后,梯度回到第一次计算的结果(对角线4,其余2)

输出结果说明

  • 第一次打印:梯度矩阵中对角线为4,其余为2
  • 第二次打印:梯度累积后,对角线为8,其余为4
  • 清零后打印:梯度重置,回到对角线4,其余为2

这个例子清晰展示了PyTorch中梯度的计算机制:默认会累积梯度,因此在训练神经网络时,通常需要在每个batch训练前使用zero_()方法清零梯度。

提问环节:

Q1:第二次反向传播结果为什么是8和4?

核心前提

PyTorch中,backward()会默认将新计算的梯度累加到已有梯度上(即 grad = 旧梯度 + 新梯度),而非覆盖。这是第二次传播后梯度变为8的根本原因。

详细计算步骤

1. 定义计算图与求导公式

已知计算过程:

out = (inp + 1).pow(2).t()  # t()是转置,不影响元素值,仅改变形状

简化后,每个元素的计算关系为:
out_ij = (inp_ji + 1)²(转置导致索引互换,不影响导数大小)

inp中任意元素inp_xy求导(链式法则):
∂out / ∂inp_xy = 2*(inp_xy + 1)

2. 初始输入inp的值
inp = torch.eye(4, 5)  # 4行5列单位矩阵

inp的非零元素仅在对角线(i=j时)为1,其余为0:

inp = [
 [1, 0, 0, 0, 0],
 [0, 1, 0, 0, 0],
 [0, 0, 1, 0, 0],
 [0, 0, 0, 1, 0]
]
3. 第一次反向传播的梯度计算
  • 调用 out.backward(torch.ones_like(out)),其中 torch.ones_like(out) 是全1的梯度初始值(相当于对out的每个元素求导权重为1)。

  • 根据求导公式 ∂out / ∂inp_xy = 2*(inp_xy + 1)

    • 对角线元素(inp_xy=1):2*(1+1) = 4
    • 非对角线元素(inp_xy=0):2*(0+1) = 2

    因此,第一次传播后 inp.grad 为:

    [
     [4, 2, 2, 2, 2],
     [2, 4, 2, 2, 2],
     [2, 2, 4, 2, 2],
     [2, 2, 2, 4, 2]
    ]
    
4. 第二次反向传播的梯度累积
  • 再次调用 out.backward(torch.ones_like(out)) 时:

    • 新计算的梯度与第一次完全相同(因为inp的值未改变):对角线4,非对角线2。
    • PyTorch自动执行梯度累积新梯度 = 旧梯度 + 新计算的梯度

    因此,第二次传播后 inp.grad 为:

    • 对角线元素:4(旧) + 4(新) = 8
    • 非对角线元素:2(旧) + 2(新) = 4

    结果为:

    [
     [8, 4, 4, 4, 4],
     [4, 8, 4, 4, 4],
     [4, 4, 8, 4, 4],
     [4, 4, 4, 8, 4]
    ]
    

结论

第二次反向传播中出现的8,是第一次梯度(4)与第二次梯度(4)累积相加的结果,体现了PyTorch梯度自动累积的特性。这也是训练神经网络时,每个batch前需要用zero_()清零梯度的原因(避免历史梯度干扰当前计算)。

Q2:第三次为啥又和第一次一样了?

第三次反向传播是在执行了梯度清零操作(inp.grad.zero_())之后进行的,这个过程很好地展示了梯度清零的作用。我们详细拆解计算步骤:

1. 梯度清零操作

在第三次反向传播前,执行了:

inp.grad.zero_()  # 将inp的梯度张量所有元素重置为0

此时,inp.grad 中的值不再是第二次传播后的累积结果(对角线8、非对角线4),而是全部变为0:

inp.grad = [
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0]
]

2. 第三次反向传播的梯度计算

再次调用:

out.backward(torch.ones_like(out), retain_graph=True)

此时的计算逻辑与第一次反向传播完全相同,原因是:

  • inp 本身的值未发生改变(始终是初始的4x5单位矩阵)
  • 计算图未变(out = (inp+1).pow(2).t() 的关系不变)
  • 反向传播的初始梯度仍为 torch.ones_like(out)(全1矩阵)

根据求导公式 ∂out / ∂inp_xy = 2*(inp_xy + 1)

  • 对角线元素(inp_xy=1):2*(1+1) = 4
  • 非对角线元素(inp_xy=0):2*(0+1) = 2

3. 第三次传播后的梯度结果

由于之前执行了 zero_(),梯度不会累积,而是直接存储本次计算的结果:

inp.grad = [
 [4, 2, 2, 2, 2],
 [2, 4, 2, 2, 2],
 [2, 2, 4, 2, 2],
 [2, 2, 2, 4, 2]
]

这个结果与第一次反向传播的输出完全一致,说明梯度清零后,新的反向传播会重新计算梯度,而不受历史梯度的影响。

总结

第三次反向传播的核心是梯度清零的作用验证

  • 清零后,梯度不再累积历史值,而是从0开始计算
  • 结果与第一次传播完全相同,证明每次反向传播的梯度计算逻辑一致
  • 这正是神经网络训练中,每个batch前需要清零梯度的原因(确保每个batch的梯度独立计算,不被上一个batch干扰)

您可能感兴趣的与本文相关的镜像

PyTorch 2.7

PyTorch 2.7

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值