Series · Linear Algebra · Chapter 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 章)。


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

1.1 一切都要变成向量

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

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

原因很简单:向量是一切线性代数运算的原子。一旦数据被向量化,就可以做内积(衡量相似度)、距离(k-NN)、矩阵作用(线性变换)、协方差分解(PCA)。没有向量化,就要为每种数据类型写一套专门的算法;有了向量化,同一个 SVD 例程就能同时处理图片和蛋白质。

1.2 特征空间的几何

把 $n$ 个样本(每个 $p$ 维)按行堆成设计矩阵

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

每一行是 $\mathbb{R}^p$ 中的一个点;每一列是这一特征在所有样本上的观测。几乎所有 ML 任务都能在这个空间里用几何语言表达:

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

这个几何视角不是装饰:它直接告诉你该用哪个算法。“找方差最大的方向"就是 PCA,“找把两团点云分开的方向"就是 LDA,“找把两类分到两侧的曲面"就是 SVM。

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

Word2Vec、GloVe,以及现代 LLM 的 embedding 层,都把 token 映射为稠密向量,使得语义相近的词在空间中距离更近。一个令人惊叹的经验现象是,简单的向量算术就能捕捉语义关系:

$$ \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
11
12
13
14
import numpy as np
from gensim.models import KeyedVectors

# 预训练 Google News 词向量(300 维)
model = KeyedVectors.load_word2vec_format(
    'GoogleNews-vectors-negative300.bin', binary=True)

# 类比:king 之于 man 如同 ? 之于 woman
print(model.most_similar(positive=['king', 'woman'], negative=['man'], topn=1))
# [('queen', 0.7118)]

# 余弦相似度是嵌入空间的天然度量
print(model.similarity('cat', 'dog'))    # 0.76
print(model.similarity('cat', 'galaxy')) # 0.04

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


2. 主成分分析(PCA)

2.1 直觉

把一把回形针撒在桌上俯视,它们明显沿某个方向排列。PCA 自动找出这个方向:方差最大的轴。为什么关心方差?因为方差才有信息。如果某个方向上数据几乎不变,它就帮不上任何下游任务。

PCA:二维数据云的主轴

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

2.2 推导

把数据中心化,使 $\mathbf{X}^\top\mathbf{1} = \mathbf{0}$。我们要找一个单位向量 $\mathbf{u}$,使投影坐标的方差最大:

$$ \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}$ s.t. $\|\mathbf{u}\|=1$,得到 $\mathbf{C}\mathbf{u} = \lambda\mathbf{u}$。最优的 $\mathbf{u}$ 就是协方差矩阵最大特征值对应的特征向量,对应的方差就是 $\lambda$。后续主成分是接下来的特征向量,它们彼此正交(因为 $\mathbf{C}$ 是对称矩阵)。

2.3 用 SVD 算 PCA:生产代码真正用的方法

直接构造 $\mathbf{X}^\top\mathbf{X}$ 在 $p$ 很大时(比如 4096 维 BERT 嵌入)很浪费内存,而且当特征近共线时数值上很糟(条件数被平方)。真正的实现都用中心化数据矩阵的 SVD:

$$ \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
19
20
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

# scikit-learn(n_components < min(n, p) 时底层用 SVD)
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))   # 同样的方差

2.4 PCA 在生产中的真实用途

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

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

2.5 核 PCA:流形是弯曲的怎么办

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

1
2
3
4
5
6
7
8
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)
# X_kpca 干净地分开两条月牙;X_pca 不行

在可视化场景下,核 PCA 已经基本被自编码器和 t-SNE / UMAP 取代,但作为核方法在无监督任务上的最干净的示例,它依然值得理解。


3. 线性判别分析(LDA)

3.1 PCA 看不到的盲区,用标签补上

PCA 是无监督的:它忽略类别标签,只追逐方差。这在分类任务里可能完全错误。设想两团长条形、平行的雪茄状点云分别属于 A、B 两类。PCA 会选沿着雪茄方向(方差最大),可这个方向恰恰把 A 和 B 混在一起。真正该选的方向是垂直于雪茄的——能把它们劈开的方向。

LDA 是有监督的,它要找一个投影方向,同时满足:

  1. 类别中心之间投影后尽可能远(类间散度大)。
  2. 每一类自身投影后尽可能紧(类内散度小)。

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

图中清晰地展示了这一失效模式:PCA 的箭头沿着点云的延伸方向,把两类投影后叠在一起;LDA 的箭头则横跨两类的间隙,下方面板的一维直方图只有右图实现了干净的类别分离。

3.2 数学

设有 $C$ 类,第 $c$ 类有 $n_c$ 个样本,均值为 $\boldsymbol{\mu}_c$,总均值为 $\boldsymbol{\mu}$:

$$ \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. $$

LDA 最大化广义瑞利商

$$ 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}. $$

二分类时退化为 Fisher 判别

$$ \mathbf{w}^* \;\propto\; \mathbf{S}_W^{-1}(\boldsymbol{\mu}_1 - \boldsymbol{\mu}_2). $$
1
2
3
4
5
6
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)
# 鸢尾花(3 类)干净地降到 2 个 LDA 维度

3.3 PCA vs LDA 速查表

性质PCALDA
监督性无监督有监督
目标最大化方差最大化类间/类内比
最大输出维度$\min(n, p)$$C - 1$
典型用途探索、压缩、去噪分类前的特征塑形
假设线性结构类内高斯、协方差相同

那个 $C-1$ 上限常常出乎意料:二分类时 LDA 只输出一个维度——Fisher 方向。对线性分类器够用,但用来可视化就只能做一维直方图了。


4. 支持向量机与核技巧

4.1 为什么是"最大间隔”

线性可分的两类之间能塞下无数个超平面。SVM 选了唯一的一个:使间隔(到两侧最近训练点的距离)最大的那个。直觉上,间隔越宽,新来的测试点越有可能落在正确一侧,泛化越好。这种直觉被 VC 维和 PAC-Bayes 界等理论严格化了。

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

图中实线是决策边界,橙色虚线是间隔边界,绿色圆圈住的、恰好落在间隔上的点是支持向量——只有它们决定了解,删除任何非支持向量都不会改变边界位置。

4.2 对偶问题:核函数登场的地方

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

对偶问题(引入拉格朗日乘子 $\alpha_i \ge 0$ 后):

$$ \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)$,就相当于隐式地把输入空间换成另一个(可能更高维的)特征空间——这就是核技巧。

4.3 升到高维

如果两类数据在二维平面上是同心圆,没有任何直线能把它们分开。但映射 $\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。

4.4 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 至今仍是默认选择,往往能与梯度提升树打平甚至取胜。


5. 推荐系统中的矩阵分解

5.1 Netflix 问题

用户-电影评分矩阵 $\mathbf{R} \in \mathbb{R}^{m\times n}$ 极其庞大(数千万用户、数十万影片),且几乎完全缺失——一个典型用户只评分了目录中 0.1% 的内容。目标是预测未观测的格子,把预测分最高的未看过项目推荐给用户。Netflix Prize(2006-2009)确立了矩阵分解的统治地位,至今 Spotify、YouTube 和所有主要电商推荐系统都仍在用它的变种。

5.2 低秩假设

假设用户偏好和电影特性都能用 $k$ 个隐因子刻画——比如"动作量”、“浪漫度”、“独立片还是大片"等等。那么用户 $u$ 给电影 $j$ 的评分就近似于两人因子向量的内积:

$$ \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}$,并且能泛化到未观测的格子上。

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

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

5.3 交替最小二乘(ALS)

直接做 SVD 不行,因为大部分元素是缺失的。改成只在观测到的位置上优化(带正则):

$$ \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

5.4 冷启动、偏置和扩展

纯矩阵分解在冷启动上彻底失败:一个全新的、没有任何评分的用户,根本没有因子向量可言。生产系统通过以下方式打补丁:

  • 加入侧信息(人口学特征、内容元数据),构成混合模型:$\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$ 能显著提升精度。
  • 升级为神经协同过滤(双塔模型):用户和物品分别经过神经网络嵌入,端到端联合训练。最后那个内积头依然是线性代数。

5.5 非负矩阵分解(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:]

6. 线性回归的矩阵形式

6.1 模型

多元线性回归 $y = \beta_0 + \beta_1 x_1 + \cdots + \beta_p x_p + \epsilon$ 在 $n$ 个样本上堆起来:

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

这里 $\mathbf{X}$ 是设计矩阵,第一列是全 1(截距列)。

6.2 最小二乘是正交投影

要最小化残差平方和 $\|\mathbf{y} - \mathbf{X}\boldsymbol{\beta}\|^2$。几何上,$\mathbf{X}\boldsymbol{\beta}$ 在 $\mathbf{X}$ 的列空间里跑遍所有可能的取值,我们要在这个子空间里找离 $\mathbf{y}$ 最近的点——最近就是垂足,就是正交投影。

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

残差 $\mathbf{y} - \mathbf{X}\hat{\boldsymbol{\beta}}$ 必须正交于 col$(\mathbf{X})$,这给出了正规方程 $\mathbf{X}^\top(\mathbf{y} - \mathbf{X}\hat{\boldsymbol{\beta}}) = \mathbf{0}$,即

$$ \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}$。

6.3 岭回归:以收缩换条件

如果 $\mathbf{X}^\top\mathbf{X}$ 病态(特征共线、$p > n$ 等等),OLS 解会非常不稳定。岭回归加上一个 $\ell_2$ 惩罚:

$$ \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)$——小奇异值(噪声方向)被狠狠压低,大奇异值(信号方向)几乎不动。

6.4 LASSO:用 $\ell_1$ 换稀疏

LASSO 把 $\ell_2$ 惩罚换成 $\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

7. 神经网络中的线性层

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

每一个 “Linear” 或 “Dense” 层都是仿射变换加一个逐元素非线性:

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

去掉 $\sigma$,多层就坍缩成单层:$\mathbf{W}_3\mathbf{W}_2\mathbf{W}_1$ 也只是另一个矩阵。激活函数破坏线性,让网络具备表达任意连续函数的能力(万能逼近定理)。

7.2 批量化:把矩阵-向量乘变成矩阵-矩阵乘

GPU 不是魔法,它只是对一件事极快:稠密大矩阵乘法。深度学习训练之所以适合 GPU,正是因为批处理这个技巧。把 $B$ 个输入堆成 $\mathbf{X} \in \mathbb{R}^{B\times d_{\text{in}}}$:

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

一次 SGEMM 调用就完成了原本需要 $B$ 次独立矩阵-向量乘法的工作,FLOP 利用率高得多。

7.3 反向传播是矩阵微积分

对 $\mathbf{h} = \mathbf{W}\mathbf{x}$,链式法则给出

$$ \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

7.4 注意力也是线性代数

每个 Transformer 的核心自注意力块就是

$$ \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 和线性注意力一系列工作要解决的问题。


8. 优化算法的线性代数基础

8.1 梯度与 Hessian

Hessian $\mathbf{H}$ 装着 $f$ 的全部二阶偏导。在驻点附近做二阶泰勒展开:

$$ 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 标志着真正的极小点;它的特征值就是对应特征方向上的曲率。

8.2 条件数与"之"字形

梯度下降的收敛速度取决于 Hessian 的条件数 $\kappa = \lambda_{\max}/\lambda_{\min}$。$\kappa$ 大时,$f$ 的等高线是高度拉长的椭圆,梯度向量横切椭圆而不是沿椭圆下行,于是出现著名的"之"字形。在二次目标上,纯梯度下降的每步收敛率是 $\bigl((\kappa-1)/(\kappa+1)\bigr)^2$;当 $\kappa = 1000$ 时这个比率是 $\approx 0.996$,几乎不前进。

8.3 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)$ 的额外代价驯服了"之"字形。


9. 练习题

概念理解题

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

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

第 3 题。 对 $\mathbf{x}, \mathbf{y} \in \mathbb{R}^2$,找一个显式特征映射 $\phi$,使 $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})$ 上的解,等价于 OLS 在增广数据 $\tilde{\mathbf{X}} = \begin{bmatrix}\mathbf{X}\\\sqrt{\lambda}\mathbf{I}\end{bmatrix}, \; \tilde{\mathbf{y}} = \begin{bmatrix}\mathbf{y}\\\mathbf{0}\end{bmatrix}$ 上的解。

编程题

第 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?训逻辑回归 baseline 之前呢?CNN 的特征图与 PCA 主成分有什么本质差异?


10. 本章总结

  • 先把数据向量化。每条 ML 流水线的起点都是把数据嵌入 $\mathbb{R}^p$。一旦进了这个空间,几何就告诉你该用哪个算法。
  • PCA = 协方差矩阵的顶层特征向量。生产中用 SVD 算更稳。核 PCA 把它推广到非线性流形。
  • LDA = 有监督的 PCA。最大化类间/类内散度比,输出维度上限是 $C - 1$。
  • SVM 只通过内积接触数据。这让核技巧成为可能,Mercer 条件告诉我们什么样的函数才是合法核函数。
  • 推荐系统是低秩的。ALS 和 NMF 把评分矩阵分解为用户和物品嵌入,偏置项与侧信息打补丁解决冷启动。
  • 线性回归是投影。岭回归($\ell_2$)改善条件,LASSO($\ell_1$)做特征选择,本质都是带不同先验的最小二乘。
  • 神经网络是矩阵乘法 + 非线性。前向后向都是矩阵乘法,注意力机制是同一个原语作用在相似度矩阵上。
  • 优化的几何就是 Hessian。条件数控制收敛速度,Adam 是一个廉价的对角预条件器。

下次遇到一个新的 ML 算法,把它当成一个问题来追问:这里面藏着什么子空间、投影或分解? 答案几乎总是有的,而看到它就能把"黑箱"变成一页推导。


参考资料

  • 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.

系列导航

Liked this piece?

Follow on GitHub for the next one — usually one a week.

GitHub