01 核心原理(大白话版)

想象你有一张 100 维的"地图",想把它印到一张纸上(2D)。PCA 的做法是:找一个好角度拍张照片,保留最大方差的方向。

t-SNE 的做法完全不同:它不管全局结构,只关心邻居关系——原本挨在一起的点,降维后也要挨在一起;原本很远的点,降维后可以随意放。

代价是:t-SNE 的全局距离没有意义,两个簇之间的距离不代表任何东西,只有簇内部的结构是可信的

四步理解 t-SNE

1
高维相似度:高斯核

对每个点 i,用高斯函数把它到其他点的距离转成概率 pj|i——越近的邻居概率越高,越远的趋近于 0。参数 σ 由 Perplexity 决定(自动二分搜索)。

2
对称化:Pij = (pj|i + pi|j) / 2n

把不对称的条件概率变成对称的联合概率,保证每对点只有一个相似度值。

3
低维相似度:t 分布

在 2D 空间里用 t 分布(Student-t,自由度=1)计算相似度 Qij。t 分布比高斯更"胖",允许不相似的点在低维里离得更远,缓解"拥挤问题"。

4
最小化 KL 散度:梯度下降

损失 = KL(P||Q) = Σ Pij log(Pij/Qij)。梯度下降调整 2D 坐标,让 Q 尽量接近 P。

步骤1:看看原始高维数据长什么样

Iris 数据集有 4 个特征(花萼长/宽、花瓣长/宽),先用前两个特征画个散点图感受一下:

步骤2:计算高维相似度 P

用高斯核把欧氏距离转成概率——相邻点概率高,远点概率趋近 0。下面展示点 #0 对其余各点的相似度分布:

步骤3:梯度下降优化 2D 布局

随机初始化 2D 坐标,反复执行梯度下降让 Q 接近 P,200 步后看效果:

步骤4:对比 PCA vs t-SNE

PCA 是线性投影,t-SNE 是非线性——在 Iris 这种线性可分的数据上,两者差异不大;在复杂流形数据上,t-SNE 优势明显:

Perplexity 参数:可以理解为"每个点期望有多少个有效邻居"。太小(如 5)→ 只关注极近邻,簇碎裂;太大(如 50)→ 邻域过宽,簇融合。通常取 5~50,默认 30。

02 代码

完整手写 t-SNE,支持 Perplexity 参数(修改代码中的 perplexity 变量)。JS 版在浏览器运行 150 条 Iris,约需 10~20 秒;Python 版可用 sklearn 一行搞定。

03 学术性讲解

为什么用 t 分布而不是高斯?

高维空间中,两个不相似的点的距离普遍较大;如果低维空间也用高斯核,这些点会被"压缩"到一起,产生拥挤问题(Crowding Problem)。 t 分布的尾部更厚,同样的 Qij 对应更大的低维距离,让不相似的点可以"撑开",避免所有点挤成一团。

KL 散度的梯度

对低维坐标 yi 求导:

∂C/∂y_i = 4 Σ_j (P_ij - Q_ij)(y_i - y_j)(1 + ||y_i - y_j||²)⁻¹

正项(P > Q)产生引力,把高维相似的点拉近;负项(P < Q)产生斥力,把高维不相似的点推开。

Perplexity 与 σ 的关系

对每个点 i 用二分搜索找到 σi,使得:

Perplexity = 2^{H(P_i)},其中 H(P_i) = -Σ_j p_{j|i} log p_{j|i}

每个点的 σi 不同,密集区域 σ 小,稀疏区域 σ 大,自适应局部密度。

t-SNE 的局限性

全局距离无意义

两个簇之间的距离不反映真实的高维距离,不能用来比较"哪两类更相似"。

结果不稳定

随机初始化导致每次运行布局不同,但拓扑结构(聚类)应当一致。

计算复杂度高

朴素实现 O(n²),大数据集(n > 10万)需用 Barnes-Hut 近似(sklearn 默认启用)。

适合可视化,不适合特征工程

t-SNE 的低维坐标不能用于后续分类/回归,仅用于可视化探索。

t-SNE vs PCA:如何选择?

优先 PCA

需要快速预处理、保留全局方差结构、结果可复现、数据量大时作为 t-SNE 的预处理步骤(先 PCA 降到 50 维)。

优先 t-SNE

目的是可视化聚类结构、数据存在非线性流形结构、关注局部邻域关系时。