2026/10/14 2:18:49

图解线性代数:用Python复现矩阵变换与SVD的几何直觉

图解线性代数:用Python复现矩阵变换与SVD的几何直觉 简介《线性代数的艺术》中文版PDF是一份面向线性代数初学者与进阶学习者的图形化学习资料作者Kenji Hiranabe在Gilbert Strang《线性代数为所有人》理论基础上用可视化方式重新诠释矩阵运算与分解。资源包内仅含1个PDF文件大小约1.43MB轻量便携适合在电脑或平板上随时翻阅。内容从矩阵的四种视角切入依次讲解向量点积与秩1矩阵、矩阵乘向量与矩阵乘矩阵的线性组合含义并重点展开CR、LU、QR、QΛQT与奇异值分解五种矩阵分解方法配有大量彩色示意图与章节交叉引用便于对照Strang原书章节深入理解。目前已有573人学习浏览适合希望摆脱纯公式推导、通过图形直觉把握线性代数核心结构的读者可作为课堂辅助读物或考前梳理矩阵分解知识框架的参考笔记。1. 一份把线性代数画成图的 PDF为什么值得你花一晚上啃完如果你和我一样第一次翻开传统线性代数教材时被满页的矩阵下标和行列式展开劝退那你大概能理解我看到《The-Art-of-Linear-Algebra-zh-CN.pdf》时的反应——它把矩阵乘法、特征值、奇异值分解这些抽象概念全部画成了几何图形。这不是一本习题册也不是考试速成笔记而是一份用视觉直觉重建线性代数认知的图解文档。它解决的核心问题是你背得出公式却说不清矩阵到底在对空间做什么。适合谁适合已经学过一遍线代但脑子里只有计算步骤、没有几何图像的工程师、研究生和自学者。我自己的血泪经验是做机器学习调参时真正卡住你的不是代码而是对矩阵分解的直觉缺失这份文档恰好补的就是这块。2. 从矩阵乘法到四个子空间图解线代到底在画什么2.1 矩阵不是数字表格是空间变换的操作说明书传统教材告诉你矩阵乘法是行乘列求和但图解文档会先问你一个问题一个 2×2 矩阵作用在平面上到底发生了什么答案是把单位正方形变成平行四边形。矩阵的每一列就是基向量变换后的落点。这个视角一旦建立行列式就不再是展开式而是面积缩放因子——行列式为 0 意味着空间被压扁了信息不可逆。我一般会建议读者先拿纸笔画三个矩阵旋转矩阵、缩放矩阵、剪切矩阵分别看它们把单位正方形变成什么形状。这一步花不了二十分钟但能让你后面看特征值的时候少走很多弯路。图解文档里对这部分处理得很克制没有堆砌公式而是用箭头和色块把“列向量就是新基”这件事讲透了。2.2 四个子空间把 Axb 的解结构画成一张地图列空间、零空间、行空间、左零空间——这四个词在教材里通常出现在不同章节学生很难把它们串起来。图解文档的做法是把它们画在同一张图上用正交关系连接。列空间是 A 能到达的所有输出零空间是被 A 压成零的所有输入两者维度满足秩-零化度定理。这里有一个关键直觉如果 b 不在列空间里Axb 就无解如果零空间维度大于 0解就不唯一。我见过太多工程师在调试最小二乘拟合时搞不清为什么正规方程有时候解不稳定根源就是对这四个空间的关系没有图像化理解。文档里用二维和三维的例子反复演示虽然简单但足够建立正确的心理模型。2.3 特征值与特征向量变换中不改变方向的那些线特征值的几何意义是矩阵作用在特征向量上只拉伸不旋转。这个定义看起来简单但真正理解它需要你同时看到三件事——特征向量张成的线、对应的拉伸倍数、以及不同特征值之间的竞争关系。图解文档用一组动态示意图展示了对称矩阵和非对称矩阵的区别对称矩阵的特征向量正交非对称矩阵的不一定。我自己的习惯是拿到一个矩阵先问它是不是对称的因为对称矩阵的谱定理保证了实数特征值和正交特征基这在数值计算里是巨大的便利。文档里对这部分没有深入数值算法但视觉直觉给得很足后续你去看幂迭代或 QR 算法时脑子里会有图。2.4 奇异值分解所有矩阵都能拆成旋转-拉伸-旋转SVD 是线性代数里最漂亮的分解但也是最难用直觉理解的。图解文档把它画成三步先旋转再沿坐标轴拉伸再旋转。左奇异向量是输入空间的正交基右奇异向量是输出空间的正交基奇异值是拉伸倍数。这里有一个常见误解很多人以为 SVD 只适用于方阵其实任何 m×n 矩阵都有 SVD。文档里用长方形矩阵的例子专门澄清了这一点。我建议读者在看完这部分后拿一个 3×2 的矩阵手算一遍 SVD不用求精确值只要把三个步骤的几何意义对应上就行。这一步做完你再看 PCA、推荐系统里的隐语义模型会顺畅很多。3. 用 Python 把 PDF 里的图复现一遍最小可运行代码3.1 环境准备与依赖安装图解文档里的图虽然精美但如果你只是看过两天就忘了。我的做法是把它里面的核心图用 Python 复现一遍这样肌肉记忆和视觉记忆会同时建立。你需要的基础环境很简单Python 3.9 以上、NumPy、Matplotlib。不需要深度学习框架不需要 GPU。# 创建虚拟环境避免污染主环境 python -m venv linalg-vis source linalg-vis/bin/activate # Windows 用 linalg-vis\Scripts\activate # 安装三个核心库 pip install numpy matplotlib这里没有指定版本号因为这三个库的 API 在近几个大版本里足够稳定。如果你用的是较老的 Python 3.7建议升级否则 Matplotlib 的某些绘图参数会不兼容。安装完成后用python -c import numpy; print(numpy.__version__)确认一下。3.2 画一个矩阵对单位正方形的变换下面这段代码复现的是文档里最基础的那张图一个 2×2 矩阵如何把单位正方形变成平行四边形。我加了网格和箭头方便你看到基向量的落点。import numpy as np import matplotlib.pyplot as plt # 定义变换矩阵这里选一个剪切缩放的组合 A np.array([[1.5, 0.8], [0.2, 1.2]]) # 单位正方形的四个顶点逆时针 square np.array([[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]) # 最后一点回到起点方便画闭合 # 对每个顶点做矩阵乘法注意转置 transformed (A square.T).T fig, ax plt.subplots(figsize(6, 6)) # 画原始正方形 ax.plot(square[:, 0], square[:, 1], b--, label原始单位正方形) # 画变换后的平行四边形 ax.plot(transformed[:, 0], transformed[:, 1], r-, linewidth2, label变换后) # 画基向量变换前后的箭头 origin np.array([0, 0]) ax.arrow(0, 0, A[0, 0], A[1, 0], head_width0.08, colorgreen) ax.arrow(0, 0, A[0, 1], A[1, 1], head_width0.08, colororange) ax.set_xlim(-0.5, 3) ax.set_ylim(-0.5, 3) ax.set_aspect(equal) ax.grid(True, alpha0.3) ax.legend() ax.set_title(矩阵 A 对单位正方形的变换) plt.show()这段代码的逻辑很直接square是原始形状的顶点坐标A square.T做矩阵乘法再转置回来得到变换后的坐标。关键参数是A矩阵本身你可以改成任何 2×2 矩阵观察形状变化。绿色箭头是第一列橙色箭头是第二列它们就是新的基向量。如果行列式为负你会看到图形翻转这是方向改变的可视化。3.3 用特征向量看变换中的不变量下面这段代码画一个对称矩阵的特征向量并验证它们确实只被拉伸不旋转。import numpy as np import matplotlib.pyplot as plt # 对称矩阵保证有实特征值和正交特征向量 A np.array([[2.0, 1.0], [1.0, 2.0]]) # 计算特征值和特征向量 eigenvalues, eigenvectors np.linalg.eigh(A) # eigh 专门用于对称矩阵 print(特征值:, eigenvalues) print(特征向量:\n, eigenvectors) fig, ax plt.subplots(figsize(6, 6)) # 画原始特征向量方向 for i in range(2): v eigenvectors[:, i] ax.arrow(0, 0, v[0], v[1], head_width0.08, colorblue, alpha0.5, labelf原始 v{i1}) # 画变换后的向量应该在同一方向上长度变为 eigenvalue 倍 for i in range(2): v eigenvectors[:, i] Av A v ax.arrow(0, 0, Av[0], Av[1], head_width0.08, colorred, alpha0.8, labelfA v{i1}) ax.set_xlim(-2, 2) ax.set_ylim(-2, 2) ax.set_aspect(equal) ax.grid(True, alpha0.3) ax.legend() ax.set_title(对称矩阵的特征向量只拉伸不旋转) plt.show()这里用np.linalg.eigh而不是eig是因为对称矩阵用eigh更稳定且保证实数输出。特征向量矩阵的每一列是一个特征向量A v的结果应该等于eigenvalue * v。如果你把A改成非对称矩阵比如[[1, 2], [0, 1]]你会发现特征向量不够用这时候就需要 SVD 了。3.4 手算一个 SVD 并验证三个步骤SVD 的代码验证稍微复杂一点但值得做。下面这段代码把 SVD 拆成旋转-拉伸-旋转三步并画出来。import numpy as np import matplotlib.pyplot as plt # 一个长方形矩阵2×3 A np.array([[1.0, 2.0, 0.5], [0.5, 1.0, 1.5]]) # 计算 SVD U, S, Vt np.linalg.svd(A, full_matricesFalse) print(U 形状:, U.shape) print(奇异值:, S) print(Vt 形状:, Vt.shape) # 验证 A U diag(S) Vt Sigma np.diag(S) A_reconstructed U Sigma Vt print(重构误差:, np.linalg.norm(A - A_reconstructed)) # 画奇异值衰减这是判断矩阵有效秩的常用方法 fig, axes plt.subplots(1, 2, figsize(10, 4)) axes[0].bar(range(1, len(S)1), S, colorsteelblue) axes[0].set_xlabel(奇异值序号) axes[0].set_ylabel(大小) axes[0].set_title(奇异值谱) # 画原始矩阵和秩-1 近似 A_approx U[:, :1] np.diag(S[:1]) Vt[:1, :] axes[1].imshow(A, cmapBlues, aspectauto) axes[1].set_title(原始矩阵) axes[1].set_xticks([]) axes[1].set_yticks([]) plt.tight_layout() plt.show() print(原始矩阵:\n, A) print(秩-1 近似:\n, A_approx)这段代码的关键参数是full_matricesFalse它返回紧凑形式的 SVD对于长方形矩阵能节省内存。U是 2×2S是长度为 2 的向量Vt是 2×3。重构误差应该接近机器精度。秩-1 近似展示了 SVD 在降维中的核心用途只保留最大的奇异值就能抓住矩阵的主要结构。你可以把A换成图像矩阵看看保留前几个奇异值能恢复出什么。4. 图解线代在工程里的三个真实落点4.1 最小二乘与投影为什么正规方程有时候会翻车最小二乘的几何意义是把 b 投影到 A 的列空间上。如果 A 的列向量近似线性相关列空间会变得很“窄”投影矩阵的条件数爆炸数值解就不稳定。图解文档里用二维例子展示了这个现象两条几乎平行的线交点对扰动极其敏感。工程上的常见做法是加正则项也就是岭回归。从线性代数角度看正则项相当于给 A 的对角线加了一个小量把列空间“撑开”。我一般会先看奇异值谱如果最小奇异值和最大奇异值差了好几个数量级就会考虑正则化或者降维。这一步不需要复杂计算np.linalg.svd跑一下就行。4.2 PCA 不是黑匣子它就是 SVD 在中心化数据上的应用很多人把 PCA 当成一个调包操作但如果你理解 SVDPCA 就是透明的。把数据矩阵中心化后做 SVD右奇异向量就是主成分方向奇异值的平方除以样本数就是方差解释比例。图解文档里虽然没有专门讲 PCA但 SVD 那部分的几何直觉直接迁移过来。我自己的习惯是做 PCA 之前先画奇异值谱看拐点在哪里。如果奇异值衰减很慢说明数据在各个方向上方差差不多降维意义不大。如果前两三个奇异值占了 90% 以上那降到二维可视化是合理的。这些判断不需要跑模型几行 NumPy 就能搞定。4.3 推荐系统里的隐语义模型矩阵分解的工程权衡推荐系统里的矩阵分解本质上是在找一个低秩近似。用户-物品评分矩阵通常很大但很稀疏直接做 SVD 不可行因为缺失值太多。工程上的做法是用随机梯度下降或者交替最小二乘来拟合隐向量。但如果你理解 SVD 的几何意义就知道隐向量的维度选择本质上是在偏差和方差之间权衡。维度太低欠拟合推荐不准维度太高过拟合泛化差。我一般会从 20 到 50 维开始试看验证集上的 RMSE 曲线。图解文档里对低秩近似的可视化能帮你建立“为什么低秩能抓住主要模式”的直觉这比调参本身更重要。5. 避坑与排查复现图解线代时最容易踩的五个坑5.1 特征向量顺序与符号别被输出结果搞晕现象用np.linalg.eig算特征向量发现和手算结果差一个负号或者顺序对不上。原因特征向量的符号和顺序不是唯一的NumPy 不保证按特征值大小排序也不保证符号方向。解决用np.linalg.eigh处理对称矩阵它返回升序特征值需要降序就手动翻转。符号问题不影响几何意义画图时统一方向即可。5.2 矩阵乘法维度不匹配转置用错地方现象A square报错提示维度不匹配。原因square是 5×2 的顶点列表而A是 2×2矩阵乘法要求内维一致。解决写成(A square.T).T先转置再乘最后转回来。或者直接用square A.T效果一样。记住一个原则行向量右乘矩阵列向量左乘矩阵。5.3 画图时坐标轴比例失真几何直觉被扭曲现象画出来的平行四边形看起来不像平行四边形角度和长度都不对。原因Matplotlib 默认的坐标轴比例不是 1:1x 轴和 y 轴的单位长度不同。解决加ax.set_aspect(equal)。这一步在画任何几何变换图时都必须做否则你的视觉直觉会被误导。我见过有人因为没设这个参数误以为某个矩阵是保角的。5.4 SVD 的 full_matrices 参数内存和形状的权衡现象对一个 1000×10 的矩阵做 SVDU的形状是 1000×1000内存直接爆掉。原因np.linalg.svd默认full_matricesTrue返回完整的方阵 U 和 Vt。解决设full_matricesFalseU 变成 1000×10Vt 变成 10×10节省大量内存。只有在需要完整正交基时才用默认值工程里几乎总是用紧凑形式。5.5 数值精度问题为什么重构误差不是零现象A - U diag(S) Vt的范数不是精确的 0而是 1e-15 量级。原因浮点数运算有舍入误差这是正常的。解决用np.allclose而不是来判断相等。如果误差到了 1e-5 以上那可能是矩阵条件数太大或者算法选择有问题。对于病态矩阵考虑用np.linalg.lstsq而不是直接求逆。6. 把图解 PDF 变成自己的直觉一个我坚持了三年的习惯我自己的做法是每学一个新的线性代数概念就强迫自己用 Matplotlib 画一张图。不追求好看只追求把“输入是什么、变换是什么、输出是什么”这三件事画清楚。这个习惯坚持了三年最大的收获不是绘图技巧而是遇到问题时脑子里能自动浮现几何图像。比如调试一个卡尔曼滤波器状态协方差矩阵的正定性问题我第一反应是去看它的特征值是不是有负的而不是去翻公式。如果你也想建立这种直觉我建议从这份 PDF 里挑三张你最看不懂的图用 Python 复现一遍。不用全部复现三张就够。复现的过程中你会被迫回答很多看的时候忽略的问题这个箭头为什么指向这里这个角度为什么是 90 度这个矩阵为什么是对称的这些问题才是真正让你从“知道”变成“理解”的开关。最后一个技巧把复现的图存成 PNG和 PDF 原图放在同一个文件夹里隔一周再打开对比。如果你能一眼看出自己画的图和原图的差异并且说出差异的原因那这个概念你就拿下了。如果看不出差异说明你还没真正理解回去再画一遍。这个验证方法比做习题有效得多因为它不给你蒙对的机会。希望帮到你。本文还有配套的精品资源点击获取