473_文本聚类方法¶
一句话说明¶
文本聚类是在没有标签的情况下,自动把相似文本归为一组的无监督学习方法,常用于话题发现、文档整理、用户意图分析。
核心知识点¶
- 表示方式:TF-IDF稀疏向量 或 BERT/Sentence-BERT语义密集向量
- 聚类算法:K-Means(指定簇数)、DBSCAN(自动发现噪声)、层次聚类
- 降维可视化:PCA / t-SNE / UMAP 将高维向量压缩到2D便于观察
- 评估指标:
- 有标签:ARI(调整兰德指数)、NMI
- 无标签:轮廓系数(Silhouette Score)、Davies-Bouldin Index
经典方法对比¶
| 方法 | 特点 | 需指定K | 噪声处理 |
|---|---|---|---|
| K-Means | 快速、球形簇假设 | 是 | 不支持 |
| DBSCAN | 任意形状、自动噪声 | 否 | 支持 |
| 层次聚类 | 可视树状图 | 可后验选 | 不支持 |
| BERTopic | 语义主题建模 | 自动 | 支持 |
| GMM | 软聚类(概率) | 是 | 弱 |
代码示例¶
# 基于 Sentence-BERT + K-Means 的文本语义聚类
from sentence_transformers import SentenceTransformer
from sklearn.cluster import KMeans, DBSCAN
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA
import numpy as np
import matplotlib.pyplot as plt
# ---- 1. 文本数据 ----
texts = [
"深度学习在图像识别中的应用",
"卷积神经网络识别猫和狗",
"自然语言处理的最新进展",
"BERT模型在文本分类中的效果",
"股票市场今日大跌",
"央行调整利率政策",
"足球世界杯冠军赛回顾",
"篮球NBA季后赛精彩片段",
]
# ---- 2. 用 Sentence-BERT 生成语义向量 ----
# Sentence-BERT专为句子级别语义相似度设计,比直接用BERT的[CLS]好
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
embeddings = model.encode(texts, show_progress_bar=True)
# embeddings.shape: (8, 384) 每条文本一个384维向量
print(f"向量形状: {embeddings.shape}")
# ---- 3. K-Means 聚类 ----
n_clusters = 3 # 先假设有3个话题
kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
labels = kmeans.fit_predict(embeddings) # 每个文本的簇标签
for text, label in zip(texts, labels):
print(f"[簇{label}] {text}")
# ---- 4. 用轮廓系数选最优K ----
silhouette_scores = []
k_range = range(2, 6) # 尝试K=2到5
for k in k_range:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
lbs = km.fit_predict(embeddings)
score = silhouette_score(embeddings, lbs) # 越接近1越好
silhouette_scores.append(score)
print(f"K={k}: Silhouette={score:.4f}")
best_k = k_range[np.argmax(silhouette_scores)]
print(f"最优K: {best_k}")
# ---- 5. 降维可视化(PCA到2D)----
pca = PCA(n_components=2)
embeddings_2d = pca.fit_transform(embeddings) # (8, 2)
plt.figure(figsize=(8, 6))
colors = ['red', 'blue', 'green', 'orange', 'purple']
for i, (x, y) in enumerate(embeddings_2d):
plt.scatter(x, y, c=colors[labels[i]], s=100)
plt.annotate(texts[i][:10], (x, y), fontsize=8)
plt.title('文本聚类可视化(PCA降维)')
plt.savefig('text_cluster.png', dpi=100)
# ---- 6. BERTopic 主题建模(推荐!)----
# pip install bertopic
# from bertopic import BERTopic
# topic_model = BERTopic(language="multilingual", min_topic_size=2)
# topics, probs = topic_model.fit_transform(texts)
# topic_model.get_topic_info() # 查看每个主题的关键词
# ---- 7. DBSCAN(无需指定K,自动发现噪声)----
dbscan = DBSCAN(eps=0.5, min_samples=2, metric='cosine')
db_labels = dbscan.fit_predict(embeddings)
# -1 表示噪声点(不属于任何簇)
print("DBSCAN簇标签:", db_labels)
面试常问点¶
- K-Means的缺点是什么?
需预先指定K;对初始化敏感;只适合球形、等大小簇
Sentence-BERT相比BERT直接编码好在哪?
专门针对句子相似度优化(对比学习训练),语义表示更准确
如何确定最优聚类数K?
- 肘部法则(Elbow):SSE随K减少,找"弯折"点
轮廓系数:K=1时无意义,找最大轮廓系数对应的K
文本聚类在实际业务中的应用?
- 用户评论自动分组、客服工单意图分类、新闻话题发现
速查表¶
| 任务 | 方案 |
|---|---|
| 快速原型 | TF-IDF + K-Means |
| 语义聚类 | Sentence-BERT + K-Means |
| 自动话题数 | BERTopic |
| 有噪声数据 | DBSCAN |
| 可视化 | UMAP降维 + matplotlib |