【AI大模型--NumPy-07】高级线性代数完全指南

07_linearAlgebra.py - 高级线性代数完全指南

学习路径第 7 步 (共 10 步) | 难度:中高级

概述

NumPy 的 linalg 模块是机器学习的数学基础。本文件系统讲解特征值分解、SVD、最小二乘法、QR/Cholesky 分解,并手写实现 PCA

学习目标

  • 理解特征值与特征向量的几何意义
  • 掌握 SVD 奇异值分解及其在降维中的应用
  • 学会使用最小二乘法进行数据拟合
  • 了解 QR 分解和 Cholesky 分解的实际用途
  • 手写 PCA 主成分分析,理解其数学本质

核心内容 (7 个模块)

模块 核心知识点
1. 特征值分解 eigenvalues / eigenvectors / 矩阵对角化
2. SVD 奇异值分解 完整/截断 SVD、矩阵秩、低秩近似
3. 最小二乘法拟合 np.linalg.lstsq、超定方程组求解
4. 矩阵分解 QR 分解、Cholesky 分解(对称正定矩阵)
5. 矩阵性质 行列式、秩、条件数、迹(trace)
6. 线性方程组 求解 Ax=b、逆矩阵 vs solve
7. PCA 手写实现 基于 SVD 的逐步推导与代码实现

CODE

python 复制代码
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
=====================================
NumPy 高级线性代数完全指南 (Advanced Linear Algebra)
=====================================

本案例深入介绍 NumPy 的线性代数能力(基于 LAPACK/BLAS):

1. 特征值分解 (Eigenvalue Decomposition)
2. SVD 奇异值分解 (Singular Value Decomposition)
3. 最小二乘法 (Least Squares Fitting)
4. 矩阵分解: QR / Cholesky
5. 行列式、秩、条件数
6. 实战应用: 主成分分析 (PCA) 手写实现
7. 实战应用: 线性方程组求解

【前置知识】
  线性代数是机器学习、信号处理、计算机图形学的数学基石。
  numpy.linalg 是 Python 科学计算的线性代数引擎。

作者:bloxed
"""

import numpy as np
import time


def separator(title):
    print(f"\n{'='*60}")
    print(f"  {title}")
    print('='*60)


# ============================================================
# 第一部分:特征值与特征向量
# ============================================================
separator("一、特征值分解 (Eigenvalue Decomposition)")

print("""
【定义】对于方阵 A, 若存在非零向量 v 和标量 λ 使得:
        Av = λv
  则 λ 称为特征值, v 称为特征向量。

【几何意义】
  特征向量是矩阵变换下方向不变的轴,
  特征值表示该方向上的缩放因子。

【应用】
  - 主成分分析 (PCA)
  - PageRank 算法 (Google搜索)
  - 振动模式分析
  - 矩阵幂运算加速
""")

# 构造一个对称矩阵 (实对称矩阵的特征值和特征向量都是实数)
A = np.array([
    [4, 2, 1],
    [2, 5, 3],
    [1, 3, 6]
], dtype=float)

print(f"矩阵 A (3x3 对称):\n{A}\n")

eigenvalues, eigenvectors = np.linalg.eig(A)

print(f"特征值 λ: {eigenvalues.round(4)}")
print(f"特征向量 (每列是一个特征向量):\n{eigenvectors.round(4)}")

# 验证 Av = λv
print(f"\n--- 验证 Av = λv ---")
for i in range(3):
    lam = eigenvalues[i]
    vec = eigenvectors[:, i]
    av = A @ vec
    lam_v = lam * vec
    error = np.linalg.norm(av - lam_v)
    print(f"  λ{i}={lam:.4f}: ||Av - λv|| = {error:.2e} (应接近0)")


# ============================================================
# 第二部分:奇异值分解 SVD
# ============================================================
separator("二、奇异值分解 (SVD) ★★★")

print("""
【公式】m×n 矩阵 A 可以分解为:
        A = U · Σ · V^T
        
  U: m×m 正交矩阵 (左奇异向量)
  Σ: m×n 对角矩阵 (奇异值, 非负递减)
  V^T: n×n 正交矩阵 (右奇异向量)

【为什么 SVD 如此重要?】
  [OK] 任何矩阵都能做 SVD (不需要方阵!)
  [OK] 揭示矩阵的 "能量分布"
  [OK] 用于降维 (保留主要奇异值)
  [OK] 图像压缩、推荐系统、去噪
""")

# 构造非方矩阵
B = np.array([
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
    [13, 14, 15, 16]
], dtype=float)

U, s, Vt = np.linalg.svd(B, full_matrices=True)

print(f"原始矩阵 B: shape {B.shape}")
print(f"\nU (左奇异向量): shape {U.shape}")
print(f"Σ (奇异值): {s.round(4)}")
print(f"V^T (右奇异向量转置): shape {Vt.shape}")

# 用 SVD 重构矩阵
Sigma = np.zeros(B.shape)
Sigma[:len(s), :len(s)] = np.diag(s)
reconstructed = U @ Sigma @ Vt

reconstruction_error = np.linalg.norm(B - reconstructed)
print(f"\n重构误差 ||B - USV^T|| = {reconstruction_error:.2e} (应接近0)")

# SVD 降维演示 (图像压缩概念)
print(f"\n--- 低秩近似 (数据压缩) ---")
total_energy = np.sum(s**2)
cumulative_energy = np.cumsum(s**2) / total_energy

print(f"{'保留奇异值数':>12} {'累积能量%':>12} {'信息损失':>12}")
for k in [1, 2, 3, 4]:
    energy_pct = cumulative_energy[k-1] * 100
    loss = 100 - energy_pct
    # 低秩重构
    B_k = U[:, :k] @ np.diag(s[:k]) @ Vt[:k, :]
    err = np.linalg.norm(B - B_k)
    print(f"  k={k:>2}         {energy_pct:>11.1f}%  误差={err:.4f}")


# ============================================================
# 第三部分:最小二乘法
# ============================================================
separator("三、最小二乘法 (Least Squares)")

print("""
【问题】给定数据点 (xi, yi),找到最佳拟合直线 y = ax + b
  使残差平方和最小: min Σ(yi - (axi+b))²

【矩阵形式】X·β = y 的最小二乘解:
  β = (X^T X)^(-1) X^T y
  或直接用 np.linalg.lstsq()
""")

# 生成带噪声的线性数据
rng = np.random.default_rng(123)
n_points = 50
true_slope = 2.5
true_intercept = 10.0
x_data = np.linspace(0, 10, n_points)
noise = rng.normal(0, 2.0, size=n_points)
y_data = true_slope * x_data + true_intercept + noise

print(f"真实参数: slope={true_slope}, intercept={true_intercept}")
print(f"数据点数: {n_points}")

# 方式1: lstsq (推荐)
X = np.column_stack([x_data, np.ones(n_points)])  # 设计矩阵
result, residuals, rank, sv = np.linalg.lstsq(X, y_data, rcond=None)

slope_lstsq, intercept_lstsq = result
residual_norm = np.sqrt(residuals[0]) if len(residuals) > 0 else 0
print(f"\n--- np.linalg.lstsq 结果 ---")
print(f"  斜率 a = {slope_lstsq:.4f} (真实值 {true_slope})")
print(f"  截距 b = {intercept_lstsq:.4f} (真实值 {true_intercept})")
print(f"  残差范数: {residual_norm:.4f}")

# 方式2: 多项式拟合 (二次)
coeffs_2nd = np.polyfit(x_data, y_data, deg=2)
print(f"\n--- np.polyfit 二次拟合 ---")
print(f"  y = {coeffs_2nd[0]:.4f}x^2 + {coeffs_2nd[1]:.4f}x + {coeffs_2nd[2]:.4f}")

# 方式3: 手动正规方程 (教学目的)
beta_manual = np.linalg.inv(X.T @ X) @ (X.T @ y_data)
print(f"\n--- 手动正规方程 (X^T X)^{-1} X^T y ---")
print(f"  beta = [{beta_manual[0]:.4f}, {beta_manual[1]:.4f}]")

# R² 判定系数
y_pred = X @ result
ss_res = np.sum((y_data - y_pred)**2)
ss_tot = np.sum((y_data - y_data.mean())**2)
r_squared = 1 - ss_res / ss_tot
print(f"\n  R² 判定系数 = {r_squared:.4f} (越接近1拟合越好)")


# ============================================================
# 第四部分:QR 分解 与 Cholesky 分解
# ============================================================
separator("四、矩阵分解: QR 和 Cholesky")

# --- QR 分解 ---
print("--- QR 分解 ---")
C = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 7]
], dtype=float)

Q, R = np.linalg.qr(C)
print(f"原始矩阵 C:\n{C}")
print(f"\nQ (正交矩阵, Q^T Q = I):")
print(Q.round(4))
print(f"\nR (上三角矩阵):")
print(R.round(4))

qr_check = Q @ R
print(f"\n验证 Q·R = C? 误差: {np.linalg.norm(C - qr_check):.2e}")
orthogonality_check = Q.T @ Q
print(f"Q的正交性 Q^T·Q:\n{orthogonality_check.round(4)}")

print("\n[用途] QR分解用于求解线性方程组、特征值迭代算法等")

# --- Cholesky 分解 (仅适用于对称正定矩阵) ---
print("\n--- Cholesky 分解 ---")
D = np.array([
    [4, 2, 1],
    [2, 5, 3],
    [1, 3, 6]
], dtype=float)

try:
    L = np.linalg.cholesky(D)
    print(f"对称正定矩阵 D:\n{D}")
    print(f"\nL (下三角矩阵, D = L·L^T):")
    print(L.round(4))
    cholesky_check = L @ L.T
    print(f"\n验证 L·L^T = D? 误差: {np.linalg.norm(D - cholesky_check):.2e}")
except np.linalg.LinAlgError as e:
    print(f"Cholesky 失败: {e} (矩阵可能不是正定的)")

print("\n[用途] Cholesky用于快速求解正定线性系统, 速度快于一般LU分解")


# ============================================================
# 第五部分:行列式、秩、条件数
# ============================================================
separator("五、行列式、秩、条件数")

matrices = {
    "满秩矩阵": np.array([[1, 2], [3, 4]], dtype=float),
    "奇异矩阵(行相关)": np.array([[1, 2], [2, 4]], dtype=float),
    "近奇异矩阵": np.array([[1, 2], [1, 2.001]], dtype=float),
}

print(f"{'名称':<18} {'行列式':>10} {'秩':>5} {'条件数':>12} {'是否奇异'}")
print("-" * 58)

for name, M in matrices.items():
    det = np.linalg.det(M)
    rank = np.linalg.matrix_rank(M)
    cond = np.linalg.cond(M)
    is_singular = "YES" if abs(det) < 1e-10 else "No"
    print(f"{name:<18} {det:>10.4f} {rank:>5} {cond:>12.1f} {is_singular}")

print(f"""
[解读]
  行列式 = 0 → 矩阵奇异 (不可逆)
  条件数越大 → 矩阵越接近奇异, 数值求解越不稳定
  一般 cond > 1e10 就认为数值上接近奇异
""")


# ============================================================
# 第六部分:实战 ------ 手写 PCA (主成分分析)
# ============================================================
separator("六、实战: 手写 PCA (主成分分析)")

print("""
PCA 步骤:
  1. 数据标准化 (零均值, 单位方差)
  2. 计算协方差矩阵
  3. 特征值分解协方差矩阵
  4. 取 top-k 特征向量作为主成分
  5. 投影数据到低维空间
""")

# 模拟数据: 3个特征, 100个样本
rng = np.random.default_rng(42)
n_samples = 100

# 特征1和特征2有强相关性 (构造出来)
feature1 = rng.normal(0, 1, n_samples)
feature2 = feature1 * 0.8 + rng.normal(0, 0.3, n_samples)  # 与f1高度相关
feature3 = rng.normal(0, 1, n_samples)                        # 与其他无关

data_raw = np.column_stack([feature1, feature2, feature3])
print(f"原始数据形状: {data_raw.shape} (100样本 × 3特征)")

# Step 1: 标准化
data_mean = data_raw.mean(axis=0)
data_std = data_raw.std(axis=0)
data_normalized = (data_raw - data_mean) / data_std

# Step 2: 协方差矩阵
cov_matrix = np.cov(data_normalized.T)
print(f"\n协方差矩阵:\n{cov_matrix.round(3)}")

# Step 3: 特征值分解
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
# eigh 返回升序排列, 反转为降序
idx_sorted = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx_sorted]
eigenvectors = eigenvectors[:, idx_sorted]

variance_explained = eigenvalues / eigenvalues.sum()
cumulative_variance = np.cumsum(variance_explained)

print(f"\n各主成分解释方差:")
for i in range(3):
    print(f"  PC{i+1}: 特征值={eigenvalues[i]:.3f}, "
          f"解释方差={variance_explained[i]*100:.1f}%, "
          f"累积={cumulative_variance[i]*100:.1f}%")

# Step 5: 降维投影 (降到 2 维)
k = 2
W = eigenvectors[:, :k]  # 投影矩阵
data_projected = data_normalized @ W

print(f"\n降维后数据形状: {data_projected.shape}")
print(f"保留了 {cumulative_variance[k-1]*100:.1f}% 的信息")
print(f"(从 3 维降至 {k} 维)")


# ============================================================
# 第七部分:实战 ------ 线性方程组求解对比
# ============================================================
separator("七、实战: 线性方程组求解方法对比")

print("""
求解 Ax = b 的多种方法及其适用场景
""")

# 创建测试方程组: 5个未知数
rng_test = np.random.default_rng(99)
size = 5
A_test = rng_test.standard_normal((size, size))
# 确保 A 可逆 (条件数不太大)
A_test += size * np.eye(size)  # 对角增强
x_true = rng_test.standard_normal(size)
b_test = A_test @ x_true

print(f"未知数个数: {size}")
print(f"真实解: x = {x_true.round(4)}\n")

methods_results = {}

# 方法1: inv(A) * b (最直观但不推荐)
start = time.perf_counter()
x_inv = np.linalg.inv(A_test) @ b_test
methods_results['inv(A)*b'] = (time.perf_counter() - start, x_inv)

# 方法2: solve (推荐用于单个方程组)
start = time.perf_counter()
x_solve = np.linalg.solve(A_test, b_test)
methods_results['solve()'] = (time.perf_counter() - start, x_solve)

# 方法3: lstsq (也适用于超定/欠定系统)
start = time.perf_counter()
x_lstsq = np.linalg.lstsq(A_test, b_test, rcond=None)[0]
methods_results['lstsq()'] = (time.perf_counter() - start, x_lstsq)

print(f"{'方法':<15} {'耗时(us)':>10} {'最大误差':>12}")
print("-" * 38)
for name, (elapsed, solution) in methods_results.items():
    error = np.max(np.abs(solution - x_true))
    print(f"{name:<15} {elapsed*1e6:>10.1f} {error:>12.2e}")

print(f"""
[建议]
  solve(A, b)     → 方阵唯一解 (最快最准)
  lstsq(A, b)     → 超定/欠定/不适定系统
  inv(A) @ b      → 仅教学用, 实际中避免显式求逆
  (inv 运算量大且数值不稳定)
""")


# ============================================================
# 总结
# ============================================================
separator("总结: NumPy Linear Algebra 速查")

summary = """
+------------------------------------------------------------+
|  numpy.linalg 核心函数速查                                   |
+------------------------------------------------------------+
|                                                            |
|  [特征值/特征向量]                                          |
|  np.linalg.eig(A)      → 一般方阵的特征分解                |
|  np.linalg.eigh(A)     → 对称/Hermite矩阵 (更快更稳定)      |
|                                                            |
|  [SVD]                                                     |
|  np.linalg.svd(A)       → 奇异值分解                       |
|                                                            |
|  [方程组求解]                                               |
|  np.linalg.solve(A, b)  → Ax=b 的精确解                   |
|  np.linalg.lstsq(A, b)  → 最小二乘解                       |
|  np.linalg.inv(A)        → 逆矩阵 (慎用!)                 |
|  np.linalg.pinv(A)       → Moore-Penrose伪逆               |
|                                                            |
|  [矩阵分解]                                                 |
|  np.linalg.qr(A)         → QR分解                           |
|  np.linalg.cholesky(A)   → Cholesky (正定矩阵)            |
|  np.linalg.lu(A)         → LU分解 (SciPy)                  |
|                                                            |
|  [矩阵属性]                                                 |
|  np.linalg.det(A)        → 行列式                           |
|  np.linalg.matrix_rank(A)→ 秩                               |
|  np.linalg.cond(A)        → 条件数                           |
|  np.linalg.norm(A)        → 范数                             |
|  np.trace(A)              → 迹 (对角线之和)                 |
|                                                            |
|  [向量化外积/内积]                                         |
|  np.dot(a, b) / a@b     → 矩阵乘法/内积                     |
|  np.outer(a, b)          → 外积                             |
|  np.inner(a, b)          → 内积                             |
|  np.vdot(a, b)           → 向量化内积                       |
+------------------------------------------------------------+
"""
print(summary)

print("\n运行完毕!")
相关推荐
GitCode官方14 小时前
《开源友的聊》第二季,回来了。
人工智能·开源·atomgit
SLD_Allen14 小时前
从Prompt、Context到Harness,工程的三次进化与终局之战
人工智能·prompt
z小猫不吃鱼14 小时前
06 Tokenizer 详解:BPE、WordPiece、SentencePiece 有什么区别?
人工智能·语言模型·自然语言处理·transformer
HKT_China14 小时前
5G IoT升级营运模式,打通数据脉络,驱动AI应用
人工智能·物联网·5g·iot
weixin_4684668514 小时前
PyTorch 深度学习框架核心能力与实战评测
人工智能·pytorch·深度学习·神经网络·计算机视觉·动态图·模型训练
薛会14 小时前
当世界模型学会”想”和”造”:3D-Belief 与 GigaWorld-0 揭示的两条进化路线
人工智能·深度学习·机器学习
G***技14 小时前
IB3-771:为智慧工厂巡检机器人打造“感知-决策-执行”一体化控制核心
人工智能·嵌入式主板
海兰14 小时前
手把手elasticsearch学习之构建 HITL AI 代理
人工智能·学习·elasticsearch
zhangxingchao14 小时前
AI 大模型核心五:从 Transformer、RAG 到 Agent 架构
前端·人工智能·后端