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

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


完整代码

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

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


代码 1

python 复制代码
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

python 复制代码
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

python 复制代码
out.backward()

解释

  1. out.backward()

    • out 调用 .backward() 会计算 x 的梯度。
    • 梯度的计算目标 :计算 outx 的偏导数,即 ∂ out ∂ x \frac{\partial \text{out}}{\partial x} ∂x∂out。
  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} ∂xij∂out=2⋅xij

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

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

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

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

代码 4

python 复制代码
print(x.grad)

解释

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

结果

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

代码的整体逻辑总结

  1. 初始化张量

    python 复制代码
    x = torch.tensor([[2., -1.], [1., 1.]], requires_grad=True)

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

  2. 前向计算

    python 复制代码
    out = x.pow(2).sum()

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

  3. 反向传播

    python 复制代码
    out.backward()

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

  4. 打印梯度

    python 复制代码
    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 维张量),梯度是输入张量的每个元素对这个标量的偏导数,梯度张量的形状与输入张量一致。

    例子:

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

    • 如果输出张量是多维的,例如 x.pow(2) 的结果:

      python 复制代码
      out = x.pow(2)

      out 的形状是 (2, 2)

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


如何解决

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

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

python 复制代码
out = x.pow(2).sum()  # 转为标量
out.backward()

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

python 复制代码
out = x.pow(2).mean()  # 转为标量
out.backward()

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

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

示例

python 复制代码
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 ∂x∂out=2x。

总结

  • backward() 要求输出必须是标量,否则会报错。
  • 可以通过 .sum().mean() 将多维张量转换为标量。
  • 对于特殊情况,可以使用 backward(gradient=...) 指定非标量输出的梯度权重。
相关推荐
遇健李的幸运1 分钟前
深入浅出:Agent如何调用工具——从OpenAI Function Call到CrewAI框架
人工智能
天天讯通3 分钟前
AI语音机器人大模型是什么?
人工智能·机器人
说私域10 分钟前
微商关系维系与服务创新:链动2+1模式、AI智能名片与S2B2C商城小程序的应用研究
人工智能·小程序
hnmpf23 分钟前
flask_sqlalchemy relationship 子表排序
后端·python·flask
人机与认知实验室24 分钟前
人-AI协同如何重塑未来战争?
人工智能
学技术的大胜嗷25 分钟前
小目标检测难点分析和解决策略
人工智能·目标检测·计算机视觉
疯狂学习GIS34 分钟前
互联网大中小厂实习面经:滴滴、美团、货拉拉、蔚来、信通院等
c++·python
Nobita Chen1 小时前
Python实现windows自动关机
开发语言·windows·python
码路刺客1 小时前
一学就废|Python基础碎片,OS模块
开发语言·python
z千鑫1 小时前
【Python】Python之Selenium基础教程+实战demo:提升你的测试+测试数据构造的效率!
开发语言·python·selenium