boxmoe_header_banner_img

Hello! 欢迎来到悠悠畅享网!

文章导读

Python怎样实现数据聚类?K-means算法优化


avatar
站长 2025年8月12日 2

k-means算法通过迭代将数据分配到最近的簇中心并更新中心,直至收敛;2. 优化策略包括使用n_init多次运行选择最优结果、k-means++初始化减少对初始值敏感、肘部法则和轮廓系数法确定k值;3. 局限性有对初始值和异常值敏感、需预设k值、假设簇为球形且密度均匀;4. 改进方法包括mini-batch k-means加速计算、k-medoids提升鲁棒性、x-means自动选k、降维预处理提升效果;5. 其他聚类算法如dbscan可发现任意形状簇并识别噪声、层次聚类无需预设k且可可视化树状图、gmm提供概率性聚类更灵活;6. 实际应用中应根据数据特征选择合适算法,通常从k-means入手,效果不佳时尝试dbscan或gmm,结合多种方法优化聚类结果。

Python怎样实现数据聚类?K-means算法优化

Python实现数据聚类,K-means是常用且直观的算法。它通过迭代将数据点分配到最近的簇中心,并更新中心点位置,直至收敛。优化K-means通常涉及初始中心选择、评估指标和变种算法,以克服其对初始值敏感、易陷入局部最优等局限性。

K-means算法,说实话,第一次接触时觉得它简单得有点粗暴,但正是这种直观性,让它在很多场景下都非常实用。不过,粗暴不代表没有技巧,它的优化空间可不小。

在Python中,我们通常会用到

scikit-learn

库来实现K-means。它的使用非常简洁明了。核心思路就是:你告诉它想分成几类(K值),它就会帮你把数据点分好。

立即学习Python免费学习笔记(深入)”;

import numpy as np from sklearn.cluster import KMeans import matplotlib.pyplot as plt from sklearn.datasets import make_blobs  # 生成一些示例数据 X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=0)  # 初始化KMeans模型,设定簇的数量为4 # n_init='auto' (或一个整数) 告诉算法运行多少次不同的初始质心,并选择最好的结果,这本身就是一种优化 kmeans = KMeans(n_clusters=4, random_state=0, n_init='auto')  # 拟合数据 kmeans.fit(X)  # 获取聚类结果和簇中心 labels = kmeans.labels_ centroids = kmeans.cluster_centers_  # 可视化结果 plt.scatter(X[:, 0], X[:, 1], c=labels, s=50, cmap='viridis') plt.scatter(centroids[:, 0], centroids[:, 1], c='red', s=200, alpha=0.7, marker='X') plt.title('K-means Clustering') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.show()  print("簇中心点:n", centroids)

这里面,

n_init='auto'

(或者明确指定一个整数,比如

n_init=10

)是一个很关键的优化点。它不是只跑一次K-means,而是会用不同的初始中心点跑多次,然后选一个总误差(惯性,inertia_)最小的结果。这大大降低了K-means陷入局部最优的风险。另外,

init='k-means++'

是默认的初始化策略,它会选择距离尽可能远的初始中心点,这比随机选择要好得多,也是一个重要的内置优化。

如何选择K值?——K-means聚类中的“肘部法则”与轮廓系数法

K值,也就是聚类的簇数量,这玩意儿选不好,结果可能就差强人意。我个人感觉,这就像是给数据点找家,家分多了太散,分少了又挤,得有个合适的度。确定K值没有一个放之四海而皆准的“最佳”方法,但“肘部法则”和“轮廓系数法”是两种非常常用的经验性方法。

肘部法则 (Elbow Method)

它的核心思想是:随着K值的增加,每个簇内部的平方和(WCSS,Within-Cluster Sum of Squares,也就是

inertia_

属性)会逐渐减小。当K值达到一个“恰到好处”的点时,WCSS的下降速度会显著放缓,这个点在图上看起来就像一个手肘。

wcss = [] # 尝试不同的K值 for i in range(1, 11): # 尝试从1到10个簇     kmeans = KMeans(n_clusters=i, random_state=0, n_init='auto')     kmeans.fit(X)     wcss.append(kmeans.inertia_) # inertia_ 就是WCSS  plt.plot(range(1, 11), wcss) plt.title('Elbow Method') plt.xlabel('Number of Clusters (K)') plt.ylabel('WCSS') plt.show()

看上面这张图,你会发现WCSS一开始下降很快,然后逐渐平缓。那个“拐点”就是我们希望找到的K值。不过,有时候这个“肘部”并不那么明显,这确实让人有点头疼。

轮廓系数法 (Silhouette Score)

轮廓系数则提供了一个更量化的评估指标,它衡量了每个样本点与其所属簇的相似度(内聚性)以及与其他簇的相异度(分离性)。轮廓系数的取值范围是-1到1:

  • 接近1:样本点与自身簇非常匹配,与其他簇相距很远。
  • 接近0:样本点在两个簇的边界附近,可能分配不明确。
  • 接近-1:样本点可能被错误地分配到错误的簇。

我们通常会选择使平均轮廓系数最大的K值。

from sklearn.metrics import silhouette_score  silhouette_scores = [] # K值至少需要2个簇才能计算轮廓系数 for i in range(2, 11):     kmeans = KMeans(n_clusters=i, random_state=0, n_init='auto')     kmeans.fit(X)     score = silhouette_score(X, kmeans.labels_)     silhouette_scores.append(score)  plt.plot(range(2, 11), silhouette_scores) plt.title('Silhouette Score Method') plt.xlabel('Number of Clusters (K)') plt.ylabel('Silhouette Score') plt.show()  # 找到最佳K值 best_k = np.argmax(silhouette_scores) + 2 # 因为我们从K=2开始 print(f"根据轮廓系数法,最佳K值可能是:{best_k}")

我个人更偏爱轮廓系数,因为它提供了一个明确的数值,不像肘部法则那样需要“肉眼识别”。但两者结合使用,往往能得到更可靠的K值。

K-means算法的局限性与常见优化策略有哪些?

说句实在的,K-means虽然好用,但它也不是万能药。总有些时候,你会发现它的表现不尽如人意,甚至有些“脾气”。比如,你跑了几次,结果可能都不一样,这多半是初始中心点搞的鬼。

K-means的常见局限性:

  1. 对初始中心点敏感: 不同的初始质心可能导致不同的聚类结果,甚至陷入局部最优。虽然
    k-means++

    n_init

    能缓解,但无法完全消除。

  2. 需要预设K值: 这是最头疼的一点,很多时候我们对数据到底有几类是完全没概念的。
  3. 对异常值敏感: 少数远离大部分数据点的异常值可能会严重影响簇中心的位置,导致聚类结果偏差。
  4. 假设簇是球形的且大小相似: K-means使用欧氏距离,倾向于发现球形或凸形的簇。如果你的数据簇是任意形状(比如月牙形、环形),K-means的表现就会很差。
  5. 无法处理密度不均的簇: 如果不同簇的密度差异很大,K-means可能无法很好地分离它们。

针对这些局限性,除了前面提到的

n_init

k-means++

,我们还有一些常见的优化策略和替代方案:

  • Mini-Batch K-Means: 当数据集非常大时,每次迭代都计算所有样本到所有质心的距离会非常耗时。Mini-Batch K-Means每次只使用数据的一个小批量(mini-batch)来更新簇中心,这大大加快了收敛速度,尤其适用于大数据集。它在
    sklearn.cluster

    中也有实现。

  • K-medoids (K-中心点算法): 与K-means使用均值作为簇中心不同,K-medoids选择簇内的一个实际数据点作为中心点(medoid)。这使得它对异常值不那么敏感,因为medoid是数据集中真实存在的点,而不是计算出的平均值,但计算成本通常更高。
  • X-means: 这是一种自动确定K值的算法。它在K-means的基础上,通过BIC(贝叶斯信息准则)来评估和分裂簇,从而找到一个相对最优的K值。这解决了K值预设的难题,但计算复杂度会增加。
  • 使用其他距离度量: 对于特定类型的数据,欧氏距离可能不是最佳选择。例如,对于文本数据,余弦相似度可能更合适。虽然
    sklearn

    的K-means默认是欧氏距离,但理论上可以自定义距离函数(虽然实现起来会复杂些,通常需要自己编写K-means的迭代过程)。

  • 降维预处理: 对于高维数据,先使用PCA(主成分分析)或t-SNE等降维技术,可以去除冗余信息、降低噪声,同时使数据在低维空间中更容易被K-means处理,有时还能提升聚类效果。

除了K-means,Python还有哪些强大的聚类算法?

有时候,K-means确实满足不了需求,比如你的数据形状很奇特,或者你压根不知道该分几类。这时候,就得请出其他“高手”了。我个人对DBSCAN情有独钟,因为它能找出那些不规则的“甜甜圈”形状的簇,这可是K-means的盲区。

1. DBSCAN (Density-Based Spatial Clustering of Applications with Noise)

DBSCAN是一种基于密度的聚类算法。它不需要预设簇的数量,能够发现任意形状的簇,并且能有效识别噪声点(异常值)。它的核心思想是:如果一个数据点周围足够密集,它就属于一个簇。

  • 优点: 发现任意形状的簇;无需预设K值;能识别噪声。
  • 缺点: 对参数
    eps

    (邻域半径)和

    min_samples

    (最小样本数)敏感;对于密度差异较大的簇效果不佳。

from sklearn.cluster import DBSCAN from sklearn.preprocessing import StandardScaler  # DBSCAN对特征缩放敏感,通常需要先进行标准化 X_scaled = StandardScaler().fit_transform(X)  # eps: 邻域半径, min_samples: 形成核心点所需的最小样本数 dbscan = DBSCAN(eps=0.3, min_samples=5) clusters = dbscan.fit_predict(X_scaled)  plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=clusters, s=50, cmap='viridis') plt.title('DBSCAN Clustering') plt.xlabel('Scaled Feature 1') plt.ylabel('Scaled Feature 2') plt.show() print("DBSCAN发现的簇标签:", np.unique(clusters)) # -1表示噪声点

2. 层次聚类 (Hierarchical Clustering / Agglomerative Clustering)

层次聚类构建了一个簇的层次结构。它可以是凝聚的(Agglomerative,从单个点开始,逐步合并最近的簇)或分裂的(Divisive,从一个大簇开始,逐步分裂)。最常用的是凝聚层次聚类。它不需要预设K值,你可以通过“剪切”树状图(dendrogram)来选择簇的数量。

  • 优点: 不需要预设K值;可以生成树状图,便于理解簇的嵌套关系;能处理非球形簇。
  • 缺点: 计算复杂度高(尤其对于大数据集);对噪声和异常值敏感。
from sklearn.cluster import AgglomerativeClustering from scipy.cluster.hierarchy import dendrogram, linkage  # 生成一些新的数据,以便更好地展示层次聚类 X_hier, _ = make_blobs(n_samples=50, centers=3, cluster_std=0.8, random_state=42)  # 凝聚层次聚类 # n_clusters=None 表示不预设簇的数量,可以后续通过linkage矩阵和dendrogram来确定 # linkage='ward' 是一种合并策略,尝试最小化合并后簇内方差的增加 agg_cluster = AgglomerativeClustering(n_clusters=3, linkage='ward') agg_labels = agg_cluster.fit_predict(X_hier)  plt.scatter(X_hier[:, 0], X_hier[:, 1], c=agg_labels, s=50, cmap='viridis') plt.title('Agglomerative Clustering') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.show()  # 绘制树状图(需要scipy库) linked = linkage(X_hier, method='ward') plt.figure(figsize=(10, 7)) dendrogram(linked,            orientation='top',            distance_sort='descending',            show_leaf_counts=True) plt.title('Dendrogram for Agglomerative Clustering') plt.xlabel('Sample Index') plt.ylabel('Distance') plt.show()

3. 高斯混合模型 (Gaussian Mixture Models, GMM)

GMM是一种概率模型,它假设数据点是由若干个高斯分布(正态分布)混合生成的。每个簇对应一个高斯分布。GMM不仅能给出每个点属于哪个簇,还能给出属于每个簇的概率,这比K-means的硬性划分要更灵活。

  • 优点: 概率性聚类,提供每个点属于每个簇的概率;能处理非球形、大小不一的簇;对噪声相对不敏感。
  • 缺点: 需要预设高斯分布的数量(类似于K值);对数据量较小或维度较高时可能不稳定;计算成本相对较高。
from sklearn.mixture import GaussianMixture  # n_components 对应于簇的数量 gmm = GaussianMixture(n_components=4, random_state=0) gmm.fit(X) gmm_labels = gmm.predict(X)  plt.scatter(X[:, 0], X[:, 1], c=gmm_labels, s=50, cmap='viridis') plt.title('Gaussian Mixture Model Clustering') plt.xlabel('Feature 1') plt.ylabel('Feature 2') plt.show()  # 还可以获取每个样本属于每个簇的概率 # probabilities = gmm.predict_proba(X) # print("部分样本属于各簇的概率:n", probabilities[:5])

选择哪种聚类算法,最终还是得看你的数据特点和具体需求。没有绝对的“最好”,只有最适合。我通常会先用K-means快速跑一下看看大概情况,如果效果不理想,再尝试DBSCAN或GMM。毕竟,数据科学很多时候就是一场不断尝试和优化的过程。



评论(已关闭)

评论已关闭