线性变换¶
每一次矩阵乘法都是一个线性变换,一个在保持线性的同时重塑、旋转或投影向量的函数。本文涵盖旋转、反射、缩放、剪切、投影、映射的核与像,以及神经网络层如何串联这些变换。
-
线性变换(或线性映射)是一个接收向量并产生另一个向量的函数,同时保持加法和数乘。如果 \(T\) 是线性的,那么:
- \(T(\mathbf{u} + \mathbf{v}) = T(\mathbf{u}) + T(\mathbf{v})\)
- \(T(c\mathbf{u}) = cT(\mathbf{u})\)
-
每一个线性变换都可以表示为乘以一个矩阵。这个矩阵就是变换本身。当你将一个向量乘以一个矩阵时,就是在对它应用一个线性变换。
-
可以把一个 \(2 \times 2\) 矩阵想象成一台机器,它接收二维向量并输出新的二维向量。矩阵的列告诉你标准基向量 \(\hat{\mathbf{i}}\) 和 \(\hat{\mathbf{j}}\) 在变换后落到了哪里。其他一切都由线性性决定。
- 例如,如果
那么 \(\hat{\mathbf{i}} = [1, 0]^T\) 落到 \([2, 1]^T\)(第1列),\(\hat{\mathbf{j}} = [0, 1]^T\) 落到 \([1, 2]^T\)(第2列)。其他每个向量都是这两个的组合,因此它的输出自动随之确定。
-
两个矩阵相乘可以看作先应用一个变换再应用另一个变换。如果 \(B\) 将向量从一个空间变换,然后 \(A\) 变换结果,那么 \(AB\) 按顺序执行两者。在游戏引擎中,先旋转角色再向前移动,与先移动再旋转的结果不同,这就是为什么矩阵乘法不可交换。
-
旋转 将向量转动一个角度 \(\theta\) 而不改变其长度。向量的大小保持不变,只是指向了一个新的方向。
- 在二维中,旋转矩阵为:
- 对于 \(\theta = 90°\):
所以 \([1, 0]^T\) 变成 \([0, 1]^T\)。指向右的向量现在指向上。旋转矩阵是正交的,且行列式恒为 1。当你在手机上旋转一张照片时,这就是应用到每个像素坐标上的精确矩阵。
- 在三维中,每个轴都有单独的旋转矩阵。机械臂的每个关节绕特定轴旋转,每个关节就是一个旋转矩阵。绕 z 轴的旋转看起来像嵌入到三维中的二维情况:
- 缩放 沿每个轴独立地拉伸或压缩向量:
-
\(S(2, 1.5)\) 将 x 分量加倍,并将 y 分量乘以 1.5。沿一个轴缩放 -1 会翻转该分量。对角矩阵总是缩放变换。当你将图像缩小到 50% 时,就是在对每个像素坐标应用 \(S(0.5, 0.5)\)。
-
反射 像镜子一样将向量翻转经过一个轴或直线。关于 x 轴的反射保持 x 分量不变,取反 y 分量:
- 例如,\([3, 2]^T\) 变成 \([3, -2]^T\)。当你的手机水平翻转自拍使文字正确显示时,就是在应用一个反射矩阵。关于直线 \(y = x\) 的反射交换两个分量:
-
反射矩阵的行列式为 \(-1\),确认它们翻转了定向。
-
旋转和反射都是刚体变换:它们保持距离和角度。表示它们的矩阵是正交矩阵,这就是为什么正交矩阵的行列式总是 \(+1\)(旋转)或 \(-1\)(反射)。
-
剪切 沿一个轴按另一个轴的比例使向量倾斜。水平剪切因子 \(k\):
-
每个点水平滑动 \(k\) 乘以其高度。当 \(k = 0.5\) 时,高度为 2 的点向右移动 1。底行保持不动,顶行滑动。这就是斜体文字的原理:直立的字母被剪切,从而向右倾斜。
-
上述所有变换(旋转、缩放、反射、剪切)都是线性变换。它们保持原点固定,并保持直线。但是平移(将所有点移动一个固定量)呢?
-
平移不是线性变换,因为它移动了原点。如果你将所有点向右移动 3,零向量就会移动到 \([3, 0]^T\),破坏了线性性。为了处理它,我们使用仿射变换,它结合了一个线性变换和一个平移:
- 为了将其表示为单个矩阵乘法,我们使用齐次坐标:给每个向量添加一个额外的 1,并使用一个 \((n+1) \times (n+1)\) 的矩阵:
-
仿射变换保持直线和平行性,但不一定保持角度或长度。游戏中的每个物体都使用仿射变换定位:旋转它、缩放它,然后将其放置在正确的位置,所有这些都编码在一个单一的矩阵中。
-
退化变换(奇异矩阵)将空间压缩到更低的维度。
-
例如,矩阵
将每个二维向量映射到一条直线上,因为两列指向相同的方向。行列式为零,信息丢失,变换无法撤销。
-
将彩色图像(每个像素 3 个值:红、绿、蓝)转换为灰度(每个像素 1 个值)是一个退化变换:颜色信息永久丢失。
-
在机器学习中,线性变换是神经网络的核心。数据被表示为一个矩阵(向量的堆叠,表示物体如人、飞机、文本、图像……任何东西的特征)。
-
每一层都应用一次矩阵乘法(线性变换),细节将在其他章节中介绍,我们需要解释如何构建这些数据并正确地引出神经网络。
-
然而,当今最常用的技术几乎总是只通过一堆线性变换传递数据,我们称之为Transformer。
-
Gemini、ChatGPT、Claude、Qwen、DeepSeek 以及当今世界上性能最佳的 AI,都是 Transformer!
编程任务(使用 CoLab 或 notebook)¶
-
对向量应用旋转矩阵,并绘制原始向量和旋转后的向量。尝试不同的角度。
import jax.numpy as jnp import matplotlib.pyplot as plt theta = jnp.pi / 3 R = jnp.array([[jnp.cos(theta), -jnp.sin(theta)], [jnp.sin(theta), jnp.cos(theta)]]) v = jnp.array([1.0, 0.0]) v_rot = R @ v plt.figure(figsize=(5, 5)) plt.quiver(0, 0, v[0], v[1], angles='xy', scale_units='xy', scale=1, color='red', label='原始向量') plt.quiver(0, 0, v_rot[0], v_rot[1], angles='xy', scale_units='xy', scale=1, color='blue', label='旋转后') plt.xlim(-1.5, 1.5); plt.ylim(-1.5, 1.5) plt.grid(True); plt.legend(); plt.gca().set_aspect('equal') plt.show() -
对构成正方形的点集应用剪切变换,并可视化变形后的形状。
import jax.numpy as jnp import matplotlib.pyplot as plt square = jnp.array([[0,0],[1,0],[1,1],[0,1],[0,0]]).T k = 0.5 shear = jnp.array([[1, k], [0, 1]]) sheared = shear @ square plt.figure(figsize=(6, 4)) plt.plot(square[0], square[1], 'r-o', label='原始正方形') plt.plot(sheared[0], sheared[1], 'b-o', label='剪切后') plt.grid(True); plt.legend(); plt.gca().set_aspect('equal') plt.show()