从零开始:Python实现文本聚类的完整流程解析

张开发
2026/4/16 9:24:43 15 分钟阅读

分享文章

从零开始:Python实现文本聚类的完整流程解析
1. 文本聚类入门指南第一次接触文本聚类时我也被那些专业术语搞得一头雾水。简单来说文本聚类就像把一堆杂乱无章的文档自动分类整理让相似的文档聚在一起。想象你有一屋子散落的书籍文本聚类就是那个能自动把小说、科普、历史书分别放到不同书架上的智能图书管理员。在实际项目中我用文本聚类做过电商评论分析。当时有上万条商品评论人工分类根本不可能。通过聚类算法我们自动把这些评论分成了产品质量、物流服务、使用体验等几大类帮运营团队快速抓住了用户反馈的重点。文本聚类主要有两个关键步骤首先要把文字转换成计算机能理解的数字特征提取然后用算法把这些数字分组向量聚类。听起来简单但每个环节都有不少门道。下面我就带你用Python一步步实现这个流程我会把踩过的坑和实战经验都分享给你。2. 数据预处理实战2.1 文本清洗技巧拿到原始文本数据时经常遇到各种脏数据。我处理过的一个电商评论数据集里有表情符号、错别字、甚至火星文。这时候就需要先做清洗import re def clean_text(text): # 去除特殊符号和表情 text re.sub(r[^\w\s], , text) # 合并连续空格 text re.sub(r\s, , text) return text.strip() # 示例 dirty_text 这款手机真的超级棒买它买它~ clean_text(dirty_text) # 输出: 这款手机真的超级棒 买它买它2.2 中文分词实战英文有天然空格分隔单词但中文需要专门的分词工具。我对比过多个分词工具后发现百度LAC在准确率和速度上表现都不错from LAC import LAC # 加载分词模型 lac LAC(modelac) text 自然语言处理很有趣 tokens lac.run(text)[0] # 输出: [自然语言, 处理, 很, 有趣]实际使用中会遇到新词识别问题。比如电商评论里的种草拔草这类网络用语可以在LAC中添加自定义词典lac.load_customization(custom_words.txt) # 每行一个自定义词2.3 停用词处理停用词就像语言中的的、了、是这类高频但信息量低的词。我整理过一份中文停用词表包含1200个词。使用时要注意根据业务调整比如分析美食评论时味道可能是关键词而非停用词。with open(stopwords.txt, r, encodingutf-8) as f: stopwords [line.strip() for line in f] # 过滤停用词 filtered_tokens [word for word in tokens if word not in stopwords]3. 特征提取详解3.1 词袋模型实现词袋模型是文本处理的经典方法。下面这个例子展示了如何用scikit-learn实现from sklearn.feature_extraction.text import CountVectorizer corpus [ 我爱自然语言处理, 自然语言处理很有趣, 我爱Python编程 ] # 创建词袋模型 vectorizer CountVectorizer() X vectorizer.fit_transform(corpus) print(vectorizer.get_feature_names_out()) # 输出词汇表 print(X.toarray()) # 输出词频矩阵词袋模型有个明显缺点忽略词序。比如狗咬人和人咬狗会被表示成相同的向量。在实际项目中我通常会增加n-gram特征来缓解这个问题# 添加二元语法特征 bigram_vectorizer CountVectorizer(ngram_range(1, 2))3.2 TF-IDF实战TF-IDF能降低高频词的权重提升有区分度词汇的重要性。这是我常用的调参组合from sklearn.feature_extraction.text import TfidfVectorizer tfidf TfidfVectorizer( max_features5000, # 限制特征数量 ngram_range(1, 2), # 使用1-2元语法 stop_wordsstopwords # 使用自定义停用词 ) tfidf_matrix tfidf.fit_transform(corpus)3.3 词向量进阶当项目需要更丰富的语义表示时我会用预训练的词向量。下面是用Gensim加载中文词向量的例子import gensim # 加载预训练模型 model gensim.models.KeyedVectors.load_word2vec_format( zh.vec, # 中文词向量文件 binaryFalse ) # 获取词向量 vector model[北京] # 300维向量对于短文本我通常取所有词向量的平均值作为文档表示。对于长文本可以尝试SIF加权等方法。4. 聚类算法选择4.1 K-Means实战K-Means是最常用的聚类算法适合数据分布呈球形的情况。这是我的实现模板from sklearn.cluster import KMeans # 假设X是TF-IDF矩阵 kmeans KMeans( n_clusters5, # 聚类数量 random_state42, # 随机种子 n_init10 # 初始化次数 ) clusters kmeans.fit_predict(X)确定最佳K值是个难题。我常用肘部法则结合业务需求import matplotlib.pyplot as plt inertias [] for k in range(2, 10): kmeans KMeans(n_clustersk).fit(X) inertias.append(kmeans.inertia_) plt.plot(range(2, 10), inertias) plt.xlabel(K) plt.ylabel(Inertia) plt.show()4.2 DBSCAN应用当数据分布不规则时我会选择DBSCAN。它最大的优势是不需要指定聚类数量from sklearn.cluster import DBSCAN dbscan DBSCAN( eps0.5, # 邻域半径 min_samples5 # 核心点所需最小样本数 ) clusters dbscan.fit_predict(X)调试DBSCAN时我常用k-距离图确定eps参数from sklearn.neighbors import NearestNeighbors neigh NearestNeighbors(n_neighbors5) nbrs neigh.fit(X) distances, _ nbrs.kneighbors(X) distances np.sort(distances[:, -1], axis0) plt.plot(distances) plt.show()4.3 层次聚类应用当需要分析聚类层次关系时我会选择层次聚类from sklearn.cluster import AgglomerativeClustering agg AgglomerativeClustering( n_clustersNone, distance_threshold0.5, linkageward ) clusters agg.fit_predict(X)可视化层次聚类结果很有帮助from scipy.cluster.hierarchy import dendrogram def plot_dendrogram(model, **kwargs): counts np.zeros(model.children_.shape[0]) n_samples len(model.labels_) for i, merge in enumerate(model.children_): current_count 0 for child_idx in merge: if child_idx n_samples: current_count 1 else: current_count counts[child_idx - n_samples] counts[i] current_count linkage_matrix np.column_stack([model.children_, model.distances_, counts]).astype(float) dendrogram(linkage_matrix, **kwargs) plot_dendrogram(agg, truncate_modelevel, p3)5. 结果分析与优化5.1 聚类评估方法没有真实标签时可以用轮廓系数评估聚类质量from sklearn.metrics import silhouette_score score silhouette_score(X, clusters) print(f轮廓系数: {score:.3f})在我的项目中轮廓系数在0.5以上通常说明聚类效果不错。但也要结合业务看有时候人工检查几个聚类样本更直接。5.2 主题关键词提取了解每个聚类的主题很重要我常用TF-IDF权重提取关键词def get_top_keywords(cluster_idx, n10): cluster_mask (clusters cluster_idx) cluster_tfidf tfidf_matrix[cluster_mask] avg_tfidf np.asarray(cluster_tfidf.mean(axis0)).ravel() top_indices avg_tfidf.argsort()[-n:][::-1] return [tfidf.get_feature_names_out()[i] for i in top_indices] # 获取第0个聚类的关键词 print(get_top_keywords(0))5.3 可视化技巧高维数据可视化前需要降维。我常用t-SNE因为它能保持局部结构from sklearn.manifold import TSNE import matplotlib.pyplot as plt tsne TSNE(n_components2, random_state42) X_tsne tsne.fit_transform(X.toarray()) plt.scatter(X_tsne[:, 0], X_tsne[:, 1], cclusters, cmapviridis) plt.colorbar() plt.show()5.4 参数调优经验经过多个项目实践我总结了一些调参经验文本长度差异大时TF-IDF比纯词频效果好K-Means前建议做标准化处理DBSCAN的eps参数对结果影响很大需要反复试验当特征维度很高时5000先用PCA降维到50-100维6. 完整项目案例6.1 电商评论分析实战下面是我做过的一个真实项目案例分析香水商品评论import pandas as pd from sklearn.pipeline import Pipeline # 1. 加载数据 df pd.read_csv(perfume_reviews.csv) texts df[content].tolist() # 2. 创建处理管道 pipeline Pipeline([ (tfidf, TfidfVectorizer( max_features3000, stop_wordsstopwords, ngram_range(1, 2) )), (pca, PCA(n_components50)), (cluster, KMeans(n_clusters5, random_state42)) ]) # 3. 训练并预测 clusters pipeline.fit_predict(texts) # 4. 分析结果 df[cluster] clusters for i in range(5): cluster_samples df[df[cluster] i].sample(3) print(f\nCluster {i} 示例:) print(cluster_samples[content].values)6.2 常见问题解决在实际项目中遇到过几个典型问题内存不足处理大文本时使用HashingVectorizer替代TfidfVectorizer聚类效果差尝试不同的文本表示方法如加入词性特征类别不平衡调整聚类算法参数或采样策略6.3 性能优化技巧当处理海量文本时这些技巧很管用使用稀疏矩阵存储特征对文本先聚类抽样再全量处理使用MiniBatchKMeans替代KMeans并行化处理利用多核CPUfrom sklearn.cluster import MiniBatchKMeans mbk MiniBatchKMeans( n_clusters100, batch_size1000, n_init3 ) mbk.fit(X)7. 进阶方向7.1 深度学习应用传统方法效果有限时可以尝试深度聚类from transformers import AutoTokenizer, AutoModel import torch # 加载预训练模型 tokenizer AutoTokenizer.from_pretrained(bert-base-chinese) model AutoModel.from_pretrained(bert-base-chinese) # 获取文本向量 inputs tokenizer(texts, return_tensorspt, paddingTrue, truncationTrue) with torch.no_grad(): outputs model(**inputs) embeddings outputs.last_hidden_state.mean(dim1).numpy()7.2 半监督学习当有少量标注数据时可以尝试约束聚类from sklearn.semi_supervised import LabelPropagation # 假设我们有少量标注数据 partial_labels np.array([0, -1, -1, 1, -1]) # -1表示未标注 label_prop LabelPropagation(kernelknn, n_neighbors3) label_prop.fit(X, partial_labels)7.3 在线聚类对于流式文本数据可以使用增量聚类from sklearn.cluster import MiniBatchKMeans mbk MiniBatchKMeans(n_clusters10) for batch in text_stream: # 假设是文本流 X_batch vectorizer.transform(batch) mbk.partial_fit(X_batch)8. 工程化建议8.1 代码结构优化建议将聚类流程模块化这是我常用的项目结构text_cluster/ ├── preprocessing/ # 文本预处理 │ ├── cleaner.py │ └── tokenizer.py ├── features/ # 特征工程 │ ├── bow.py │ └── embeddings.py ├── clustering/ # 聚类算法 │ ├── kmeans.py │ └── dbscan.py └── evaluation/ # 评估模块 ├── metrics.py └── visualization.py8.2 模型持久化训练好的模型需要保存以便复用import joblib # 保存管道 joblib.dump(pipeline, text_cluster_pipeline.joblib) # 加载管道 pipeline joblib.load(text_cluster_pipeline.joblib)8.3 API服务化用FastAPI暴露聚类服务from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class TextRequest(BaseModel): texts: list[str] app.post(/cluster) def cluster_texts(request: TextRequest): clusters pipeline.predict(request.texts) return {clusters: clusters.tolist()}9. 避坑指南在文本聚类项目中踩过不少坑这里分享几个典型问题中文编码问题总是遇到gbk/utf-8编码错误现在我会在代码开头统一设置import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)内存爆炸处理大文本时TF-IDF矩阵可能撑爆内存解决方案使用HashingVectorizer替代分批处理数据增加稀疏矩阵的使用聚类结果不稳定特别是K-Means解决方法设置固定random_state多次运行取最优结果使用K-Means初始化维度灾难当特征维度很高时聚类效果差建议先用PCA降维使用t-SNE可视化检查尝试特征选择评估困难在没有真实标签时可以人工抽查样本结合多个内部评估指标设计业务相关指标10. 实用工具推荐经过多个项目验证这些工具能大幅提升效率中文处理百度LAC优秀的中文分词工具Jieba轻量级分词工具HanLP功能全面的中文NLP工具包特征工程Gensim主题模型和词向量训练Texthero文本处理快速上手Sentence-transformers获取优质句子向量可视化PyLDAvis主题模型可视化Wordcloud生成词云Plotly交互式可视化分布式计算Spark MLlib处理海量文本Dask并行加速scikit-learnRay分布式计算框架标注工具Label Studio聚类结果标注Prodigy商业级标注工具Brat关系标注工具# 示例使用Texthero快速处理文本 import texthero as hero df[clean_text] hero.clean(df[text]) df[tfidf] hero.tfidf(df[clean_text])

更多文章