特征值与特征向量

特征值和特征向量揭示了矩阵最本质的性质——它在哪些方向上只做拉伸而不改变方向(Av = λv)。在 AI 中:PCA 降维对协方差矩阵做特征值分解,取最大特征值对应的主成分,广泛用于图像压缩、人脸识别(Eigenface)、高维数据可视化(t-SNE 预处理常先 PCA 到 50 维);谱聚类对图拉普拉斯矩阵求最小特征向量实现非凸聚类;Google PageRank 求转移矩阵的主特征向量来排序网页;协方差矩阵的特征值分布判断特征共线性——条件数(最大/最小特征值之比)过高意味着梯度不稳定;奇异值分解(SVD)与特征值分解紧密关联,用于推荐系统矩阵分解、NLP 词向量训练(GloVe)。本节从 2×2 特征多项式出发,到完整 PCA 流程,每一步用具体数字说明。

一、定义:什么是特征值与特征向量

对于方阵 A(n×n),如果存在非零向量 v 和标量 λ,使得:
Av = λv

则 λ 是 A 的特征值(eigenvalue),v 是对应的特征向量(eigenvector)。

直觉理解:
矩阵 A 本质上是一个线性变换。大多数向量经过 A 变换后会改变方向。
但特征向量很特殊——A 作用在它身上,方向不变,只是被拉伸了 λ 倍。
λ > 0:同方向拉伸(λ > 1 放大,0 < λ < 1 缩小)
λ < 0:反方向拉伸(翻转)
λ = 0:被压缩成零向量(该方向的信息全部丢失)
λ = 1:不变(恒等变换方向)

二、手动计算特征值与特征向量(完整推导)

方法:求解 det(A - λI) = 0(特征方程),然后代入求特征向量。

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

第1步:构造 A - λI
A - λI = [[4-λ, 1],
          [2, 3-λ]]

第2步:计算行列式 det(A - λI) = 0
det = (4-λ)(3-λ) - 1×2
    = 12 - 4λ - 3λ + λ² - 2
    = λ² - 7λ + 10 = 0

第3步:解二次方程
λ = (7 ± √(49-40)) / 2 = (7 ± 3) / 2
λ₁ = 5,λ₂ = 2

第4步:求特征向量(代入 (A-λI)v = 0)

对于 λ₁ = 5:
(A - 5I) = [[4-5, 1], [2, 3-5]] = [[-1, 1], [2, -2]]
方程组:-v₁ + v₂ = 0 → v₁ = v₂
取 v₁ = (1, 1)(或任意倍数,如 (2,2)、(-1,-1) 都是)

验证:Av₁ = [[4,1],[2,3]] × [1,1]ᵀ
= [4×1+1×1, 2×1+3×1]ᵀ = [5, 5]ᵀ = 5 × [1,1]ᵀ ✓

对于 λ₂ = 2:
(A - 2I) = [[2, 1], [2, 1]]
方程:2v₁ + v₂ = 0 → v₂ = -2v₁
取 v₂ = (1, -2)

验证:Av₂ = [[4,1],[2,3]] × [1,-2]ᵀ
= [4×1+1×(-2), 2×1+3×(-2)]ᵀ = [2, -4]ᵀ = 2 × [1,-2]ᵀ ✓

三、特征值分解(对角化)

如果 n×n 矩阵 A 有 n 个线性无关的特征向量,则:
A = PDP⁻¹

P = [v₁, v₂, ..., vₙ](特征向量组成的矩阵)
D = diag(λ₁, λ₂, ..., λₙ)(特征值组成的对角矩阵)

用途:Aⁿ = PDⁿP⁻¹,对角矩阵的 n 次幂只需每个元素取 n 次幂。
这把矩阵幂运算从 O(n³×k) 降到 O(n³)(k 是幂次数)。
接上例的数值验证:
P = [[1, 1], [1, -2]]
D = [[5, 0], [0, 2]]

P⁻¹:det(P) = 1×(-2) - 1×1 = -3
P⁻¹ = (1/-3) × [[-2, -1], [-1, 1]] = [[2/3, 1/3], [1/3, -1/3]]

验证 PDP⁻¹ = A:
PD = [[1×5, 1×2], [1×5, -2×2]] = [[5, 2], [5, -4]]
PDP⁻¹ = [[5, 2], [5, -4]] × [[2/3, 1/3], [1/3, -1/3]]
(1,1) = 5×2/3 + 2×1/3 = 10/3 + 2/3 = 12/3 = 4 ✓
(1,2) = 5×1/3 + 2×(-1/3) = 5/3 - 2/3 = 3/3 = 1 ✓
(2,1) = 5×2/3 + (-4)×1/3 = 10/3 - 4/3 = 6/3 = 2 ✓
(2,2) = 5×1/3 + (-4)×(-1/3) = 5/3 + 4/3 = 9/3 = 3 ✓

PDP⁻¹ = [[4,1],[2,3]] = A ✓

四、对称矩阵的特殊性质

对称矩阵(A = Aᵀ)有非常好的性质:
① 所有特征值都是实数(不会出现复数)
② 不同特征值对应的特征向量相互正交(垂直)
③ 一定可以对角化,且可以用正交矩阵对角化:A = QΛQᵀ
   (Q 是正交矩阵,QᵀQ = I,即 Q⁻¹ = Qᵀ)

协方差矩阵就是对称矩阵 → PCA 依赖的就是这些性质。
对称矩阵示例:
S = [[5, 2],
     [2, 2]](注意 S = Sᵀ)

特征方程:(5-λ)(2-λ) - 4 = 0
λ² - 7λ + 6 = 0
λ₁ = 6,λ₂ = 1

对于 λ₁ = 6:(S-6I)v = 0 → [[-1,2],[2,-4]]v = 0 → v₁ = 2v₂ → v₁ = (2,1)
对于 λ₂ = 1:(S-I)v = 0 → [[4,2],[2,1]]v = 0 → v₁ = -v₂/2 → v₂ = (1,-2)

验证正交性:v₁ · v₂ = 2×1 + 1×(-2) = 0 ✓ 互相垂直!

归一化(使长度=1):
|v₁| = √(4+1) = √5,归一化 q₁ = (2/√5, 1/√5) = (0.894, 0.447)
|v₂| = √(1+4) = √5,归一化 q₂ = (1/√5, -2/√5) = (0.447, -0.894)

五、AI 核心应用——PCA 主成分分析

PCA 是最经典的降维方法,核心就是对协方差矩阵做特征值分解。

完整 PCA 示例(学生考试成绩数据):

5 个学生的数学和物理成绩:
学生数学(x₁)物理(x₂)
A9085
B7060
C8075
D6055
E5045
第1步:中心化(减均值)
x̄₁ = (90+70+80+60+50)/5 = 350/5 = 70
x̄₂ = (85+60+75+55+45)/5 = 320/5 = 64

学生x₁-x̄₁x₂-x̄₂
A2021
B0-4
C1011
D-10-9
E-20-19
第2步:计算协方差矩阵 Σ
Σ₁₁ = Var(x₁) = (20²+0²+10²+(-10)²+(-20)²)/(5-1) = (400+0+100+100+400)/4 = 1000/4 = 250
Σ₂₂ = Var(x₂) = (21²+(-4)²+11²+(-9)²+(-19)²)/4 = (441+16+121+81+361)/4 = 1020/4 = 255
Σ₁₂ = Cov(x₁,x₂) = (20×21+0×(-4)+10×11+(-10)×(-9)+(-20)×(-19))/4
     = (420+0+110+90+380)/4 = 1000/4 = 250

Σ = [[250, 250],
     [250, 255]]

第3步:对 Σ 做特征值分解
特征方程:(250-λ)(255-λ) - 250² = 0
λ² - 505λ + (250×255-62500) = 0
λ² - 505λ + (63750-62500) = 0
λ² - 505λ + 1250 = 0
λ = (505 ± √(255025-5000))/2 = (505 ± √250025)/2 = (505 ± 500.02)/2
λ₁ ≈ 502.5,λ₂ ≈ 2.5

第4步:解释方差比
第1主成分解释的方差比 = 502.5/(502.5+2.5) = 502.5/505 = 99.5%
第2主成分解释的方差比 = 2.5/505 = 0.5%

结论:第 1 个主成分就包含了 99.5% 的信息!可以把 2 维数据降为 1 维,几乎不损失信息。
这是因为数学和物理成绩高度正相关(Cov=250 接近 Var),数据基本分布在一条直线附近。
PCA 在图像压缩中的应用(MNIST):

MNIST 每张图片 28×28 = 784 个像素(784 维向量)。
对 60000 张训练图片做 PCA:

保留主成分数解释方差比分类准确率(KNN)速度提升
784(原始)100%97.1%1x
15095%97.0%5x
5085%96.3%15x
2070%94.5%40x
1055%90.1%78x
从 784 维降到 150 维,准确率几乎不变(97.1%→97.0%),但计算快 5 倍!
这说明手写数字图片中大量像素是冗余的(背景全是 0,笔画模式高度重复)。

六、其他 AI 应用

谱聚类(SpectralClustering):

对图(Graph)的拉普拉斯矩阵做特征值分解来发现聚类结构。

有 6 个数据点,构建相似度图后得到拉普拉斯矩阵 L。
计算 L 的最小几个特征值:λ₁=0, λ₂=0.1, λ₃=0.8, λ₄=2.3, ...
λ₂ 和 λ₃ 之间有明显的跳跃(0.1 → 0.8),说明数据有 2 个聚类。
取前 2 个特征向量对应的坐标,然后用 K-Means 聚类。
Google PageRank 算法(简化版):

有 3 个网页 A、B、C 互相链接:
A→B, A→C, B→A, C→A, C→B

转移矩阵 M:
M = [[0, 1, 1/2],
     [1/2, 0, 1/2],
     [1/2, 0, 0]]

M 的主特征值 λ₁=1(Markov 矩阵一定有),对应的特征向量就是 PageRank 分数。
求解后:r = [0.4, 0.4, 0.2](A 和 B 并列最重要,C 最不重要)
因为 A 被 B 和 C 两个页面指向,B 被 A 和 C 指向,C 只被 A 指向。

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

C#(.NET 10)

// dotnet run 即可执行
// 2×2 特征值分解: 特征多项式 λ²-trace·λ+det=0
void Eig2(double a, double b, double c, double d,
          out double l1, out double l2, out double[] v1, out double[] v2)
{
    double tr = a + d, det = a * d - b * c;
    double disc = Math.Sqrt(tr * tr - 4 * det);
    l1 = (tr + disc) / 2; l2 = (tr - disc) / 2;
    // 特征向量: (A-λI)v=0, v ∝ [b, λ-a]
    if (Math.Abs(b) > 1e-12) {
        v1 = new[] { b, l1 - a }; v2 = new[] { b, l2 - a };
    } else { v1 = new[] { 1.0, 0.0 }; v2 = new[] { 0.0, 1.0 }; }
    double n1 = Math.Sqrt(v1[0]*v1[0]+v1[1]*v1[1]);
    double n2 = Math.Sqrt(v2[0]*v2[0]+v2[1]*v2[1]);
    v1[0] /= n1; v1[1] /= n1; v2[0] /= n2; v2[1] /= n2;
}

// ===== 基本特征值分解 =====
Eig2(4, 1, 2, 3, out double lam1, out double lam2, out double[] ev1, out double[] ev2);
Console.WriteLine($"特征值: [{lam1}, {lam2}]");
Console.WriteLine($"特征向量: [{ev1[0]:F4},{ev1[1]:F4}], [{ev2[0]:F4},{ev2[1]:F4}]");
Console.WriteLine($"Av1 = [{4*ev1[0]+1*ev1[1]:F4}, {2*ev1[0]+3*ev1[1]:F4}]");
Console.WriteLine($"λ1·v1= [{lam1*ev1[0]:F4}, {lam1*ev1[1]:F4}]");
Console.WriteLine($"Av2 = [{4*ev2[0]+1*ev2[1]:F4}, {2*ev2[0]+3*ev2[1]:F4}]");
Console.WriteLine($"λ2·v2= [{lam2*ev2[0]:F4}, {lam2*ev2[1]:F4}]");

// ===== 完整 PCA 流程 =====
double[,] data = {{90,85},{70,60},{80,75},{60,55},{50,45}};
int n = 5;
double[] mu = new double[2];
for (int j = 0; j < 2; j++) { for (int i = 0; i < n; i++) mu[j] += data[i,j]; mu[j] /= n; }

double[,] cen = new double[5,2];
for (int i = 0; i < n; i++) for (int j = 0; j < 2; j++) cen[i,j] = data[i,j] - mu[j];

// 样本协方差 (ddof=1)
double[,] cov = new double[2,2];
for (int j1 = 0; j1 < 2; j1++) for (int j2 = 0; j2 < 2; j2++) {
    for (int i = 0; i < n; i++) cov[j1,j2] += cen[i,j1]*cen[i,j2];
    cov[j1,j2] /= n - 1;
}
Console.WriteLine($"\n协方差矩阵:\n  [{cov[0,0]:F2}, {cov[0,1]:F2}]\n  [{cov[1,0]:F2}, {cov[1,1]:F2}]");

Eig2(cov[0,0],cov[0,1],cov[1,0],cov[1,1], out double e1, out double e2, out double[] pc1, out _);
Console.WriteLine($"特征值: [{e1:F2}, {e2:F2}]");
Console.WriteLine($"方差解释比: [{e1/(e1+e2):F4}, {e2/(e1+e2):F4}]");

Console.Write("第1主成分得分: ");
for (int i = 0; i < n; i++) Console.Write($"{cen[i,0]*pc1[0]+cen[i,1]*pc1[1]:F2} ");
Console.WriteLine();

// ===== 对称矩阵验证正交性 =====
Eig2(5, 2, 2, 2, out _, out _, out double[] u1, out double[] u2);
Console.WriteLine($"\n对称矩阵 [[5,2],[2,2]] 特征向量内积: {u1[0]*u2[0]+u1[1]*u2[1]:F10}");

Rust

// 2×2 特征值分解
fn eig2(a: f64, b: f64, c: f64, d: f64) -> (f64, f64, [f64;2], [f64;2]) {
    let (tr, det) = (a + d, a.mul_add(d, -(b * c)));
    let disc = tr.mul_add(tr, -4.0*det).sqrt();
    let (l1, l2) = ((tr+disc)/2.0, (tr-disc)/2.0);
    let (mut v1, mut v2) = if b.abs() > 1e-12 {
        ([b, l1-a], [b, l2-a])
    } else { ([1.0, 0.0], [0.0, 1.0]) };
    let n1 = v1[0].mul_add(v1[0], v1[1]*v1[1]).sqrt();
    let n2 = v2[0].mul_add(v2[0], v2[1]*v2[1]).sqrt();
    v1[0]/=n1; v1[1]/=n1; v2[0]/=n2; v2[1]/=n2;
    (l1, l2, v1, v2)
}

fn main() {
    // ===== 基本特征值分解 =====
    let (l1, l2, v1, v2) = eig2(4.0, 1.0, 2.0, 3.0);
    println!("特征值: [{l1}, {l2}]");
    println!("特征向量: [{:.4},{:.4}], [{:.4},{:.4}]", v1[0],v1[1], v2[0],v2[1]);
    println!("Av1 = [{:.4}, {:.4}]", 4.0_f64.mul_add(v1[0], v1[1]), 2.0_f64.mul_add(v1[0], 3.0*v1[1]));
    println!("λ1·v1= [{:.4}, {:.4}]", l1*v1[0], l1*v1[1]);
    println!("Av2 = [{:.4}, {:.4}]", 4.0_f64.mul_add(v2[0], v2[1]), 2.0_f64.mul_add(v2[0], 3.0*v2[1]));
    println!("λ2·v2= [{:.4}, {:.4}]", l2*v2[0], l2*v2[1]);

    // ===== PCA =====
    let data = [[90.0,85.0],[70.0,60.0],[80.0,75.0],[60.0,55.0],[50.0,45.0_f64]];
    let n = 5;
    let mut mu = [0.0_f64; 2];
    for j in 0..2 { for i in 0..n { mu[j] += data[i][j]; } mu[j] /= n as f64; }

    let mut cen = [[0.0_f64;2];5];
    for i in 0..n { for j in 0..2 { cen[i][j] = data[i][j]-mu[j]; } }

    let mut cov = [[0.0_f64;2];2];
    for j1 in 0..2 { for j2 in 0..2 {
        for i in 0..n { cov[j1][j2] += cen[i][j1]*cen[i][j2]; }
        cov[j1][j2] /= (n-1) as f64;
    }}
    println!("\n协方差矩阵:\n  [{:.2}, {:.2}]\n  [{:.2}, {:.2}]",
             cov[0][0],cov[0][1],cov[1][0],cov[1][1]);

    let (e1, e2, pc1, _) = eig2(cov[0][0],cov[0][1],cov[1][0],cov[1][1]);
    println!("特征值: [{e1:.2}, {e2:.2}]");
    println!("方差解释比: [{:.4}, {:.4}]", e1/(e1+e2), e2/(e1+e2));

    print!("第1主成分得分: ");
    for i in 0..n { print!("{:.2} ", cen[i][0].mul_add(pc1[0], cen[i][1]*pc1[1])); }
    println!();

    // ===== 对称矩阵正交性 =====
    let (_, _, u1, u2) = eig2(5.0, 2.0, 2.0, 2.0);
    println!("\n对称矩阵 [[5,2],[2,2]] 特征向量内积: {:.10}",
             u1[0].mul_add(u2[0], u1[1]*u2[1]));
}

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