矩阵求导与逆矩阵

矩阵求导是反向传播算法的数学基础——全连接层权重梯度 ∂L/∂W = δ·xT、卷积核梯度、注意力层梯度,全都归结为矩阵对矩阵的导数运算。理解矩阵求导才能真正看懂框架 autograd 在做什么。逆矩阵在线性回归的正规方程解析解 w = (XTX)-1XTy、岭回归 (XTX + λI)-1、卡尔曼滤波、高斯过程推断中均不可或缺;Fisher 信息矩阵的逆给出参数估计的最优方差下界(Cramér-Rao 不等式)。实际工程中,大矩阵逆通常用 LU/Cholesky 分解或迭代法替代直接求逆以保证数值稳定。本节用具体数字逐步推导 Softmax Jacobian、全连接层反向传播梯度、正规方程等核心公式。

一、逆矩阵的定义

数学定义:对于方阵 A(n×n),如果存在矩阵 B 使得 AB = BA = I(单位矩阵),则 B 就是 A 的逆矩阵,记为 A⁻¹。

A × A⁻¹ = A⁻¹ × A = I

前提条件:
① A 必须是方阵(行数=列数)
② A 的行列式 det(A) ≠ 0(即 A 是"非奇异"的)
③ 如果 det(A) = 0,A 是奇异矩阵,没有逆矩阵
单位矩阵 I 是什么:
I = [[1, 0, 0],
     [0, 1, 0],
     [0, 0, 1]]

对角线上全是 1,其余全是 0。任意矩阵乘以 I 等于自身:AI = IA = A。
就像数字乘以 1 一样:5 × 1 = 1 × 5 = 5。

二、2×2 矩阵逆的手动计算

公式:若 A = [[a, b], [c, d]]
A⁻¹ = (1/det(A)) × [[d, -b], [-c, a]]
其中 det(A) = ad - bc

逐步数值计算:

A = [[4, 7],
     [2, 6]]

第1步:计算行列式
det(A) = 4×6 - 7×2 = 24 - 14 = 10
det(A) = 10 ≠ 0,所以逆矩阵存在。

第2步:构造伴随矩阵
交换主对角线元素,副对角线取反号:
[[d, -b], [-c, a]] = [[6, -7], [-2, 4]]

第3步:除以行列式
A⁻¹ = (1/10) × [[6, -7], [-2, 4]]
A⁻¹ = [[0.6, -0.7],
       [-0.2, 0.4]]

验证 A × A⁻¹ = I:
(1,1) = 4×0.6 + 7×(-0.2) = 2.4 - 1.4 = 1.0
(1,2) = 4×(-0.7) + 7×0.4 = -2.8 + 2.8 = 0.0
(2,1) = 2×0.6 + 6×(-0.2) = 1.2 - 1.2 = 0.0
(2,2) = 2×(-0.7) + 6×0.4 = -1.4 + 2.4 = 1.0

确认 A × A⁻¹ = [[1,0],[0,1]] = I ✓

三、逆矩阵的重要性质

① (A⁻¹)⁻¹ = A(逆的逆等于自身)
② (AB)⁻¹ = B⁻¹A⁻¹(乘积的逆 = 逐个逆的反序乘积)
③ (Aᵀ)⁻¹ = (A⁻¹)ᵀ(转置和逆可以交换顺序)
④ (kA)⁻¹ = (1/k)A⁻¹
AI 应用——线性回归的解析解(正规方程):

线性回归目标:找到 w 使得 ||Xw - y||² 最小
对 w 求导令其为 0,得到正规方程:
w = (XᵀX)⁻¹Xᵀy

用房价数据实例演示:
3个房屋样本,2个特征(面积、房间数),加上偏置列(全1):

X = [[1, 80, 2], ← 偏置, 80㎡, 2房
     [1, 120, 3], ← 偏置, 120㎡, 3房
     [1, 150, 4]] ← 偏置, 150㎡, 4房
y = [200, 350, 450]ᵀ(房价,万元)

第1步:计算 XᵀX(3×3 矩阵)
Xᵀ = [[1, 1, 1],
       [80, 120, 150],
       [2, 3, 4]]

XᵀX 的 (1,1) = 1×1+1×1+1×1 = 3
XᵀX 的 (1,2) = 1×80+1×120+1×150 = 350
XᵀX 的 (1,3) = 1×2+1×3+1×4 = 9
XᵀX 的 (2,2) = 80²+120²+150² = 6400+14400+22500 = 43300
XᵀX 的 (2,3) = 80×2+120×3+150×4 = 160+360+600 = 1120
XᵀX 的 (3,3) = 4+9+16 = 29

XᵀX = [[3, 350, 9],
         [350, 43300, 1120],
         [9, 1120, 29]]

第2步:计算 Xᵀy
Xᵀy = [1×200+1×350+1×450, 80×200+120×350+150×450, 2×200+3×350+4×450]
     = [1000, 125500, 3250]

第3步:求 (XᵀX)⁻¹ 后乘以 Xᵀy 得到 w
(实际中用线性代数库求解,如 C# 的 MathNet.Numerics 或 Rust 的 nalgebra)

最终得到回归系数,解释为:
w₀ = 截距(基础房价),w₁ = 面积系数(每增加 1㎡ 房价变化),w₂ = 房间系数
为什么实际中很少用逆矩阵:
① 计算复杂度高:n×n 矩阵求逆需要 O(n³) 运算。当特征维度是几万时,计算量巨大。
② 数值不稳定:当 det(A) 接近 0(接近奇异)时,求逆结果会有很大的舍入误差。
③ 实际替代方案:使用 LU 分解、QR 分解或迭代法(梯度下降)来求解线性方程组。
深度学习完全不用解析解,而是用梯度下降迭代优化。

四、行列式

数学定义:行列式是方阵到标量的一个映射,记为 det(A) 或 |A|。它衡量矩阵代表的线性变换对空间体积的缩放因子。

2×2 行列式:det([[a,b],[c,d]]) = ad - bc

3×3 行列式(按第一行展开):
det([[a,b,c],[d,e,f],[g,h,i]])
= a(ei-fh) - b(di-fg) + c(dh-eg)

逐步数值计算(3×3):

A = [[2, 1, 3],
     [0, -1, 2],
     [1, 4, -1]]

按第一行展开:
det(A) = 2 × det[[-1,2],[4,-1]] - 1 × det[[0,2],[1,-1]] + 3 × det[[0,-1],[1,4]]

子式1:det[[-1,2],[4,-1]] = (-1)×(-1) - 2×4 = 1 - 8 = -7
子式2:det[[0,2],[1,-1]] = 0×(-1) - 2×1 = 0 - 2 = -2
子式3:det[[0,-1],[1,4]] = 0×4 - (-1)×1 = 0 + 1 = 1

det(A) = 2×(-7) - 1×(-2) + 3×1
       = -14 + 2 + 3
       = -9

几何含义:|det(A)| = 9 表示矩阵 A 把单位体积放大了 9 倍;
det(A) < 0 表示变换翻转了空间的方向(把左手系变成右手系)。
AI 应用——协方差矩阵行列式(多维高斯分布):

多维高斯分布的概率密度函数:
p(x) = (1/√((2π)ⁿ|Σ|)) × exp(-½(x-μ)ᵀΣ⁻¹(x-μ))

其中 |Σ| 是协方差矩阵 Σ 的行列式。

例子:2 维高斯分布,均值 μ = [0, 0],协方差矩阵 Σ = [[1, 0.5], [0.5, 2]]
det(Σ) = 1×2 - 0.5×0.5 = 2 - 0.25 = 1.75

如果将 Σ 改为 [[1, 0.9], [0.9, 1]](两个变量高度相关):
det(Σ) = 1×1 - 0.9×0.9 = 1 - 0.81 = 0.19

行列式越小 → 分布越"扁"(集中在某个方向)→ 不确定性越小。
行列式 = 0 → 协方差矩阵奇异 → 数据完全在一个低维子空间里(某些特征完全线性相关)。

五、矩阵求导——反向传播的数学核心

矩阵求导(矩阵微积分)描述的是"损失函数对模型参数矩阵的导数怎么算"。反向传播就是用链式法则逐层算矩阵导数的过程。

5.1 标量对向量求导

设 L 是标量(如损失值),w = (w₁, w₂, ..., wₙ) 是参数向量

∂L/∂w = (∂L/∂w₁, ∂L/∂w₂, ..., ∂L/∂wₙ)

结果是一个与 w 同形的向量,每个元素是 L 对对应 wᵢ 的偏导数。
这个向量就叫做"梯度",记为 ∇wL。

逐步数值计算:

假设损失函数 L = (y - ŷ)²,其中 ŷ = w₁x₁ + w₂x₂ + b

已知:x₁ = 2, x₂ = 3, y = 10(真实值)
当前参数:w₁ = 1.5, w₂ = 2.0, b = 0.5

第1步:前向计算
ŷ = 1.5×2 + 2.0×3 + 0.5 = 3.0 + 6.0 + 0.5 = 9.5
L = (10 - 9.5)² = 0.5² = 0.25

第2步:求各偏导数
∂L/∂ŷ = 2(ŷ - y) = 2(9.5 - 10) = -1.0

∂ŷ/∂w₁ = x₁ = 2
∂ŷ/∂w₂ = x₂ = 3
∂ŷ/∂b = 1

链式法则:
∂L/∂w₁ = ∂L/∂ŷ × ∂ŷ/∂w₁ = (-1.0) × 2 = -2.0
∂L/∂w₂ = ∂L/∂ŷ × ∂ŷ/∂w₂ = (-1.0) × 3 = -3.0
∂L/∂b = ∂L/∂ŷ × ∂ŷ/∂b = (-1.0) × 1 = -1.0

梯度向量 ∇L = (-2.0, -3.0, -1.0)
梯度为负 → 增大参数可以减小损失 → 参数应该增大

第3步:参数更新(学习率 α = 0.1)
w₁_new = 1.5 - 0.1×(-2.0) = 1.5 + 0.2 = 1.7
w₂_new = 2.0 - 0.1×(-3.0) = 2.0 + 0.3 = 2.3
b_new = 0.5 - 0.1×(-1.0) = 0.5 + 0.1 = 0.6

验证更新后预测值更接近真实值:
ŷ_new = 1.7×2 + 2.3×3 + 0.6 = 3.4 + 6.9 + 0.6 = 10.9
L_new = (10 - 10.9)² = 0.81

咦?损失反而增大了!这是因为学习率太大导致"过冲"。
改用 α = 0.01:
w₁ = 1.5 + 0.02 = 1.52, w₂ = 2.0 + 0.03 = 2.03, b = 0.5 + 0.01 = 0.51
ŷ = 1.52×2 + 2.03×3 + 0.51 = 3.04 + 6.09 + 0.51 = 9.64
L = (10-9.64)² = 0.1296
损失从 0.25 降到 0.13 ✓ 朝正确方向移动了!

5.2 标量对矩阵求导

设 L 是标量,W 是 m×n 矩阵

∂L/∂W 也是 m×n 矩阵,第 (i,j) 个元素 = ∂L/∂Wᵢⱼ

即:对矩阵中每一个元素分别求偏导,得到一个同形矩阵。
AI 应用——全连接层的梯度计算:

设 z = Wx + b(前向传播),假设已经通过反向传播得到 ∂L/∂z

公式推导:
z = Wx + b 中,zᵢ = Σⱼ Wᵢⱼxⱼ + bᵢ
∂zᵢ/∂Wᵢⱼ = xⱼ(只有第 i 行 j 列的 W 影响 zᵢ)
∂L/∂Wᵢⱼ = (∂L/∂zᵢ) × xⱼ

写成矩阵形式:∂L/∂W = (∂L/∂z) × xᵀ(外积形式)

数值示例:
已知反向传播传来 ∂L/∂z = [0.3, -0.5]ᵀ (2维),输入 x = [0.8, 0.2, 0.5]ᵀ (3维)

∂L/∂W = [0.3, -0.5]ᵀ × [0.8, 0.2, 0.5](外积,2×3 矩阵)

(1,1) = 0.3 × 0.8 = 0.24
(1,2) = 0.3 × 0.2 = 0.06
(1,3) = 0.3 × 0.5 = 0.15
(2,1) = -0.5 × 0.8 = -0.40
(2,2) = -0.5 × 0.2 = -0.10
(2,3) = -0.5 × 0.5 = -0.25

∂L/∂W = [[0.24, 0.06, 0.15],
           [-0.40, -0.10, -0.25]]

继续向前传播(传给上一层):
∂L/∂x = Wᵀ × (∂L/∂z)
如果 W = [[0.15, 0.20, -0.30], [-0.40, 0.50, 0.10]]
∂L/∂x = [[0.15, -0.40], [0.20, 0.50], [-0.30, 0.10]] × [0.3, -0.5]ᵀ
= [0.15×0.3 + (-0.40)×(-0.5), 0.20×0.3 + 0.50×(-0.5), (-0.30)×0.3 + 0.10×(-0.5)]
= [0.045 + 0.200, 0.060 - 0.250, -0.090 - 0.050]
= [0.245, -0.190, -0.140]

这个结果传递给更前面的层继续计算梯度——这就是"反向传播"名字的由来。

5.3 常用矩阵求导公式

① ∂(aᵀx)/∂x = a   (线性函数的导数是系数)
② ∂(xᵀAx)/∂x = (A + Aᵀ)x   (二次形式的导数)
   当 A 对称时 = 2Ax
③ ∂(Wx)/∂x = Wᵀ   (用于反向传播计算上一层梯度)
④ ∂(Wx)/∂W = (∂L/∂z)xᵀ   (用于计算权重梯度)
⑤ ∂||x||²/∂x = 2x   (L2 范数平方的导数)
⑥ ∂tr(AB)/∂A = Bᵀ   (迹的导数)
公式②的实际应用——岭回归(Ridge Regression):

岭回归损失函数:L = ||Xw - y||² + λ||w||²
= (Xw-y)ᵀ(Xw-y) + λwᵀw

展开:L = wᵀXᵀXw - 2yᵀXw + yᵀy + λwᵀw

对 w 求导(用上面的公式):
∂L/∂w = 2XᵀXw - 2Xᵀy + 2λw

令 ∂L/∂w = 0:
2XᵀXw + 2λw = 2Xᵀy
(XᵀX + λI)w = Xᵀy
w = (XᵀX + λI)⁻¹Xᵀy

与普通线性回归的对比:
普通线性回归:w = (XᵀX)⁻¹Xᵀy
岭回归:w = (XᵀX + λI)⁻¹Xᵀy

多了个 +λI,这有两个好处:
① 即使 XᵀX 本身接近奇异(det≈0),加上 λI 后行列式变大,逆矩阵更稳定
② λ 起到正则化效果,约束 w 不能太大,防止过拟合

六、Jacobian 矩阵

定义:当函数的输入和输出都是向量时,Jacobian 矩阵包含所有偏导数。

设 f: ℝⁿ → ℝᵐ,即 f(x) = (f₁(x), f₂(x), ..., fₘ(x))

Jacobian 矩阵 J (m×n):
Jᵢⱼ = ∂fᵢ/∂xⱼ

J = [[∂f₁/∂x₁, ∂f₁/∂x₂, ..., ∂f₁/∂xₙ],
     [∂f₂/∂x₁, ∂f₂/∂x₂, ..., ∂f₂/∂xₙ],
     ...
     [∂fₘ/∂x₁, ∂fₘ/∂x₂, ..., ∂fₘ/∂xₙ]]
AI 应用——Softmax 的 Jacobian(详细推导):

Softmax 函数:σᵢ = e^(zᵢ) / Σⱼ e^(zⱼ)

当 i = j 时:∂σᵢ/∂zᵢ = σᵢ(1 - σᵢ)
当 i ≠ j 时:∂σᵢ/∂zⱼ = -σᵢσⱼ

数值示例:z = [2.0, 1.0, 0.5]
e^2.0 = 7.389, e^1.0 = 2.718, e^0.5 = 1.649
总和 = 7.389 + 2.718 + 1.649 = 11.756
σ = [0.6285, 0.2312, 0.1403]

Jacobian 矩阵 (3×3):
J₁₁ = σ₁(1-σ₁) = 0.6285×0.3715 = 0.2335
J₁₂ = -σ₁σ₂ = -0.6285×0.2312 = -0.1453
J₁₃ = -σ₁σ₃ = -0.6285×0.1403 = -0.0882
J₂₁ = -σ₂σ₁ = -0.2312×0.6285 = -0.1453
J₂₂ = σ₂(1-σ₂) = 0.2312×0.7688 = 0.1778
J₂₃ = -σ₂σ₃ = -0.2312×0.1403 = -0.0324
J₃₁ = -σ₃σ₁ = -0.1403×0.6285 = -0.0882
J₃₂ = -σ₃σ₂ = -0.1403×0.2312 = -0.0324
J₃₃ = σ₃(1-σ₃) = 0.1403×0.8597 = 0.1206

J ≈ [[0.234, -0.145, -0.088],
     [-0.145, 0.178, -0.032],
     [-0.088, -0.032, 0.121]]

注意:每行之和 ≈ 0(因为概率总和恒=1,所以一个分量增大必然其他减小)。
反向传播时:∂L/∂z = J × (∂L/∂σ),把 Softmax 上游的梯度传递到 z。

七、代码验证(C# / Rust)

C#(.NET 10)

// dotnet run 即可执行
// 3×3 行列式 & 逆矩阵 (余子式展开)
double Det3(double[,] m) =>
    m[0,0]*(m[1,1]*m[2,2]-m[1,2]*m[2,1])
   -m[0,1]*(m[1,0]*m[2,2]-m[1,2]*m[2,0])
   +m[0,2]*(m[1,0]*m[2,1]-m[1,1]*m[2,0]);
double[,] Inv3(double[,] m){ double d=Det3(m); return new double[,]{
    {(m[1,1]*m[2,2]-m[1,2]*m[2,1])/d,(m[0,2]*m[2,1]-m[0,1]*m[2,2])/d,(m[0,1]*m[1,2]-m[0,2]*m[1,1])/d},
    {(m[1,2]*m[2,0]-m[1,0]*m[2,2])/d,(m[0,0]*m[2,2]-m[0,2]*m[2,0])/d,(m[0,2]*m[1,0]-m[0,0]*m[1,2])/d},
    {(m[1,0]*m[2,1]-m[1,1]*m[2,0])/d,(m[0,1]*m[2,0]-m[0,0]*m[2,1])/d,(m[0,0]*m[1,1]-m[0,1]*m[1,0])/d}
};}

// ===== 2×2 逆矩阵 =====
double[,] A = {{4,7},{2,6}};
double detA = A[0,0]*A[1,1]-A[0,1]*A[1,0];
Console.WriteLine($"行列式: {detA}");
double[,] Ai = {{A[1,1]/detA,-A[0,1]/detA},{-A[1,0]/detA,A[0,0]/detA}};
Console.WriteLine($"逆矩阵:\n  [{Ai[0,0]:F1}, {Ai[0,1]:F1}]\n  [{Ai[1,0]:F1}, {Ai[1,1]:F1}]");
Console.Write("验证 A@A⁻¹:\n");
for (int i = 0; i < 2; i++){
    for (int j = 0; j < 2; j++){ double v=0; for(int k=0;k<2;k++) v+=A[i,k]*Ai[k,j]; Console.Write($"  {v:F1}"); }
    Console.WriteLine();
}

// ===== 正规方程求线性回归 =====
double[,] X = {{1,80,2},{1,120,3},{1,150,4}};
double[] y = {200,350,450};
double[,] XtX = new double[3,3]; double[] Xty = new double[3];
for(int i=0;i<3;i++) for(int j=0;j<3;j++) for(int k=0;k<3;k++) XtX[i,j]+=X[k,i]*X[k,j];
for(int i=0;i<3;i++) for(int k=0;k<3;k++) Xty[i]+=X[k,i]*y[k];
var inv = Inv3(XtX);
double[] w = new double[3];
for(int i=0;i<3;i++) for(int j=0;j<3;j++) w[i]+=inv[i,j]*Xty[j];
Console.Write("回归系数: "); foreach(var v in w) Console.Write($"{v:F2} ");
Console.Write("\n预测值:   ");
for(int i=0;i<3;i++){double p=0;for(int j=0;j<3;j++) p+=X[i,j]*w[j]; Console.Write($"{p:F1} ");}
Console.WriteLine();

// ===== 岭回归 =====
double lam=0.1;
var R=(double[,])XtX.Clone(); for(int i=0;i<3;i++) R[i,i]+=lam;
var ri=Inv3(R); double[] wr=new double[3];
for(int i=0;i<3;i++) for(int j=0;j<3;j++) wr[i]+=ri[i,j]*Xty[j];
Console.Write("岭回归系数: "); foreach(var v in wr) Console.Write($"{v:F2} ");
Console.WriteLine();

// ===== Softmax Jacobian =====
double[] z = {2.0,1.0,0.5};
double sumE=0; foreach(var v in z) sumE+=Math.Exp(v);
double[] sig = z.Select(v => Math.Exp(v)/sumE).ToArray();
Console.Write("Softmax: "); foreach(var s in sig) Console.Write($"{s:F4} ");
Console.Write("\nJacobian:\n");
for(int i=0;i<3;i++){
    for(int j=0;j<3;j++) Console.Write($"{(i==j?sig[i]:0)-sig[i]*sig[j],8:F4}");
    Console.WriteLine();
}
Console.Write("每行之和: ");
for(int i=0;i<3;i++){double s=0;for(int j=0;j<3;j++) s+=(i==j?sig[i]:0)-sig[i]*sig[j]; Console.Write($"{s:F4} ");}
Console.WriteLine();

// ===== 全连接层梯度 =====
double[,] W = {{0.15,0.20,-0.30},{-0.40,0.50,0.10}};
double[] x = {0.8,0.2,0.5};
double[] dLdz = {0.3,-0.5};
Console.Write("dL/dW:\n");
for(int i=0;i<2;i++){for(int j=0;j<3;j++) Console.Write($"  {dLdz[i]*x[j]:F2}");Console.WriteLine();}
Console.Write("dL/dx: ");
for(int j=0;j<3;j++){double v=0;for(int i=0;i<2;i++) v+=W[i,j]*dLdz[i]; Console.Write($"{v:F3} ");}
Console.WriteLine();

Rust

// 3×3 行列式
fn det3(m: &[[f64;3];3]) -> f64 {
    m[0][0]*(m[1][1]*m[2][2]-m[1][2]*m[2][1])
   -m[0][1]*(m[1][0]*m[2][2]-m[1][2]*m[2][0])
   +m[0][2]*(m[1][0]*m[2][1]-m[1][1]*m[2][0])
}
// 3×3 逆矩阵
fn inv3(m: &[[f64;3];3]) -> [[f64;3];3] {
    let d = det3(m);
    [[(m[1][1]*m[2][2]-m[1][2]*m[2][1])/d,(m[0][2]*m[2][1]-m[0][1]*m[2][2])/d,(m[0][1]*m[1][2]-m[0][2]*m[1][1])/d],
     [(m[1][2]*m[2][0]-m[1][0]*m[2][2])/d,(m[0][0]*m[2][2]-m[0][2]*m[2][0])/d,(m[0][2]*m[1][0]-m[0][0]*m[1][2])/d],
     [(m[1][0]*m[2][1]-m[1][1]*m[2][0])/d,(m[0][1]*m[2][0]-m[0][0]*m[2][1])/d,(m[0][0]*m[1][1]-m[0][1]*m[1][0])/d]]
}

fn main() {
    // ===== 2×2 逆矩阵 =====
    let a = [[4.0,7.0],[2.0,6.0_f64]];
    let det = a[0][0].mul_add(a[1][1], -(a[0][1]*a[1][0]));
    println!("行列式: {det}");
    let ai = [[a[1][1]/det,-a[0][1]/det],[-a[1][0]/det,a[0][0]/det]];
    println!("逆矩阵:\n  [{:.1}, {:.1}]\n  [{:.1}, {:.1}]", ai[0][0],ai[0][1],ai[1][0],ai[1][1]);
    println!("验证 A@A⁻¹:");
    for i in 0..2 {
        for j in 0..2 {
            let v = a[i][0].mul_add(ai[0][j], a[i][1]*ai[1][j]);
            print!("  {v:.1}");
        }
        println!();
    }

    // ===== 正规方程 =====
    let x = [[1.0,80.0,2.0],[1.0,120.0,3.0],[1.0,150.0,4.0_f64]];
    let y = [200.0,350.0,450.0_f64];
    let mut xtx = [[0.0_f64;3];3];
    let mut xty = [0.0_f64;3];
    for i in 0..3 { for j in 0..3 { for k in 0..3 { xtx[i][j]+=x[k][i]*x[k][j]; } } }
    for i in 0..3 { for k in 0..3 { xty[i]+=x[k][i]*y[k]; } }
    let inv = inv3(&xtx);
    let mut w = [0.0_f64;3];
    for i in 0..3 { for j in 0..3 { w[i]+=inv[i][j]*xty[j]; } }
    print!("回归系数: "); for v in w { print!("{v:.2} "); }
    print!("\n预测值:   ");
    for i in 0..3 { let p: f64=(0..3).map(|j|x[i][j]*w[j]).sum(); print!("{p:.1} "); }
    println!();

    // ===== 岭回归 =====
    let mut r = xtx;
    for i in 0..3 { r[i][i] += 0.1; }
    let ri = inv3(&r);
    let mut wr = [0.0_f64;3];
    for i in 0..3 { for j in 0..3 { wr[i]+=ri[i][j]*xty[j]; } }
    print!("岭回归系数: "); for v in wr { print!("{v:.2} "); }
    println!();

    // ===== Softmax Jacobian =====
    let z = [2.0, 1.0, 0.5_f64];
    let sum_e: f64 = z.iter().map(|v| v.exp()).sum();
    let sig: [f64;3] = core::array::from_fn(|i| z[i].exp()/sum_e);
    print!("Softmax: "); for s in sig { print!("{s:.4} "); }
    println!("\nJacobian:");
    for i in 0..3 {
        for j in 0..3 {
            let jac = if i==j { sig[i] } else { 0.0 } - sig[i]*sig[j];
            print!("{jac:8.4}");
        }
        println!();
    }
    print!("每行之和: ");
    for i in 0..3 {
        let s: f64=(0..3).map(|j|(if i==j{sig[i]}else{0.0})-sig[i]*sig[j]).sum();
        print!("{s:.4} ");
    }
    println!();

    // ===== 全连接层梯度 =====
    let ww = [[0.15,0.20,-0.30_f64],[-0.40,0.50,0.10]];
    let xv = [0.8, 0.2, 0.5_f64];
    let dl = [0.3, -0.5_f64];
    println!("dL/dW:");
    for i in 0..2 { for j in 0..3 { print!("  {:.2}",dl[i]*xv[j]); } println!(); }
    print!("dL/dx: ");
    for j in 0..3 { let v=ww[0][j]*dl[0]+ww[1][j]*dl[1]; print!("{v:.3} "); }
    println!();
}

已阅读当前小节,可返回首页继续浏览其它主题。