使用Scikit-learn进行K-means聚类时出现请求簇数缺失的问题求助
关于Scikit-learn KMeans出现空簇的问题解答
嘿,我之前在项目里也碰到过一模一样的情况!来跟你拆解下这个问题:
为什么会出现指定4个簇却只得到3个非空簇?
这大概率是初始簇中心的选择导致的:
- 默认的
k-means++初始化虽然比随机初始化更优,但偶尔还是会选到一个离所有样本都非常远的初始点,在迭代过程中没有任何样本被分配到这个簇,最终就变成了空簇。 - 另外也有可能你的数据集本身的分布特性更适合3个簇,强行指定4个簇时,算法找不到足够的样本支撑第4个簇的存在。
为什么Scikit-learn的KMeans不会自动处理空簇?
标准的KMeans算法流程里并没有“检测空簇并重新初始化中心”的步骤,Scikit-learn的实现严格遵循了这一标准流程,所以不会自动为空簇重新选择新的样本作为中心。不同的KMeans实现(比如一些统计软件)可能会有额外的处理逻辑,但sklearn默认没有这个功能。
解决办法
给你几个实用的方案:
1. 增加初始化次数
通过设置n_init参数让算法多跑几次不同的初始中心,降低碰到空簇的概率。新版本的sklearn默认n_init='auto',你可以手动设置一个更大的值,比如20:
from sklearn.cluster import KMeans import pandas as pd # 增加初始化次数,同时设置random_state保证可复现 kmeans = KMeans(n_clusters=4, n_init=20, random_state=42) kmeans.fit(your_dataset) # 查看每个簇的样本数量,确认是否还有空簇 label_counts = pd.Series(kmeans.labels_).value_counts() print(label_counts)
2. 先评估最优簇数
如果不是必须要4个簇,建议先通过肘部法则或轮廓系数评估数据集的最优簇数,说不定3个簇才是最适合的:
from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt from sklearn.cluster import KMeans # 肘部法则 inertias = [] range_n_clusters = range(2, 6) for n in range_n_clusters: kmeans = KMeans(n_clusters=n, n_init=20, random_state=42) kmeans.fit(your_dataset) inertias.append(kmeans.inertia_) plt.plot(range_n_clusters, inertias, 'bo-') plt.xlabel('Number of clusters') plt.ylabel('Inertia') plt.title('Elbow Method') plt.show() # 轮廓系数 for n in range_n_clusters: kmeans = KMeans(n_clusters=n, n_init=20, random_state=42) labels = kmeans.fit_predict(your_dataset) score = silhouette_score(your_dataset, labels) print(f"n_clusters={n}, Silhouette Score={score:.2f}")
3. 手动处理空簇(如果必须要4个簇)
如果业务上一定需要4个簇,可以自己写代码检测空簇,然后选取离现有中心最远的样本作为新中心重新迭代。举个简单的示例逻辑:
import numpy as np from sklearn.cluster import KMeans import pandas as pd def kmeans_with_empty_cluster_fix(X, n_clusters): kmeans = KMeans(n_clusters=n_clusters, n_init=1, random_state=42) kmeans.fit(X) # 检查是否有空簇 unique_labels = np.unique(kmeans.labels_) while len(unique_labels) < n_clusters: # 找到离所有中心最远的样本 distances = kmeans.transform(X) min_distances = np.min(distances, axis=1) farthest_idx = np.argmax(min_distances) # 替换空簇的中心 empty_cluster_idx = set(range(n_clusters)) - set(unique_labels) kmeans.cluster_centers_[list(empty_cluster_idx)[0]] = X[farthest_idx] # 重新拟合 kmeans.fit(X) unique_labels = np.unique(kmeans.labels_) return kmeans # 使用这个函数 fixed_kmeans = kmeans_with_empty_cluster_fix(your_dataset, 4) print(pd.Series(fixed_kmeans.labels_).value_counts())
内容的提问来源于stack exchange,提问作者beginner
相关产品推荐
相关产品推荐

