简介:本资源是一套完整的基于K均值聚类的音乐数据可视化分析系统,专为计算机类专业本科生课程设计、毕业设计及大作业开发,适用于计科、人工智能、数据科学等方向的学习者与教师。系统以Python实现核心算法,融合情感词典分析、音乐风格聚类与多维度可视化功能,支持对经典老歌、网络热歌、民谣、伤感情歌等类别数据进行特征提取与分组呈现。压缩包共48个文件,含4个关键Python源码(clustering.py、main.py等)、2个CSV数据集、10张风格分类PNG图表、16个文本资源(含中英文情感词库、数据预处理日志)、2个MP4录屏演示及完整IDE配置文件,整体大小22.52MB。已有181人学习下载,提供可稳定运行的全链路代码、详细资料文档与实操过程录屏,兼顾入门理解与二次开发拓展,是掌握机器学习聚类应用与数据可视化实践的优质教学案例。
1. 用 K 均值聚类给音乐“贴标签”,不是做推荐系统,而是让数据自己说话
你有没有试过把几十首歌的歌词、节奏、情绪词频扔进 Excel,想看出“伤感情歌”和“网络热歌”在数据上到底差在哪?手动标颜色、拉散点图、反复调整坐标轴——最后只得到一张谁也看不懂的混乱图表。这个课程设计项目不走常规路:它不预测你爱听什么,而是用 K 均值聚类(K-Means)强行让音乐数据自我分组,再用 Matplotlib + Seaborn 把分组结果“画”成可解释的二维投影图。核心逻辑很硬核:把每首歌抽象为一个高维向量(词频 TF-IDF + 节奏特征 + 情绪得分),K 均值在向量空间里找中心点,聚类结果直接对应“民谣”“伤感情歌”“流行”等真实标签。它不是黑盒模型,而是可追溯、可验证、可调试的数据探针——适合计科、大数据、人工智能方向的学生跑通从原始文本到可视化结论的完整链路,尤其适合作为课程设计答辩时能讲清每一步“为什么这么算”的硬核案例。
2. K 均值聚类不是调个sklearn.cluster.KMeans就完事:特征工程决定聚类质量上限
K 均值对输入特征极其敏感。直接拿原始歌词字符串喂给算法,结果只会是随机噪声。本项目真正拉开差距的地方,在于三层特征构建:文本语义层、音乐属性层、情感极性层。这三者必须融合为统一向量,才能让聚类中心具备业务可解释性。
2.1 文本语义特征:TF-IDF 不是终点,而是起点
项目中music_words.txt和Txt/目录下的正负面评价词表不是摆设。clustering.py中实际采用的是加权 TF-IDF:
- 先用
jieba(或英文场景下nltk.word_tokenize)对all_list.txt中每首歌的歌词分词; - 过滤停用词后,统计每个词在单首歌内的出现频次(TF);
- 关键在 IDF 计算:不是基于全部歌词库,而是仅基于
正面评价词语(英文).txt和负面评价词语(英文).txt构建的极性词典。这意味着“heartbreak”“lonely”这类词的 IDF 值被人为抬高,而通用高频词如“the”“and”被抑制。
# clustering.py 片段(已还原关键逻辑) from sklearn.feature_extraction.text import TfidfVectorizer import jieba # 加载极性词典(仅保留有情感倾向的词) with open("Txt/正面评价词语(英文).txt", "r", encoding="utf-8") as f: pos_words = set(line.strip() for line in f) with open("Txt/负面评价词语(英文).txt", "r", encoding="utf-8") as f: neg_words = set(line.strip() for line in f) polar_words = pos_words | neg_words # 合并为极性词集合 # 自定义 tokenizer:只保留极性词 + 长度 > 2 的词 def custom_tokenizer(text): words = jieba.lcut(text.lower()) return [w for w in words if w in polar_words and len(w) > 2] vectorizer = TfidfVectorizer( tokenizer=custom_tokenizer, max_features=500, # 限制维度,避免稀疏度过高 ngram_range=(1, 2) # 加入二元词组,捕获"broken heart"等组合 ) tfidf_matrix = vectorizer.fit_transform(lyrics_list)提示:
max_features=500是经过实测的平衡点。设太高(如 2000)会导致向量极度稀疏,K 均值中心点漂移;设太低(如 100)则丢失关键区分词。你可以在main.py中修改此参数后观察silhouette_score变化。
2.2 音乐属性与情感极性特征:结构化数据必须归一化
data.csv文件包含每首歌的结构化字段:BPM(节拍速度)、Key(调性)、Duration(时长)、Energy(能量值)、Valence(积极度)。这些数值型特征不能直接拼接进 TF-IDF 矩阵——它们的量纲(BPM 单位是 BPM,Duration 是秒)和分布范围(Energy 0~1,BPM 60~180)差异巨大。clustering.py中使用StandardScaler进行标准化:
from sklearn.preprocessing import StandardScaler import pandas as pd # 读取 data.csv 并提取数值列 df_data = pd.read_csv("data.csv") numeric_features = df_data[["BPM", "Duration", "Energy", "Valence"]].values # 标准化:(x - mean) / std scaler = StandardScaler() scaled_numeric = scaler.fit_transform(numeric_features) # 将 TF-IDF 矩阵(稀疏)与标准化数值(稠密)拼接 from scipy.sparse import hstack combined_features = hstack([tfidf_matrix, scaled_numeric])表:data.csv关键字段说明与业务含义
| 字段名 | 数据类型 | 典型值范围 | 业务含义 | 聚类中作用 |
|---|---|---|---|---|
| BPM | float | 60–180 | 每分钟节拍数,反映歌曲节奏快慢 | 区分“民谣”(70–90)与“网络热歌”(100–130) |
| Energy | float | 0.0–1.0 | 声音强度与活跃度,基于频谱分析 | “伤感情歌”通常 <0.4,“流行”常 >0.6 |
| Valence | float | 0.0–1.0 | 歌曲传达的积极/消极情绪程度 | 直接对应“正面/负面评价词表”的语义锚点 |
| Key | string | C, D#, Gm 等 | 调性,影响听感色彩 | 项目中未直接使用,但可扩展为 one-hot 编码特征 |
2.3 为什么 K=4?用肘部法则和轮廓系数双重验证
项目默认K=4(对应“民谣”“伤感情歌”“流行”“网络热歌”四类),但这不是拍脑袋定的。main.py中内置了肘部法则(Elbow Method)和平均轮廓系数(Silhouette Score)计算:
from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score import matplotlib.pyplot as plt inertias = [] sil_scores = [] K_range = range(2, 8) for k in K_range: kmeans = KMeans(n_clusters=k, random_state=42, n_init=10) kmeans.fit(combined_features) inertias.append(kmeans.inertia_) sil_scores.append(silhouette_score(combined_features, kmeans.labels_)) # 绘制双指标曲线 plt.figure(figsize=(12, 4)) plt.subplot(1, 2, 1) plt.plot(K_range, inertias, 'bo-') plt.xlabel('K') plt.ylabel('Inertia') plt.title('Elbow Method') plt.subplot(1, 2, 2) plt.plot(K_range, sil_scores, 'ro-') plt.xlabel('K') plt.ylabel('Silhouette Score') plt.title('Silhouette Analysis') plt.show()注意:运行此代码前需确保
matplotlib已安装(pip install matplotlib)。若报错ModuleNotFoundError: No module named 'matplotlib',请先执行pip install matplotlib。这是 Python 安装环节最常被忽略的依赖之一——很多同学卡在第一步,就是因为没装绘图库。
3. 可视化不是“画图”,而是用降维技术让高维聚类结果可解释
K 均值输出的是 500+ 维空间中的簇标签,人眼无法理解。main.py的核心价值在于用t-SNE 降维 + Matplotlib 着色,把抽象的数学分组转化为肉眼可辨的地理分布。这不是简单调用plt.scatter(),而是严格遵循“先降维、再着色、后标注”的三步铁律。
3.1 t-SNE 降维:为什么不用 PCA?
PCA 会压缩方差最大的方向,但音乐特征中“情感极性”和“节奏”可能方差相近,PCA 会把它们混在一起。t-SNE 则专注于保持局部邻域关系:如果两首歌在高维空间中词频和 BPM 都接近,t-SNE 保证它们在二维图上也挨得很近。main.py中关键参数设置如下:
from sklearn.manifold import TSNE # t-SNE 参数详解(非默认值!) tsne = TSNE( n_components=2, # 必须为2,用于绘图 perplexity=30, # 邻居数量:30 表示每个点关注约30个最近邻 learning_rate=200, # 学习率:200 是经验最优值,太小收敛慢,太大飞散 n_iter=1000, # 迭代次数:1000 保证充分优化 random_state=42 # 固定随机种子,确保每次结果一致 ) tsne_result = tsne.fit_transform(combined_features.toarray()) # 注意:转为稠密数组提示:
combined_features是稀疏矩阵,t-SNE 要求稠密输入,故调用.toarray()。若内存不足(如数据量 >1000 首),可改用TruncatedSVD先降维到 50 维再进 t-SNE。
3.2 可视化实现:用不同形状+颜色区分真实标签与聚类标签
main.py最终生成的图(如经典老歌.png)包含两层信息:
- 背景散点:按 K 均值聚类结果着色(红/蓝/绿/紫),代表算法认为的“相似歌曲群”;
- 边缘标记:用三角形(△)、方形(□)、圆形(○)等符号标注每首歌的真实流派标签(来自
data.csv的Genre列),直观显示聚类准确率。
import matplotlib.pyplot as plt # 假设 labels_true 来自 data.csv 的 Genre 列,labels_pred 来自 KMeans plt.figure(figsize=(10, 8)) scatter = plt.scatter( tsne_result[:, 0], tsne_result[:, 1], c=labels_pred, cmap='tab10', alpha=0.7, s=50 ) # 添加真实标签符号(关键!) for i, (x, y) in enumerate(tsne_result): if labels_true[i] == "民谣": marker = "^" # 三角形 elif labels_true[i] == "伤感情歌": marker = "s" # 方形 elif labels_true[i] == "流行": marker = "o" # 圆形 else: marker = "D" # 菱形 plt.scatter(x, y, marker=marker, c='black', s=80, edgecolors='white', linewidth=1.5) plt.colorbar(scatter, label="K-Means Cluster") plt.title("t-SNE Visualization of Music Clusters") plt.xlabel("t-SNE Dimension 1") plt.ylabel("t-SNE Dimension 2") plt.savefig("music_clusters_tsne.png", dpi=300, bbox_inches='tight') plt.show()表:聚类结果与真实标签匹配度速查(基于data.csv示例数据)
| K 均值簇ID | 主要包含真实标签 | 典型特征(TF-IDF + 数值) | 常见误分歌曲 |
|---|---|---|---|
| Cluster 0 | 民谣 | 高“acoustic”“guitar”词频,BPM 70–85,Energy <0.3 | 少量轻快民谣被分入流行簇 |
| Cluster 1 | 伤感情歌 | 高“tears”“lonely”词频,Valence <0.2,BPM 65–75 | 部分“伤感+快节奏”电子情歌误入网络热歌簇 |
| Cluster 2 | 流行 | 高“dance”“beat”词频,Energy >0.6,BPM 110–125 | 少量抒情流行被分入伤感情歌簇 |
| Cluster 3 | 网络热歌 | 高“viral”“trend”词频,Duration <200 秒,Valence 0.4–0.6 | 无显著误分,因该类特征最鲜明 |
4. 从课程设计到可交付成果:绕过 Windows 路径中文坑、快速验证聚类效果
解压后路径含中文(如D:\我的毕设\音乐分析系统\)是本项目第一大拦路虎。Python 在 Windows 下读取data.csv或Txt/正面评价词语(英文).txt时,会因编码和路径解析失败直接报FileNotFoundError或UnicodeDecodeError。这不是代码 bug,而是环境配置问题——必须用最简方式破局。
4.1 三步解决中文路径问题(Windows 用户必做)
- 重命名项目文件夹为纯英文:将
基于k均值聚类的音乐数据可视化分析系统python源码+完整资料+录屏演示(课程设计)改为music_kmeans_analysis; - 用命令行进入该目录,执行
pip install -r requirement.txt:requirement.txt明确列出numpy==1.24.3,scikit-learn==1.3.0,matplotlib==3.7.1,jieba==0.42.1,版本锁定可避免兼容性冲突; - 运行前检查工作目录:在
main.py开头插入强制路径切换代码,确保所有open()操作基于当前脚本位置:
import os # 强制将工作目录设为 main.py 所在目录(解决相对路径失效) os.chdir(os.path.dirname(os.path.abspath(__file__))) print(f"Current working directory: {os.getcwd()}") # 调试用,确认路径正确提示:若
pip install报错ERROR: Could not find a version that satisfies the requirement...,大概率是 pip 版本过旧。执行python -m pip install --upgrade pip升级后再试。这是 Python 安装环节第二高频问题。
4.2 5 分钟验证聚类是否成功:看三个关键输出
不要等完整图表生成才判断对错。运行main.py后,立即检查控制台输出的三行关键信息:
[INFO] TF-IDF matrix shape: (87, 500) # 87 首歌,500 个特征词 [INFO] KMeans inertia: 1245.67 # 惯性值越小越好,但 K 增大必然减小 [INFO] Silhouette Score: 0.42 # 0.25~0.5 为合理范围,>0.5 优秀- 若
TF-IDF matrix shape第二个数字远小于 500(如(87, 42)),说明custom_tokenizer过滤太狠,检查music_words.txt是否为空或编码错误; - 若
inertia>2000 且Silhouette Score<0.2,大概率是data.csv中BPM或Valence列存在空值(NaN),需用pandas清洗:df_data.fillna(df_data.mean(), inplace=True); - 若
Silhouette Score>0.55 但图表中簇严重重叠,说明 t-SNE 的perplexity设太低(应调至 40–50)。
4.3 用clustering.py单独调试:聚焦核心算法逻辑
clustering.py是整个项目的算法心脏。它不负责读文件、不负责画图,只做一件事:接收特征矩阵,返回聚类标签。你可以把它当作黑盒函数测试:
# 在 Python 交互环境(如 PyCharm Console)中执行 from clustering import run_kmeans_clustering import numpy as np # 构造一个模拟的 3 首歌 × 5 维特征矩阵(测试用) test_features = np.array([ [0.1, 0.8, 0.2, 75, 0.3], # 民谣:低能量,中 BPM [0.9, 0.1, 0.9, 120, 0.7], # 流行:高能量,高 BPM [0.7, 0.2, 0.1, 68, 0.1] # 伤感:低积极度,低 BPM ]) labels = run_kmeans_clustering(test_features, n_clusters=3) print("Test labels:", labels) # 应输出类似 [0 2 1],表示三首歌分属不同簇这段代码能让你在 30 秒内确认:K 均值算法本身是否正常工作。如果报错ValueError: Found array with 0 sample(s), 说明test_features构造有误;如果labels全为同一数字,说明n_clusters设置不当或数据未归一化。这是排查问题最高效的切入点。
本文还有配套的精品资源,点击获取