【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运行完毕!")
相关推荐
WikiLeake3 分钟前
一个健壮且灵活的大语言模型(LLM)API 配置加载器
数据库·人工智能
龙亘川5 分钟前
电子印章安全审计管理业务建模:从证书密钥、安全策略到审计预警的全链路闭环
运维·人工智能·安全·智慧城市·开源软件·数据可视化
饼饼学习空间智能9 分钟前
物流仓储机器人训练数据从哪来?真机、仿真与数据回流技术解析
人工智能
2501_938140072 小时前
做内容很省心,2026好用AI笔记,支持抖音小红书视频无水印保存
人工智能
sell-cloud5 小时前
Prompt Engineering要凉了?Fable 5.5能“猜“意图,但银行正在封杀AI Agent——企业AI的真正护城河是什么
人工智能·prompt
Navicat中国7 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI7 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区7 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris9 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy9 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae