3.2 练习

image

在使用torch.autograd.grad计算二阶导数时,可以仅设置create_graph=True而无需设置retain_graph=True。以下是关键点总结:

  1. create_graph的作用
    当设置为True时,会保留梯度计算图,使得后续能对梯度再次求导(如计算二阶导数)。这是高阶导数计算的关键参数。

  2. retain_graph的作用
    控制是否在反向传播后保留整个计算图。默认False会释放计算图以节省内存,但在多次调用backward()grad()时需设置为True以避免图被提前释放。

  3. 二阶导数的场景

    • 计算一阶导数时,需设置create_graph=True,以便保留梯度计算图用于二阶导数。
    • 此时即使retain_graph=False,PyTorch仍会保留一阶导数的计算图(因create_graph=True),故无需额外设置retain_graph=True
  4. 示例验证

    import torch
    
    x = torch.tensor([2.0], requires_grad=True)
    y = x ** 3
    
    # 一阶导数:create_graph=True,retain_graph=False
    dy_dx = torch.autograd.grad(y, x, create_graph=True, retain_graph=False)[0]
    print("一阶导数:", dy_dx)  # tensor([12.])
    
    # 二阶导数:直接使用一阶导数的计算图
    d2y_dx2 = torch.autograd.grad(dy_dx, x, retain_graph=False)[0]
    print("二阶导数:", d2y_dx2)  # tensor([12.])
    

    此代码能正确运行,证明即使retain_graph=False,二阶导数仍可计算。

结论
在计算二阶导数时,仅需在一阶导数计算中设置create_graph=True,无需额外设置retain_graph=True。PyTorch会自动保留必要的计算图部分,而释放无关部分以优化内存。

在 PyTorch 中,torch.autograd.grad 的行为与数学中的向量对向量导数略有不同,其设计更贴合深度学习中的实际需求(如反向传播的链式法则)。以下是详细解释:


1. 向量对向量导数的数学定义

数学中,若 y = f(x) 是向量函数(xy 均为向量),则导数是一个雅可比矩阵 (Jacobian Matrix),其维度为 (dim_y, dim_x),每个元素为:

\[J[i, j] = \frac{\partial y[i]}{\partial x[j]} \]

例如,若 xy 均为 2 维向量:

\[x = [x_1, x_2], \quad y = [y_1 = x_1^3, y_2 = x_2^3] \]

则雅可比矩阵为:

\[J = \begin{bmatrix} \frac{\partial y_1}{\partial x_1} & \frac{\partial y_1}{\partial x_2} \\ \frac{\partial y_2}{\partial x_1} & \frac{\partial y_2}{\partial x_2} \end{bmatrix} = \begin{bmatrix} 3x_1^2 & 0 \\ 0 & 3x_2^2 \end{bmatrix} \]


2. PyTorch 的 torch.autograd.grad 行为

PyTorch 的自动微分机制默认设计用于标量对张量的导数(如损失函数对参数的梯度)。当输入和输出均为向量时,torch.autograd.grad 不会直接返回雅可比矩阵,而是通过 grad_outputs 参数实现更灵活的梯度传播。

关键参数 grad_outputs

  • 作用:指定一个与 y 同形状的“权重向量”,用于计算加权和的梯度
  • 数学意义:计算的是雅可比矩阵与 grad_outputs 的乘积:

    \[\text{result} = J^T \cdot \text{grad\_outputs} \]

    其中 \(J\) 是雅可比矩阵,grad_outputs 是外部梯度。

你的代码分析

x = torch.randn((2), requires_grad=True)
y = x ** 3
dy = torch.autograd.grad(
    outputs=y, 
    inputs=x, 
    grad_outputs=torch.ones(x.shape),  # 关键参数
    retain_graph=True, 
    create_graph=True
)
  • grad_outputs=torch.ones(x.shape)
    等价于对 y 的每个分量赋予权重 1,计算的是雅可比矩阵与全 1 向量的乘积:

    \[\text{dy} = J^T \cdot [1, 1] = \begin{bmatrix} 3x_1^2 \cdot 1 + 0 \cdot 1 \\ 0 \cdot 1 + 3x_2^2 \cdot 1 \end{bmatrix} = \begin{bmatrix} 3x_1^2 \\ 3x_2^2 \end{bmatrix} \]

    因此 dy 是一个与 x 同形状的向量,而非雅可比矩阵。

3. 如何获取完整的雅可比矩阵?

若需要显式计算雅可比矩阵,有以下两种方法:

方法 1:逐分量计算梯度

jacobian = []
for i in range(len(y)):
    # 对 y[i] 求梯度,grad_outputs 中仅第 i 个位置为 1
    dy_i = torch.autograd.grad(
        outputs=y[i], 
        inputs=x, 
        grad_outputs=torch.tensor(1.0), 
        retain_graph=True
    )[0]
    jacobian.append(dy_i)
jacobian = torch.stack(jacobian)
print(jacobian)  # 形状为 (2, 2)

方法 2:使用 torch.autograd.functional.jacobian

PyTorch 提供了直接计算雅可比矩阵的工具:

from torch.autograd.functional import jacobian

x = torch.randn(2, requires_grad=True)
y = x ** 3
J = jacobian(func=lambda x: x**3, inputs=x)
print(J)  # 形状为 (2, 2)

4. 参数 retain_graphcreate_graph 的作用

  • retain_graph=True
    保留计算图,允许后续多次调用 backward()grad()。若不设置,计算图会在第一次求导后被释放。

  • create_graph=True
    保留梯度计算图,使得可以对梯度再次求导(如计算二阶导数)。

在你的代码中,设置 create_graph=True 是为了后续可能的高阶导数计算(如对 dy 再求导),而 retain_graph=True 确保计算图不被释放。


5. 总结

  • 向量对向量导数在数学中是雅可比矩阵,但 torch.autograd.grad 默认返回的是 雅可比矩阵与 grad_outputs 的乘积(结果为向量)。
  • 若需完整雅可比矩阵,需显式逐分量计算或使用 jacobian() 函数。
  • 参数 grad_outputs 决定了梯度传播的权重,是理解 grad() 行为的关键。
posted @ 2025-03-08 19:37  最爱丁珰  阅读(40)  评论(0)    收藏  举报