使用K-Means聚类20万文本时95%样本归为一类的原因排查
文本K-Means聚类95%样本集中单簇的成因分析
问题背景
使用K-Means(含MiniBatchKMeans)对20万条英文文本进行聚类,通过肘部法设定聚类数为5,尝试过random_state=0/42、init='k-means++'等参数,但结果始终是95%的样本集中在一个簇,其余样本分散到剩下4个簇。
核心成因拆解
1. TF-IDF向量构建的缺陷
你的TF-IDF仅使用了stop_words="english",缺乏关键的文本预处理步骤:
- 未做小写统一:大小写混合的词汇(如"Apple"和"apple")会被识别为不同特征,稀释有效语义信息;
- 未去除标点、特殊字符:这类无意义符号会生成冗余特征,大幅增加向量维度与稀疏度;
- 未做词根/词干还原:同根词(如"run"和"running")无法被聚合,导致语义相似的文本向量差异被放大;
- 特征维度无限制:
TfidfVectorizer默认保留所有出现过的词汇,20万条文本可能生成几十万甚至上百万维的向量,高维稀疏空间中大部分文本的向量距离趋近于相等,K-Means无法区分有效簇。
2. K-Means算法的先天局限性
- 维度灾难:TF-IDF是高维稀疏向量,欧氏距离在高维空间中失去区分度——大部分样本间的距离几乎一致,算法自然倾向于将大量样本归为同一簇;
- MiniBatchKMeans的特性:它通过随机采样批次更新质心,对数据分布的不均匀性更敏感,若数据本身无明显簇结构,极易出现"一个大簇+多个小簇"的结果;
- 假设前提不成立:K-Means默认簇是凸形、大小相近、密度均匀的,但文本数据往往不符合该假设——大部分文本可能属于通用类别,仅少数文本有独特主题,强行分5类只会让大簇"收纳"绝大多数样本。
3. 聚类数设定的偏差
肘部法在高维稀疏文本数据中的可靠性不足:
- 高维空间中,SSE(误差平方和)的下降曲线会变得平缓,难以找到明确的"肘部"点,你认定的5个簇可能并非数据真实的簇数量;
- 若数据本身仅存在1-2个核心簇,强行设定5个聚类数,算法只能将少量离群样本分到其他簇,形成"一大四小"的结果。
4. 代码中的潜在问题
你的聚类代码存在几个细节错误,可能干扰结果:
# 错误1:重复训练模型 kmeans = MiniBatchKMeans(n_clusters=5, init= 'k-means++',random_state=0).fit(tfidf) labels = kmeans.fit_predict(tfidf) # 正确做法:fit后直接predict,无需再次fit_predict labels = kmeans.predict(tfidf) # 错误2:稀疏矩阵转DataFrame方式错误 X_n=pd.DataFrame(tfidf,columns=['tf-idf']) # TF-IDF是高维稀疏矩阵,每行是数百/数千维向量,单个列名无法承载,会导致数据混乱 # 错误3:concat轴参数缺失 result = pd.concat([data['Column_name'],X_n,labels_n]) # 默认axis=0是行拼接,应该用axis=1做列拼接 result = pd.concat([data['Column_name'], labels_n], axis=1)
验证与改进方向
- 优化文本预处理:添加小写化、去标点、词干/词根还原,用
max_features限制TF-IDF特征数量(如设为10000),降低维度; - 更换距离度量:用余弦相似度替代欧氏距离(可通过
sklearn.metrics.pairwise.cosine_distances转换后再聚类); - 重新评估聚类数:结合轮廓系数、Calinski-Harabasz指数辅助判断,不局限于肘部法;
- 尝试其他聚类算法:比如DBSCAN(基于密度,适配非凸簇)、层次聚类,或用预训练语言模型(如BERT)生成语义向量后再聚类。
内容的提问来源于stack exchange,提问作者sai_0033
相关产品推荐
相关产品推荐

