t-SNE
PCA 把数据线性压扁,t-SNE 则把邻居关系尽量原样"摆"到 2D 平面上——聚在一起的还是聚在一起
01 核心原理(大白话版)
想象你有一张 100 维的"地图",想把它印到一张纸上(2D)。PCA 的做法是:找一个好角度拍张照片,保留最大方差的方向。
t-SNE 的做法完全不同:它不管全局结构,只关心邻居关系——原本挨在一起的点,降维后也要挨在一起;原本很远的点,降维后可以随意放。
代价是:t-SNE 的全局距离没有意义,两个簇之间的距离不代表任何东西,只有簇内部的结构是可信的。
四步理解 t-SNE
对每个点 i,用高斯函数把它到其他点的距离转成概率 pj|i——越近的邻居概率越高,越远的趋近于 0。参数 σ 由 Perplexity 决定(自动二分搜索)。
把不对称的条件概率变成对称的联合概率,保证每对点只有一个相似度值。
在 2D 空间里用 t 分布(Student-t,自由度=1)计算相似度 Qij。t 分布比高斯更"胖",允许不相似的点在低维里离得更远,缓解"拥挤问题"。
损失 = 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
目的是可视化聚类结构、数据存在非线性流形结构、关注局部邻域关系时。