一、KNN
1.1 近邻原理
K 近邻(K-Nearest Neighbors, KNN)是一类基于实例 的学习方法:它不显式训练模型,而把整个训练集存起来,预测时找最近的 KKK 个邻居、按多数投票(分类)或均值(回归)给出结果。
基本流程
text
训练:仅存储 (X_train, y_train)
预测(对每个测试样本 x):
1. 计算 x 与所有训练样本的距离 d(x, x_i)
2. 取距离最小的 K 个训练样本的下标
3. 分类:以这 K 个样本的多数类作为预测
回归:以这 K 个样本 y 的均值(或加权均值)作为预测
为什么"近邻"有效
低维流形假设:相近的样本往往有相近的标签。在特征空间中,"距离近"是"语义近"的代理。
K 的选择
| KKK 值 | 模型复杂度 | 偏差 | 方差 | 适用 |
|---|---|---|---|---|
| 1 | 极复杂 | 低 | 极高 | 噪声数据上过拟合 |
| 适中(3--10) | 中等 | 中 | 中 | 多数场景 |
| 极大(接近 NNN) | 极简单 | 高 | 极低 | 噪声数据下欠拟合 |
K 越大 → 决策边界越平滑;K 越小 → 对局部结构越敏感、易过拟合。
算法变体
- 最近邻(K=1):训练误差 0,但泛化差、边界噪声大。
- 加权 KNN :距离近的样本权重高,weighti=1/d(x,xi)\text{weight}_i = 1/d(x, x_i)weighti=1/d(x,xi) 或 exp(−d2/τ2)\exp(-d^2 / \tau^2)exp(−d2/τ2)。
weights="distance"是 sklearn 选项。 - 编辑 KNN(Edited KNN):训练时剔除非自身的"误分类"邻居,压缩训练集。
- 条件 KNN :对每个查询自适应地选 KKK。
维度诅咒(Curse of Dimensionality)
KNN 在高维空间急剧失效:
- ddd 维空间体积按 rdr^drd 增长,半径 rrr 需同步放大才能包含固定数量的样本。
- 高维空间中,所有样本对之间的距离趋于相等(距离集中),"最近邻"与"最远邻"几乎无差异。
- 工程应对:先做特征选择或降维(PCA、特征哈希);用 cosine 相似度替代欧氏距离。
KNN 的复杂度
- 训练 O(1)O(1)O(1):仅存储。
- 预测 O(Nd)O(Nd)O(Nd):朴素实现需逐样本算距离。
- 用 KD 树 / Ball Tree / 近似最近邻(ANN)可降至 O(logN)O(\log N)O(logN) 或 O(dlogN)O(d \log N)O(dlogN)(§1.3)。
KNN 是"白盒"还是"黑盒"
- 优点:可解释------"为什么预测 1?因为它的 5 个最近邻中有 4 个是 1"。
- 缺点:模型容量大、存储与计算代价高;对噪声与异常值敏感。
scikit-learn 实现
python
from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor
clf = KNeighborsClassifier(
n_neighbors=5,
weights="uniform", # 或 "distance"
metric="minkowski", # p=2 即欧氏距离
p=2,
n_jobs=-1, # 并行
)
clf.fit(X_train, y_train)
易错点
- 未标准化 :KNN 对量级敏感,必须先
StandardScaler。 - 维度高时仍用欧氏距离:先降维或换 cosine 距离。
- 把"训练时间"当零:训练快不代表预测快;预测时仍要遍历所有样本。
- 类别不平衡 + 多数投票 :K 个邻居大概率都属多数类,预测恒为多数类;用
weights="distance"或重采样。
1.2 距离度量
距离的数学性质
- 非负性 :d(x,y)≥0d(x, y) \ge 0d(x,y)≥0
- 同一性 :d(x,y)=0 ⟺ x=yd(x, y) = 0 \iff x = yd(x,y)=0⟺x=y
- 对称性 :d(x,y)=d(y,x)d(x, y) = d(y, x)d(x,y)=d(y,x)
- 三角不等式 :d(x,z)≤d(x,y)+d(y,z)d(x, z) \le d(x, y) + d(y, z)d(x,z)≤d(x,y)+d(y,z)(用于 KD 树剪枝)
满足对称性、同一性、三角不等式三条即称度量(metric;非负性可由后三条推出)。违反三角不等式的相似度(如 cosine 距离、JS 散度)不能直接用于依赖剪枝的结构(KD 树),需特殊处理。
常用距离
| 距离名称 | 公式 | 适用场景 | 特点 |
|---|---|---|---|
| 欧氏距离 (L2) | ∑i(xi−yi)2\sqrt{\sum_i (x_i - y_i)^2}∑i(xi−yi)2 | 通用、连续特征 | 最常用的默认距离,直观表示直线距离 |
| 曼哈顿距离 (L1) | ∑i∣xi−yi∣\sum_i |x_i - y_i|∑i∣xi−yi∣ | 高维稀疏特征、网格路径 | 对异常值更鲁棒,计算速度快 |
| 切比雪夫距离 (L∞) | maxi∣xi−yi∣\max_i |x_i - y_i|maxi∣xi−yi∣ | 棋盘格路径、特定游戏场景 | 只关注各维度差值的最大值 |
| 闵可夫斯基距离 (Minkowski / Lp) | (∑i∣xi−yi∣p)1/p(\sum_i |x_i - y_i|^p)^{1/p}(∑i∣xi−yi∣p)1/p | 泛化的距离公式 | 当 p=1p=1p=1 退化为曼哈顿,p=2p=2p=2 退化为欧氏 |
| 马哈拉诺比斯距离 | (x−y)⊤Σ−1(x−y)\sqrt{(x - y)^\top \Sigma^{-1} (x - y)}(x−y)⊤Σ−1(x−y) | 考虑特征相关性与量级 | 消除了特征间相关性和量纲的影响 |
| 余弦距离 (Cosine) | 1−x⊤y∣x∣∣y∣1 - \frac{x^\top y}{|x| |y|}1−∣x∣∣y∣x⊤y | 文本、高维特征 | 关注向量的方向,忽略向量长度(常用于NLP) |
| 汉明距离 (Hamming) | 不同位数的比例 | 离散特征、二元特征 | 比较两个等长字符串对应位置的不同字符数 |
| 杰卡德距离 (Jaccard) | 1−∣A∩B∣∣A∪B∣1 - \frac{|A \cap B|}{|A \cup B|}1−∣A∪B∣∣A∩B∣ | 集合相似度、二元特征 | 衡量两个集合的交集与并集的比例 |
| 编辑距离 | 字符串转化的最少操作次数 | 字符串匹配、拼写纠错 | NLP常用,衡量两个字符串的差异程度 |
Minkowski 距离族
dp(x,y)=(∑i∣xi−yi∣p)1/pd_p(x, y) = \left( \sum_i |x_i - y_i|^p \right)^{1/p}dp(x,y)=(i∑∣xi−yi∣p)1/p
- p=1p = 1p=1:曼哈顿。
- p=2p = 2p=2:欧氏。
- p→∞p \to \inftyp→∞:切比雪夫。
sklearn 的 metric="minkowski", p=p 暴露这族距离。
Mahalanobis 距离的归一化
Mahalanobis(x,y)=(x−y)⊤Σ−1(x−y)\text{Mahalanobis}(x, y) = \sqrt{(x - y)^\top \Sigma^{-1} (x - y)}Mahalanobis(x,y)=(x−y)⊤Σ−1(x−y)
其中 Σ\SigmaΣ 是数据协方差矩阵。当 Σ=I\Sigma = IΣ=I(各特征独立同方差)退化为欧氏。Mahalanobis 把数据"白化"------把相关特征解耦、把不同量级标准化;在多变量高斯假设下,它的平方就是负对数似然的等值面(扣除常数)。
scikit-learn 的 Mahalanobis 距离需传入协方差矩阵(注意:DistanceMetric 自 sklearn 1.1 起已迁移到 sklearn.metrics,旧路径 sklearn.neighbors.DistanceMetric 已移除):
python
from sklearn.metrics import DistanceMetric
dist = DistanceMetric.get_metric("mahalanobis", V=cov) # V 为协方差矩阵
# 或直接在 KNN 中使用
from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(metric="mahalanobis", metric_params={"V": cov})
余弦相似度 vs 欧氏距离
- 余弦相似度:只看方向(夹角),与量级无关。适合 TF-IDF 词向量("文档长度"是噪声)。
- 欧氏距离:看绝对位置。适合绝对值有意义的场景(身高、价格)。
文本检索中,cosine 几乎总是优于欧氏;KNN 也常 metric="cosine"。
距离度量的选择流程
- 特征独立同分布、量级统一 → 欧氏距离。
- 高维稀疏(如文本) → cosine 距离。
- 特征高度相关或量级差异大 → Mahalanobis。
- 集合型(二元)特征 → Jaccard / Hamming。
- 网格 / 棋盘问题 → 切比雪夫。
- 字符串 → 编辑距离 / 余弦(字符级嵌入后)。
数据归一化对距离的影响
任何距离对量级都敏感:
- 某特征量级 1000,另一特征量级 0.001 → 距离几乎由前者决定。
- 标准化后(
StandardScaler)各特征等方差贡献。 - 业务上:是否标准化、是否做非线性变换,对 KNN / SVM / PCA 都有大影响,但对树模型无影响。
易错点
- 数据未标准化直接用欧氏距离 :必坏。标准做法是
StandardScaler或MinMaxScaler。 - 样本向量是稀疏的仍用欧氏距离:cosine 更合适。
- 标准化时用全部数据 fit(含测试集):必须只在训练集上 fit,再 transform 测试集------否则泄漏。
- KNN 默认 p=2p=2p=2 不一定最优:先用
metric="minkowski", p=2起手,再用交叉验证比较 ppp。
1.3 加速结构
朴素 KNN 预测时遍历所有训练样本,复杂度 O(Nd)O(Nd)O(Nd)。大数据下不可行。加速结构通过空间划分或哈希,把查询降至亚线性。
KD 树(K-Dimensional Tree)
KD 树是二叉空间划分树,对 ddd 维空间递归切分:
- 选一个维度(如方差最大的),在中位数处切分。
- 递归构建左/右子树。
- 查询时沿树下行找到包含查询点的叶,然后回溯搜索更近的点。
平均查询复杂度 O(logN)O(\log N)O(logN)(低维),最坏 O(N)O(N)O(N)(高维)。d>20d > 20d>20 后 KD 树性能下降(维度诅咒)。
Ball Tree
Ball Tree 把空间划分为嵌套的超球(balls):
- 每个节点存一个球心、半径。
- 查询时用"球-点距离下界"做剪枝。
- 比 KD 树在 ddd 较大时更稳定。
Hierarchical Navigable Small Worlds(HNSW)
基于图的近似最近邻(ANN)算法,是当前 ANN 库的常用方案(HNSWlib、Faiss(HNSW 索引)、Qdrant、Milvus 底层)。在百万到亿级向量上 O(logN)O(\log N)O(logN) 查询、内存可接受、近似最优。
Locality Sensitive Hashing(LSH)
对相似度函数族(cosine、Jaccard 等)设计哈希函数,使相似样本的哈希冲突概率高、不相似的低。批量查询效率高、内存省,但精度略低于 KD 树 / HNSW。
scikit-learn 加速选项
python
from sklearn.neighbors import NearestNeighbors
# algorithm 可选: 'auto', 'ball_tree', 'kd_tree', 'brute'
nn = NearestNeighbors(n_neighbors=5, algorithm="auto", leaf_size=30)
nn.fit(X_train)
distances, indices = nn.kneighbors(X_test)
algorithm="auto":根据数据自动选(默认)。algorithm="kd_tree":低维(d<20d < 20d<20)用 KD 树。algorithm="ball_tree":中高维用 Ball Tree。algorithm="brute":暴力遍历(高维或小数据)。leaf_size:叶子节点的最大样本数,影响构建与查询的折衷。
FAISS / HNSWlib 库
对百万级以上向量:
- FAISS(Facebook AI Similarity Search):GPU 加速、IVF/PQ/HNSW 多索引。
- HNSWlib / nmslib:纯 CPU 上的 HNSW 实现。
- Annoy(Spotify):随机投影树,简单稳健。
pip install faiss-cpu 或 pip install hnswlib 后即可用。
精确 vs 近似最近邻
| 类型 | 复杂度 | 精度 | 适用 |
|---|---|---|---|
| 精确(KD/Ball Tree) | O(logN)O(\log N)O(logN) 低维 / O(N)O(N)O(N) 高维 | 100% | 数据量 < 10 万、d<20d < 20d<20 |
| ANN(HNSW / IVF) | O(logN)O(\log N)O(logN) 高维 | 90--99% | 百万级以上、高维 |
易错点
- ddd 大但用 KD 树 :KD 树在 d>20d > 20d>20 后接近暴力,要换 Ball Tree 或 HNSW。
- 数据集规模 / ddd 错配 :
leaf_size=30是默认,对大 NNN 可调到 100+ 减少树深度。 - ANN 精度损失 :HNSW 的
ef_construction/ef_search决定精度-速度权衡。 - cosine 距离的 L2 化技巧 :预先把样本 L2 归一化,则 欧氏2=2×cosine 距离\text{欧氏}^2 = 2 \times \text{cosine 距离}欧氏2=2×cosine 距离------两者只差常数倍,排序完全等价,可省一次归一化计算。
二、朴素贝叶斯
2.1 条件独立假设
朴素贝叶斯(Naive Bayes, NB)是一类基于贝叶斯定理与特征条件独立假设的生成式分类器。它简单、极快、对小数据友好,常作为文本分类(如垃圾邮件识别)的强基线。
模型形式
对 KKK 类分类,给定 P(Y=k)P(Y = k)P(Y=k)(先验)与 P(X∣Y=k)P(X | Y = k)P(X∣Y=k)(类条件密度),贝叶斯定理:
P(Y=k∣X=x)=P(X=x∣Y=k)P(Y=k)P(X=x)P(Y = k | X = \mathbf{x}) = \frac{P(X = \mathbf{x} | Y = k) P(Y = k)}{P(X = \mathbf{x})}P(Y=k∣X=x)=P(X=x)P(X=x∣Y=k)P(Y=k)
预测 y^=argmaxkP(Y=k∣X=x)\hat{y} = \arg\max_k P(Y = k | X = \mathbf{x})y^=argmaxkP(Y=k∣X=x)。分母 P(X=x)P(X = \mathbf{x})P(X=x) 与 kkk 无关,比较时省略。
条件独立假设(naive)
朴素贝叶斯的"朴素"在于假设:在给定类 YYY 下,特征 X1,...,XdX_1, \dots, X_dX1,...,Xd 相互独立。
P(X=x∣Y=k)=∏j=1dP(Xj=xj∣Y=k)P(X = \mathbf{x} | Y = k) = \prod_{j=1}^d P(X_j = x_j | Y = k)P(X=x∣Y=k)=j=1∏dP(Xj=xj∣Y=k)
这让联合密度分解为 ddd 个一元密度的乘积,参数从指数级降为 O(dK)O(d K)O(dK)。
后验
P(Y=k∣X=x)∝P(Y=k)∏j=1dP(Xj=xj∣Y=k)P(Y = k | X = \mathbf{x}) \propto P(Y = k) \prod_{j=1}^d P(X_j = x_j | Y = k)P(Y=k∣X=x)∝P(Y=k)j=1∏dP(Xj=xj∣Y=k)
类别 NB 变体(按特征类型)
| 类 | 特征类型 | P(Xj=xj∣Y=k)P(X_j = x_j \mid Y = k)P(Xj=xj∣Y=k) 形式 |
|---|---|---|
| GaussianNB | 连续(高斯) | N(xj;μjk,σjk2)\mathcal{N}(x_j; \mu_{jk}, \sigma_{jk}^2)N(xj;μjk,σjk2) |
| MultinomialNB | 计数(词频) | Njk!∏xij!∏pjkxij\frac{N_{jk}!}{\prod x_{ij}!} \prod p_{jk}^{x_{ij}}∏xij!Njk!∏pjkxij |
| BernoulliNB | 二元 | pjkxj(1−pjk)1−xjp_{jk}^{x_j} (1-p_{jk})^{1-x_j}pjkxj(1−pjk)1−xj |
| CategoricalNB | 类别 | 类别 xjx_jxj 在类 kkk 下的经验频率 |
| ComplementNB | 不平衡文本 | MultinomialNB 的变体,专门处理不平衡 |
生成式判别式(Ng & Jordan 2002)
朴素贝叶斯是生成式 模型(直接建模 P(X,Y)P(X, Y)P(X,Y) = P(X∣Y)P(Y)P(X|Y) P(Y)P(X∣Y)P(Y)),与判别式(直接建模 P(Y∣X)P(Y|X)P(Y∣X),如逻辑回归)形成对照:
- 生成式:小数据上更快收敛(Ng & Jordan 给出 NB 收敛速率 O(logn)O(\log n)O(logn),LR O(n)O(n)O(n))。
- 判别式:渐近误差更低,大数据上更准。
- 实操:文本分类 NB 常略胜 LR;结构化数据 LR 更稳。
对特征条件独立的违反
现实中特征极少真的条件独立。NB 对这种违反有一定鲁棒性------
- 违反程度轻:决策面仍接近最优。
- 违反严重(如高度相关特征):概率估计偏置,但分类决策可能仍可用(对单调变换不敏感)。
MLE 估计
对 GaussianNB:μjk=xˉjk\mu_{jk} = \bar{x}{jk}μjk=xˉjk,σjk2=1Nk∑i:yi=k(xij−μjk)2\sigma{jk}^2 = \frac{1}{N_k} \sum_{i: y_i = k} (x_{ij} - \mu_{jk})^2σjk2=Nk1∑i:yi=k(xij−μjk)2。
对 MultinomialNB:pjk=∑i:yi=kxij+α∑i:yi=k∑jxij+αVp_{jk} = \frac{\sum_{i: y_i = k} x_{ij} + \alpha}{\sum_{i: y_i = k} \sum_j x_{ij} + \alpha V}pjk=∑i:yi=k∑jxij+αV∑i:yi=kxij+α(Laplace 平滑,§2.2)。
log 空间的累加
为避免概率连乘下溢,实际计算取对数:
logP(Y=k∣X=x)=logP(Y=k)+∑jlogP(Xj=xj∣Y=k)+const\log P(Y = k | X = \mathbf{x}) = \log P(Y = k) + \sum_j \log P(X_j = x_j | Y = k) + \text{const}logP(Y=k∣X=x)=logP(Y=k)+j∑logP(Xj=xj∣Y=k)+const
加法在数值上比乘法稳。
scikit-learn 实现
python
from sklearn.naive_bayes import GaussianNB, MultinomialNB, BernoulliNB
clf = GaussianNB().fit(X_train, y_train)
proba = clf.predict_proba(X_test) # 生成式模型,概率由模型直接给出
NB 的优缺点
| 维度 | 评价 |
|---|---|
| 训练速度 | 极快(仅统计量) |
| 预测速度 | 极快 |
| 内存 | 极小(仅存类条件统计量) |
| 概率输出 | 直接给出 $P(Y |
| 可解释 | 类条件概率的乘积,透明 |
| 对特征相关 | 鲁棒但概率估计偏置 |
| 类别不平衡 | 需先验调整(class_prior) |
| 维度扩展 | 文本场景极强(万级词表) |
NB 与 LR 的等价性
在特定参数化 下,GaussianNB 与 LR 产生相同的决策边界(Ng & Jordan 2002):
- 当 X∣YX|YX∣Y 为高斯、协方差为各类共享时(对角或非对角均可,共享才线性;NB 常取对角),NB 的后验与 LR 的形式一致(logit 后验 = 特征的线性函数)。
- 这是 NB 在某些数据集上"几乎不输"LR 的根因------决策面一致。
易错点
- GaussianNB 对异常值敏感 :MLE 用样本均值/方差,未做鲁棒估计。建议先剔除异常值或做变换。
- MultinomialNB 输入必须是非负计数:负值会报错或行为未定义。
- 测试集出现训练集未见特征值 :如某个词在训练集未出现,P(Xj∣Y)=0P(X_j| Y) = 0P(Xj∣Y)=0 → 整个后验变 0 → 用平滑(§2.2)。
- 类别不平衡时 :默认
class_prior用训练集频率。文本分类里常用 ComplementNB 或调sample_weight。
2.2 平滑处理
零概率问题
NB 中 P(Xj=v∣Y=k)P(X_j = v | Y = k)P(Xj=v∣Y=k) 直接用经验频率估计:
P^(Xj=v∣Y=k)=count(Y=k∧Xj=v)count(Y=k)\hat{P}(X_j = v | Y = k) = \frac{\text{count}(Y=k \wedge X_j=v)}{\text{count}(Y=k)}P^(Xj=v∣Y=k)=count(Y=k)count(Y=k∧Xj=v)
当某个类 kkk 下某特征值 vvv 从未出现 时,P^=0\hat{P} = 0P^=0 → 后验 P(Y=k∣X)=0P(Y = k | X) = 0P(Y=k∣X)=0------所有出现 Xj=vX_j = vXj=v 的样本都不会被分到 kkk ,即使其它特征强烈支持 kkk。
Laplace 平滑(加一平滑)
P^Lap(Xj=v∣Y=k)=count(Y=k∧Xj=v)+1count(Y=k)+V\hat{P}_{\text{Lap}}(X_j = v | Y = k) = \frac{\text{count}(Y=k \wedge X_j=v) + 1}{\text{count}(Y=k) + V}P^Lap(Xj=v∣Y=k)=count(Y=k)+Vcount(Y=k∧Xj=v)+1
其中 VVV 是 XjX_jXj 的可能取值数。分子加 1(伪计数)、分母加 VVV(归一化),保证所有概率 > 0 且和为 1。
Lidstone 平滑(加 α\alphaα 平滑)
P^α(Xj=v∣Y=k)=count(Y=k∧Xj=v)+αcount(Y=k)+αV\hat{P}_{\alpha}(X_j = v | Y = k) = \frac{\text{count}(Y=k \wedge X_j=v) + \alpha}{\text{count}(Y=k) + \alpha V}P^α(Xj=v∣Y=k)=count(Y=k)+αVcount(Y=k∧Xj=v)+α
- α=1\alpha = 1α=1:Laplace。
- α∈(0,1)\alpha \in (0, 1)α∈(0,1):效果常更好。
- α\alphaα 越大 → 后验越均匀 → 越鲁棒;过大会欠拟合。
MultinomialNB(alpha=1.0) 是 sklearn 默认。
平滑的贝叶斯解释
Laplace / Lidstone 平滑等价于给 Xj∣Y=kX_j | Y = kXj∣Y=k 一个对称 Dirichlet 先验:
P(Xj∣Y=k)∼Dir(α,α,...,α)P(X_j | Y = k) \sim \text{Dir}(\alpha, \alpha, \dots, \alpha)P(Xj∣Y=k)∼Dir(α,α,...,α)
后验在数据基础上更新,α\alphaα 是先验的"伪计数"------α\alphaα 越大,先验越强,平滑越重。
为何文本分类特别需要平滑
词袋模型中,词汇表 VVV 通常 1 万--10 万维。训练集只覆盖其中 30--70%,未登录词(OOV)大量存在。无平滑时,任何含 OOV 的样本后验 = 0,模型失效。
未见类(Class Imbalance 与未见类)
当 P(Y=k)=0P(Y = k) = 0P(Y=k)=0(类 kkk 在训练集中无样本)时:
- 加平滑后先验为:P^(Y=k)=α/(N+Kα)>0\hat{P}(Y = k) = \alpha / (N + K \alpha) > 0P^(Y=k)=α/(N+Kα)>0,但被极端压低。
- 工程上:先验不需平滑时应剔除该类(无法训练)。
平滑强度的选择
- 小数据 / 高维 :α\alphaα 大(0.1--1.0)。
- 大数据 :α\alphaα 小(0.001--0.01)。
- 交叉验证 :
MultinomialNB(alpha=...)网格搜索。
scikit-learn 行为
MultinomialNB(alpha=1.0)默认;alpha=0退化为无平滑(理论 NB,但实际不推荐)。BernoulliNB(alpha=1.0)同上。GaussianNB用 MLE 估计高斯参数,不直接受 α\alphaα 影响 (方差估计可加var_smoothing防数值问题)。
Empirical Bayes 与自适应平滑
- Empirical Bayes(EB) :从数据估计 α\alphaα。
sklearn暂未提供。 - Katz 回退(Katz backoff):N-gram 模型中,未见 n-gram 回退到 (n-1)-gram,权重由平滑参数控制。
- Kneser-Ney:经典 N-gram 平滑方法,在 NLP 中效果优。
NB 的最大似然与 MAP 视角
- MLE (α=0\alpha = 0α=0):直接用经验频率 → 易过拟合 + 零概率问题。
- MAP with Dirichlet prior (α>0\alpha > 0α>0):引入伪计数 → 平滑 + 防零概率。
- Full Bayes :保留 P(Xj∣Y,α)P(X_j | Y, \alpha)P(Xj∣Y,α) 的后验分布 → 集成方式预测,但工程上少见。
scikit-learn 调参示例
python
from sklearn.naive_bayes import MultinomialNB
from sklearn.model_selection import GridSearchCV
param_grid = {"alpha": [0.001, 0.01, 0.1, 0.5, 1.0]}
grid = GridSearchCV(MultinomialNB(), param_grid, cv=5, scoring="accuracy")
grid.fit(X_train, y_train)
print("best alpha:", grid.best_params_, "score:", grid.best_score_)
α\alphaα 选择经验表
| 数据规模 | 文本词频特征 | 低维结构化特征 |
|---|---|---|
| N<104N < 10^4N<104 | α=0.1\alpha = 0.1α=0.1--1.01.01.0 | α\alphaα 较小(0.01--0.1) |
| N≥104N \ge 10^4N≥104 | α=0.001\alpha = 0.001α=0.001--0.10.10.1 | α\alphaα 接近 0 + 交叉验证 |
| 高维稀疏(d≫Nd \gg Nd≫N) | 较大 α\alphaα 防 0 概率 | 一般不用 NB |
与生成式模型的本质联系
NB 是最简单的生成式模型------其它更复杂的生成模型(高斯混合、贝叶斯网络、VAE、扩散模型)都建立在 P(X,Y)=P(X∣Y)P(Y)P(X, Y) = P(X|Y) P(Y)P(X,Y)=P(X∣Y)P(Y) 的分解上,区别在于对 P(X∣Y)P(X|Y)P(X∣Y) 的参数化。
易错点
- α\alphaα 设 0:零概率问题;务必 > 0。
- 不同特征用不同 α\alphaα :NB 假设所有特征同分布,但实际可以给高频/低频特征不同 α\alphaα(如 TF-IDF 加权后调 α\alphaα)。
- 测试集 OOV 与训练集 OOV 不一致 :训练时 OOV 的 α\alphaα 贡献不应让测试时新 OOV 一致平滑;有时需保留单独的"未知"类。
- 类先验严重不均 :若 P(Y)P(Y)P(Y) 偏向某类,平滑后仍偏向;可用
class_prior显式覆盖,或用 ComplementNB。
三、聚类
3.1 K-Means
K-Means 是最经典的划分式聚类 算法:把 NNN 个样本分到 KKK 个簇中,使簇内平方和(SSE, Sum of Squared Errors)最小。
目标函数
给定 KKK 个簇中心 μ1,...,μK\boldsymbol{\mu}1, \dots, \boldsymbol{\mu}Kμ1,...,μK 与分配 rik∈{0,1}r{ik} \in \{0, 1\}rik∈{0,1}(rik=1r{ik} = 1rik=1 表示样本 iii 属于簇 kkk):
J=∑i=1N∑k=1Krik∣xi−μk∣22J = \sum_{i=1}^N \sum_{k=1}^K r_{ik} | \mathbf{x}_i - \boldsymbol{\mu}_k |_2^2J=i=1∑Nk=1∑Krik∣xi−μk∣22
Lloyd 算法(标准 K-Means)
K-Means 求解(NP 难)通常用迭代两步骤逼近:
- 分配步 :固定 μk\boldsymbol{\mu}kμk,把每个样本分到最近中心:
rik={1k=argmink∣xi−μk∣220otherwiser{ik} = \begin{cases} 1 & k = \arg\min_k | \mathbf{x}_i - \boldsymbol{\mu}_k |_2^2 \\ 0 & \text{otherwise} \end{cases}rik={10k=argmink∣xi−μk∣22otherwise - 更新步 :固定 rikr_{ik}rik,更新每个簇中心为该簇样本均值:
μk=∑irikxi∑irik\boldsymbol{\mu}k = \frac{\sum_i r{ik} \mathbf{x}i}{\sum_i r{ik}}μk=∑irik∑irikxi
交替迭代至收敛。保证收敛 (每步 J 不增),但通常收敛到局部最优。
初始化
- 随机选 KKK 个样本作为初始中心(最朴素,常多次重启取最优)。
- K-Means++ (Arthur & Vassilvitskii 2007):按与已有中心的距离成比例的概率选下一个中心,显著降低收敛到次优解的概率 。sklearn 默认(
init="k-means++")。 - 多次重启 (
n_init):sklearn 1.4 之前默认n_init=10;1.4+ 改为n_init="auto",当init="k-means++"(默认)时仅跑 1 次,仅在init="random"或 callable 时跑 10 次。工程上要多次重启取最优应显式设n_init=10。
收敛判据
- 簇分配不再变化。
- 中心变化幅度 <
tol。 - 达到
max_iter(默认 300)。
K 的选择
- 肘部法则(Elbow) :画 JJJ vs KKK 曲线,找"拐点"。
- 轮廓系数(Silhouette) :si=(bi−ai)/max(ai,bi)s_i = (b_i - a_i) / \max(a_i, b_i)si=(bi−ai)/max(ai,bi),aia_iai 同簇样本平均距离、bib_ibi 最近异簇平均距离。值域 −1,1-1, 1−1,1,越大越优。
- Gap Statistic:与均匀分布参考对比。
无银弹------多种方法结合 + 业务考量。
K-Means 的局限性
- 只发现球形簇:以欧氏距离为基础,难以处理细长、环形、不规则形状。
- 对异常值敏感:均值被异常值拉偏。
- 需先定 K :KKK 难以自动确定。
- 对量级敏感:必须先标准化。
- 类大小不均时:大类会"吞并"小类。
Mini-Batch K-Means
用小批量(mini-batch)近似更新中心,加速大数据(百万级):
- 每步只用一个 mini-batch,更新相关中心。
- 收敛快但可能略粗糙。
- sklearn 的
MiniBatchKMeans。
高斯混合模型(GMM)与 EM 算法
K-Means 是"硬分配":每个样本只属于一个簇。高斯混合模型 (Gaussian Mixture Model, GMM)把它软化为概率分配:假设数据由 KKK 个高斯分量混合生成
P(x)=∑k=1KπkN(x;μk,Σk),∑kπk=1P(\mathbf{x}) = \sum_{k=1}^K \pi_k \mathcal{N}(\mathbf{x}; \boldsymbol{\mu}_k, \boldsymbol{\Sigma}_k), \quad \sum_k \pi_k = 1P(x)=k=1∑KπkN(x;μk,Σk),k∑πk=1
其中 πk\pi_kπk 是各分量的混合系数。模型参数 {πk,μk,Σk}\{\pi_k, \boldsymbol{\mu}_k, \boldsymbol{\Sigma}_k\}{πk,μk,Σk} 用 EM 算法(Expectation-Maximization)迭代估计------第二章 §2.1 中"用高斯混合模型对传感器读数做密度估计",背后就是这个算法。
- E 步(Expectation) :固定参数,计算每个样本属于各分量的后验责任(soft assignment):
γik=P(zi=k∣xi)=πkN(xi;μk,Σk)∑jπjN(xi;μj,Σj)\gamma_{ik} = P(z_i = k \mid \mathbf{x}_i) = \frac{\pi_k \mathcal{N}(\mathbf{x}_i; \boldsymbol{\mu}_k, \boldsymbol{\Sigma}k)}{\sum{j} \pi_j \mathcal{N}(\mathbf{x}_i; \boldsymbol{\mu}_j, \boldsymbol{\Sigma}_j)}γik=P(zi=k∣xi)=∑jπjN(xi;μj,Σj)πkN(xi;μk,Σk)
- M 步(Maximization):固定责任,最大化加权对数似然,闭式更新参数:
μk=∑iγikxi∑iγik,Σk=∑iγik(xi−μk)(xi−μk)⊤∑iγik,πk=∑iγikN\boldsymbol{\mu}k = \frac{\sum_i \gamma{ik} \mathbf{x}i}{\sum_i \gamma{ik}}, \quad \boldsymbol{\Sigma}k = \frac{\sum_i \gamma{ik} (\mathbf{x}_i - \boldsymbol{\mu}_k)(\mathbf{x}i - \boldsymbol{\mu}k)^\top}{\sum_i \gamma{ik}}, \quad \pi_k = \frac{\sum_i \gamma{ik}}{N}μk=∑iγik∑iγikxi,Σk=∑iγik∑iγik(xi−μk)(xi−μk)⊤,πk=N∑iγik
交替迭代至似然收敛。与 K-Means 一样,每步似然单调不减,但只能保证局部最优,需多次随机初始化取最优。
GMM 与 K-Means 的关系
| 维度 | K-Means | GMM |
|---|---|---|
| 分配方式 | 硬(0/1) | 软(概率 γik\gamma_{ik}γik) |
| 簇形状 | 球形(各向同性) | 椭球形(每簇学自己的协方差) |
| 目标函数 | 簇内平方和 | 对数似然 |
| 求解算法 | Lloyd(分配 / 更新) | EM(E 步 / M 步) |
| 额外输出 | 无 | 各簇概率、密度估计 |
极限情形:把所有 Σk\boldsymbol{\Sigma}_kΣk 固定为 ϵI\epsilon IϵI 并令 ϵ→0\epsilon \to 0ϵ→0,E 步退化为"分到最近中心"、M 步退化为均值更新------K-Means 就是硬分配、球形协方差约束下的 GMM,两者的"分配 / 更新"两步在结构上完全同构。
python
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=3, covariance_type="full",
n_init=10, random_state=0)
labels = gmm.fit_predict(X) # 硬标签(取最大责任)
proba = gmm.predict_proba(X) # 软分配:每个样本属于各簇的概率
density = gmm.score_samples(X) # 对数密度,可用于异常检测
covariance_type:"full"(任意椭球)/"tied"(各簇共享一个协方差)/"diag"(对角,最接近 K-Means)/"spherical"(球形)。- 适用:簇是椭球形、需要软分配或密度估计的场景;"低密度点 = 异常"是常见的衍生用法。
- 局限:分量数 KKK 仍需指定(可用 BIC 选择:
gmm.bic(X)取最小者对应的 KKK);对初始化敏感;EM 只保证局部最优。
K-Means 与 KNN 的对比
| 维度 | K-Means | KNN |
|---|---|---|
| 任务 | 聚类(无标签) | 分类 / 回归(有标签) |
| K 含义 | 簇数 | 邻居数 |
| 训练 | 找簇中心 | 仅存储 |
| 预测 | 找最近中心 | 找 K 个最近邻投票 |
scikit-learn 实现
python
from sklearn.cluster import KMeans
km = KMeans(
n_clusters=8,
init="k-means++",
n_init=10,
max_iter=300,
random_state=0,
)
labels = km.fit_predict(X)
print(km.cluster_centers_.shape, km.inertia_)
易错点
- 未标准化 :K-Means 对量级极敏感,必
StandardScaler。 n_init=1单次随机初始化:可能陷入糟糕的局部最优;保持 ≥ 10。- 用 K-Means 处理非球形数据:先升维 / 核化,或换谱聚类 / DBSCAN。
- K 选错:强行聚成不存在的群组(如鸢尾花 K=5),簇内方差下降但无语义。
3.2 层次与密度聚类
层次聚类(Hierarchical Clustering)
无需预设 KKK,构建树状层次结构(dendrogram):
- 凝聚(Agglomerative):自底向上,每个样本起一个簇,迭代合并最近的两个簇。
- 分裂(Divisive):自顶向下,所有样本一个簇,迭代分裂最异质的簇。
凝聚更常用,复杂度 O(N2logN)O(N^2 \log N)O(N2logN) 或 O(N3)O(N^3)O(N3)(视实现)。
链接(Linkage)准则
合并两个簇的距离由链接方式决定:
| 链接 | 定义 | 倾向 |
|---|---|---|
| Single | 最近两样本距离 | 链状、稀疏簇 |
| Complete | 最远两样本距离 | 紧凑球形簇 |
| Average | 平均两两距离 | 折衷 |
| Ward | 合并后簇内方差增加量 | 紧凑、平衡簇 |
scikit-learn 层次聚类
python
from sklearn.cluster import AgglomerativeClustering
ac = AgglomerativeClustering(n_clusters=4, linkage="ward")
labels = ac.fit_predict(X)
linkage="ward" 默认要求欧氏距离;其他链接可配其他 metric。
层次聚类的可视化
SciPy 提供 linkage 与 dendrogram:
python
from scipy.cluster.hierarchy import linkage, dendrogram, fcluster
Z = linkage(X, method="ward")
dendrogram(Z)
labels = fcluster(Z, t=4, criterion="maxclust")
层次聚类能直观看到"什么时候合并"------可辅助选 KKK。
层次 vs K-Means
| 维度 | 层次聚类 | K-Means |
|---|---|---|
| 时间复杂度 | O(N2)O(N^2)O(N2) 至 O(N3)O(N^3)O(N3) | O(NKd)O(NKd)O(NKd) |
| 内存 | O(N2)O(N^2)O(N2) 距离矩阵 | O(Nd)O(Nd)O(Nd) |
| K 选取 | 事后可切树 | 需先定 |
| 簇形状 | 取决于链接 | 球形 |
| 增量 | 难 | 难 |
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)
DBSCAN 基于密度定义簇:
- ϵ\epsilonϵ-邻域:以样本为中心、ϵ\epsilonϵ 为半径的球。
- 核心点(core) :ϵ\epsilonϵ-邻域内至少有
min_samples个样本。 - 边界点(border) :在核心点 ϵ\epsilonϵ-邻域内但自身不是核心。
- 噪声点(noise):两者都不是。
簇 = 核心点 + 与核心点密度相连的边界点;噪声点不归任何簇。
DBSCAN 的优势
- 无需预设 KKK。
- 可发现任意形状的簇(环形、链形)。
- 天然识别噪声。
- 对异常值鲁棒(噪声独立标注)。
DBSCAN 的局限
- ϵ\epsilonϵ 与
min_samples的选择对结果影响大,常需试。 - 密度不均的簇:同一对参数难以适应所有簇。
- 高维数据:距离集中 → 失效(与 KNN 类似)。
scikit-learn DBSCAN
python
from sklearn.cluster import DBSCAN
db = DBSCAN(eps=0.5, min_samples=5, metric="euclidean")
labels = db.fit_predict(X)
# labels: -1 表示噪声;0..K-1 表示簇编号
HDBSCAN(Hierarchical DBSCAN)
HDBSCAN 是 DBSCAN 的层次化扩展:
- 用 mutual reachability distance 削弱异常值影响。
- 自适应 ϵ\epsilonϵ。
- 不需预设 ϵ\epsilonϵ。
- sklearn 自 1.3 起原生提供
sklearn.cluster.HDBSCAN;社区hdbscan库同步维护。
OPTICS
OPTICS 是 DBSCAN 的扩展,按"可达距离"排序样本,不强制一个全局 ϵ\epsilonϵ ,可处理多密度簇。sklearn 的 OPTICS 类提供。
均值漂移(Mean Shift)
非参数密度估计 + 聚类:
- 每个样本沿核密度梯度上升,直到达局部密度极大点。
- 同一峰的所有样本归一簇。
- 无需 KKK ,但带宽参数
bandwidth需调。 - 复杂度 O(N2)O(N^2)O(N2),大数据慢。
谱聚类(Spectral Clustering)
用数据的相似度矩阵 (如 RBF 核)做特征分解,取前 kkk 个特征向量做 K-Means。
- 可发现非凸簇。
- 复杂度 O(N3)O(N^3)O(N3) 特征分解,大数据不适用。
- 需 KKK(或手动选)。
- sklearn 的
SpectralClustering。
聚类评估指标
内部指标(无标签):
- 轮廓系数(Silhouette) :−1,1-1, 1−1,1,越大越好。
- Calinski-Harabasz Index:簇间散度 / 簇内散度(按自由度归一),越大越好。
- Davies-Bouldin Index:越小越好。
外部指标(有真实标签,但实际中很少有):
- ARI(Adjusted Rand Index):同质性 + 完整性的修正 Rand 指数。
- NMI(Normalized Mutual Information):互信息归一化。
- V-measure:同质性 + 完整性的调和平均。
scikit-learn 评估 API
python
from sklearn.metrics import silhouette_score, calinski_harabasz_score
print(silhouette_score(X, labels))
print(calinski_harabasz_score(X, labels))
易错点
- DBSCAN 调参 :先
NearestNeighbors看 kkk 距离图选 ϵ\epsilonϵ,再试min_samples。 - 聚类前未标准化 :所有距离类算法(K-Means、层次、DBSCAN)都需先
StandardScaler。 - 密度严重不均时用 DBSCAN:DBSCAN 是密度驱动而非大小驱动,小密度簇易被错标为噪声或被大密度簇吞并;改用 HDBSCAN(自适应密度)。
- 用 K-Means 默认
n_init:sklearn 1.4+n_init="auto"+init="k-means++"默认只跑 1 次;要多次重启取最优解应显式设n_init=10。 - 聚类后直接当标签使用:聚类结果与真实类别可能不一一对应;评估需用 ARI、NMI 等外部指标。
四、降维
4.1 PCA 降维
主成分分析(Principal Component Analysis, PCA)是最经典的线性降维方法。它把数据投影到方差最大的方向,使投影后的低维表示保留尽可能多的原始信息。
核心思想
找正交方向 v1,...,vk\mathbf{v}_1, \dots, \mathbf{v}_kv1,...,vk,使数据 X∈RN×dX \in \mathbb{R}^{N \times d}X∈RN×d 在这些方向上的投影方差最大。等价于最大化重建误差的减少:
- 投影 Z=XVZ = X VZ=XV,V=v1,...,vk∈Rd×kV = \\mathbf{v}_1, \\dots, \\mathbf{v}_k \in \mathbb{R}^{d \times k}V=v1,...,vk∈Rd×k。
- 重建 X^=ZV⊤=XVV⊤\hat{X} = Z V^\top = X V V^\topX^=ZV⊤=XVV⊤。
- 最小化 ∣X−X^∣F2| X - \hat{X} |_F^2∣X−X^∣F2。
协方差矩阵的特征分解
数据中心化(每列减均值)后,协方差矩阵 C=1NX⊤X∈Rd×d\mathbf{C} = \frac{1}{N} X^\top X \in \mathbb{R}^{d \times d}C=N1X⊤X∈Rd×d,对 C\mathbf{C}C 做特征分解 C=QΛQ⊤\mathbf{C} = \mathbf{Q} \mathbf{\Lambda} \mathbf{Q}^\topC=QΛQ⊤。
- 特征值 λ1≥λ2≥⋯≥λd\lambda_1 \ge \lambda_2 \ge \dots \ge \lambda_dλ1≥λ2≥⋯≥λd 描述各方向的方差。
- 选前 kkk 个最大特征值对应的特征向量 v1,...,vk\mathbf{v}_1, \dots, \mathbf{v}_kv1,...,vk 作为主成分。
- 信息保留比例 =∑i=1kλi/∑i=1dλi= \sum_{i=1}^k \lambda_i / \sum_{i=1}^d \lambda_i=∑i=1kλi/∑i=1dλi。
SVD 等价形式
对 XXX 做 SVD X=UΣV⊤X = U \Sigma V^\topX=UΣV⊤,VVV 的列就是 C\mathbf{C}C 的特征向量,λi=σi2/N\lambda_i = \sigma_i^2 / Nλi=σi2/N。SVD 在数值上更稳。
PCA 的算法流程
text
1. 中心化: X_c = X - X.mean(axis=0)
2. (可选)白化: X_w = X_c / X.std(axis=0)
3. SVD: X_c = U Σ Vᵀ
4. 选前 k 个右奇异向量 V[:, :k]
5. 投影: Z = X_c @ V[:, :k]
PCA 不是分类器
PCA 是无监督方法,只看数据分布。监督信息的利用需用 LDA(线性判别分析,见下文)。
白化(Whitening)
把各主成分的方差归一为 1:
Xwhite=XcVΛ−1/2=UΣΛ−1/2X_{\text{white}} = X_c V \Lambda^{-1/2} = U \Sigma \Lambda^{-1/2}Xwhite=XcVΛ−1/2=UΣΛ−1/2
即先投影到主成分坐标系、再把每维除以该方向的标准差(λi=σi2/N\lambda_i = \sigma_i^2 / Nλi=σi2/N 时,UΣΛ−1/2=N UU \Sigma \Lambda^{-1/2} = \sqrt{N}\, UUΣΛ−1/2=N U,只差常数因子)。在白化空间中,各维等方差且不相关。深度学习常用 BatchNorm / LayerNorm 实现类似效果。
保留主成分数的选择
- 阈值法 :选使累计方差贡献 ≥90%/95%/99%\ge 90\% / 95\% / 99\%≥90%/95%/99% 的最小 kkk。
- 肘部法则:画累计贡献率曲线找拐点。
- 交叉验证 :把下游任务(如分类)的性能作为 kkk 的函数。
PCA 的假设与局限
- 线性:只能发现线性结构。非线性流形(瑞士卷)需用核 PCA / t-SNE / UMAP。
- 全局:最大化全局方差,可能忽略局部结构。
- 对量级敏感 :必须先
StandardScaler。 - 不可解释(vs LDA):主成分是特征的线性组合,无业务语义。
LDA(线性判别分析)------监督降维的对照
PCA 最大化方差 ,LDA 最大化类间散度 / 类内散度之比 ,是有监督的线性降维(也可直接作分类器):
J(w)=w⊤SBww⊤SWwJ(\mathbf{w}) = \frac{\mathbf{w}^\top S_B \mathbf{w}}{\mathbf{w}^\top S_W \mathbf{w}}J(w)=w⊤SWww⊤SBw
其中 SBS_BSB 是类间散度矩阵(各类中心到全局中心的加权散度),SWS_WSW 是类内散度矩阵(各类内部样本的散度之和)。最优 w\mathbf{w}w 由 SW−1SBS_W^{-1} S_BSW−1SB 的前 kkk 个最大特征值对应的特征向量给出,且至多取 K−1K - 1K−1 维 (KKK 为类别数)。
| 维度 | PCA | LDA |
|---|---|---|
| 监督 | 无 | 有(需要标签) |
| 目标 | 方差最大 | 类间/类内散度比最大 |
| 最大维数 | min(N−1,d)\min(N-1, d)min(N−1,d) | K−1K - 1K−1 |
| 适用 | 无标签压缩、去噪 | 有标签的分类前降维 |
示例:二分类下 LDA 把数据投影到"两类中心连线方向"(同时按类内协方差白化),1 维即可完成判别;PCA 则可能把方差最大的"与标签无关"方向排在前面。sklearn.discriminant_analysis.LinearDiscriminantAnalysis(n_components=k) 同时支持降维与分类。
核 PCA(Kernel PCA)
把数据先映射到高维空间再 PCA,等价于在原空间用核函数 K(x,z)K(\mathbf{x}, \mathbf{z})K(x,z) 计算 Gram 矩阵:
K=ϕ(X)ϕ(X)⊤K = \phi(X) \phi(X)^\topK=ϕ(X)ϕ(X)⊤
直接对 KKK 做特征分解不能 得到真正的主成分------必须先对核矩阵做双重中心化:
K~=(I−1N11⊤)K(I−1N11⊤)\tilde K = \left(I - \frac{1}{N}\mathbf{1}\mathbf{1}^\top\right) K \left(I - \frac{1}{N}\mathbf{1}\mathbf{1}^\top\right)K~=(I−N111⊤)K(I−N111⊤)
再对 K~\tilde KK~ 做特征分解,取前 kkk 个非零特征向量(投影新数据时还需按 Nλi\sqrt{N \lambda_i}Nλi 归一化)。常用核:RBF、多项式、sigmoid。sklearn 的 KernelPCA 内部已做此处理。
增量 PCA(IncrementalPCA)
数据太大不能一次加载时,逐批拟合:
python
from sklearn.decomposition import IncrementalPCA
ipca = IncrementalPCA(n_components=50, batch_size=100)
ipca.fit(X_stream)
partial_fit 单批更新;内存友好但收敛略慢。
稀疏 PCA(SparsePCA)
主成分仅用部分原始特征(系数 L1 约束)。结果可解释------每个主成分可看作特征的子集线性组合。
截断 SVD
对稀疏数据(如 TF-IDF 文本),用 TruncatedSVD 而非 PCA:
python
from sklearn.decomposition import TruncatedSVD
svd = TruncatedSVD(n_components=100)
Z = svd.fit_transform(X_sparse) # X_sparse 是 scipy.sparse 矩阵
数学上等价于 PCA,但不显式中心化(避免破坏稀疏性)。
PCA 的几何直观
- 在 ddd 维数据中,PCA 找 kkk 个相互正交、能"最好刻画数据变化"的方向。
- 把数据"压扁"到这 kkk 维超平面。
- 重构误差 = 被丢弃的 d−kd - kd−k 个方向的方差之和。
PCA 用于人脸识别(Eigenfaces)
将人脸图像按像素展成向量,PCA 取前 kkk 个主成分,每个主成分是"特征脸"(Eigenface)。新脸在 Eigenface 上的系数即其表示,可做最近邻分类。
scikit-learn PCA
python
from sklearn.decomposition import PCA
pca = PCA(n_components=0.95) # 保留 95% 方差
Z = pca.fit_transform(X) # PCA 内部自动中心化(每列减均值);若外部已用 StandardScaler,则是中心化+方差归一
print(pca.explained_variance_ratio_)
print(pca.n_components_)
易错点
- 未中心化 :PCA 内部会中心化,但若用了
StandardScaler后再 PCA,已是中心化+方差归一,效果不同。 - 先 PCA 再分类:PCA 是无监督的,可能丢失对分类有用的信息(保留方差最大的方向未必保留类别区分度);监督场景用 LDA 或直接用原始特征。
- 高维稀疏数据用普通 PCA :破坏稀疏结构;用
TruncatedSVD。 - 非球形簇用 PCA 后 K-Means:K-Means 假设簇是球形,方差最大的方向未必是分类有用的方向;可能反而恶化聚类。
4.2 流形降维
线性降维(PCA)只能发现数据的线性 结构。当数据分布在一个非线性流形上(如瑞士卷、月牙、字母数字的图像空间),需要非线性降维。
流形假设
许多高维数据真实内在维度远低于观测维度。例如:
- 100×100 灰度人脸图像本质可能由 20--50 维参数(姿态、光照、表情)控制。
- 语音波形由几十维发音参数驱动。
- 文档-词项矩阵中,文档可能在几十维的"主题空间"中。
流形学习假设数据均匀采样自一个低维流形嵌入在高维空间。
Isomap(Isometric Mapping)
Isomap 用测地距离(沿流形的最短路径)替代欧氏距离,再做 MDS(多维标度):
- 构图 :ϵ\epsilonϵ-邻域或 KKK 近邻图,边权 = 欧氏距离。
- 最短路径:用 Floyd-Warshall 或 Dijkstra 算每对点的测地距离。
- MDS:对测地距离矩阵做特征分解,得到低维嵌入。
优点 :保留流形的全局几何。缺点 :ϵ\epsilonϵ 或 KKK 选择敏感;复杂度 O(N3)O(N^3)O(N3)。
Locally Linear Embedding(LLE)
LLE 假设每个样本可由其近邻线性组合重建,最小化重建误差:
minW∑i∣xi−∑jwijxj∣2s.t. wij=0 if j∉N(i),∑jwij=1\min_W \sum_i \left| \mathbf{x}i - \sum_j w{ij} \mathbf{x}j \right|^2 \quad \text{s.t. } w{ij} = 0 \text{ if } j \notin N(i), \sum_j w_{ij} = 1Wmini∑ xi−j∑wijxj 2s.t. wij=0 if j∈/N(i),j∑wij=1
对每个样本求出局部重建权重 WWW,再在低维空间保持 WWW:
minZ∑i∣zi−∑jwijzj∣2\min_{Z} \sum_i \left| \mathbf{z}i - \sum_j w{ij} \mathbf{z}_j \right|^2Zmini∑ zi−j∑wijzj 2
稀疏特征值问题。保留局部线性结构。
Laplacian Eigenmaps(拉普拉斯特征映射)
基于图拉普拉斯:保持"近邻关系"在低维空间中不变。
minZ∑i,jwij∣zi−zj∣2s.t. Z⊤DZ=I\min_Z \sum_{i,j} w_{ij} | \mathbf{z}_i - \mathbf{z}_j |^2 \quad \text{s.t. } Z^\top D Z = IZmini,j∑wij∣zi−zj∣2s.t. Z⊤DZ=I
WWW 是邻接权重,DDD 是度矩阵。等价于对 L=D−WL = D - WL=D−W(图拉普拉斯)做特征分解取最小非零特征值对应的特征向量。
t-SNE(t-Distributed Stochastic Neighbor Embedding)
t-SNE 专门用于可视化(2D/3D),保留局部邻域结构:
- 高维:用高斯核定义相似度 pijp_{ij}pij。
- 低维:用 ttt 分布(重尾)核定义 qijq_{ij}qij。
- 最小化 KL 散度 DKL(P∥Q)D_{\text{KL}}(P \| Q)DKL(P∥Q)。
优点 :可视化效果好,揭示复杂结构(如 MNIST 的 10 簇)。缺点:
- 计算复杂度:精确版 O(N2)O(N^2)O(N2),Barnes-Hut 近似版(sklearn 默认)O(NlogN)O(N \log N)O(NlogN)。
- 不可用于新数据投影(无显式映射函数,需重新跑)。
- 簇大小无意义(受 perplexity 影响)。
- 多次运行结果可能不同。
sklearn 的 TSNE(n_components=2, perplexity=30, learning_rate=200)。
python
from sklearn.manifold import TSNE
import umap
# sklearn t-SNE:不支持新数据 transform
Z_tsne = TSNE(n_components=2, perplexity=30, random_state=0).fit_transform(X)
# umap-learn:支持新数据 transform
umap_model = umap.UMAP(n_components=2, n_neighbors=15, random_state=0).fit(X)
Z_umap = umap_model.transform(X)
Z_new = umap_model.transform(X_new) # 投影新样本
注意 :sklearn 的 TSNE 不支持 transform;但 openTSNE / FIt-SNE 等增量实现提供 transform API,能在原模型上映射新数据。
UMAP(Uniform Manifold Approximation and Projection)
UMAP 是 t-SNE 的现代替代,理论基础(代数拓扑)更强:
- 保留更多全局结构。
- 更快(基于交叉熵 + 负采样)。
- 支持新数据投影 (
transform)。 umap-learn库提供。
t-SNE vs UMAP:t-SNE 偏局部、UMAP 偏全局;UMAP 训练/推理都快。
自编码器(Autoencoder)作为降维
神经网络视角的非线性降维:
- 编码器 z=fϕ(x)z = f_\phi(x)z=fϕ(x),zzz 为低维表示。
- 解码器 x^=gθ(z)\hat{x} = g_\theta(z)x^=gθ(z)。
- 训练目标 min∣x−x^∣2\min |x - \hat{x}|^2min∣x−x^∣2。
当 zzz 维度远小于 xxx 维度时,zzz 即非线性降维。变体:
- 去噪自编码器(DAE) :输入加噪,输出去噪后的 xxx。
- 变分自编码器(VAE) :加概率约束,zzz 服从高斯先验。
- 对比学习(SimCLR、BYOL):自监督预训练得到通用表示。
随机邻嵌入与线性方法的选择
| 方法 | 局部 | 全局 | 速度 | 新数据 | 可视化 |
|---|---|---|---|---|---|
| PCA | 弱 | 强 | 快 | ✓ | 一般 |
| LDA(监督) | --- | 强 | 快 | ✓ | 一般 |
| Isomap | 中 | 强 | 慢 | ✗ | 中 |
| LLE | 强 | 弱 | 中 | ✗ | 中 |
| Laplacian | 强 | 中 | 中 | ✗ | 中 |
| t-SNE | 极强 | 弱 | 慢 | ✗ | 极好 |
| UMAP | 强 | 中 | 较快 | ✓ | 好 |
| Autoencoder | 强 | 中 | 视网络 | ✓ | 中 |
流形降维的工程实践
- 可视化首选 UMAP / t-SNE :用
n_components=2投影 + 类别着色。 - 特征工程:高维 → UMAP / 自编码器 → 低维 → 监督模型。
- 降维后再分类:先监督降维(LDA、Partial Least Squares)效果常优于无监督(PCA、UMAP)。
- 大规模数据 :Mini-Batch t-SNE、
openTSNE、PyTorch UMAP 加速。
易错点
- 用 t-SNE 嵌入做下游任务 :t-SNE 无显式映射函数,不能 transform 新数据;工程上需重新跑。
- perplexity 不当:太小 → 看不出全局结构;太大 → 簇内细节丢失。常用 5--50。
- 标准化 :所有距离类方法都需先
StandardScaler。 - LLE / Isomap / Laplacian 的 KKK(邻域数)选错:太小 → 图不连通;太大 → 失去局部性。常用 5--20。
- 非线性降维用于分类前:无监督降维可能丢失判别信息;用 LDA / 监督 UMAP / 自监督预训练更稳。