欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > pytorch小记(五):pytorch中的求导操作:backward()

pytorch小记(五):pytorch中的求导操作:backward()

2025/1/14 8:09:49 来源:https://blog.csdn.net/xzs1210652636/article/details/145094950  浏览:    关键词:pytorch小记(五):pytorch中的求导操作:backward()

pytorch小记(五):pytorch中的求导操作:backward()

  • 完整代码
  • 代码 1
    • 解释
  • 代码 2
    • 解释
  • 代码 3
    • 解释
  • 代码 4
    • 解释
  • 代码的整体逻辑总结
  • 补充:`requires_grad` 与 `backward` 的作用
  • 疑问
    • 原因分析
    • 如何解决
      • 方法 1:通过 `.sum()` 转为标量
      • 方法 2:通过 `.mean()` 转为标量
    • 高级用法:为非标量指定梯度权重
      • 示例
    • 总结


完整代码


x = torch.tensor([[2., -1.], [1., 1.]], requires_grad=True)
print(x)
out = x.pow(2).sum()
print(out)
out.backward()
print(x.grad)
>>>
tensor([[ 2., -1.],[ 1.,  1.]], requires_grad=True)
tensor(9., grad_fn=<SumBackward0>)
tensor([[12.,  3.],[ 3.,  3.]])

让我们逐行详细分析代码的执行逻辑及其背后的原理。


代码 1

x = torch.tensor([[2., -1.], [1., 1.]], requires_grad=True)
print(x)

解释

  1. torch.tensor

    • 创建一个二维张量 x,内容为 [[2., -1.], [1., 1.]]
    • 数据类型为浮点数(float32),因为传入的是小数。
  2. requires_grad=True

    • 启用自动求导功能。
    • 任何以 x 为输入的张量计算图都会记录下来,用于后续的梯度计算。
  3. 结果
    打印张量 x

    tensor([[ 2., -1.],[ 1.,  1.]], requires_grad=True)
    

代码 2

out = x.pow(2).sum()
print(out)

解释

  1. x.pow(2)

    • 计算 x 的每个元素的平方。
    • 张量的每个元素被逐元素计算: x i j 2 x_{ij}^2 xij2

    计算结果:

    tensor([[4., 1.],[1., 1.]], requires_grad=True)
    
  2. .sum()

    • 对张量中所有元素求和,计算结果为:
      4 + 1 + 1 + 1 = 7 4 + 1 + 1 + 1 = 7 4+1+1+1=7
    • requires_grad=True 表示 out 依赖于 x,可以反向传播。
  3. 结果
    打印 out

    tensor(7., grad_fn=<SumBackward0>)
    
    • grad_fn=<SumBackward0> 表示这是通过 sum() 操作计算得出的,反向传播时会回溯到这个操作。

代码 3

out.backward()

解释

  1. out.backward()

    • out 调用 .backward() 会计算 x 的梯度。
    • 梯度的计算目标:计算 outx 的偏导数,即 ∂ out ∂ x \frac{\partial \text{out}}{\partial x} xout
  2. 计算过程

    • out = x.pow(2).sum(),展开公式为:
      out = x 11 2 + x 12 2 + x 21 2 + x 22 2 \text{out} = x_{11}^2 + x_{12}^2 + x_{21}^2 + x_{22}^2 out=x112+x122+x212+x222

    • x_{ij} 求偏导:
      ∂ out ∂ x i j = 2 ⋅ x i j \frac{\partial \text{out}}{\partial x_{ij}} = 2 \cdot x_{ij} xijout=2xij

    • 每个元素的梯度计算结果:

      [[2 * 2., 2 * -1.],[2 * 1., 2 * 1.]]
      
    • 结果为:

      [[ 4., -2.],[ 2.,  2.]]
      
  3. 梯度存储

    • 计算出的梯度会存储在 x.grad 中。

代码 4

print(x.grad)

解释

  1. 打印 x 的梯度,即 x.grad
  2. x.grad 包含了之前通过 .backward() 计算的梯度值。

结果

tensor([[ 4., -2.],[ 2.,  2.]])

代码的整体逻辑总结

  1. 初始化张量

    x = torch.tensor([[2., -1.], [1., 1.]], requires_grad=True)
    

    创建一个二维张量,并启用梯度跟踪。

  2. 前向计算

    out = x.pow(2).sum()
    

    计算 x 的平方和,得到标量 out

  3. 反向传播

    out.backward()
    

    自动计算 outx 的梯度,结果存储在 x.grad 中。

  4. 打印梯度

    print(x.grad)
    

    查看 x 的梯度,其值为:

    [[ 4., -2.],[ 2.,  2.]]
    

    这表明 outx 的变化率是该梯度值。


补充:requires_gradbackward 的作用

  • requires_grad=True
    • 启用张量的自动求导功能,参与计算的操作会记录到计算图中。
  • backward()
    • 执行反向传播,沿着计算图回溯并计算梯度。
    • 梯度存储在 x.grad 中,用于优化或调试。

疑问

在 PyTorch 中,out.backward() 是用来计算张量的梯度的操作。然而,当调用 backward() 时,输出张量必须是标量(即只有一个数值)。如果输出张量不是标量,就会引发以下错误:

RuntimeError: grad can be implicitly created only for scalar outputs

原因分析

  1. 标量输出的要求

    • backward() 会计算输入张量(如 x)对输出张量(如 out)的梯度。
    • 如果输出张量是标量(0 维张量),梯度是输入张量的每个元素对这个标量的偏导数,梯度张量的形状与输入张量一致。

    例子:

    out = x.pow(2).sum()
    out.backward()
    
    • 这里 out 是一个标量,因此可以计算梯度。
  2. 非标量输出的情况

    • 如果输出张量是多维的,例如 x.pow(2) 的结果:
      out = x.pow(2)
      
      out 的形状是 (2, 2)
      tensor([[4., 1.],[1., 1.]], requires_grad=True)
      
    • 这种情况下,backward() 不知道如何将梯度传播到输入张量 x,因为输出张量的每个元素都可能有不同的梯度。

如何解决

如果想对多维张量调用 backward(),需要将其 转换为标量,例如通过求和或取均值。

方法 1:通过 .sum() 转为标量

out = x.pow(2).sum()  # 转为标量
out.backward()

方法 2:通过 .mean() 转为标量

out = x.pow(2).mean()  # 转为标量
out.backward()

高级用法:为非标量指定梯度权重

如果你需要对非标量张量调用 backward(),可以通过 out.backward(gradient=...) 指定梯度权重,告诉 PyTorch 如何将梯度聚合到输入张量。

示例

out = x.pow(2)  # 非标量张量,形状为 (2, 2)
gradient = torch.ones_like(out)  # 权重张量,形状必须与 out 相同
out.backward(gradient=gradient)

解释

  • gradient=... 指定了 out 每个元素在反向传播中的权重。
  • 例如,对于 o u t = x 2 out = x^2 out=x2,若 gradient=1,梯度仍然是 ∂ o u t ∂ x = 2 x \frac{\partial out}{\partial x} = 2x xout=2x

总结

  • backward() 要求输出必须是标量,否则会报错。
  • 可以通过 .sum().mean() 将多维张量转换为标量。
  • 对于特殊情况,可以使用 backward(gradient=...) 指定非标量输出的梯度权重。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com