如何用Python将BERT/mBART生成的高维句嵌入可视化至2D或3D空间?
如何用Python将BERT/mBART生成的高维句嵌入可视化至2D或3D空间?
我太懂你用PCA跑出来聚类模糊的郁闷了!高维句嵌入里的语义关系大多是非线性的,PCA这种线性降维方法只能抓全局方差,对这种精细的语义聚类确实不太给力。下面我结合自己平时做NLP可视化的经验,给你一步步讲清楚怎么搞定这件事:
一、先搞懂:PCA、t-SNE、UMAP选哪个?
直接给结论:优先用UMAP,t-SNE作为备选,PCA可以彻底放弃(除非你只是想看全局方差分布,不是语义聚类)。
具体为啥:
- PCA:线性降维,只保留方差最大的维度,完全忽略语义之间的非线性关联,像你遇到的情况,机器学习和动物类的句子可能方差差异不大,所以混在一起,根本分不出聚类。
- t-SNE:擅长捕捉局部结构,能把相似的点聚得很密,但全局结构拉胯——比如本来机器学习类和动物类在高维空间里距离很远,但t-SNE可能把它们画得很近;而且计算速度慢,数据量一大就卡。
- UMAP:兼顾局部和全局结构,既能把相似的句子聚成清晰的类,又能保持不同类之间的相对距离;计算速度比t-SNE快很多,对句嵌入这种数据适配性最好。
二、实操步骤:从降维到可视化
我就用你给的4个句子的例子来写代码,假设你已经拿到了embeddings(768维的数组,形状是(4,768))和sentences列表。
1. 先做降维:用UMAP
首先得安装UMAP,直接pip install umap-learn就行。然后代码如下:
import umap import numpy as np # 先对句嵌入做L2归一化(可选,但能让降维效果更稳定) normalized_embeddings = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True) # 初始化UMAP,参数可以根据数据调整 reducer = umap.UMAP( n_components=2, # 降到2D,要3D就改成3 n_neighbors=15, # 控制局部/全局平衡,句嵌入一般选10-50,数据少的话选小一点 min_dist=0.1, # 控制聚类紧密程度,越小聚类越紧凑 random_state=42 # 固定随机种子,结果可复现 ) reduced_embeddings = reducer.fit_transform(normalized_embeddings)
这里提一下,L2归一化不是必须的,但很多预训练模型的句嵌入做了归一化后,语义相似度的计算更稳定,降维后的聚类效果也会更好。
2. 静态可视化:用Matplotlib/Seaborn
如果只是要生成静态图,用Matplotlib就够了,能快速看到聚类效果:
import matplotlib.pyplot as plt plt.figure(figsize=(10, 8)) # 画散点 scatter = plt.scatter(reduced_embeddings[:, 0], reduced_embeddings[:, 1]) # 给每个点加标签 for i, sentence in enumerate(sentences): plt.annotate(sentence, (reduced_embeddings[i, 0], reduced_embeddings[i, 1]), fontsize=10, ha='center') plt.title("Sentence Embeddings Visualization (UMAP)") plt.show()
这样你就能看到,机器学习的两个句子会紧紧聚在一起,猫和狗的句子要么各自分开,要么聚成一个动物小簇,和机器学习的簇离得很远。
3. 交互式可视化:用Plotly(支持hover、缩放)
如果要做交互式的,比如鼠标hover显示完整句子、缩放拖拽,那Plotly是我最常用的工具,完全满足你的需求:
import plotly.express as px # 把数据转成DataFrame方便Plotly处理 import pandas as pd df = pd.DataFrame({ 'x': reduced_embeddings[:, 0], 'y': reduced_embeddings[:, 1], 'sentence': sentences }) # 生成交互式散点图 fig = px.scatter(df, x='x', y='y', hover_name='sentence', title="Interactive Sentence Embeddings Visualization (UMAP)", labels={'x': 'UMAP Dimension 1', 'y': 'UMAP Dimension 2'}) # 可以自定义点的大小、颜色 fig.update_traces(marker=dict(size=12, opacity=0.8)) fig.show()
运行后会弹出一个网页(或者在Jupyter里直接显示),你可以缩放、拖拽,鼠标放到点上就能看到完整的句子,完全不用担心标签重叠看不清的问题。
三、优化语义聚类的小技巧
如果用UMAP还是觉得聚类不够清晰,可以试试这些调参和预处理技巧:
- 调整UMAP的核心参数:
n_neighbors:如果你的数据集小(比如几十条),可以调到5-10,更关注局部聚类;如果数据量大(几百上千条),调到30-50,兼顾全局结构。min_dist:如果想让聚类更紧凑,把这个值调小到0.01;如果想让不同簇之间的距离更大,调大到0.5试试。
- 降维前做标准化:除了L2归一化,也可以用
sklearn.preprocessing.StandardScaler把每个特征的均值拉到0,方差拉到1,对有些句嵌入数据集有帮助。 - 如果用t-SNE,调perplexity:要是你非要用t-SNE,
perplexity参数是关键,一般设置为数据集大小的1/5到1/10,比如你有4条数据,perplexity设为2-3,这样局部聚类会更准,但注意t-SNE的结果随机性很强,要固定random_state。
四、预期效果验证
用上面的方法处理你给的4个句子,应该能得到这样的效果:
- 「I love machine learning」和「Deep learning is amazing」会紧紧聚在同一个小簇里
- 「The cat is sleeping」和「Dogs are very loyal」要么形成一个独立的动物小簇,要么各自分开,但肯定和机器学习的簇离得很远
- 交互式图里,hover就能看到完整句子,缩放后细节也清晰
这样应该就能完美解决你的问题啦,要是还有细节调不好的地方,随时再问!




