张芷铭的个人博客

t-SNE 降维

t-SNE(t-Distributed Stochastic Neighbor Embedding,t-分布随机邻域嵌入)是一种非常流行且强大的非线性降维技术,尤其擅长将高维数据映射到二维或三维空间,以便进行可视化。它由 Laurens van der Maaten 和 Geoffrey Hinton 于 2008 年提出。

简单来说,你可以把它想象成一个专门用于绘制高维数据“地图”的算法。它的核心目标是:在低维地图上,在原始高维空间里相似(距离近)的点,在图上也应该聚集在一起;而不相似(距离远)的点,在图上也应该分开。它特别适合用于探索性数据分析,来观察数据中是否存在自然的簇(cluster)或模式,正如你在聚类代码中应用的那样。

✨ 核心思想与形象比喻

为了更直观地理解,我们可以用绘制地图来类比:

  • PCA(主成分分析) 就像绘制一张世界地图。它会尽力保持各大洲之间正确的地理位置和大致形状(全局结构),但不可避免地会扭曲某些局部区域的面积或距离(例如,格陵兰岛看起来和非洲差不多大)。
  • t-SNE 则像绘制一张详细的城市街区地图。它不那么关心这个城市在整个国家中的绝对位置,但会非常精确地保证学校、超市、公园等相邻的建筑物(局部相似点)在图上紧挨在一起,形成有意义的社区(簇)。它会牺牲一些全局的相对位置信息,来极致地展现局部结构。

⚙️ 工作原理简述

t-SNE 的实现主要包含以下几个关键步骤:

  1. 在高维空间度量相似性:算法首先计算数据点两两之间的相似度。这个相似度不是简单的欧氏距离,而是用条件概率来表示。具体来说,它使用高斯分布来建模,一个点 $x_j$ 是另一个点 $x_i$ 的“邻居”的概率。这个概率反映了点与点之间的局部关系。
  2. 在低维空间构建相似性:算法随机初始化一组低维空间(比如2维平面)中的点。然后,它同样计算这些低维点之间的相似度,但这里使用的是尾部更厚重的t分布。选择t分布是为了解决高维数据降维到低维时产生的“拥挤问题”(Crowding Problem),即不同簇的数据点容易挤在一起难以区分。
  3. 最小化分布差异:t-SNE 的核心优化目标是让低维空间中的相似度分布尽可能地接近高维空间中的相似度分布。它通过梯度下降法最小化两个分布之间的差异,这个差异由 Kullback-Leibler (KL) 散度 来衡量。通过不断迭代更新低维空间中点的位置,使得KL散度最小化。

主要特点与注意事项

特性说明
✅ 优势保留局部结构:能出色地揭示数据中的聚类结构。
处理非线性关系:能发现PCA等线性方法无法识别的复杂模式。
❌ 局限性计算成本高:时间复杂度高,处理大规模数据时较慢。
结果具有随机性:每次运行结果可能略有不同,需设置随机种子(random_state)复现结果。
难以解释轴含义:降维后的坐标轴没有直接的物理意义。
不能用于推理新数据:训练好的t-SNE模型不能直接用于转换新的数据点,必须重新运行整个算法。
🎯 关键参数perplexity(困惑度):大致表示每个点考虑多少个近邻,影响簇的大小和形状,通常建议在5到50之间。
learning_rate(学习率):控制优化过程的步长,太高或太低都会影响可视化效果。
n_iter(迭代次数):优化过程的迭代次数,需足够大以确保收敛。

与PCA的简单对比

特性t-SNEPCA
核心思想非线性,保留局部结构(邻近点关系)线性,保留全局结构(最大方差方向)
最佳用途数据可视化,探索聚类结构数据压缩,去噪,特征提取,作为其他算法的预处理步骤
计算效率较慢,尤其对于大数据集非常快
结果可解释性坐标轴无明确含义主成分有明确的方差贡献度

代码

 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
def visualize_clusters(self, output_path=None, method='tsne', max_points=1000):
	"""
	可视化聚类结果(使用降维)
	"""
	if self.labels_ is None:
		raise ValueError("No clustering results. Call cluster() first.")
	
	# 抽样显示以避免内存问题
	if len(self.features_) > max_points:
		indices = np.random.choice(len(self.features_), max_points, replace=False)
		features_subset = self.features_[indices]
		labels_subset = self.labels_[indices]
	else:
		features_subset = self.features_
		labels_subset = self.labels_
	
	print(f"Visualizing {len(features_subset)} points using {method}...")
	
	if method == 'tsne':
		reducer = TSNE(n_components=2, random_state=self.random_state, perplexity=30)
	else:
		from sklearn.decomposition import PCA
		reducer = PCA(n_components=2)
	
	embeddings_2d = reducer.fit_transform(features_subset)
	
	plt.figure(figsize=(12, 10))
	scatter = plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1], 
						c=labels_subset, cmap='Spectral', alpha=0.7, s=10)
	plt.colorbar(scatter)
	plt.title(f'Image Clusters Visualization ({self.method}, n_clusters={self.n_clusters})')
	
	if output_path:
		plt.savefig(output_path, dpi=300, bbox_inches='tight')
		print(f"Visualization saved to {output_path}")
	
	plt.show()

这段代码中,t-SNE将高维的图像特征降至二维,然后根据聚类算法生成的 labels_ 进行着色。这样,您可以直观地判断聚类效果:

  • 如果同一颜色的点清晰地聚集在一起,并且不同颜色的点良好分离,说明聚类效果可能不错。
  • 如果不同颜色的点严重混杂,则可能意味着聚类效果不理想,或者参数(如K值)需要调整。

您代码中设置的 perplexity=30 是一个常用的默认值,对于中等规模的数据子集(您使用了 max_points=1000)通常是合理的。如果发现可视化效果不理想(例如所有点挤成一团或分散过度),可以尝试调整这个参数以及 learning_rate 等。

希望这个详细的解释能帮助您更好地理解 t-SNE 这一强大工具!

💬 评论