K-Means算法实战:从原理到Python实现与调优
1. K-Means算法原理从牧师-村民模型说起想象一个偏远山村有四位牧师在布道。最初他们随机选择地点设立讲坛村民们会前往最近的讲坛听讲。每次布道结束后牧师们会统计所有听众的居住位置将讲坛移动到这些位置的中心点。随着时间推移牧师们不断调整位置村民们也随之选择最近的讲坛最终形成稳定的分布——这就是K-Means最经典的牧师-村民比喻。算法数学本质将n个数据点划分为k个簇使得每个点到其所属簇中心的距离平方和最小。目标函数表示为import numpy as np def compute_sse(data, centroids, labels): 计算误差平方和(SSE) return sum(np.linalg.norm(data[i]-centroids[labels[i]])**2 for i in range(len(data)))关键计算步骤随机选择k个点作为初始质心将每个点分配到最近的质心形成簇重新计算每个簇的质心均值点重复2-3步直到质心不再变化注意算法可能收敛到局部最优解实践中需要多次运行选择最佳结果2. Python实现详解从零编写K-Means让我们用Python实现一个完整的K-Means流程使用西瓜数据集作为示例import numpy as np import matplotlib.pyplot as plt def load_watermelon(): 加载西瓜数据集 return np.array([ [0.697, 0.460], [0.774, 0.376], [0.634, 0.264], [0.608, 0.318], [0.556, 0.215], [0.403, 0.237], [0.481, 0.149], [0.437, 0.211], [0.666, 0.091], [0.243, 0.267], [0.245, 0.057], [0.343, 0.099], [0.639, 0.161], [0.657, 0.198], [0.360, 0.370], [0.593, 0.042], [0.719, 0.103], [0.359, 0.188], [0.339, 0.241], [0.282, 0.257], [0.748, 0.232], [0.714, 0.346], [0.483, 0.312], [0.478, 0.437], [0.525, 0.369], [0.751, 0.489], [0.532, 0.472], [0.473, 0.376], [0.725, 0.445], [0.446, 0.459] ]) def k_means(data, k3, max_iter100): K-Means核心算法 # 随机初始化质心 centroids data[np.random.choice(len(data), k, replaceFalse)] for _ in range(max_iter): # 计算距离矩阵 distances np.linalg.norm(data[:, np.newaxis] - centroids, axis2) # 分配簇标签 labels np.argmin(distances, axis1) # 更新质心 new_centroids np.array([data[labelsi].mean(axis0) for i in range(k)]) # 检查收敛 if np.allclose(centroids, new_centroids): break centroids new_centroids return centroids, labels # 运行示例 data load_watermelon() centroids, labels k_means(data)可视化聚类结果plt.scatter(data[:, 0], data[:, 1], clabels, cmapviridis) plt.scatter(centroids[:, 0], centroids[:, 1], marker*, s300, cred) plt.xlabel(密度) plt.ylabel(含糖率) plt.title(西瓜数据集K-Means聚类结果) plt.show()3. 关键调优技巧如何选择最佳K值手肘法实战通过观察SSE随K值变化的拐点确定最佳簇数def find_optimal_k(data, max_k10): sse [] for k in range(1, max_k1): centroids, labels k_means(data, k) sse.append(compute_sse(data, centroids, labels)) plt.plot(range(1, max_k1), sse, bo-) plt.xlabel(K值) plt.ylabel(SSE) plt.title(手肘法选择K值) plt.show() find_optimal_k(data)轮廓系数法结合簇内凝聚度和簇间分离度评估聚类质量from sklearn.metrics import silhouette_score def silhouette_analysis(data, max_k10): scores [] for k in range(2, max_k1): _, labels k_means(data, k) scores.append(silhouette_score(data, labels)) plt.plot(range(2, max_k1), scores, ro-) plt.xlabel(K值) plt.ylabel(轮廓系数) plt.title(轮廓系数分析) plt.show() silhouette_analysis(data)4. 工程实践中的常见陷阱与解决方案问题1初始质心敏感现象不同随机种子导致完全不同的聚类结果解决方案采用K-Means初始化def kmeans_plus_plus_init(data, k): K-Means初始化 centroids [data[np.random.randint(len(data))]] for _ in range(1, k): distances np.min([np.linalg.norm(data - c, axis1)**2 for c in centroids], axis0) prob distances / distances.sum() centroids.append(data[np.argmax(prob)]) return np.array(centroids)问题2异常值影响现象少数离群点导致质心偏移解决方案使用中位数代替均值或先进行异常值检测问题3数据尺度不统一现象量纲大的特征主导距离计算解决方案数据标准化from sklearn.preprocessing import StandardScaler scaler StandardScaler() scaled_data scaler.fit_transform(data)问题4非凸形状簇现象对非球形分布数据效果差解决方案考虑谱聚类或DBSCAN等其他算法5. 高级优化技巧提升算法性能加速技巧1Elkan算法利用三角不等式避免不必要的距离计算适合高维数据加速技巧2Mini-Batch K-Means对大数据集使用随机子样本进行迭代from sklearn.cluster import MiniBatchKMeans mbk MiniBatchKMeans(n_clusters3, batch_size100) mbk.fit(data)特征工程技巧对于分类特征使用One-Hot编码对于文本数据先用TF-IDF向量化并行化实现from joblib import Parallel, delayed def parallel_kmeans(data, k, n_init10): results Parallel(n_jobs-1)( delayed(k_means)(data, k) for _ in range(n_init) ) # 选择SSE最小的结果 best_idx np.argmin([compute_sse(data, c, l) for c, l in results]) return results[best_idx]6. 实战案例客户分群完整流程假设我们有一个电商用户数据集包含以下特征最近购买时间(R)购买频率(F)消费金额(M)完整分析流程数据清洗处理缺失值、异常值特征工程对数变换金额特征标准化使各特征具有相同尺度确定K值结合业务理解和分析方法模型训练使用优化后的K-Means结果分析绘制雷达图展示各簇特征def analyze_clusters(df, features, n_clusters4): # 预处理 df[log_M] np.log1p(df[M]) scaler StandardScaler() X scaler.fit_transform(df[features]) # 聚类 kmeans KMeans(n_clustersn_clusters, initk-means) df[cluster] kmeans.fit_predict(X) # 分析 cluster_profile df.groupby(cluster)[features].mean() # 可视化 angles np.linspace(0, 2*np.pi, len(features), endpointFalse) fig plt.figure(figsize(8, 8)) ax fig.add_subplot(111, polarTrue) for i in range(n_clusters): values cluster_profile.loc[i].values.tolist() values values[:1] # 闭合图形 ax.plot(angles, values, linewidth2, labelfCluster {i}) ax.set_xticks(angles) ax.set_xticklabels(features) plt.legend() plt.show()7. 算法变体与扩展应用K-Means改进的初始化方法使初始质心尽可能分散二分K-Means自上而下分裂簇直到达到指定K值K-Medoids使用实际数据点作为中心对异常值更鲁棒应用场景扩展图像压缩将颜色空间聚类为K种代表色文档聚类结合TF-IDF特征分析文本主题异常检测远离所有簇中心的点视为异常# 图像压缩示例 from sklearn.cluster import KMeans from PIL import Image def compress_image(image_path, n_colors16): img Image.open(image_path) pixels np.array(img).reshape(-1, 3) kmeans KMeans(n_clustersn_colors).fit(pixels) new_pixels kmeans.cluster_centers_[kmeans.labels_] new_img Image.fromarray(new_pixels.reshape(img.size[1], img.size[0], 3).astype(uint8)) return new_img

相关新闻

最新新闻

日新闻

周新闻

月新闻