news 2026/9/6 15:48:15

ML-For-Beginners 中的 K-Means 聚类实战:用 Scikit-learn 对尼日利亚音乐数据做分簇、选 k 与质量评估

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ML-For-Beginners 中的 K-Means 聚类实战:用 Scikit-learn 对尼日利亚音乐数据做分簇、选 k 与质量评估

ML-For-Beginners 中的 K-Means 聚类实战:用 Scikit-learn 对尼日利亚音乐数据做分簇、选 k 与质量评估

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

本篇指南基于 ML-For-Beginners 仓库《Clustering》系列的第 2 课,完整讲解如何用 Scikit-learn 的 K-Means 算法对 Spotify 抓取的尼日利亚歌曲数据集做聚类:从数据预处理、特征编码,到用肘部法(Elbow Method)确定簇数 k、用轮廓系数(Silhouette Score)与 WCSS/Inertia 评估簇质量,再到可视化簇结果并分析"方差问题"。读完后,你将掌握一套完整的 K-Means 建模流程,并能判断一份数据是否适合用 K-Means 做聚类、以及该如何改进。

核心概念:K-Means 是什么,它如何工作

K-Means Clustering 是一种源自信号处理领域的经典聚类方法,用于将数据划分为 k 个簇。它的核心思想是:每个数据点都会被归入距离它最近的"均值点"(即簇中心/质心)。簇的空间结构可以可视化为 Voronoi 图——每个质心(种子点)与其对应的所属区域构成一块"领地",如上图所示。

K-Means 的执行过程是一个三步循环:

  1. 算法先从数据集中采样选出 k 个初始中心点,随后进入迭代循环;
  2. 循环中:将每个样本分配给最近的质心;
  3. 再计算每个簇内所有样本的均值,生成新的质心;然后比较新旧质心的差异,重复迭代直到质心收敛稳定。

K-Means 的主要缺点是:你必须事先指定 k(质心/簇的数量)。幸运的是,"肘部法"(Elbow Method)可以帮助你估计一个合适的 k 的起始值——这正是本课实验部分要动手做的事情。

本课涉及的核心术语包括:

  • Silhouette scoring(轮廓系数):衡量簇内紧密程度与簇间分离程度的指标,取值 -1 到 1;
  • Elbow method(肘部法):通过 WCSS 曲线上的"拐点"估计最优簇数;
  • Inertia(惯性):簇内所有样本到其质心距离平方和的度量,反映簇的内部一致性;
  • Variance(方差):数据点相对均值的离散程度,方差过大是 K-Means 效果差的一类典型问题。

数据与前置准备

本课程的实现在 notebook.ipynb 中,完整参考答案见 solution/notebook.ipynb。数据集是上一课(Introduction to clustering)中导入并清洗过的尼日利亚歌曲数据 nigerian-songs.csv——一份从 Spotify 抓取的数据,共 530 行、16 列,包含popularitydanceabilityacousticnessenergyloudnesstempo等 Spotify 音频特征,以及artist_top_genre(艺人主打流派)等文本列。

上一课的过滤逻辑在 notebook 开头复现,只保留三个流派并去掉无热度打分的歌曲:

import matplotlib.pyplot as plt import pandas as pd import seaborn as sns df = pd.read_csv("../data/nigerian-songs.csv") # 只聚焦 3 个流派,期望能聚出 3 个簇 df = df[(df['artist_top_genre'] == 'afro dancehall') | (df['artist_top_genre'] == 'afropop') | (df['artist_top_genre'] == 'nigerian pop')] df = df[(df['popularity'] > 0)]

按仓库中的实际数据核对:原始 530 行经过上述两步过滤后剩 286 行,其中 afro dancehall 206 首、afropop 61 首、nigerian pop 19 首——三种类别本身就不均衡,这一点会在后面的"方差问题"中再次体现。

用箱线图检查离群值

在建模前先"体检":对每一列画箱线图(boxplot),观察数据范围与离群点:

plt.figure(figsize=(20,20), dpi=200) plt.subplot(4,3,1) sns.boxplot(x = 'popularity', data = df) plt.subplot(4,3,2) sns.boxplot(x = 'acousticness', data = df) plt.subplot(4,3,3) sns.boxplot(x = 'energy', data = df) plt.subplot(4,3,4) sns.boxplot(x = 'instrumentalness', data = df) plt.subplot(4,3,5) sns.boxplot(x = 'liveness', data = df) plt.subplot(4,3,6) sns.boxplot(x = 'loudness', data = df) plt.subplot(4,3,7) sns.boxplot(x = 'speechiness', data = df) plt.subplot(4,3,8) sns.boxplot(x = 'tempo', data = df) plt.subplot(4,3,9) sns.boxplot(x = 'time_signature', data = df) plt.subplot(4,3,10) sns.boxplot(x = 'danceability', data = df) plt.subplot(4,3,11) sns.boxplot(x = 'length', data = df) plt.subplot(4,3,12) sns.boxplot(x = 'release_date', data = df)

从参考 notebook 的标注可以看到,这份数据"有点吵"(noisy):逐列观察箱线图能明显看到离群值。逐个手工剔除离群值会让数据变得过于稀疏,因此课程选择了更务实的策略——挑选范围相近的列来做聚类。

选列与类别编码

K-Means 只接受数值特征,而artist_top_genre是文本列。做法是:选出范围量级相近的 6 列,并用LabelEncoder把流派编码成数字,同时把它作为"参照标签"保留下来用于最后的准确性对比:

from sklearn.preprocessing import LabelEncoder le = LabelEncoder() X = df.loc[:, ('artist_top_genre','popularity','danceability','acousticness','loudness','energy')] y = df['artist_top_genre'] X['artist_top_genre'] = le.fit_transform(X['artist_top_genre']) y = le.transform(y)

注意这里的选择逻辑:danceabilityacousticnessenergyloudness都是 Spotify 返回的 0~1(或相近量级)特征,popularity是 0~100 的整数,量级相对接近,适合作为 K-Means 的输入;而length(毫秒,十万量级)、tempo(几十到两百)这类大范围列如果混入,会主导距离计算。答案 notebook 中还额外导入了StandardScaler并对它做了注释处理(见后文"方差与改进"一节)。

第一次建模:KMeans 与 predict

既然数据里"雕刻"出了 3 个流派,先直接让 K-Means 聚 3 个簇试试:

from sklearn.cluster import KMeans nclusters = 3 seed = 0 km = KMeans(n_clusters=nclusters, random_state=seed) km.fit(X) # 为每个数据点预测所属簇 y_cluster_kmeans = km.predict(X) y_cluster_kmeans

运行后你会得到一个数组,每个元素是 0、1 或 2,即 DataFrame 每一行被预测到的簇编号。random_state参数"决定质心初始化时的随机数生成"——固定种子可以让每次运行得到一致的初始质心,保证实验可复现;而初始化方式(默认init='k-means++')则会让初始质心彼此尽量远离,通常优于纯随机初始化(下一节会显式指定它)。

用轮廓系数(Silhouette Score)评估簇质量

拿到预测标签后,先计算一次轮廓系数:

from sklearn import metrics score = metrics.silhouette_score(X, y_cluster_kmeans) score

如何解读这个分数:

  • 轮廓系数取值范围是-1 到 1,越接近 1 越好:得分为 1 表示簇既紧密(intra-cluster 距离小)又与其他簇充分分离;
  • 得分接近 0 表示簇相互重叠,样本落在相邻簇的决策边界附近;
  • 得分为负值则意味着样本可能被分到了错误的簇。

本例的得分是0.53,恰好处于中间地带。这说明这份数据并不是特别适合 K-Means 这类基于"紧凑球状簇"假设的方法——但课程并没有就此止步,而是继续走完整个评估流程,看看问题究竟出在哪里。

选择最优簇数:WCSS、Inertia 与肘部法

K-Means 必须预先给定 k,那么"3"真的是最优解吗?课程的做法是遍历 k = 1~10,记录每次的 WCSS(Within-Cluster Sum of Squares,簇内平方和):

from sklearn.cluster import KMeans wcss = [] for i in range(1, 11): kmeans = KMeans(n_clusters = i, init = 'k-means++', random_state = 42) kmeans.fit(X) wcss.append(kmeans.inertia_)

这段代码里有几个值得展开的要点:

  • range(1, 11):聚类过程的迭代扫描,依次试验 1 到 10 个簇;
  • random_state:固定随机种子,决定质心初始化时的随机数生成,保证结果可复现;
  • init='k-means++':Scikit-learn 提供的优化初始化策略,其原理是"初始化时让质心彼此(大体上)远离",从而通常比随机初始化得到更好的收敛结果;
  • WCSS / Inertiainertia_是 K-Means 的目标函数值——簇内所有样本到其质心距离的平方和,衡量"簇的内部一致性"。K-Means 算法在迭代中不断调整质心以最小化 inertia,每轮迭代后把该值追加进wcss列表。

k 越大,inertia 必然单调下降(极端情况下 k = n 时每个点自成一簇,inertia 为 0)。肘部法(Elbow Method)就是利用这一曲线寻找"性价比拐点":下降速度从陡峭转为平缓的那个位置,就是增加簇数不再带来显著收益的 k。

plt.figure(figsize=(10,5)) sns.lineplot(x=range(1, 11), y=wcss, marker='o', color='red') plt.title('Elbow') plt.xlabel('Number of clusters') plt.ylabel('WCSS') plt.show()

观察图中曲线的"折弯"(bend)位置即可判断最优簇数。在这个数据集上,拐点确实落在了 3 附近——也就是说,凭业务直觉猜测的"3 个流派 = 3 个簇"被肘部法验证了。

显示簇并检查模型准确性

确定 k=3 后,重新拟合模型并把簇以散点图显示出来:

from sklearn.cluster import KMeans kmeans = KMeans(n_clusters = 3) kmeans.fit(X) labels = kmeans.predict(X) plt.scatter(df['popularity'], df['danceability'], c = labels) plt.xlabel('popularity') plt.ylabel('danceability') plt.show()

由于数据集中恰好带有流派标签(artist_top_genre),可以把它当作"准监督信号"来核对聚类的准确性——这只是探索性验证,生产环境中无标签数据是没有这种捷径的:

labels = kmeans.labels_ correct_labels = sum(y == labels) print("Result: %d out of %d samples were correctly labeled." % (correct_labels, y.size)) print('Accuracy score: {0:0.2f}'. format(correct_labels/float(y.size)))

结果并不理想。簇的形状给出了线索:三个色块彼此贴得很近、边界模糊。课程对此的总结是——这份数据过于不均衡、列与列之间相关性太弱、各列取值之间的方差又太大,因此不适合直接聚类。而且形成的簇很可能被上面定义的那三个流派类别本身所主导(artist_top_genre既是特征又是对照标签,聚类结果自然会向它靠拢)。

Scikit-learn 的官方文档用一张"问题模型"示意图归纳了聚类数据的常见病灶,像本例这种"簇边界很不清晰"的情况,就属于其中的variance(方差)问题

理解方差问题与改进方向

方差(Variance)定义为"所有数据点与均值之差的平方的平均值"。放到本聚类问题中,它指的是:数据集的数值相对各自均值离散得过于厉害——各特征列的分布形态、量级、离群情况差异明显,距离度量被少数大方差维度主导,K-Means 就很难切出干净的簇。

课程在此给出的改进思路(也是本课 Challenge 的内容):

  1. 进一步清洗数据:比如用箱线图定位到的离群值做处理;
  2. 更换特征列:挑选相关性更强、量级更接近的列组合;
  3. 换算法:数据形状明显不符合"紧凑球状簇"假设时,可考虑密度型、层次型等其他聚类方法(对应本课程的 Assignment:用非 K-Means 的聚类方法重新做一遍并记录结论);
  4. 特征缩放(feature scaling):这是最关键的提示。答案 notebook 里保留了一段被注释掉的StandardScaler代码:
from sklearn.preprocessing import LabelEncoder, StandardScaler le = LabelEncoder() # scaler = StandardScaler() ... # X = scaler.fit_transform(X)

取消注释即可对 X 做标准化缩放,让各列在范围上更趋一致。课程特别指出一个反直觉现象:缩放之后,轮廓系数会下降,但肘部图中的"拐点"反而变得更平滑清晰。其原因是:不缩放的数据会让方差小的特征在距离计算中"权重偏大"、方差大的特征主导一切,两者都会扭曲簇的形状;标准化之后各维度的贡献趋于均衡,WCSS 曲线也就更能如实反映簇结构的转折点。

小结与延伸

把本课的完整链路串起来,就是一套可复用的 K-Means 工作流:

  1. 探索:箱线图体检各列的范围与离群值(sns.boxplot);
  2. 预处理:筛选量级相近的数值列,LabelEncoder编码类别列(并留一份作对照标签);
  3. 初训KMeans(n_clusters=k, random_state=seed)拟合后predict得到簇标签;
  4. 评估metrics.silhouette_score快速打分,本例 0.53 提示数据与 K-Means 的适配度一般;
  5. 选 kk-means++初始化下遍历 k 记录inertia_(WCSS),画肘部图找拐点,本例验证了 k=3;
  6. 诊断:散点图 + 对照标签核对准确性,识别出"方差问题",用StandardScaler等手段迭代改进。

延伸阅读方面,课程建议:使用交互式 K-Means 模拟器(可自行调整数据随机性、簇数与质心数)直观感受质心如何迭代收敛;再配合 Stanford 提供的 K-Means 讲义材料加深算法推导层面的理解。

最后要强调一点本课最重要的收获——"聚类效果不好"本身也是有价值的结论。通过轮廓系数、肘部图和簇形状,我们定位到数据不均衡、相关性弱、方差大这三个具体原因,并给出了缩放、清洗、换列、换算法四条可验证的改进路径。这正是 ML-For-Beginners 这门课想传递的方法论:无监督学习没有标准答案,但有一套严谨的"建模—评估—诊断—改进"闭环。

【免费下载链接】ML-For-Beginners12 weeks, 26 lessons, 52 quizzes, classic Machine Learning for all项目地址: https://gitcode.com/GitHub_Trending/ml/ML-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 15:47:32

Buzz离线语音转文字:免费本地转录,十分钟上手指南

Buzz离线语音转文字:免费本地转录,十分钟上手指南 【免费下载链接】buzz Buzz transcribes and translates audio offline on your personal computer. Powered by OpenAIs Whisper. 项目地址: https://gitcode.com/GitHub_Trending/buz/buzz 你手…

作者头像 李华
网站建设 2026/9/6 15:44:15

5G终端白皮书解读:从频段、功耗到体验的关键门槛

简介:中国移动5G终端白皮书是一份面向通信行业管理者、终端产业链从业者及研究者的权威参考文档,系统梳理了从4G到5G的演进脉络,并围绕底层技术、终端产品、业务模式、ToB/ToC市场机遇与产业规模普及等维度展开解读。文件包内含1个PDF文档&am…

作者头像 李华
网站建设 2026/9/6 15:40:33

基于卷积神经网络的恶意URL检测:从数据预处理到上线部署全实践

简介:针对恶意URL检测场景,这份PDF资源面向网络安全研究人员、算法工程师及深度学习入门者,系统介绍了基于卷积神经网络(CNN)的检测模型如何替代传统机器学习中繁琐的特征提取过程。资料源自2018年《通信技术》期刊论文…

作者头像 李华
网站建设 2026/9/6 15:36:06

Coze Studio 监控实操:4 个核心指标看懂智能体运行状态

Coze Studio 监控实操:4 个核心指标看懂智能体运行状态 【免费下载链接】coze-studio An AI agent development platform with all-in-one visual tools, simplifying agent creation, debugging, and deployment like never before. Coze your way to AI Agent cre…

作者头像 李华
网站建设 2026/9/6 15:36:02

全国大学生数学竞赛备考全攻略:真题拆解与高效复习方法

简介:《全国大学生数学竞赛试题宝典》是一本面向全国大学生数学竞赛(CMC)备赛者、高校理工科学生及数学爱好者的综合试题集,覆盖数论、代数、几何、微积分以及跨学科综合题等核心模块,既可帮助读者系统补强数学基础&am…

作者头像 李华