自动求导区别于符号求导。
自动求导使用的是Numerical method，用一个很小的h来直接计算f'(x).
$$f'(x) = \lim_{h \rightarrow 0} \frac{f(x+h) - f(x)}{h}.$$
在 PyTorch 等框架中，**`requires_grad=True` 是被训练权重的标识**。只有标记为 `True` 的张量才会在反向传播时被计算并保存梯度，并最终被优化器更新；标记为 `False` 的张量（如输入数据、中间缓存）**不会改变**，仅作为数据载体参与计算。

## 例子：单层线性网络

我们首先定义一个标准的单层线性变换，明确每个张量的**形状（Shape）**：

- 输入向量：$x \in \mathbb{R}^{n \times 1}$（列向量）
- 权重矩阵：$W \in \mathbb{R}^{m \times n}$
- 偏置向量：$b \in \mathbb{R}^{m \times 1}$（本实例中忽略，设为 0）
- 输出向量：$z = Wx + b \quad \in \mathbb{R}^{m \times 1}$
- 标量损失（平方和的一半）：$L = \frac{1}{2} \sum_{i=1}^{m} z_i^2 \quad \in \mathbb{R}$

> - $\frac{\partial L}{\partial z}$ 的形状必须为 $\mathbb{R}^{m \times 1}$（与 $z$ 一致）
> - $\frac{\partial L}{\partial W}$ 的形状必须为 $\mathbb{R}^{m \times n}$（与 $W$ 一致）
> - $\frac{\partial L}{\partial x}$ 的形状必须为 $\mathbb{R}^{n \times 1}$（与 $x$ 一致）

---

## 2. 具体数值实例设定（严格区分 `requires_grad`）

为了兼顾后续的“参数更新”解释，我们构建一个**2层微型网络**，并明确哪些变量是固定的，哪些是待训练的。

### 2.1 输入数据（固定，`requires_grad=False`）
数据值在整个训练过程中 **永恒不变**。
$$
x \in \mathbb{R}^{3 \times 1}, \quad x = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}
$$

### 2.2 隐藏层权重（待训练，`requires_grad=True`）
即将被优化器更新，**数值会发生改变**。
$$
W_1 \in \mathbb{R}^{2 \times 3}, \quad W_1 = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}
$$

### 2.3 输出层权重（待训练，`requires_grad=True`）
即将被优化器更新，**数值会发生改变**。
$$
W_2 \in \mathbb{R}^{1 \times 2}, \quad W_2 = \begin{bmatrix} 0.5 & -0.5 \end{bmatrix}
$$

---

## 3. 前向传播（Forward Pass）

### 步骤 3.1：第 1 层线性变换
**通用公式**：
$$
z = W_1 x \quad \text{其中 } z \in \mathbb{R}^{2 \times 1}
$$

**数值实例**：
$$
z = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix} = \begin{bmatrix} 1+4+9 \\ 4+10+18 \end{bmatrix} = \begin{bmatrix} 14 \\ 32 \end{bmatrix}
$$

### 步骤 3.2：第 2 层线性变换（输出标量得分）
**通用公式**：
$$
a = W_2 z \quad \text{其中 } a \in \mathbb{R}^{1 \times 1} \text{（标量）}
$$

**数值实例**：
$$
a = \begin{bmatrix} 0.5 & -0.5 \end{bmatrix} \begin{bmatrix} 14 \\ 32 \end{bmatrix} = 7 - 16 = -9
$$

### 步骤 3.3：计算标量损失
**通用公式**：
$$
L = \frac{1}{2} a^2 \quad \text{其中 } L \in \mathbb{R}
$$

**数值实例**：
$$
L = \frac{1}{2} \times (-9)^2 = 40.5
$$

---

## 4. 反向传播（链式法则的严格形状推导与数值实例）

这是最核心的部分。我们将严格按照“**先写通用形状公式，再代入数值**”的顺序进行。

### 4.1 起点：损失函数对标量 $a$ 求导
**通用数学表达式与形状**：
$$
\frac{\partial L}{\partial a} = a \quad \text{其中 } \frac{\partial L}{\partial a} \in \mathbb{R}^{1 \times 1} \text{（标量）}
$$

**数值实例**：
$$
\frac{\partial L}{\partial a} = -9
$$

---

### 4.2 梯度回传至中间变量 $z$（从输出层传向隐藏层）
**通用数学表达式与形状（矩阵乘法法则）**：
$$
\boxed{\frac{\partial L}{\partial z} = W_2^T \cdot \frac{\partial L}{\partial a}}
$$
**形状验证**：$W_2^T \in \mathbb{R}^{2 \times 1}$，$\frac{\partial L}{\partial a} \in \mathbb{R}^{1 \times 1}$  
结果形状 = $(2 \times 1) \times (1 \times 1) = \mathbb{R}^{2 \times 1}$，完美匹配 $z$ 的形状。

**数值实例**：
$$
\frac{\partial L}{\partial z} = \begin{bmatrix} 0.5 \\ -0.5 \end{bmatrix} \times (-9) = \begin{bmatrix} -4.5 \\ 4.5 \end{bmatrix}
$$

---

### 4.3 计算输出层权重 $W_2$ 的梯度（该参数将被更新）
**通用数学表达式与形状（上游梯度外积本层输入）**：
$$
\boxed{\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial a} \cdot z^T}
$$
**形状验证**：$\frac{\partial L}{\partial a} \in \mathbb{R}^{1 \times 1}$，$z^T \in \mathbb{R}^{1 \times 2}$  
结果形状 = $(1 \times 1) \times (1 \times 2) = \mathbb{R}^{1 \times 2}$，完美匹配 $W_2$ 的形状 $(1 \times 2)$。

**数值实例**：
$$
\frac{\partial L}{\partial W_2} = (-9) \times \begin{bmatrix} 14 & 32 \end{bmatrix} = \begin{bmatrix} -126 & -288 \end{bmatrix}
$$

> ✅ 因为 $W_2$ 的 `requires_grad=True`，此梯度将被保存至 `W_2.grad`，用于后续更新。

---

### 4.4 计算隐藏层权重 $W_1$ 的梯度（该参数将被更新）
此时传入 $W_1$ 节点的上游梯度正是 $\frac{\partial L}{\partial z}$。

**通用数学表达式与形状（上游梯度外积本层输入 $x$）**：
$$
\boxed{\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial z} \cdot x^T}
$$
**形状验证**：$\frac{\partial L}{\partial z} \in \mathbb{R}^{2 \times 1}$，$x^T \in \mathbb{R}^{1 \times 3}$  
结果形状 = $(2 \times 1) \times (1 \times 3) = \mathbb{R}^{2 \times 3}$，完美匹配 $W_1$ 的形状 $(2 \times 3)$。

**数值实例（外积计算）**：
$$
\frac{\partial L}{\partial W_1} = \begin{bmatrix} -4.5 \\ 4.5 \end{bmatrix} \times \begin{bmatrix} 1 & 2 & 3 \end{bmatrix} = \begin{bmatrix}
-4.5 \times 1 & -4.5 \times 2 & -4.5 \times 3 \\
4.5 \times 1 & 4.5 \times 2 & 4.5 \times 3
\end{bmatrix} = \begin{bmatrix}
-4.5 & -9 & -13.5 \\
4.5 & 9 & 13.5
\end{bmatrix}
$$

> ✅ 因为 $W_1$ 的 `requires_grad=True`，此梯度将被保存至 `W_1.grad`，用于后续更新。

---

### 4.5 计算输入 $x$ 的梯度（仅用于回传，不更新参数）
虽然 $x$ 的 `requires_grad=False`，但为了将误差继续传回更前面的层，框架必须计算它。

**通用数学表达式与形状（权重转置乘以上游梯度）**：
$$
\boxed{\frac{\partial L}{\partial x} = W_1^T \cdot \frac{\partial L}{\partial z}}
$$
**形状验证**：$W_1^T \in \mathbb{R}^{3 \times 2}$，$\frac{\partial L}{\partial z} \in \mathbb{R}^{2 \times 1}$  
结果形状 = $(3 \times 2) \times (2 \times 1) = \mathbb{R}^{3 \times 1}$，完美匹配 $x$ 的形状 $(3 \times 1)$。

**数值实例**：
$$
\frac{\partial L}{\partial x} = \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix} \times \begin{bmatrix} -4.5 \\ 4.5 \end{bmatrix} = \begin{bmatrix}
1\times(-4.5) + 4\times4.5 \\
2\times(-4.5) + 5\times4.5 \\
3\times(-4.5) + 6\times4.5
\end{bmatrix} = \begin{bmatrix}
13.5 \\
13.5 \\
13.5
\end{bmatrix}
$$

> ❌ 由于 `x.requires_grad=False`，这个梯度结果会被框架**直接丢弃**，不会分配给 `x.grad`，`x` 的值保持 `[1, 2, 3]^T` 不变。

---

## 5. 梯度更新（SGD 优化器视角）

有了上述梯度，优化器执行参数更新。我们只更新 `requires_grad=True` 的张量。

假设学习率 $\eta = 0.01$，更新公式为：
$$
\theta_{\text{new}} = \theta_{\text{old}} - \eta \cdot \frac{\partial L}{\partial \theta}
$$

### 5.1 更新 $W_2$（被训练，值发生改变）
**形状**：$\mathbb{R}^{1 \times 2}$
$$
W_2^{\text{new}} = \begin{bmatrix} 0.5 & -0.5 \end{bmatrix} - 0.01 \times \begin{bmatrix} -126 & -288 \end{bmatrix}
$$
$$
= \begin{bmatrix} 0.5 + 1.26 & -0.5 + 2.88 \end{bmatrix} = \boxed{\begin{bmatrix} 1.76 & 2.38 \end{bmatrix}}
$$

### 5.2 更新 $W_1$（被训练，值发生改变）
**形状**：$\mathbb{R}^{2 \times 3}$
$$
W_1^{\text{new}} = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} - 0.01 \times \begin{bmatrix} -4.5 & -9 & -13.5 \\ 4.5 & 9 & 13.5 \end{bmatrix}
$$
$$
= \begin{bmatrix} 1+0.045 & 2+0.09 & 3+0.135 \\ 4-0.045 & 5-0.09 & 6-0.135 \end{bmatrix} = \boxed{\begin{bmatrix} 1.045 & 2.09 & 3.135 \\ 3.955 & 4.91 & 5.865 \end{bmatrix}}
$$

### 5.3 查看 $x$（未被训练，值永恒不变）
**形状**：$\mathbb{R}^{3 \times 1}$
$$
x^{\text{new}} = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}
$$

> 尽管反向传播算出了 $\frac{\partial L}{\partial x} = \begin{bmatrix} 13.5 \\ 13.5 \\ 13.5 \end{bmatrix}$，但优化器的 `step()` 方法根本不会遍历 $x$，因此它的数值 **纹丝不动**。

---

## 6. 自动求导（Autograd）与 `requires_grad` 的核心机制汇总

下表清晰总结了在整个链式法则执行过程中，不同张量的命运：

| 变量            | 数学形状                      | `requires_grad` | 反向传播是否计算梯度？   | 是否存储 `.grad`？   | 优化器是否更新？ | **最终值是否改变？** |
| :------------ | :------------------------ | :-------------- | :------------ | :-------------- | :------- | :----------- |
| **$x$（输入）**   | $\mathbb{R}^{3 \times 1}$ | `False`         | **是**（公式 4.5） | **否**           | **否**    | **❌ 不变**     |
| **$z$（中间）**   | $\mathbb{R}^{2 \times 1}$ | `False`         | **是**（仅传递）    | **否**           | **否**    | **❌ 不变**     |
| **$W_1$（权重）** | $\mathbb{R}^{2 \times 3}$ | `True`          | **是**（公式 4.4） | **是**（存入 .grad） | **是**    | **✅ 改变**     |
| **$W_2$（权重）** | $\mathbb{R}^{1 \times 2}$ | `True`          | **是**（公式 4.3） | **是**（存入 .grad） | **是**    | **✅ 改变**     |

---

## 7. 总结：夯实基础的三大铁律（形状篇）

1. **形状即正义**：写任何梯度公式前，先画形状。例如，$\frac{\partial L}{\partial W}$ 必须等于 $W$ 的形状。如果形状对不上，公式一定写错了。
2. **更新与传播的分水岭**：
   - **更新参数**（$W$）时，用 **上游梯度** 外积 **本层输入**（$\frac{\partial L}{\partial W} = \delta \cdot x^T$）。
   - **传播梯度**（给 $x$）时，用 **本层权重转置** 乘以 **上游梯度**（$\frac{\partial L}{\partial x} = W^T \cdot \delta$）。
3. **`requires_grad` 决定物理改变**：无论反向传播计算了多少中间梯度，只有标记为 `True` 且被传入优化器的张量，其**数值才会在内存中被改写**。

现在，您已经具备了从**数学表达式**到**数值实例**，再到**框架底层机制**的完整认知链。这在深度学习的进阶之路上是最坚实的基石。

---

# 🔒 `requires_grad` 视角下的链式法则与参数更新实例

## 1. 核心规则预设（明确训练目标）

我们定义以下张量的 `requires_grad` 状态：

- **输入数据** $x$：`requires_grad=False`（固定数据，不更新）。
- **隐藏层权重** $W_1$：`requires_grad=True`（**待训练参数**）。
- **输出层权重** $W_2$：`requires_grad=True`（**待训练参数**）。
- **中间变量** $z, a$：均为计算过程中的派生张量，默认 `requires_grad=False`（框架不直接更新它们）。

> 🚨 优化器（如 SGD）的 `step()` 方法只会迭代 **`requires_grad=True`** 且属于 `parameters()` 列表的张量。

---

## 2. 设定具体数值（严格区分固定与可变）

为了清晰区分，我们将三个张量分开陈列，注释全部放在数学公式之外。

> **[!info] 训练身份标识**
> - `requires_grad = False`：**固定数据**（仅作为原始输入）。
> - `requires_grad = True`：**待训练参数**（数值即将在反向传播后发生改变）。

**① 输入数据（固定，不可训练）**  
`requires_grad = False`，其数值在整个训练过程中 **不变**。

$$
x = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}
$$

**② 隐藏层权重（待训练，即将改变）**  
`requires_grad = True`，反向传播计算出梯度后，优化器将更新其数值。

$$
W_1 = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}
$$

**③ 输出层权重（待训练，即将改变）**  
`requires_grad = True`，反向传播计算出梯度后，优化器将更新其数值。

$$
W_2 = \begin{bmatrix} 0.5 & -0.5 \end{bmatrix}
$$

---

## 3. 前向传播（所有变量参与，但仅参数被监控）

**第1层线性变换**：
$$
z = W_1 x = \begin{bmatrix} 1\times1 + 2\times2 + 3\times3 \\ 4\times1 + 5\times2 + 6\times3 \end{bmatrix} = \begin{bmatrix} 14 \\ 32 \end{bmatrix}
$$

**第2层线性变换（得到标量得分）**：
$$
a = W_2 z = \begin{bmatrix} 0.5 & -0.5 \end{bmatrix} \begin{bmatrix} 14 \\ 32 \end{bmatrix} = -9
$$

**标量损失（平方损失的一半）**：
$$
L = \frac{1}{2} a^2 = 40.5
$$

> **此时计算图已构建**：Autograd 引擎记录了所有依赖关系。它知道 $W_1$ 和 $W_2$ 带有 `requires_grad=True`，因此会为它们预留梯度存储空间（`.grad`），而 $x$ 由于是 `False`，其梯度空间不会被分配。

---

## 4. 反向传播（链式法则的数值实例）

### 4.1 起点：损失对标量 $a$ 求导
$$
\frac{\partial L}{\partial a} = a = -9
$$

### 4.2 回传至 $z$（上游梯度流向 $W_2$ 和 $W_1$）
$$
\frac{\partial L}{\partial z} = W_2^T \cdot \frac{\partial L}{\partial a} = \begin{bmatrix} 0.5 \\ -0.5 \end{bmatrix} \times (-9) = \begin{bmatrix} -4.5 \\ 4.5 \end{bmatrix}
$$

### 4.3 计算 $W_2$ 的梯度（$\boldsymbol{W_2}$ 待训练）
根据链式法则，使用**本层输入** $z$：
$$
\frac{\partial L}{\partial W_2} = \frac{\partial L}{\partial a} \cdot z^T = (-9) \times \begin{bmatrix} 14 & 32 \end{bmatrix} = \begin{bmatrix} -126 & -288 \end{bmatrix}
$$

> ✅ **这个梯度值会被保存到 `W_2.grad` 中**，因为 `W_2.requires_grad=True`。

### 4.4 计算 $W_1$ 的梯度（$\boldsymbol{W_1}$ 待训练）
传入 $W_1$ 的上游梯度是 $\frac{\partial L}{\partial z}$，使用**本层输入** $x$：
$$
\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial z} \cdot x^T = \begin{bmatrix} -4.5 \\ 4.5 \end{bmatrix} \times \begin{bmatrix} 1 & 2 & 3 \end{bmatrix} = \begin{bmatrix}
-4.5 & -9 & -13.5 \\
4.5 & 9 & 13.5
\end{bmatrix}
$$

> ✅ **这个梯度值会被保存到 `W_1.grad` 中**，因为 `W_1.requires_grad=True`。

### 4.5 计算 $x$ 的梯度（$\boldsymbol{x}$ 被固定，不训练）
为了让梯度能传回 $W_1$，框架**必须**计算 $\frac{\partial L}{\partial x}$（因为 $W_1$ 的梯度依赖于 $x$，链式法则需要它作为中间因子）。计算如下：
$$
\frac{\partial L}{\partial x} = W_1^T \cdot \frac{\partial L}{\partial z} = \begin{bmatrix} 1 & 4 \\ 2 & 5 \\ 3 & 6 \end{bmatrix} \times \begin{bmatrix} -4.5 \\ 4.5 \end{bmatrix} = \begin{bmatrix} 13.5 \\ 13.5 \\ 13.5 \end{bmatrix}
$$

> ❌ **但是这个梯度值会被直接丢弃（或被临时缓存后清空）**，因为 `x.requires_grad=False`。框架不会为 `x` 分配 `.grad` 属性，优化器也根本看不到 $x$。

---

## 5. 梯度更新（SGD 优化器视角）

假设学习率 $\eta = 0.01$。优化器 `optimizer.step()` 的内部逻辑是：

```python
for param in [W1, W2]:  # 只遍历 requires_grad=True 的参数
    param.data -= lr * param.grad
```

**更新 $W_2$（被训练）**：
$$
W_2^{\text{new}} = W_2 - 0.01 \times \frac{\partial L}{\partial W_2} = \begin{bmatrix} 0.5 & -0.5 \end{bmatrix} - 0.01 \times \begin{bmatrix} -126 & -288 \end{bmatrix}
$$
$$
= \begin{bmatrix} 0.5 + 1.26 & -0.5 + 2.88 \end{bmatrix} = \boxed{\begin{bmatrix} 1.76 & 2.38 \end{bmatrix}}
$$

**更新 $W_1$（被训练）**：
$$
W_1^{\text{new}} = W_1 - 0.01 \times \frac{\partial L}{\partial W_1} = \begin{bmatrix}
1 & 2 & 3 \\
4 & 5 & 6
\end{bmatrix} - 0.01 \times \begin{bmatrix}
-4.5 & -9 & -13.5 \\
4.5 & 9 & 13.5
\end{bmatrix}
$$
$$
= \begin{bmatrix}
1+0.045 & 2+0.09 & 3+0.135 \\
4-0.045 & 5-0.09 & 6-0.135
\end{bmatrix} = \boxed{\begin{bmatrix}
1.045 & 2.09 & 3.135 \\
3.955 & 4.91 & 5.865
\end{bmatrix}}
$$

**查看 $x$（未被训练，原封不动）**：
$$
x^{\text{new}} = x = \begin{bmatrix} 1 \\ 2 \\ 3 \end{bmatrix}
$$

> 注意看！即使反向传播计算了 $\frac{\partial L}{\partial x}$（`[13.5, 13.5, 13.5]`），**$x$ 的值绝对没有加上或减去任何东西**。它依然是 `[1, 2, 3]`。

---

## 6. 自动求导（Autograd）的底层运作机制

在反向传播的链式法则执行过程中，自动求导引擎遵循一套严格的“资格检查”逻辑：

| 变量 | `requires_grad` | 反向时是否计算梯度？ | 反向时是否**存储** `.grad`？ | 优化器是否更新其值？ | **训练后值是否改变？** |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **$x$（输入）** | `False` | **是**（作为计算因子必算） | **否**（内存即时释放） | **否**（不在参数列表） | **❌ 绝对不变** |
| **$z, a$（中间）** | `False`（默认） | **是**（仅传递上游梯度） | **否**（除非 `retain_grad()`） | **否** | **❌ 绝对不变** |
| **$W_1$（权重）** | `True` | **是** | **是**（存入 `.grad`） | **是**（核心目标） | **✅ 发生改变** |
| **$W_2$（权重）** | `True` | **是** | **是**（存入 `.grad`） | **是**（核心目标） | **✅ 发生改变** |

---

## 7. 深度学习筑基总结（基于 `requires_grad` 的思维导图）

```mermaid
graph TD
    A[前向计算 L] --> B{Autograd 构建计算图};
    B --> C[反向传播求导];
    C --> D{检查每个张量的 requires_grad};
    D -->|True| E[保存梯度到 .grad 属性];
    D -->|False| F[计算梯度但立即丢弃 / 不存储];
    E --> G[优化器 step 遍历 parameters];
    G --> H[参数 = 参数 - 学习率 × 参数.grad];
    H --> I[✅ 权重值发生改变];
    F --> J[❌ 数据/中间值保持原样];
```

---

**最终核心结论**：

1. **被训练的**：只有 `requires_grad=True` 且被传入优化器的张量。在这个实例中，**只有 $W_1$ 和 $W_2$ 的值发生了改变**。
2. **未被训练的**：`requires_grad=False` 的输入数据 $x$，以及中间激活值 $z$ 和 $a$。即使反向传播为了链式法则“路过”它们时计算了导数，**它们的数值也绝不会因训练而改变**。

