LLM系列:2.pytorch入门:3.基本优化思想与最小二乘法

基本优化思想与最小二乘法

在正式开始进行神经网络建模之前,必须掌握机器学习中最核心的数学工具和思想:最优化方法。PyTorch的核心优势之一也就是提供了AutoGrad(自动微分)模块来辅助最优化计算。

任何一个通用的深度学习框架,都会提供许多自动优化的算法和现成的loss function,PyTorch也不例外,但如果希望能够更深入的理解神经网络、希望对深度学习的建模不仅仅停留在调包和调参的层次,那我们就必须深入一些数学领域、掌握一些数学工具,从底层提升自己的数学能力,以期能够在日后的使用深度学习算法的过程中能够更加灵活的解决问题、取得更好的建模效果。而AutoGrad模块,就是PyTorch提供的最核心的数学工具模块,我们可以利用其编写一系列的最优化方法,当然,要使用好微分工具,就首先需要了解广泛应用于机器学习建模的优化思想。

所谓优化思想,指的是利用数学工具求解复杂问题:先给出待解决问题的数值评估指标,在此基础上构建目标方程,最后采用数学工具寻找方程的最优解。

一.机器学习建模一般流程

机器学习(包含深度学习)中最通用的建模步骤:

  1. Step 1:提出基本模型。 比如设定线性回归方程 y=ax+by = ax + by=ax+b 或更复杂的神经网络模型结构,初始化参数。

  2. Step 2:确定损失函数和目标函数。 围绕让预测值尽可能接近真实值的目标,设立数值评估指标(如构造SSE损失函数)。

    损失函数 vs 目标函数:

    目标函数 = 损失函数 + 正则化项(惩罚项)

    • 损失函数(Loss Function): 衡量真实值和预测值之间差异的函数(经典损失函数: SSE - 误差平方和)。
    • 目标函数(Objective Function): 我们需要去优化(求最小值或最大值)的最终方程。
    • 大多数目标函数都由损失函数构成,在简单线性回归中,SSE既是损失函数也是目标函数。我们的最终目标是:寻找a和b的取值,使得 SSE 取得最小值。
  3. Step 3:选择优化方法,求解目标函数。 根据目标函数特性(如是否为凸函数、是否可导等),选择合适的数学优化工具(最小二乘法、梯度下降、拟牛顿法等)求解最小值,从而得出最终的模型参数。

    最小二乘法原理

    函数的凹凸性: 对于任意函数 f(x)f(x)f(x),若其上存在任意两点 x1,x2x_1, x_2x1,x2,且满足:

    f(x1)+f(x2)2≥f(x1+x22)\frac{f(x_1) + f(x_2)}{2} \ge f(\frac{x_1 + x_2}{2})2f(x1)+f(x2)≥f(2x1+x2)

    我们判定该函数为凸函数,呈现"向下凸"的碗状。(如y=x2y = x^2y=x2)。

    凸函数的最小值求解法则:

    对于凸函数,全域最小值明显存在,且在最小值点,函数的偏导数必为 0(也就是切面与坐标面平行的位置)。

    • 一元函数: 存在导数为0的点即为最小值点。
    • 多元函数: 各个自变量的偏导数均同时为0的点即为最小值点。

    概念辨析(驻点 vs 拐点):
    导数为0(所有偏导都为0)的点称为驻点(临界点)。如果凸函数存在驻点,该驻点往往就是极值点。而拐点特指函数凹凸性发生改变的点,切勿将其与驻点混淆!

二.简单线性回归的机器学习建模思路

传统的线性代数方法是通过逆矩阵求解线性方程组,而在机器学习中,我们更通用的是将其转化为最优化问题进行求解。

1.提出基本模型

如果我们在二维空间中找到一条直线,来拟合离散的数据点(如点 (1, 2)(3, 4) ),我们可以构建一个基础的简单线性回归模型:
y=ax+b y = ax + b y=ax+b


2.转化为最优化问题

建立模型的首要目标是:希望方程的预测值(y^\hat{y}y^)和真实值(yyy)相差尽可能小。

带入已知的数据点计算误差:

  • 点1预测误差:y1−y^1=2−(a+b)=2−a−by_1 - \hat{y}_1 = 2 - (a + b) = 2 - a - by1−y^1=2−(a+b)=2−a−b
  • 点2预测误差:y2−y^2=4−(3a+b)=4−3a−by_2 - \hat{y}_2 = 4 - (3a + b) = 4 - 3a - by2−y^2=4−(3a+b)=4−3a−b

为了避免正负误差相互抵消,我们计算误差总和时使用平方和,即误差平方和 (SSE - Sum of the Squared Errors):
SSE=(y1−y^1)2+(y2−y^2)2SSE = (y_1 - \hat{y}_1)^2 + (y_2 - \hat{y}_2)^2SSE=(y1−y^1)2+(y2−y^2)2
SSE=(2−a−b)2+(4−3a−b)2SSE = (2 - a - b)^2 + (4 - 3a - b)^2SSE=(2−a−b)2+(4−3a−b)2


3.函数的凹凸性与最小值求解

要利用数学工具求二元函数 SSE(a,b)SSE(a,b)SSE(a,b) 的最小值,首先需要探索函数的性质:这个是凸函数,找出梯度为0的驻点就能取到目标函数最小值。

三. 核心优化算法:最小二乘法 (Least Squares Method)

最小二乘法的核心思想:通过最小化误差的平方和(残差平方和),寻找与数据匹配度最高的函数模型。在数学操作上,体现为构建损失函数后,利用偏导数等于 0 求解方程组,从而直接得出全局最优的参数解。即:在目标函数可导且为凸函数的情况下,梯度为0的驻点取到目标函数最小值。


1. 最小二乘法的一般性求解流程

无论是简单的一元回归还是复杂的多元回归,最小二乘法的代数求解均遵循以下标准流程:

  1. 定义模型方程:设定假设函数,例如多元线性回归 f(x)=w1x1+w2x2+...+wdxd+bf(x) = w_1x_1 + w_2x_2 + ... + w_dx_d + bf(x)=w1x1+w2x2+...+wdxd+b。
  2. 构建损失函数 (SSE):将所有样本的实际观测值 yiy_iyi 与模型预测值 f(xi)f(x_i)f(xi) 的误差进行平方并求和,得到误差平方和方程 SSE=∑i=1n(yi−f(xi))2SSE = \sum_{i=1}^{n} (y_i - f(x_i))^2SSE=∑i=1n(yi−f(xi))2。
  3. 计算偏导数:对损失函数中所有的未知参数(w1,w2,...,wdw_1, w_2, ..., w_dw1,w2,...,wd 以及 bbb)分别求偏导数。
  4. 令偏导为零解方程:将所有偏导数等式置为 0(因为二次凸函数的极值点即为导数为 0 的点),联立求解这个线性方程组,即可得到各个参数的极值解。

2. 最小二乘法的矩阵解析解推导

数学补充:

  1. 双竖线 ∣∣⋅∣∣|| \cdot ||∣∣⋅∣∣:范数 (Norm)

    在数学中,双竖线用来表示一个向量的"大小"或"长度"。就像绝对值 ∣x∣|x|∣x∣ 表示实数的大小一样,∣∣v∣∣||v||∣∣v∣∣ 表示向量 vvv 在空间中的大小。

  2. 下标 2_22:L2 范数 (L2 Norm)

    向量有多种衡量大小的方法(比如 L1 曼哈顿距离),下标的 2 代表我们使用的是 L2 范数(欧几里得距离)。

    它的计算方法就是把向量里所有元素平方、求和、再开根号(就是勾股定理在多维空间的延伸)。

    假设有一个误差向量 e=[e1e2e3]e = \begin{bmatrix} e_1 \\ e_2 \\ e_3 \end{bmatrix}e= e1e2e3 ,那么:

    ∣∣e∣∣2=e12+e22+e32||e||_2 = \sqrt{e_1^2 + e_2^2 + e_3^2}∣∣e∣∣2=e12+e22+e32

  3. 上标 2^22:平方 (Squared)

    上标的2就是最普通的平方操作。它的作用极其巧妙------把 L2 范数公式里的根号给抵消掉了!

    所以,加上平方后:

    ∣∣e∣∣22=(e12+e22+e32)2=e12+e22+e32||e||_2^2 = (\sqrt{e_1^2 + e_2^2 + e_3^2})^2 = e_1^2 + e_2^2 + e_3^2∣∣e∣∣22=(e12+e22+e32 )2=e12+e22+e32

  4. 在线性代数中,一个列向量的 L2 范数平方,刚好等于它的转置乘以它自己:

    ∣∣e∣∣22=eTe=[e1e2e3][e1e2e3]=e12+e22+e32||e||_2^2 = e^T e = \begin{bmatrix} e_1 & e_2 & e_3 \end{bmatrix} \begin{bmatrix} e_1 \\ e_2 \\ e_3 \end{bmatrix} = e_1^2 + e_2^2 + e_3^2∣∣e∣∣22=eTe=[e1e2e3] e1e2e3 =e12+e22+e32

  5. 矩阵转置运算法则:

    1. 加减法的转置: (A±B)T=AT±BT(A \pm B)^T = A^T \pm B^T(A±B)T=AT±BT
    2. 乘法的转置(重点:一定要倒序!): (AB)T=BTAT(AB)^T = B^TA^T(AB)T=BTAT
  6. 矩阵求导法则(多维空间的"求导公式表"):

    在阅读机器学习推导时,我们可以直接把标量世界(高中一元函数)的求导逻辑映射到矩阵世界。
    符号统一定义:

    • w^\hat{w}w^ 代表未知的变量列向量 (类比一元函数里的自变量 xxx)。
    • a,A,B,Ca, A, B, Ca,A,B,C 等代表已知的常数标量或常数矩阵(类比一元函数里的常数系数)。

    基于分母布局(机器学习最常用),我们有如下最基础的求导规则:

    1. 常数法则
      ∂a∂w^=0\frac{\partial a}{\partial \hat{w}} = 0∂w^∂a=0

    2. 线性项法则(无论变量在左还是在右)
      ∂(CTBw^)∂w^=BTC\frac{\partial (C^T B \hat{w})}{\partial \hat{w}} = B^T C∂w^∂(CTBw^)=BTC
      ∂(w^TBTC)∂w^=BTC\frac{\partial (\hat{w}^T B^T C)}{\partial \hat{w}} = B^T C∂w^∂(w^TBTC)=BTC

    3. 二次项法则(最通用形态)
      ∂(w^TBw^)∂w^=(B+BT)w^\frac{\partial (\hat{w}^T B \hat{w})}{\partial \hat{w}} = (B + B^T) \hat{w}∂w^∂(w^TBw^)=(B+BT)w^

      如果常数矩阵 BBB 是对称矩阵(即 BT=BB^T = BBT=B)。此时公式可化简为:

      ∂(w^TBw^)∂w^=2Bw^\frac{\partial(\hat{w}^TB\hat{w})}{\partial\hat{w}} = 2B\hat{w}∂w^∂(w^TBw^)=2Bw^

    线性项矩阵求导口诀(对列向量 w^\hat{w}w^ 求导):

    1. "剥洋葱,看常数" :把包含 w^\hat{w}w^ 的那一项拿掉,剩下的就是常数项。
    2. "变量在右,常数转置" :如果 w^\hat{w}w^ 在乘法算式的最右边,把剩下的常数项整体转置
    3. "变量在左,常数照抄" :如果 w^T\hat{w}^Tw^T 在乘法算式的最左边,把剩下的常数项原封不动作为结果。

在实际工程计算中,使用矩阵表示远比代数推导简洁高效。

对于多元线性回归,我们将所有样本特征组合为特征矩阵 XXX(大小为 n×dn \times dn×d,即:n个样本,每个样本d个特征),将真实标签组合为列向量 yyy(大小为 n×1n \times 1n×1,即:n个样本)。

具象化展示(以 3个样本、2个特征 为例):

X原始=[x11x12x21x22x31x32]n×dy=[y1y2y3]n×1X_{原始} = \begin{bmatrix} x_{11} & x_{12} \\ x_{21} & x_{22} \\ x_{31} & x_{32} \end{bmatrix}{n \times d} \quad\quad y = \begin{bmatrix} y{1} \\ y_{2} \\ y_{3} \end{bmatrix}_{n \times 1}X原始= x11x21x31x12x22x32 n×dy= y1y2y3 n×1

为了把截距 bbb 融合进矩阵计算中,我们会在 XXX 的最右侧人为添加一列全为 111 的列,并将参数组合成一个统一的权重列向量 w^=[w1,w2,...,wd,b]T\hat{w} = [w_1, w_2, ..., w_d, b]^Tw^=[w1,w2,...,wd,b]T。

具象化展示(拼接常数列与权重向量):

X拼接后=[x11x121x21x221x31x321]n×(d+1)w^=[w1w2b](d+1)×1X_{拼接后} = \begin{bmatrix} x_{11} & x_{12} & \mathbf{1} \\ x_{21} & x_{22} & \mathbf{1} \\ x_{31} & x_{32} & \mathbf{1} \end{bmatrix}{n \times (d+1)} \quad\quad \hat{w} = \begin{bmatrix} w_1 \\ w_2 \\ \mathbf{b} \end{bmatrix}{(d+1) \times 1}X拼接后= x11x21x31x12x22x32111 n×(d+1)w^= w1w2b (d+1)×1

此时,模型的预测值可表示为矩阵乘法 Xw^X\hat{w}Xw^。你可以直观地看到,添加全1列后,偏置项 bbb 被完美分配给了每一个样本:

Xw^=[x11x121x21x221x31x321][w1w2b]=[w1x11+w2x12+bw1x21+w2x22+bw1x31+w2x32+b]n×1X\hat{w} = \begin{bmatrix} x_{11} & x_{12} & 1 \\ x_{21} & x_{22} & 1 \\ x_{31} & x_{32} & 1 \end{bmatrix} \begin{bmatrix} w_1 \\ w_2 \\ b \end{bmatrix} = \begin{bmatrix} w_1x_{11} + w_2x_{12} + \mathbf{b} \\ w_1x_{21} + w_2x_{22} + \mathbf{b} \\ w_1x_{31} + w_2x_{32} + \mathbf{b} \end{bmatrix}_{n \times 1}Xw^= x11x21x31x12x22x32111 w1w2b = w1x11+w2x12+bw1x21+w2x22+bw1x31+w2x32+b n×1

优化目标(损失函数 SSE)的矩阵形式可写为预测值与真实值差值的平方和:

SSE=∣∣y−Xw^∣∣22=(y−Xw^)T(y−Xw^)SSE = ||y - X\hat{w}||_2^2 = (y - X\hat{w})^T(y - X\hat{w})SSE=∣∣y−Xw^∣∣22=(y−Xw^)T(y−Xw^)

将其展开:

SSE=yTy−yTXw^−w^TXTy+w^TXTXw^SSE = y^Ty - y^TX\hat{w} - \hat{w}^TX^Ty + \hat{w}^TX^TX\hat{w}SSE=yTy−yTXw^−w^TXTy+w^TXTXw^

SSE=yTy−2w^TXTy+w^TXTXw^SSE = y^Ty - 2\hat{w}^TX^Ty + \hat{w}^TX^TX\hat{w}SSE=yTy−2w^TXTy+w^TXTXw^

对权重向量 w^\hat{w}w^ 求导并令其为 0(利用矩阵求导法则 ∂(Aw^)∂w^=AT\frac{\partial(A\hat{w})}{\partial\hat{w}} = A^T∂w^∂(Aw^)=AT 以及 ∂(w^TAw^)∂w^=2Aw^\frac{\partial(\hat{w}^TA\hat{w})}{\partial\hat{w}} = 2A\hat{w}∂w^∂(w^TAw^)=2Aw^):

∂SSE∂w^=−2XTy+2XTXw^=0\frac{\partial{SSE}}{\partial{\hat{w}}} = -2X^Ty + 2X^TX\hat{w} = 0∂w^∂SSE=−2XTy+2XTXw^=0

化简后得到正规方程 (Normal Equation):

XTXw^=XTy\boldsymbol{X^TX\hat{w} = X^Ty}XTXw^=XTy

维度校验:

  • XTXX^TXXTX 得到了一个形状为 (d+1)×(d+1)(d+1) \times (d+1)(d+1)×(d+1) 的方阵。
  • XTyX^TyXTy 得到了一个形状为 (d+1)×1(d+1) \times 1(d+1)×1 的列向量。
  • 等式两边的形状完美契合!

最小二乘法最终矩阵解析解:

要使上式有解,等价于方阵 XTXX^TXXTX (交叉乘积矩阵) 存在逆矩阵。若其可逆,则在等式两边同乘 (XTX)−1(X^TX)^{-1}(XTX)−1,最终求得权重最优解为:

w^=(XTX)−1XTy\boldsymbol{\hat{w} = (X^TX)^{-1}X^Ty}w^=(XTX)−1XTy


3. PyTorch 代码实现与验证

在已知矩阵解析解公式后,我们可以利用 PyTorch 的基础张量运算直接手写实现线性回归的求解逻辑:

python 复制代码
# y = ax + b ;两个点(1,2)和(3,4);
# 正确结果:a=1,b=1

# 1. 准备通用数据结构
# 假设 X_raw 为原始特征,我们需要在末尾拼接一列全 1 来捕捉截距 b
X = torch.tensor([[1., 1.],    # 样本1: 特征x1=1, 常数项=1
                  [3., 1.]])   # 样本2: 特征x1=3, 常数项=1
y = torch.tensor([[2.],
                  [4.]])       # 真实值列向量 y

# 2. 根据正规方程解析解 w = (X^T * X)^-1 * X^T * y 进行求解
# 使用 @ 运算符(torch.matmul)替代繁琐的 torch.mm
w = torch.linalg.inv(X.T @ X) @ X.T @ y

print("最优参数解:\n", w)
# 输出结果:
# 最优参数解:
#  tensor([[1.0000],
#         [1.0000]])

工业界补充:torch.linalg 与范数求解

  • API 避坑:在上述代码中,我们手动使用了 torch.linalg.inv。但在实际深度学习底层/工业界中,求逆矩阵不仅计算量大,而且遇到矩阵不可逆或接近奇异时容易引发数值不稳定。
  • 推荐做法:现代 PyTorch 2.x 中强烈推荐直接使用底层的最小二乘法求解器 torch.linalg.lstsq(X, y)。它内部采用更稳定的 QR 分解或 SVD 分解来求伪逆,即使 XTXX^TXXTX 不可逆也能算出最优近似解。
  • 范数计算:求解矩阵或向量范数(即推导公式中的 ∣∣X∣∣2||X||_2∣∣X∣∣2 ),推荐使用 torch.linalg.norm(t) 求解 L2 范数(欧氏距离);使用 torch.linalg.norm(t, ord=1) 求解 L1 范数(曼哈顿距离)。

4. linalg.lstsq - 最小二乘法求解器

作用:计算线性方程组 AX=BAX = BAX=B 的最小二乘解。它通过底层高度优化的算法(如 QR 分解或 SVD 分解)来最小化残差的 L2 范数平方 ∣∣AX−B∣∣22||AX - B||_2^2∣∣AX−B∣∣22。避坑提醒:在深度学习工业界,永远不要手动使用求逆矩阵公式 w^=(XTX)−1XTy\hat{w} = (X^TX)^{-1}X^Tyw^=(XTX)−1XTy 来算回归,因为当矩阵接近奇异(不可逆)时会导致严重的数值爆炸。直接调用本函数才是最安全、最现代的做法!

优化目标公式: min⁡X∣∣AX−B∣∣2\min_{X} ||AX - B||_2minX∣∣AX−B∣∣2

(注:对应回归问题中,这里的 AAA 就是特征矩阵 XXX,BBB 就是标签向量 yyy,XXX 就是要求解的权重 w^\hat{w}w^)

python 复制代码
torch.linalg.lstsq(A, B, rcond=None, driver=None)

参数:

  • 输入矩阵 (A): 左侧的系数矩阵,即我们的特征矩阵 (Tensor)。
  • 目标矩阵 (B): 右侧的目标值矩阵,即我们的真实标签 (Tensor)。
  • 截断条件 (rcond): 用于确定矩阵有效秩的奇异值截断阈值。奇异值小于 rcond * 最大奇异值 的部分会被视为 0。默认值为机器精度 (Float/None)。
  • 底层驱动 (driver): 指定后端调用的 C++/CUDA 计算引擎(如 'gelsd' 默认使用 SVD,'gelsy' 使用 QR 分解)。通常不需要手动设置,要是结果没算residuals可以设置gels (String/None)。

返回值:

  • 成功: 返回一个包含了 4 个元素的命名元组 (NamedTuple) (solution, residuals, rank, singular_values)

    1. solution (最优解): 对应回归方程中的最优权重向量 w^\hat{w}w^。
    2. residuals (残差平方和): 就是我们在上一节推导的 SSESSESSE!即预测值与真实值误差的 L2 范数平方。(注:如果方程有精确解,或者 AAA 的行数少于列数,此张量可能为空)
    3. rank (矩阵的秩): 矩阵 AAA 的有效秩。
    4. singular_values (奇异值): 矩阵 AAA 的奇异值(这证明了底层的求逆确实用到了你之前学过的 SVD 技术!)。

示例:

python 复制代码
# 1. 准备数据 (与手工求逆的例子完全相同)
# X 特征矩阵 (2个样本,已拼接好全1截距列)
X_feat = torch.tensor([[1., 1.], 
                       [3., 1.]]) 
y_true = torch.tensor([[2.], 
                       [4.]])

# 2. 调用官方最小二乘法求解器
# 注意:它返回的是一个元组,我们通常只需要取第一个元素 solution
result = torch.linalg.lstsq(X_feat, y_true)
# torch.return_types.linalg_lstsq(
# solution=tensor([[1.0000],
#         		   [1.0000]]),
# residuals=tensor([]),
# rank=tensor(2),
# singular_values=tensor([]))

四.使用PyTorch AutoGrad自动微分验证偏导数

1. autograd.grad - 自动求导与梯度计算

作用:计算并返回输出张量(函数)对输入张量(自变量)的梯度(偏导数)。这是 PyTorch 动态计算图的核心运算。与直接调用 .backward() 会把梯度偷偷累加到张量的 .grad 属性中不同,autograd.grad 会"干净"地将算出的梯度作为一个元组直接返回,不会对原有变量产生副作用。

数学表示:假设目标函数(输出标量)为 yyy,输入变量为 x1,x2x_1, x_2x1,x2,则它计算的是偏导数集合 (∂y∂x1,∂y∂x2)(\frac{\partial y}{\partial x_1}, \frac{\partial y}{\partial x_2})(∂x1∂y,∂x2∂y)。

python 复制代码
torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=False)

参数:

  • 输出张量 (outputs): 作为目标函数输出的因变量张量(通常是损失值 Loss 或误差 SSE 等标量) (Tensor 或 Tensor 列表)。
  • 输入张量 (inputs): 需要求导的自变量张量。避坑提醒:传入的这些张量在创建时,必须显式设置了 requires_grad=True,并且参与了前向计算,否则引擎找不到求导路径会直接报错! (Tensor 或 Tensor 列表)。
  • 输出梯度权重 (grad_outputs): 雅可比向量积中的权重向量。如果 outputs 是一个普通标量,这个参数完全不用管(底层默认自动填 1.0);但如果 outputs 是个多维张量,你必须传入一个和它形状完全一样的权重矩阵 (Tensor 或 Tensor 列表)。
  • 保留计算图 (retain_graph): 决定在算出梯度后,是否保留底层的计算图内存。默认为 None(通常图用完一次就自动释放销毁了)。如果你需要对同一个图进行多次求导,必须设为 True (Boolean)。
  • 创建高阶计算图 (create_graph): 决定是否为"求导过程本身"也建立计算图。如果设为 True,你可以对算出来的梯度再求一次导,这是大模型中计算二阶导数、海森矩阵(Hessian Matrix)或梯度惩罚项的核心机制 (Boolean,默认为 False)。

返回值:

  • 成功: 返回一个元组 (Tuple),里面的元素严格按照你传入 inputs 的顺序,依次对应每个自变量的梯度张量 (Tensor)。

requires_grad是tensor的一个属性可以查看修改。

python 复制代码
a = torch.tensor(1.,requires_grad = True)
a.requires_grad # True

示例:

python 复制代码
# 1. 定义自变量并允许自动求导 (requires_grad=True 极其重要)
x = torch.tensor(2.0, requires_grad=True)
w = torch.tensor(3.0, requires_grad=True)

# 2. 构建前向计算图: y = w * x^2
y = w * (x ** 2)

# 3. 求解 y 对 w 和 x 的偏导数
# 理论数学解:
# dy/dw = x^2 = 2^2 = 4
# dy/dx = 2 * w * x = 2 * 3 * 2 = 12
grads = torch.autograd.grad(outputs=y, inputs=[w, x])
# (tensor(4.), tensor(12.))
print("返回类型:", type(grads))
# 返回类型: <class 'tuple'>
print("偏导数计算结果:", grads)
# 偏导数计算结果: (tensor(4.), tensor(12.))  <-- 分别对应 w 和 x 的梯度

# 验证副作用:x 和 w 的 .grad 属性依然是 None,证明 autograd.grad 很"干净"
print("x 自身的 .grad 属性:", x.grad) # None

2. backward - 误差反向传播与梯度计算

作用:计算当前张量(通常是代表整体误差的标量 Loss)对计算图中所有叶子节点(即 requires_grad=True 的模型参数)的梯度。这是深度学习训练网络时最核心的动作!与 autograd.grad 直接返回结果不同,.backward() 是一个原地(In-place)操作,它没有返回值,而是会沿着计算图一路往回算,并把算出的梯度偷偷塞进各个参数自己的 .grad 属性中。

数学表示:执行反向传播算法,利用链式法则自动计算 ∂Loss∂wi\frac{\partial Loss}{\partial w_i}∂wi∂Loss 和 ∂Loss∂bi\frac{\partial Loss}{\partial b_i}∂bi∂Loss。

python 复制代码
tensor.backward(gradient=None, retain_graph=None, create_graph=False)

参数:

  • 梯度权重 (gradient): 雅可比向量积的初始梯度。避坑提醒:如果你对一个"标量"(比如求和/求均值后的 Loss)调用 .backward(),这个参数完全不用填(底层默认传入 1.0);但如果你对一个"多维张量"直接调用 .backward(),就必须传入一个与它形状完全相同的张量作为权重! (Tensor 或 None)。
  • 保留计算图 (retain_graph): PyTorch 为了节省极宝贵的显存,默认在一次 .backward() 执行完毕后,就会把中间的计算图直接销毁(释放内存)。如果你的代码需要在同一个前向传播的结果上调用两次 .backward(),必须在此参数传入 True (Boolean,默认为 False)。
  • 创建高阶计算图 (create_graph): 用于二阶导数或梯度惩罚(如 WGAN-GP 或大模型的某些特殊正则化)。设为 True 时,反向传播的过程本身也会被记录进计算图 (Boolean,默认为 False)。

返回值:

  • 无返回值 (None)。计算出的梯度会自动累加到对应自变量张量的 .grad 属性中。

示例与重大避坑警告:

python 复制代码
# 1. 定义模型参数 (大模型中的权重 w 和 b)
w = torch.tensor([2.0, 3.0], requires_grad=True)
x = torch.tensor([1.0, 2.0]) # 输入数据不用求导

# 2. 模拟第一次前向传播与计算 Loss
loss1 = torch.sum((w * x) ** 2) 
# Loss = (2*1)^2 + (3*2)^2 = 4 + 36 = 40

# 3. 第一次反向传播
loss1.backward()

print("第一次 backward 后的 w.grad:", w.grad) 
# tensor([ 4., 24.])  (算法: 2*w*x^2)

# 史诗级避坑:梯度累加 (Gradient Accumulation)
# 假设我们进行第二个 Batch 的训练...
loss2 = torch.sum((w * x) ** 2)

# 4. 第二次反向传播前,如果不清空梯度:
loss2.backward()
print("第二次 backward 后的 w.grad:", w.grad) 
# tensor([ 8., 48.])  <-- 致命错误!它把两次的梯度加起来了!

# 正确做法:每次反向传播前/后,必须清零梯度!
# 在实际大模型训练中,这对应着 optimizer.zero_grad() 这一关键步骤。
w.grad.zero_() 

我们可以使用 PyTorch 提供的前沿微分工具 autograd,反向验证当 a=1, b=1 时,SSE 的偏导数是否确实为 0。

自动微分的核心概念:requires_grad=True

在现代 PyTorch 中,Tensor 已不仅仅是纯粹的数字载体,它天生支持复杂的自动微分图计算。我们只需要在创建 Tensor 时标记 requires_grad=True,即可跟踪该张量上的所有运算操作用于后续求导。

python 复制代码
# 1. 定义需优化的参数,并允许自动求导
a = torch.tensor(1., requires_grad=True)
b = torch.tensor(1., requires_grad=True)

# 2. 构建目标函数 (前向传播建立计算图)
sse = torch.pow((2 - a - b), 2) + torch.pow((4 - 3 * a - b), 2)

# 3. 验证偏导数是否为 0
# torch.autograd.grad 是通用微分函数,当输入多个自变量时计算偏导数
grads = torch.autograd.grad(sse, [a, b])
print(grads)
# (tensor(-0.), tensor(-0.))

至此,从几何直觉、数学公式推导到最后利用 PyTorch 代码进行偏导数反向验证,我们彻底走通了最基础的寻找目标函数极小值的模型优化之路。

相关推荐
J_bean2 小时前
大语言模型 API Token 消耗深度剖析
人工智能·ai·llm·大语言模型·token
爱写代码的倒霉蛋2 小时前
天梯赛备赛经验分享(基础版)
经验分享·算法
醉卧考场君莫笑2 小时前
规则与传统NLP之任务范式
人工智能·自然语言处理
21439652 小时前
如何提升SQL数据更新的安全性_使用行级锁与悲观锁机制
jvm·数据库·python
叶子丶苏2 小时前
第二节_机器学习基本知识点
人工智能·python·机器学习·数据科学
小锋java12342 小时前
LangChain4j 来了,Java AI智能体开发再次起飞。。。
java·人工智能·后端
一点一一2 小时前
nestjs+langchain:Prompt Template
人工智能·后端
f3iiish2 小时前
2078. 两栋颜色不同且距离最远的房子 力扣
算法·leetcode
永霖光电_UVLED2 小时前
1.6T 光模块的能效革命
大数据·人工智能·汽车·娱乐