系列 · 线性代数 · 第 15 篇

线性代数(十五):机器学习中的线性代数——从 PCA 到推荐系统

线性代数是机器学习的'母语'。本章深入 PCA、LDA、SVM 核方法、矩阵分解推荐系统、线性回归的矩阵形式,以及神经网络中的线性层与注意力机制背后的线性代数原理。

随便找个资深机器学习工程师问一句:“你每天实际用得最多的数学是什么?”答案几乎肯定是线性代数。微积分用于推导公式,概率用于建模,但在实际运行 ML 系统时,大部分时间都花在矩阵向量乘法、分解和投影上。PyTorch 的 Linear、scikit-learn 的 PCA、Spark MLlib 的 ALS,还有 Transformer 的注意力头,其实都是同一个线性代数基本操作换了个马甲。

这一章将介绍生产环境中 ML 系统实际运行的算法,包括 PCA、LDA、带核的 SVM、推荐系统中的矩阵分解、正则化的线性回归、神经网络层和注意力机制——先讲解直觉,再探讨几何意义,最后给出公式。

你会学到:

  • 原始数据(图像、文本、行为)如何变成向量,为什么特征空间是几何的
  • 如何用特征分解和 SVD 实现 PCA,以及核 PCA 如何处理非线性流形
  • LDA 用于有监督降维,以及 PCA 在分类任务中为什么会失效
  • SVM 和核技巧,以及使核技巧成立的 Mercer 条件
  • 推荐系统中的矩阵分解(ALS、NMF),以及冷启动问题
  • 线性回归、岭回归和 LASSO 的矩阵形式,从 SVD 的角度看
  • 神经网络的全连接层、批处理和注意力机制为何全是矩阵乘法

前置知识: 特征分解(第 6 章 )、SVD(第 9 章 )、矩阵范数(第 10 章 )、矩阵微积分基础(第 11 章 )。


线性代数(十五):机器学习中的线性代数——从 PCA 到推荐系统 — 章节概览图

向量表示:数据如何进入流水线#

一切都要变成向量#

模型学习之前,现实世界中的对象必须嵌入到 $\mathbb{R}^p$ 空间:

  • 一张 $28 \times 28$ 的 MNIST 数字图片,把像素拉平就是 784 维向量。
  • 一条商品评论,可以是 30000 维稀疏的 TF-IDF 向量,也可以是 768 维稠密的 BERT 嵌入。
  • 用户最近 30 天的点击记录,编码成一个特征向量:包含计数、时间衰减和品类 one-hot 指标。
  • 一段蛋白质序列,用 ESM 模型映射成 1280 维向量。

原因很简单:向量是所有线性代数运算的基本单位。一旦数据被向量化,就能计算内积(相似度)、距离(k-NN)、矩阵作用(线性变换)和协方差分解(PCA)。没有向量化,每种数据类型都需要单独写算法;而有了向量化,同一个 SVD 例程就能处理图片和蛋白质。

特征空间的几何#

$$\mathbf{X} = \begin{bmatrix} \mathbf{x}_1^\top \\ \vdots \\ \mathbf{x}_n^\top \end{bmatrix} \in \mathbb{R}^{n \times p}$$

每一行是 $\mathbb{R}^p$ 中的一个点,每一列是某个特征在所有样本上的观测值。几乎所有机器学习任务都可以在这个空间里用几何语言描述:

任务几何描述
分类找一个超平面(或曲面)分开不同类别
聚类找点的密集区域
降维投影到低维子空间
回归找一个贴近所有点的超平面
异常检测找远离数据流形的点

这个几何视角不只是好看:它直接提示我们该选用什么算法。“找方差最大的方向”就是 PCA,“找分开两团点云的方向”就是 LDA,“找把两类分到两侧的曲面”就是 SVM。

词嵌入:当几何编码语义#

$$\text{vec}(\text{king}) - \text{vec}(\text{man}) + \text{vec}(\text{woman}) \approx \text{vec}(\text{queen})$$

“性别”关系大致对应一个固定方向,“王室”是另一个方向。平行四边形分析之所以成立,是因为关系被编码为嵌入空间里的方向,而不是隐藏在某一个坐标里。

词嵌入分析:平行方向编码语义关系

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import numpy as np
from gensim.models import KeyedVectors

model = KeyedVectors.load_word2vec_format(
    'GoogleNews-vectors-negative300.bin', binary=True)

print(model.most_similar(positive=['king', 'woman'], negative=['man'], topn=1))

print(model.similarity('cat', 'dog'))    # 0.76
print(model.similarity('cat', 'galaxy')) # 0.04

需要注意的是,真实嵌入是 300 到 4096 维的,类比性质是统计性的,而非精确的。上面的二维图只是示意。

主成分分析(PCA)#

直觉#

把一把回形针撒在桌上,从上往下看,它们明显沿着某个方向拉长。PCA 的作用就是自动找到这个方向:方差最大的轴。为什么关注方差?因为方差代表信息。如果某个方向上的数据几乎没有变化,那它对下游任务就没有帮助。

PCA:二维数据云的主轴

图中展示了一个有相关性的二维点云,两条主轴的长度按 $\sqrt{\text{方差}}$ 缩放,下方是数据投影到 PC1 后的一维坐标。橙色的 PC1 解释了大约 95% 的离散度,丢掉 PC2 几乎不会损失结构信息。

推导#

$$\text{Var}(\mathbf{u}) = \frac{1}{n}\sum_{i=1}^n (\mathbf{u}^\top \mathbf{x}_i)^2 = \mathbf{u}^\top \mathbf{C} \mathbf{u}, \qquad \mathbf{C} = \frac{1}{n}\mathbf{X}^\top\mathbf{X}.$$

用拉格朗日乘子法求解 $\max \mathbf{u}^\top\mathbf{C}\mathbf{u}$ ,约束条件是 $\|\mathbf{u}\|=1$ ,结果得到 $\mathbf{C}\mathbf{u} = \lambda\mathbf{u}$最优的 $\mathbf{u}$ 就是协方差矩阵最大特征值对应的特征向量,对应方向的方差就是 $\lambda$ 。后续主成分是接下来的特征向量,彼此正交,因为 $\mathbf{C}$ 是对称矩阵。

用 SVD 算 PCA:实际生产中的方法#

$$\mathbf{X} = \mathbf{U}\boldsymbol{\Sigma}\mathbf{V}^\top.$$

$\mathbf{V}$ 的列就是主成分方向;投影坐标是 $\mathbf{U}\boldsymbol{\Sigma}$ ;每个主成分的方差是 $\sigma_i^2/n$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import numpy as np
from sklearn.decomposition import PCA

np.random.seed(42)
X = np.random.randn(200, 2) @ np.array([[2, 0], [0, 0.5]])
theta = np.pi / 6
R = np.array([[np.cos(theta), -np.sin(theta)],
              [np.sin(theta),  np.cos(theta)]])
X = X @ R

pca = PCA(n_components=2).fit(X)
print(pca.components_)               # 主成分方向(每行一个)
print(pca.explained_variance_ratio_) # 每个主成分占总方差的比例

Xc = X - X.mean(axis=0)
U, S, Vt = np.linalg.svd(Xc, full_matrices=False)
print(Vt)              # 与 components_ 仅差正负号
print(S**2 / len(X))   # 同样的方差

PCA 在实际生产中的用途#

  • 可视化:把 768 维句向量降到二维,方便做探索性绘图。
  • 压缩:MNIST 数据集原本是 784 维,保留前 50 个主成分后再训练小分类器,准确率几乎不下降,但训练速度快了 10 倍。
  • 去噪:在金融领域,收益率矩阵的前几个主成分对应市场和板块因子,尾部基本是噪声(这就是第 14 章 提到的尖峰协方差模型)。
  • 白化以加速训练PCA(whiten=True) 把特征解相关并重新标度,让迭代优化器收敛更快。
  • Eigenfaces:早期人脸识别系统,把每张脸存成它在 100 个 PCA 系数上的表示。

经验之谈:做 PCA 之前一定要中心化,特征量纲不同时几乎一定要标准化(比如年龄按“岁”、收入按“元”)。否则方差最大的方向就只是“单位最大的那一列”。

核 PCA:流形弯曲怎么办#

线性 PCA 只能找到平的子空间。如果数据分布在像瑞士卷那样的弯曲流形上,任何线性投影都无法展开它。核 PCA 的思路是:把每个点通过非线性映射 $\phi(\mathbf{x})$ 嵌入到高维特征空间,再在那里做 PCA。关键在于,借助核技巧(下一节),我们不需要显式构造 $\phi$ ,只需要计算内积 $k(\mathbf{x}_i, \mathbf{x}_j)$

1
2
3
4
5
6
7
from sklearn.decomposition import PCA, KernelPCA
from sklearn.datasets import make_moons

X, y = make_moons(n_samples=200, noise=0.1, random_state=42)

X_pca  = PCA(n_components=2).fit_transform(X)
X_kpca = KernelPCA(n_components=2, kernel='rbf', gamma=10).fit_transform(X)

在可视化场景下,核 PCA 已经基本被自编码器和 t-SNE / UMAP 取代,但它依然是理解核方法在无监督任务中应用的一个干净示例。

线性判别分析(LDA)#

PCA 的盲点,标签来补#

PCA 是无监督的,它不管类别标签,只追求方差最大化。这在分类任务里可能完全不对劲。想象一下,两类数据点分别形成两团平行的雪茄状云。PCA 会选择沿着雪茄的方向,因为那里方差最大,但这个方向恰恰会把两类混在一起。正确的方向应该是垂直于雪茄的方向——能把它们分开的方向。

LDA 是有监督的。它寻找一个投影方向,同时满足两个条件:

  1. 让类别中心之间的距离尽可能大(类间散度大)。
  2. 让每个类内部的数据点尽量靠近自己的中心(类内散度小)。

PCA vs LDA:方差不等于类别可分性

图中清楚地展示了 PCA 的问题:PCA 的箭头沿着点云的延伸方向,结果把两类投影后叠在一起;LDA 的箭头横跨两类的间隙,底部直方图显示只有右图实现了干净的类别分离。

数学原理#

$$\mathbf{S}_W = \sum_{c=1}^{C}\sum_{\mathbf{x}\in c}(\mathbf{x}-\boldsymbol{\mu}_c)(\mathbf{x}-\boldsymbol{\mu}_c)^\top, \qquad \mathbf{S}_B = \sum_{c=1}^{C} n_c(\boldsymbol{\mu}_c - \boldsymbol{\mu})(\boldsymbol{\mu}_c - \boldsymbol{\mu})^\top.$$ $$J(\mathbf{w}) = \frac{\mathbf{w}^\top \mathbf{S}_B \mathbf{w}}{\mathbf{w}^\top \mathbf{S}_W \mathbf{w}}, \qquad \mathbf{S}_B \mathbf{w} = \lambda \mathbf{S}_W \mathbf{w}.$$ $$\mathbf{w}^* \;\propto\; \mathbf{S}_W^{-1}(\boldsymbol{\mu}_1 - \boldsymbol{\mu}_2).$$
1
2
3
4
5
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.datasets import load_iris

X, y = load_iris(return_X_y=True)
X_lda = LinearDiscriminantAnalysis(n_components=2).fit_transform(X, y)

PCA 和 LDA 对比速览#

性质PCALDA
是否监督无监督有监督
目标最大化方差最大化类间/类内比
最大输出维度$\min(n, p)$$C - 1$
典型用途数据探索、降维、去噪分类前的特征优化
假设线性结构每类高斯分布,共享协方差

那个 $C-1$ 的上限常常让人意外:二分类时,LDA 只输出一个维度,也就是 Fisher 方向。这对线性分类器够用了,但用来可视化就只能画一维直方图了。

支持向量机与核技巧#

线性代数(十五):机器学习中的线性代数——从 PCA 到推荐系统 — 章节小结图

为什么选择“最大间隔”?#

如果两类数据是线性可分的,那么可以找到无数个超平面将它们分开。SVM 的目标是找到一个唯一的超平面,它的间隔——即到两侧最近训练点的距离——是最大的。直观上讲,间隔越大,新测试点落在正确一侧的可能性就越高,模型的泛化能力也越强。这种直觉可以通过 VC 维和 PAC-Bayes 界等理论严格证明。

SVM:最大间隔超平面与支持向量

图中实线是决策边界,橙色虚线表示间隔边界,绿色圆圈标注的是恰好落在间隔上的点,这些点就是支持向量。只有支持向量决定了最终的解,删除任何非支持向量都不会改变决策边界的位置。

对偶问题:核函数的关键作用#

原问题:
$\min_{\mathbf{w}, b} \tfrac{1}{2}\|\mathbf{w}\|^2$ ,约束条件为 $y_i(\mathbf{w}^\top\mathbf{x}_i + b) \ge 1$

$$\max_{\boldsymbol{\alpha}} \sum_i \alpha_i - \frac{1}{2}\sum_{i,j} \alpha_i \alpha_j y_i y_j \,\mathbf{x}_i^\top \mathbf{x}_j, \qquad \sum_i \alpha_i y_i = 0.$$

在对偶问题中,数据仅通过内积 $\mathbf{x}_i^\top\mathbf{x}_j$ 参与计算。如果用任意函数 $k(\mathbf{x}_i, \mathbf{x}_j)$ 替换这些内积,就相当于隐式地将输入空间映射到另一个(可能更高维的)特征空间。这就是核技巧的核心思想。

升维解决复杂问题#

假设两类数据在二维平面上是同心圆,显然无法用直线将它们分开。但如果通过映射 $\phi(x_1, x_2) = (x_1, x_2, x_1^2 + x_2^2)$ 将每个点抬升到三维空间,内圈点的新 $z$ 坐标较小,外圈点较大,此时一个水平面就能轻松将它们分开。

核技巧:把不可分的同心圆抬到三维就线性可分

通常情况下,映射后的空间维度可能非常高,但有了核函数,我无需显式表示这个高维空间,只需计算内积即可。

常用核函数:

  • 线性核: $k(\mathbf{x}, \mathbf{y}) = \mathbf{x}^\top\mathbf{y}$
  • $d$ 阶多项式核: $k(\mathbf{x}, \mathbf{y}) = (\mathbf{x}^\top\mathbf{y} + c)^d$
  • RBF (高斯)核: $k(\mathbf{x}, \mathbf{y}) = \exp(-\gamma\|\mathbf{x} - \mathbf{y}\|^2)$ ——对应无限维特征空间,但计算复杂度仅为 $O(p)$
  • Sigmoid 核: $k(\mathbf{x}, \mathbf{y}) = \tanh(\kappa \mathbf{x}^\top\mathbf{y} + c)$ ——并非总是正定,历史上曾用于“神经化”SVM。

Mercer 条件#

一个函数 $k$ 是合法核函数的充要条件是:对于任意有限样本,Gram 矩阵 $K_{ij} = k(\mathbf{x}_i, \mathbf{x}_j)$ 必须是对称半正定的。这就是 Mercer 条件。它等价于存在某个特征映射 $\phi$ ,使得 $k(\mathbf{x}, \mathbf{y}) = \langle\phi(\mathbf{x}), \phi(\mathbf{y})\rangle$

1
2
3
4
5
6
from sklearn.svm import SVC
from sklearn.datasets import make_circles

X, y = make_circles(n_samples=200, noise=0.1, factor=0.3, random_state=42)
print(SVC(kernel='linear').fit(X, y).score(X, y))  # ~0.55
print(SVC(kernel='rbf', gamma=2).fit(X, y).score(X, y))  # ~1.00

在现代实践中,大规模分类任务上 SVM 已被神经网络取代。但在小规模(几千个样本)的表格类问题中,RBF-SVM 仍是默认选择,性能往往能与梯度提升树媲美甚至更优。

推荐系统中的矩阵分解#

Netflix 问题#

用户-电影评分矩阵 $\mathbf{R} \in \mathbb{R}^{m\times n}$ 非常庞大,包含数千万用户和数十万部电影,但数据几乎全是空的——普通用户只会给目录中 0.1% 的内容打分。我的目标是预测那些空白格子的分数,然后把预测得分最高的未观看影片推荐给每个用户。Netflix Prize (2006-2009)奠定了矩阵分解的核心地位,如今 Spotify、YouTube 和各大电商平台的推荐系统依然在用它的变种。

低秩假设#

$$\hat{r}_{uj} = \mathbf{p}_u^\top \mathbf{q}_j, \qquad \mathbf{R} \approx \mathbf{P}\mathbf{Q}^\top, \quad \mathbf{P} \in \mathbb{R}^{m\times k}, \mathbf{Q} \in \mathbb{R}^{n\times k}.$$

这其实就是对 $\mathbf{R}$ 做秩-$k$ 近似。当 $m=10^7, n=10^5, k=64$ 时,因子矩阵只需要存储大约 $6\times 10^8$ 个数字,而不是 $10^{12}$ 个。而且这种方法还能推广到未观测的数据上。

协同过滤就是低秩矩阵分解

图中展示了稀疏的观测矩阵(? 表示未评分)、两个因子矩阵,以及还原出的稠密预测矩阵。

交替最小二乘法(ALS)#

$$\min_{\mathbf{P}, \mathbf{Q}} \sum_{(u,j)\in\Omega}\bigl(r_{uj} - \mathbf{p}_u^\top\mathbf{q}_j\bigr)^2 + \lambda\bigl(\|\mathbf{P}\|_F^2 + \|\mathbf{Q}\|_F^2\bigr).$$

这个目标函数对 $(\mathbf{P}, \mathbf{Q})$ 联合是非凸的,但如果固定一个,另一个就是凸的。ALS 就是这样交替进行的:固定 $\mathbf{Q}$ 时,$\mathbf{P}$ 的每一行都是一个独立的岭回归闭式解;反过来也一样。每轮迭代会扫一遍所有已知评分,每行求解可以轻松并行——这就是 ALS 在 Spark MLlib 中占据主导地位的原因。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import numpy as np

def als(R, mask, k=10, n_iter=20, lam=0.1):
    m, n = R.shape
    P = np.random.randn(m, k) * 0.1
    Q = np.random.randn(n, k) * 0.1
    I = lam * np.eye(k)
    for _ in range(n_iter):
        for u in range(m):
            j = np.where(mask[u])[0]
            if j.size:
                Qj = Q[j]
                P[u] = np.linalg.solve(Qj.T @ Qj + I, Qj.T @ R[u, j])
        for j in range(n):
            u = np.where(mask[:, j])[0]
            if u.size:
                Pu = P[u]
                Q[j] = np.linalg.solve(Pu.T @ Pu + I, Pu.T @ R[u, j])
    return P, Q

冷启动、偏置和其他扩展#

纯矩阵分解在冷启动场景下完全失效:一个新用户没有任何评分记录,自然也没有因子向量。生产环境通常通过以下方法解决这个问题:

  • 加入辅助信息,比如人口统计特征或内容元数据,构建混合模型:$\hat{r}_{uj} = \mathbf{p}_u^\top\mathbf{q}_j + \mathbf{a}^\top \mathbf{f}_u + \mathbf{b}^\top \mathbf{g}_j$
  • 引入用户和物品偏置:有些用户打分普遍偏高,有些电影本身更受欢迎。用公式 $\hat{r}_{uj} = \mu + b_u + b_j + \mathbf{p}_u^\top\mathbf{q}_j$ 可以显著提升预测精度。
  • 升级到神经协同过滤(双塔模型):用户和物品分别通过神经网络嵌入,联合训练。最后那个内积层依然是线性代数。

非负矩阵分解(NMF)#

如果我希望每个因子能解释成一个可加部分,而不是带正负号的组合,就需要约束 $\mathbf{P}, \mathbf{Q} \ge 0$ 。NMF 倾向于生成稀疏且部件化的表示:在新闻语料库上做 NMF,因子看起来就像“体育”、“政治”、“科技”等主题,每个词都有非负的权重;而同样的矩阵用 SVD 分解,得到的因子带正负号,主题混在一起,难以解释。

1
2
3
4
5
6
7
8
9
from sklearn.decomposition import NMF
from sklearn.feature_extraction.text import TfidfVectorizer

vec = TfidfVectorizer(max_features=2000, stop_words='english')
X   = vec.fit_transform(corpus)               # 文档 x 词,非负
nmf = NMF(n_components=10, init='nndsvd').fit(X)
W = nmf.transform(X)        # 文档 x 主题
H = nmf.components_         # 主题 x 词
top = np.argsort(H, axis=1)[:, -10:]

线性回归的矩阵形式#

模型#

$$\mathbf{y} = \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\epsilon}, \qquad \mathbf{X} \in \mathbb{R}^{n\times(p+1)}.$$

其中 $\mathbf{X}$设计矩阵,第一列全是 1 (对应截距项)。

最小二乘的本质是正交投影#

目标是最小化残差平方和 $\|\mathbf{y} - \mathbf{X}\boldsymbol{\beta}\|^2$ 。从几何上看,$\mathbf{X}\boldsymbol{\beta}$ 的取值范围是 $\mathbf{X}$列空间。我要找的是这个子空间里离 $\mathbf{y}$ 最近的点。最近的点就是垂足,也就是 $\mathbf{y}$ 在列空间上的正交投影。

最小二乘 = 把 y 正交投影到 col(X)

$$\hat{\boldsymbol{\beta}} = (\mathbf{X}^\top\mathbf{X})^{-1}\mathbf{X}^\top\mathbf{y}.$$

实际写代码时,我不会直接求逆矩阵,而是用 np.linalg.lstsq(X, y)(底层用稳定的 QR 或 SVD 求解器),或者显式分解 $\mathbf{X} = \mathbf{Q}\mathbf{R}$ 后回代求解 $\mathbf{R}\hat{\boldsymbol{\beta}} = \mathbf{Q}^\top\mathbf{y}$

岭回归:通过收缩改善条件数#

$$\hat{\boldsymbol{\beta}}_{\text{ridge}} = (\mathbf{X}^\top\mathbf{X} + \lambda\mathbf{I})^{-1}\mathbf{X}^\top\mathbf{y}.$$

加上 $\lambda\mathbf{I}$ 后,$\mathbf{X}^\top\mathbf{X}$ 的每个特征值都增加了 $\lambda$ ,从而保证矩阵可逆,并将条件数从 $\sigma_{\max}^2/\sigma_{\min}^2$ 缩小到大约 $(\sigma_{\max}^2 + \lambda)/(\sigma_{\min}^2 + \lambda)$ 。从 SVD 的角度看,岭回归把 OLS 解的每个奇异分量乘以 $\sigma_i^2/(\sigma_i^2 + \lambda)$ ——小奇异值(噪声方向)被大幅压缩,而大奇异值(信号方向)几乎不变。

LASSO:用 $\ell_1$ 实现稀疏性#

$$\min_{\boldsymbol{\beta}} \|\mathbf{y} - \mathbf{X}\boldsymbol{\beta}\|^2 + \lambda \|\boldsymbol{\beta}\|_1.$$

$\ell_1$ 范数的等值面是一个菱形,角点正好落在坐标轴上。OLS 的等高线通常会在某个角点与 $\ell_1$ 范数相切,这时某些系数恰好为零。求解器会自动完成特征选择——这对稀疏高维问题(如基因组学、NLP、高频交易信号)来说非常实用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from sklearn.linear_model import LinearRegression, Ridge, Lasso
import numpy as np

np.random.seed(42)
n, p = 100, 20
X = np.random.randn(n, p)
beta_true = np.zeros(p); beta_true[:3] = [3, -2, 1.5]   # 只有 3 个有效特征
y = X @ beta_true + 0.5 * np.random.randn(n)

ols   = LinearRegression(fit_intercept=False).fit(X, y)
ridge = Ridge(alpha=1.0,  fit_intercept=False).fit(X, y)
lasso = Lasso(alpha=0.10, fit_intercept=False).fit(X, y)

print("OLS   非零系数:", (np.abs(ols.coef_)   > 1e-2).sum())   # 20
print("Ridge 非零系数:", (np.abs(ridge.coef_) > 1e-2).sum())   # 20
print("LASSO 非零系数:", (np.abs(lasso.coef_) > 1e-2).sum())   # ~3

神经网络中的线性层#

全连接层就是一次矩阵乘法#

$$\mathbf{h} = \sigma(\mathbf{W}\mathbf{x} + \mathbf{b}).$$

如果没有 $\sigma$ ,多层叠加就会退化成单层:$\mathbf{W}_3\mathbf{W}_2\mathbf{W}_1$ 不过是另一个矩阵。激活函数打破了线性限制,让网络能够逼近任意连续函数(万能逼近定理)。

批量处理:从矩阵-向量乘到矩阵-矩阵乘#

$$\mathbf{H} = \sigma\bigl(\mathbf{X}\mathbf{W}^\top + \mathbf{1}\mathbf{b}^\top\bigr).$$

一次 SGEMM 调用就能完成原本需要 $B$ 次独立矩阵-向量乘法的工作,FLOP 利用率大幅提升。

反向传播就是矩阵微积分#

$$\frac{\partial L}{\partial \mathbf{W}} = \frac{\partial L}{\partial \mathbf{h}}\,\mathbf{x}^\top, \qquad \frac{\partial L}{\partial \mathbf{x}} = \mathbf{W}^\top \frac{\partial L}{\partial \mathbf{h}}.$$

深层网络的反向传播,其实就是一系列这样的转置乘法操作。PyTorch 的 autograd 本质上是在前向传播时记录这些矩阵乘法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import numpy as np

class LinearLayer:
    """手写一个矩阵乘法层,前向和反向都自己实现"""
    def __init__(self, d_in, d_out):
        self.W = np.random.randn(d_out, d_in) * np.sqrt(2.0 / d_in)
        self.b = np.zeros(d_out)

    def forward(self, x):
        self.x = x
        return x @ self.W.T + self.b

    def backward(self, grad_out, lr=1e-2):
        grad_W = grad_out.T @ self.x
        grad_b = grad_out.sum(axis=0)
        grad_x = grad_out @ self.W
        self.W -= lr * grad_W
        self.b -= lr * grad_b
        return grad_x

注意力机制也是线性代数#

$$\text{Attention}(\mathbf{Q}, \mathbf{K}, \mathbf{V}) = \text{softmax}\!\left(\frac{\mathbf{Q}\mathbf{K}^\top}{\sqrt{d_k}}\right)\mathbf{V},$$

其中 $\mathbf{Q} = \mathbf{X}\mathbf{W}_Q$$\mathbf{K} = \mathbf{X}\mathbf{W}_K$$\mathbf{V} = \mathbf{X}\mathbf{W}_V$ 是对同一输入的三种线性投影。$\mathbf{Q}\mathbf{K}^\top$ 是一个 $n\times n$ 的相似度矩阵,表示序列中每对位置之间的相似性;softmax 将其转化为概率矩阵;再乘以 $\mathbf{V}$ 就是对值做加权平均。这个相似度矩阵的 $O(n^2)$ 内存开销,正是 FlashAttention 和线性注意力研究试图解决的问题。


优化算法的线性代数基础#

梯度与 Hessian#

$$f(\mathbf{x}) \approx f(\mathbf{x}_0) + \nabla f^\top(\mathbf{x} - \mathbf{x}_0) + \tfrac{1}{2}(\mathbf{x} - \mathbf{x}_0)^\top \mathbf{H}(\mathbf{x} - \mathbf{x}_0).$$

如果 Hessian 是对称正定矩阵,说明这里是一个真正的极小值点。Hessian 的特征值对应了其特征方向上的曲率。

条件数与“之”字形现象#

梯度下降的收敛速度取决于 Hessian 的条件数 $\kappa = \lambda_{\max}/\lambda_{\min}$ 。当条件数很大时,目标函数 $f$ 的等高线会变成高度拉长的椭圆。此时梯度方向横切椭圆,而不是沿着椭圆下降,导致著名的“之”字形现象。对于二次目标函数,普通梯度下降每一步的收敛率为 $\bigl((\kappa-1)/(\kappa+1)\bigr)^2$ 。比如当 $\kappa = 1000$ 时,收敛率约为 0.996,几乎没什么进展。

Adam 到底在做什么#

牛顿法通过 $\mathbf{H}^{-1}$ 对问题进行预处理,将几何形状变换为 Hessian 成为单位矩阵的形式(对于二次问题可以一步到位)。但计算十亿参数网络的 $\mathbf{H}^{-1}$ 根本不现实,所以 Adam 使用了一个对角预处理器来近似它——具体来说,就是对每个参数维护一个梯度平方的滑动平均值 $\hat{v}_t$ 。更新公式 $\theta \leftarrow \theta - \eta \,\hat{m}_t / \sqrt{\hat{v}_t}$ 对每个坐标重新缩放:高曲率方向步幅小,低曲率方向步幅大,从而以 $O(p)$ 的额外开销解决了“之”字形问题。

练习题#

概念题#

第 1 题 为什么 PCA 的主成分彼此正交?提示:对称矩阵的谱定理。

第 2 题 LDA 最多只能输出 $C - 1$ 维,原因是什么?提示:$\mathbf{S}_B$ 的秩。

第 3 题 找到一个显式的特征映射 $\phi$ ,使得对于 $\mathbf{x}, \mathbf{y} \in \mathbb{R}^2$ ,满足 $k(\mathbf{x}, \mathbf{y}) = (\mathbf{x}^\top\mathbf{y})^2 = \langle\phi(\mathbf{x}), \phi(\mathbf{y})\rangle$

第 4 题 画出单位 $\ell_1$$\ell_2$ 球的二维图,解释为什么 LASSO 能生成稀疏解,而岭回归不能。

计算题#

第 5 题$\mathbf{X} = \begin{bmatrix}1 & 2\\ -1 & 0\\ 0 & -2\end{bmatrix}$ (行已中心化)。 (a) 计算 $\mathbf{C} = \tfrac{1}{3}\mathbf{X}^\top\mathbf{X}$
(b) 求它的特征值和特征向量。
(c) 把行投影到第一主成分上。

第 6 题 类均值为 $\boldsymbol{\mu}_1 = [1, 2]^\top, \boldsymbol{\mu}_2 = [3, 4]^\top$ ,且 $\mathbf{S}_W = \mathbf{I}$ 。计算 LDA 方向以及投影后的类中心。

第 7 题 证明在 $(\mathbf{X}, \mathbf{y})$ 上的岭回归等价于在增广数据 $\tilde{\mathbf{X}} = \begin{bmatrix}\mathbf{X}\\\sqrt{\lambda}\mathbf{I}\end{bmatrix}, \; \tilde{\mathbf{y}} = \begin{bmatrix}\mathbf{y}\\\mathbf{0}\end{bmatrix}$ 上的 OLS。

编程题#

第 8 题 用 NumPy 从零实现 PCA (包括中心化和 SVD),应用到 MNIST 数据集,并绘制前两个主成分,按数字标签上色。

1
2
3
def my_pca(X, n_components):
    # 1. 中心化  2. SVD  3. 取前 k 个主成分  4. 投影
    pass

第 9 题 构造一个 $100 \times 50$ 的合成矩阵,秩为 5 并加入噪声,遮盖 80% 的元素,使用 ALS 恢复,并报告留出部分的 RMSE。

第 10 题 用 PyTorch 写一个两层 MLP 跑 MNIST,只允许使用 torch.matmul(不许用 nn.Linear),训练到测试集准确率超过 97%。

证明题#

第 11 题 证明 Gram 矩阵半正定,当且仅当存在特征映射 $\phi$ 使 $K_{ij} = \langle\phi(\mathbf{x}_i), \phi(\mathbf{x}_j)\rangle$

第 12 题 证明对于中心化的 $\mathbf{X}$ ,主成分方向就是 $\mathbf{X}$ 的右奇异向量。

第 13 题 陈述并证明 Gauss-Markov 定理:在 $\mathbf{y} = \mathbf{X}\boldsymbol{\beta} + \boldsymbol{\epsilon}$$\mathbb{E}[\boldsymbol{\epsilon}] = 0$$\text{Cov}(\boldsymbol{\epsilon}) = \sigma^2\mathbf{I}$ 的条件下,OLS 是 BLUE。

应用题#

第 14 题 为包含 10000 用户、1000 部电影、人均 50 条评分的数据集设计推荐系统。选择 $k$ 并说明理由,提出冷启动策略,并定义评估流程。

第 15 题 对于 50000 张 CIFAR-10 图像(3072 维),训练 CNN 前是否需要做 PCA?训练逻辑回归基线模型前呢?CNN 的特征图与 PCA 主成分有什么本质区别?


总结#

  • 先向量化。所有机器学习流程的第一步,都是把数据映射到 $\mathbb{R}^p$ 空间。进入这个空间后,几何特性会告诉你该用什么算法。
  • PCA 就是协方差矩阵的主特征向量。实际计算时用 SVD 更稳定。核 PCA 还能推广到非线性流形。
  • LDA 是有监督版本的 PCA。它最大化类间与类内散度的比值,最多只能得到 $C-1$ 维的结果。
  • SVM 只依赖内积。这使得核技巧成为可能,而 Mercer 条件则判断一个函数是否是合法的核函数。
  • 推荐系统的核心是低秩分解。ALS 和 NMF 把评分矩阵分解为用户和物品的嵌入,偏置项和辅助特征用来缓解冷启动问题。
  • 线性回归的本质是投影。岭回归($\ell_2$ )改善条件数,LASSO ($\ell_1$ )做特征选择,但两者本质上还是带不同先验的最小二乘法。
  • 神经网络就是矩阵乘法加非线性变换。前向传播和反向传播都靠矩阵乘法完成,注意力机制则是对相似度矩阵应用同样的基本操作。
  • 优化的几何特性由 Hessian 决定。条件数影响收敛速度,Adam 则是一种廉价的对角预条件方法。

下次碰到一个新算法,试着问自己一个问题:这里面藏着什么子空间、投影或者分解? 几乎每个算法背后都有一个这样的结构,找到它,就能把黑箱变成一页纸的推导。

参考文献#

  • Strang, G. Linear Algebra and Learning from Data. Wellesley-Cambridge Press, 2019.
  • Murphy, K. P. Probabilistic Machine Learning: An Introduction. MIT Press, 2022.
  • Goodfellow, I., Bengio, Y., & Courville, A. Deep Learning. MIT Press, 2016.
  • Bishop, C. M. Pattern Recognition and Machine Learning. Springer, 2006.
  • Koren, Y., Bell, R., & Volinsky, C. Matrix Factorization Techniques for Recommender Systems. IEEE Computer 42(8), 2009.
  • Mikolov, T. et al. Distributed Representations of Words and Phrases and their Compositionality. NeurIPS, 2013.
  • Vaswani, A. et al. Attention Is All You Need. NeurIPS, 2017.
本系列

线性代数 18 篇

  1. 01 线性代数(一):向量的本质——不仅仅是箭头
  2. 02 线性代数(二):线性组合与向量空间
  3. 03 线性代数(三):矩阵作为线性变换
  4. 04 线性代数(四):行列式的秘密
  5. 05 线性代数(五):线性方程组与列空间
  6. 06 线性代数(六):特征值与特征向量
  7. 07 线性代数(七):正交性与投影——当向量互不干扰
  8. 08 线性代数(八):对称矩阵与二次型
  9. 09 线性代数(九):奇异值分解 SVD
  10. 10 线性代数(十):矩阵范数与条件数——数值计算的健康体检
  11. 11 线性代数(十一):矩阵微积分与优化——从梯度到反向传播
  12. 12 线性代数(十二):稀疏矩阵与压缩感知——少即是多的数学奇迹
  13. 13 线性代数(十三):张量与多线性代数——从标量到高维数据立方体
  14. 14 线性代数(十四):随机矩阵理论——混沌中的秩序
  15. 15 线性代数(十五):机器学习中的线性代数——从 PCA 到推荐系统 当前
  16. 16 线性代数(十六):深度学习中的线性代数——从全连接到 Transformer
  17. 17 线性代数(十七):计算机视觉中的线性代数——从像素到三维重建
  18. 18 线性代数(十八):前沿应用与总结——量子计算、GNN、大模型,与十八章回望

读有所得?

GitHub 关注我 → 新文周更

GitHub