1004.特征工程-特征降维-t-SNE

t 分布随机邻域嵌入(t-distributed Stochastic Neighbor Embedding,t-SNE)是一种非线性、无监督降维与可视化方法。它将高维样本映射到 2D/3D优先保留局部邻域结构,广泛用于聚类展示、嵌入表征质量初筛与异常诊断。

段末注释t-SNE 输出坐标主要用于探索性看图,一般作为下游回归/分类模型的训练特征。

读前说明:配图位于同名目录 1004.特征工程-特征降维-t-SNE/;正文图链写 ./1004.特征工程-特征降维-t-SNE/文件名,便于编辑器预览与 Hexo 部署。系列索引:降维概述 | PCA | UMAP


1. 产生基础:为什么需要 t-SNE

1.1 问题来源

机器学习与组学分析中,原始特征常为高维向量

  • 蛋白 PLM 嵌入:维度 $D$ 可达 320~1280
  • 图像 CNN 特征、单细胞表达谱:维度更高。

人眼无法直接观察 $D$ 维空间中的聚类与离群结构。传统线性降维(PCA)对非线性流形(如「瑞士卷」形数据)表现差——展开后相近样本可能被压到一起。

t-SNE 的目标:在二维平面上回答——

相似的样本是否靠得近?不相似的样本是否分开?

1.2 与 PCA 的分工

方法 映射类型 保留什么
PCA 线性 全局方差最大的方向
t-SNE 非线性 局部邻域相似关系

实践中常串联:PCA 粗看方差/批次 → t-SNE 细看作图 → 监督模型定量评估


2. 演进脉络

图 1 t-SNE 演进脉络(科普示意)

年份 里程碑 要点
2002 SNE(Stochastic Neighbor Embedding) Hinton & Roweis;用高斯分布建模邻域相似度,梯度下降布局
2008 t-SNE van der Maaten & Hinton;低维改用 t 分布(重尾),缓解拥挤问题(crowding problem)
2014 Barnes-Hut t-SNE 近似近邻,将复杂度从 $O(N^2)$ 降至约 $O(N \log N)$
2010s 单细胞爆发 scRNA-seq 标准可视化工具之一
2020s UMAP 并存 蛋白 PLM 嵌入、深度学习特征探索;大规模场景更常优先 UMAP

段末注释拥挤问题指高维中「中等距离」的点过多,挤入低维小区域后难以分辨局部结构;t 分布的长尾有助于拉开这些点。


3. 算法原理

图 2 t-SNE 算法原理(科普示意)

t-SNE 分两步:高维建概率图低维对齐概率图

3.1 高维:高斯邻域相似度

对样本 $i$,以 $i$ 为中心,定义条件概率(只强调近邻):

$$
p_{j|i} = \frac{\exp\left(-| \mathbf{x}_i - \mathbf{x}j |^2 / 2\sigma_i^2\right)}{\sum{k \neq i} \exp\left(-| \mathbf{x}_i - \mathbf{x}_k |^2 / 2\sigma_i^2\right)}
$$

对称化:

$$
p_{ij} = \frac{p_{j|i} + p_{i|j}}{2n}
$$

$\sigma_i$ 由 perplexity(困惑度)二分搜索确定,可理解为每个点关注的「有效近邻数」(典型 5~50)。

3.2 低维:t 分布 + KL 散度

低维坐标 $\mathbf{y}i$ 上,用自由度为 1 的 t 分布定义 $q{ij}$(重尾,减轻拥挤)。

优化目标——最小化 KL 散度(Kullback-Leibler divergence):

$$
C = \mathrm{KL}(P | Q) = \sum_{i \neq j} p_{ij} \log \frac{p_{ij}}{q_{ij}}
$$

梯度下降迭代更新 $\mathbf{y}_i$,使低维分布 $Q$ 逼近高维分布 $P$。

3.3 算法流程(伪代码)

1
2
3
4
5
6
7
8
输入: X (n×d), perplexity, n_iter
1. 计算高维成对相似度 → 矩阵 P
2. 随机或 PCA 初始化低维 Y (n×2)
3. for t in 1..n_iter:
计算低维 q_ij (t分布)
梯度 ← ∂KL(P||Q)/∂Y
Y ← Y - lr * 梯度
输出: Y

4. 适用场景

图 3 t-SNE 适用场景(科普示意)

场景 具体用法 期望信号
蛋白/酶变体嵌入质控 ESM 嵌入 → t-SNE → 按 $\log_{10}(\mathrm{activity}/\mathrm{WT})$ 着色 高/低活性分区 → 表征可能含功能信息
单细胞 RNA-seq 细胞表达谱降维展示 细胞类型形成紧簇
深度模型特征探索 CNN/AE 隐层特征可视化 类别边界可辨
批次/异常诊断 按批次、日期、操作者着色 若批次比标签更「整齐」→ 需校正

酶改造-06 §6.2「预判表征质量」直接对应。

不适用

  • 需要精确全局距离比较(用 MDSUMAP 更合适);
  • 需要可解释特征输入回归器(用原始嵌入或 PCA);
  • 实时在线降维新样本(t-SNE 无简单 transform,新点需重跑或用近似方案)。

5. 局限性

图 4 t-SNE 局限性(科普示意)

局限 说明 应对
全局距离失真 图上两簇间距不代表高维真实差异 不根据簇间远近下生物学结论
计算成本高 朴素实现 $O(N^2)$;$N>10^4$ 很慢 PCA→50 维;Barnes-Hut;抽样
不宜作训练特征 2D 坐标语义不稳定、信息损失大 用原始高维嵌入训练模型
超参数敏感 perplexity、学习率、初始化影响形态 固定 random_stateinit='pca';多组 perplexity 对照
无标准 transform sklearn 对新样本需 fit_transform 全量重算 探索性分析可接受;生产流水线慎用

图好看 ≠ 模型准:必须结合 Spearman、RMSE、交叉验证与湿实验。


6. 主要超参数

参数 含义 建议
perplexity 局部邻域尺度 样本少 5~15;样本多 30~50;须 $< n_{\mathrm{samples}}$
n_iter 迭代次数 ≥1000(默认常 1000)
learning_rate 步长 'auto'10~1000
init 初始化 推荐 'pca' 更稳定
random_state 随机种子 必须固定

7. 示例展示与代码

7.1 合成数据演示(可直接运行)

下面用 sklearn 自带 digits 数据集(64 维手写数字 → 2D),展示 t-SNE 如何把不同数字分开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
"""t-SNE 合成示例:digits 数据集可视化"""
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_digits
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler

# 1. 加载数据(1797 样本 × 64 特征)
digits = load_digits()
X = StandardScaler().fit_transform(digits.data)
y = digits.target # 0-9 标签

# 2. t-SNE 降维
tsne = TSNE(
n_components=2,
perplexity=30,
n_iter=1000,
init="pca",
random_state=42,
)
Z = tsne.fit_transform(X)

# 3. 作图
fig, ax = plt.subplots(figsize=(8, 6))
scatter = ax.scatter(Z[:, 0], Z[:, 1], c=y, cmap="tab10", s=12, alpha=0.8)
ax.set_xlabel("t-SNE 1")
ax.set_ylabel("t-SNE 2")
ax.set_title("sklearn digits — t-SNE (perplexity=30)")
plt.colorbar(scatter, ax=ax, label="digit")
plt.tight_layout()
plt.savefig("tsne_digits_demo.png", dpi=150)
print("KL divergence (final):", tsne.kl_divergence_)

预期结果:0~9 十个数字在 2D 上形成 10 个相对独立的簇,说明 64 维像素特征中的类别信息在局部邻域中得到保留。

7.2 蛋白变体嵌入质控示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
"""蛋白变体 PLM 嵌入 + t-SNE 质控(示意)"""
import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA

# embeddings.npy: (N, D) 来自 ESM-2 mean pooling
# labels.npy: log10(activity/WT),或 0/1 高/低活性
X = np.load("embeddings.npy")
y = np.load("labels.npy")

# 大规模先 PCA 预降维(可选,N>5000 时推荐)
if X.shape[0] > 5000:
X = PCA(n_components=50, random_state=42).fit_transform(X)

perplexity = min(30, max(5, X.shape[0] // 10)) # 自适应

Z = TSNE(
n_components=2,
perplexity=perplexity,
init="pca",
random_state=42,
).fit_transform(X)

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# 左:连续活性着色
sc0 = axes[0].scatter(Z[:, 0], Z[:, 1], c=y, cmap="viridis", s=15, alpha=0.75)
plt.colorbar(sc0, ax=axes[0], label="log10(activity/WT)")
axes[0].set_title("按活性着色")

# 右:二分类高/低活性(中位数切分)
binary = (y > np.median(y)).astype(int)
sc1 = axes[1].scatter(Z[:, 0], Z[:, 1], c=binary, cmap="coolwarm", s=15, alpha=0.75)
plt.colorbar(sc1, ax=axes[1], label="high(1)/low(0)")
axes[1].set_title("高/低活性二分")

for ax in axes:
ax.set_xlabel("t-SNE 1")
ax.set_ylabel("t-SNE 2")

plt.tight_layout()
plt.savefig("tsne_protein_qc.png", dpi=150)

7.3 perplexity 敏感性对照

1
2
3
4
5
6
7
"""同一数据,不同 perplexity 对比"""
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for ax, perp in zip(axes, [5, 30, 50]):
Z = TSNE(n_components=2, perplexity=perp, init="pca", random_state=42).fit_transform(X)
ax.scatter(Z[:, 0], Z[:, 1], c=y, cmap="viridis", s=8, alpha=0.7)
ax.set_title(f"perplexity={perp}")
plt.savefig("tsne_perplexity_sweep.png", dpi=150)

8. 与 PCA、UMAP 对比

方法 线性/非线性 保留结构 速度 典型用途
PCA 线性 全局方差 预处理、建模降维
t-SNE 非线性 局部邻域 紧簇边界可视化
UMAP 非线性 局部 + 部分全局 较快 大规模流形探索

建议流程:PCA 粗看 → t-SNE/UMAP 细看作图 → 监督模型评估


9. 小结

  • t-SNE 源于 SNE,核心创新是低维 t 分布 + KL 散度,解决拥挤问题。
  • 强项是局部聚类可视化嵌入质控初筛;弱项是全局距离不可信不宜当特征
  • 蛋白工程中与 酶改造-06 配合:看图后仍需定量指标与湿实验验证。

参考文献

  1. van der Maaten L., Hinton G. Visualizing Data using t-SNE. JMLR 2008, 9, 2579–2605.
  2. van der Maaten L. Accelerating t-SNE using Tree-Based Algorithms. JMLR 2014, 15, 3221–3245.
  3. sklearn.manifold.TSNE
  4. 特征降维算法比较综述
-------------本文结束感谢您的阅读-------------