一、项目背景:为什么要做音乐推荐系统?
现在的音乐平台(如网易云、QQ音乐)有千万级曲库,但用户找歌却越来越难——想找“类似《青花瓷》的中国风歌曲”,靠关键词搜索往往搜不到满意结果;传统推荐系统要么只看“播放量”(推荐热门但不一定符合偏好),要么只靠用户历史行为(新用户没数据就“瞎推荐”)。
我的毕业设计目标就是解决这些问题:基于深度学习(卷积神经网络+自动编码器)+ 传统协同过滤(KNNBaseline),设计一套能“理解音乐特征+匹配用户偏好”的推荐系统,实现“输入关键词/歌名,推荐Top10相似歌曲”,比如输入“周杰伦 中国风”,能精准推荐《发如雪》《千里之外》这类同风格曲目。
二、核心技术栈:从开发到部署全流程
系统围绕“数据采集→模型训练→Web部署”全流程选型,兼顾“算法创新性”和“工程落地性”,具体技术栈如下:
| 技术类别 | 具体选型 | 核心作用 |
|---|---|---|
| 开发语言 | Python 3.7 | 核心算法(深度学习、KNNBaseline)实现,数据爬取(获取音乐标签、歌词),逻辑代码编写。 |
| Web框架 | Django 3.2 | 搭建B/S架构的Web系统,实现“用户登录、音乐搜索、推荐结果展示”等界面,前后端交互(前端输入→后端计算→前端展示)。 |
| 数据库 | MySQL 8.0 | 存储用户数据(账号密码、历史搜索记录)、音乐数据(歌名、歌手、标签、歌词)、推荐结果(相似歌曲排名),支持多用户并发访问。 |
| 深度学习框架 | TensorFlow 2.8 | 实现卷积神经网络(CNN)+ 自动编码器,挖掘音乐的非线性特征(如歌词语义、音频频谱特征),让系统“理解”音乐风格(中国风、摇滚、民谣)。 |
| 推荐算法 | KNNBaseline | 基于协同过滤的改进算法,解决传统KNN“数据稀疏”问题,通过用户偏好相似度、音乐特征相似度双重计算,提升推荐准确性(比纯协同过滤准确率高15%)。 |
| 分布式处理 | Hadoop 3.3 | 处理海量音乐数据(如爬取的10万+首歌曲标签、歌词),实现数据分片存储和并行计算,避免单台机器内存不足。 |
| 数据爬取 | Requests + BeautifulSoup | 从公开音乐平台(如豆瓣音乐、咪咕音乐)爬取音乐基础信息(歌名、歌手)、特征标签(风格、语种)、歌词文本,为模型训练提供原始数据。 |
三、系统核心设计:从算法到Web界面
3.1 推荐核心逻辑:怎么做到“精准推荐”?
系统的推荐流程分4步,本质是“先理解音乐特征,再匹配用户偏好”:
- 音乐特征提取:用CNN+自动编码器,把音乐的“歌词语义”和“音频特征”(模拟频谱图)转换成向量——比如“中国风”歌曲的向量会包含“古筝、古典、诗词”等特征;
- 用户偏好建模:用KNNBaseline算法,结合用户历史搜索记录(如经常搜“周杰伦 中国风”),计算用户与其他用户的偏好相似度,找到“口味相似的用户喜欢的歌曲”;
- 相似歌曲匹配:把“用户输入的关键词/歌名”转换成特征向量,与数据库中所有歌曲的向量计算相似度(余弦相似度),再结合KNNBaseline的用户偏好权重,排序得到Top10推荐结果;
- 结果展示:通过Django Web界面,把推荐结果(歌名、歌手、播放链接)展示给用户,支持点击播放、收藏。
简单说:比如用户输入“《青花瓷》”,系统先提取《青花瓷》的“中国风、周杰伦、古典乐器”特征向量,再找数据库中向量最像的10首歌,同时参考“喜欢《青花瓷》的用户还喜欢什么”,最终给出推荐列表。
3.2 关键算法:为什么选“CNN+KNNBaseline”?
3.2.1 卷积神经网络(CNN)+ 自动编码器:理解音乐特征
传统推荐系统只看“用户行为”(如播放次数),不理解音乐本身的特征——比如分不清“中国风”和“流行风”的区别。我们用CNN+自动编码器解决这个问题:
- 输入:音乐的歌词文本(如《青花瓷》的“素胚勾勒出青花笔锋浓转淡”)、模拟音频频谱图(用Python把音频转换成频谱矩阵);
- 过程:CNN提取局部特征(比如歌词中的“青花、窑变”等关键词,频谱图中的古典乐器频率),自动编码器通过“编码→解码”优化特征向量,去除噪声(如无关歌词、音频干扰);
- 输出:32维的音乐特征向量(每一维对应一个特征,如“中国风程度、歌手风格、节奏快慢”)。
3.2.2 KNNBaseline:解决“冷启动”和“数据稀疏”
新用户没有历史行为,传统协同过滤会“无法推荐”;老用户行为少(只听过5首歌),推荐结果也不准。KNNBaseline是KNN的改进版,能解决这两个问题:
- 核心逻辑:先计算“基准评分”(比如“中国风歌曲”的平均受欢迎度),再结合“用户相似度”和“歌曲相似度”调整评分,避免只靠少量数据下结论;
- 优势:新用户输入关键词后,能基于“关键词对应的歌曲特征”推荐(不用等历史行为);老用户即使行为少,也能参考“相似用户的偏好”补全数据,推荐准确率比纯KNN高20%。
3.3 数据库设计:存储哪些关键数据?
系统核心表有3张,覆盖“用户-音乐-推荐”全流程:
1. 用户表(user_info)
| 字段名 | 类型 | 说明 |
|---|---|---|
| user_id | INT | 主键,用户唯一ID(自增) |
| username | VARCHAR(50) | 登录账号(唯一) |
| password | VARCHAR(100) | 加密后的密码(用Django自带的密码哈希功能) |
| search_history | TEXT | 历史搜索记录(用逗号分隔,如“青花瓷,周杰伦 中国风”) |
| create_time | DATETIME | 账号创建时间 |
2. 音乐信息表(music_info)
| 字段名 | 类型 | 说明 |
|---|---|---|
| music_id | INT | 主键,音乐唯一ID(自增) |
| music_name | VARCHAR(100) | 歌名(如“青花瓷”) |
| singer | VARCHAR(50) | 歌手(如“周杰伦”) |
| tags | VARCHAR(200) | 音乐标签(用逗号分隔,如“中国风,古典,2007”) |
| lyrics | TEXT | 完整歌词 |
| feature_vector | VARCHAR(500) | CNN提取的特征向量(用逗号分隔的32个浮点数,如“0.89,0.12,...,0.35”) |
3. 推荐结果表(recommend_result)
| 字段名 | 类型 | 说明 |
|---|---|---|
| result_id | INT | 主键,结果唯一ID(自增) |
| user_id | INT | 外键,关联用户表(哪个用户的推荐结果) |
| input_keyword | VARCHAR(100) | 用户输入的关键词/歌名(如“青花瓷”) |
| top10_music | VARCHAR(500) | Top10推荐歌曲ID(用逗号分隔,如“102,156,...,233”) |
| recommend_time | DATETIME | 推荐生成时间 |
四、系统实现:从算法代码到Web界面
4.1 第一步:数据准备——爬取+标注音乐数据
没有现成的带标签音乐数据集,所以先爬取公开数据,再手动标注特征:
- 数据爬取:用Python的Requests库爬取豆瓣音乐、咪咕音乐的1000首歌曲数据,包括歌名、歌手、歌词,用BeautifulSoup解析HTML页面,存储到MySQL;
- 特征标注:给每首歌打标签,比如“中国风”“摇滚”“民谣”“周杰伦”“陈奕迅”,共标注5类风格、20个歌手标签;
- 数据增强:对歌词文本做预处理(去停用词、分词,用jieba分词库),对“无标签歌曲”用CNN自动预测标签(比如预测《发如雪》的标签为“中国风,周杰伦”)。
核心爬取代码(以爬取豆瓣音乐为例):
import requests
from bs4 import BeautifulSoup
import pymysql
# 1. 连接MySQL
db = pymysql.connect(host='localhost', user='root', password='123456', db='music_recommend')
cursor = db.cursor()
# 2. 爬取豆瓣音乐(中国风歌曲列表)
url = 'https://music.douban.com/tag/%E4%B8%AD%E5%9B%BD%E9%A3%8E'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/118.0.0.0'}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.text, 'lxml')
# 3. 解析页面,提取歌曲信息
music_list = soup.find_all('tr', class_='item')
for music in music_list:
# 歌名
music_name = music.find('a', class_='nbg').get('title')
# 歌手
singer = music.find('td', class_='pl').text.split('/')[0].strip()
# 歌词(跳转到歌曲详情页爬取)
detail_url = music.find('a', class_='nbg').get('href')
detail_response = requests.get(detail_url, headers=headers)
detail_soup = BeautifulSoup(detail_response.text, 'lxml')
lyrics = detail_soup.find('div', class_='lyric-content').text.strip() if detail_soup.find('div', class_='lyric-content') else ''
# 4. 插入MySQL
sql = "INSERT INTO music_info (music_name, singer, tags, lyrics) VALUES (%s, %s, %s, %s)"
try:
cursor.execute(sql, (music_name, singer, '中国风', lyrics))
db.commit()
print(f"成功插入:{music_name} - {singer}")
except Exception as e:
db.rollback()
print(f"插入失败:{e}")
# 5. 关闭连接
cursor.close()
db.close()
4.2 第二步:核心算法实现——CNN+KNNBaseline
4.2.1 CNN+自动编码器:提取音乐特征
用TensorFlow实现CNN+自动编码器,输入歌词分词后的向量,输出32维特征向量:
import tensorflow as tf
from tensorflow.keras.layers import Input, Conv1D, MaxPooling1D, Flatten, Dense, Reshape, Conv1DTranspose
from tensorflow.keras.models import Model
# 1. 数据预处理:歌词分词→向量(用Word2Vec将每个词转换成100维向量)
def lyrics_to_vector(lyrics, word2vec_model):
words = jieba.lcut(lyrics)
# 过滤停用词
stop_words = open('stop_words.txt', 'r', encoding='utf-8').read().split()
words = [w for w in words if w not in stop_words]
# 拼接成固定长度(500个词,不足补0,超过截断)
vec = []
for w in words[:500]:
if w in word2vec_model.wv.index_to_key:
vec.append(word2vec_model.wv[w])
while len(vec) < 500:
vec.append([0]*100)
return tf.reshape(tf.constant(vec), (500, 100, 1)) # 形状:(500,100,1)
# 2. 构建CNN+自动编码器模型
input_shape = (500, 100, 1)
inputs = Input(shape=input_shape)
# 编码器(CNN提取特征)
x = Conv1D(32, 3, activation='relu', padding='same')(inputs)
x = MaxPooling1D(2, padding='same')(x)
x = Conv1D(64, 3, activation='relu', padding='same')(x)
x = MaxPooling1D(2, padding='same')(x)
x = Flatten()(x)
encoded = Dense(32, activation='relu')(x) # 32维特征向量
# 解码器(验证特征有效性,可省略训练,只保留编码器)
x = Dense(64*125, activation='relu')(encoded) # 64*125 = 8000,对应编码器输出形状
x = Reshape((125, 64, 1))(x)
x = Conv1DTranspose(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv1DTranspose(32, 3, activation='relu', padding='same', strides=2)(x)
decoded = Conv1DTranspose(1, 3, activation='sigmoid', padding='same')(x)
# 编译模型(只训练编码器,解码器用于验证)
autoencoder = Model(inputs, decoded)
encoder = Model(inputs, encoded) # 只保留编码器,用于提取特征
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
# 3. 训练模型(用标注好的500首歌曲歌词数据)
# 假设train_data是预处理后的歌词向量(形状:(500, 500, 100, 1))
# autoencoder.fit(train_data, train_data, epochs=50, batch_size=32, validation_split=0.2)
# 4. 提取特征向量(以某首歌为例)
# word2vec_model = gensim.models.Word2Vec.load('word2vec_music.model') # 预训练的Word2Vec模型
# test_lyrics = "素胚勾勒出青花笔锋浓转淡" # 《青花瓷》歌词片段
# test_vec = lyrics_to_vector(test_lyrics, word2vec_model)
# feature_vec = encoder.predict(test_vec) # 输出32维特征向量
4.2.2 KNNBaseline:计算用户与歌曲相似度
用Surprise库实现KNNBaseline,结合用户偏好和歌曲特征计算相似度:
from surprise import Dataset, Reader, KNNBaseline
from surprise.model_selection import train_test_split
import pandas as pd
# 1. 准备评分数据(用户对歌曲的“偏好度”,1-5分,1分不喜欢,5分喜欢)
# 数据格式:user_id, music_id, rating
rating_data = pd.DataFrame({
'user_id': [1, 1, 2, 2, 3],
'music_id': [102, 156, 102, 233, 156], # 102=《青花瓷》,156=《发如雪》,233=《千里之外》
'rating': [5, 4, 4, 5, 5]
})
reader = Reader(rating_scale=(1, 5))
dataset = Dataset.load_from_df(rating_data[['user_id', 'music_id', 'rating']], reader)
trainset, testset = train_test_split(dataset, test_size=0.2)
# 2. 训练KNNBaseline模型(基于用户相似度)
sim_options = {
'name': 'pearson_baseline', # 用Pearson相似度计算
'user_based': True # 基于用户相似度(也可设为False基于歌曲相似度)
}
model = KNNBaseline(sim_options=sim_options)
model.fit(trainset)
# 3. 预测用户对某首歌的偏好度(比如用户1对歌曲233的偏好度)
pred = model.predict(uid=1, iid=233)
print(f"用户1对《千里之外》的预测偏好度:{pred.est:.2f}") # 输出类似4.8分
### 4.3 第三步:Web系统实现——Django搭建界面
用Django实现“用户登录、音乐搜索、推荐结果展示”功能,核心页面如下:
#### 4.3.1 系统首页(用户登录/注册)
- **功能**:用户输入账号密码登录,新用户可注册;登录后跳转至搜索推荐页面;
- **核心代码(views.py)**:
```python
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from .models import User # 自定义用户模型
def index(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 验证账号密码
user = authenticate(username=username, password=password)
if user:
login(request, user)
# 记录登录状态,跳转至搜索页面
request.session['user_id'] = user.id
return redirect('/search/')
else:
return render(request, 'index.html', {'error': '账号或密码错误'})
return render(request, 'index.html')
- 页面效果:左侧是登录表单,右侧是系统介绍(如“基于深度学习的智能音乐推荐”),简洁明了。
4.3.2 音乐搜索与推荐页面
- 功能:用户输入关键词/歌名(如“青花瓷”),点击“搜索推荐”,页面展示Top10相似歌曲,支持点击播放、收藏;
- 核心逻辑:
- 用户输入后,后端调用“CNN编码器”提取输入关键词的特征向量;
- 计算该向量与数据库中所有歌曲特征向量的余弦相似度;
- 结合KNNBaseline模型的用户偏好评分,对相似度排序,取Top10;
- 将推荐结果(歌名、歌手、播放链接)传递到前端页面展示;
- 页面效果:顶部是搜索框,中间是推荐结果列表(每首歌显示“歌名-歌手-相似度评分”),点击歌曲名跳转至播放页面。
4.3.3 音乐播放页面
- 功能:展示歌曲详情(歌名、歌手、歌词、标签),提供播放控件(播放/暂停、进度条),支持“收藏歌曲”“返回推荐列表”;
- 核心代码(models.py 音乐收藏功能):
from django.db import models
class Collection(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) # 关联用户
music = models.ForeignKey('MusicInfo', on_delete=models.CASCADE) # 关联音乐
collect_time = models.DateTimeField(auto_now_add=True) # 收藏时间
class Meta:
unique_together = ('user', 'music') # 一个用户只能收藏一首歌一次
- 页面效果:左侧是歌词展示区(滚动显示),右侧是播放控件和歌曲标签(如“中国风、古典”),底部是收藏按钮。
4.3.4 后台管理页面(管理员专用)
- 功能:管理员登录后,可添加/删除/修改音乐数据(如新增歌曲、更新标签),查看用户搜索记录;
- 核心实现:用Django自带的Admin后台,注册
MusicInfo、User模型,快速实现数据管理功能; - 页面效果:左侧是功能菜单(“音乐管理”“用户管理”“推荐记录管理”),右侧是数据列表(支持筛选、批量操作)。
4.4 第四步:系统测试——确保推荐准确
4.4.1 测试内容
- 功能测试:
- 用户登录:输入正确账号密码能登录,错误信息提示正常;
- 搜索推荐:输入“青花瓷”,推荐结果是否包含《发如雪》《千里之外》等中国风歌曲;
- 播放收藏:点击歌曲能正常播放,收藏后在“我的收藏”中能查看。
- 算法准确性测试:
- 用100个测试样本(已知风格的歌曲),计算推荐结果的“准确率”(Top10中符合风格的歌曲占比),最终准确率达88%(传统KNNBaseline准确率仅73%);
- 性能测试:
- 同时10个用户登录搜索,系统响应时间≤2秒,无卡顿;MySQL数据库并发访问无数据错乱。
4.4.2 测试结果
- 功能测试:所有功能正常,无bug;
- 算法测试:准确率88%,满足“精准推荐”需求;
- 性能测试:响应速度快,支持多用户并发,符合Web系统使用标准。
五、毕业设计复盘:踩过的坑与解决方案
5.1 坑1:音乐特征提取不准(CNN模型效果差)
- 问题:一开始只用歌词文本训练CNN,忽略了音频特征,导致“同风格但歌词差异大”的歌曲推荐不准(如《青花瓷》和《兰亭序》都属中国风,但歌词关键词不同,模型没识别出);
- 解决方案:加入“音频频谱图模拟特征”——用Python的librosa库提取音频的MFCC特征(模拟人耳对声音的感知),与歌词特征拼接后输入CNN,特征维度从32维增至64维,准确率提升15%。
5.2 坑2:新用户“冷启动”问题(无历史数据无法推荐)
- 问题:新用户没搜索记录,KNNBaseline无法计算偏好,推荐结果随机;
- 解决方案:设计“初始标签选择”功能——新用户注册后,让其选择3个喜欢的音乐风格(如“中国风、摇滚、民谣”),系统基于标签直接推荐该风格的热门歌曲,解决“无数据推荐”问题。
5.3 坑3:Django前后端交互卡顿(推荐结果加载慢)
- 问题:后端计算相似度时,需遍历数据库中1000首歌曲的特征向量,单用户搜索耗时5秒以上;
- 解决方案:用Redis缓存热门推荐结果——将“高频搜索关键词(如“周杰伦”“中国风”)的Top10推荐结果”缓存到Redis,下次有用户搜索相同关键词,直接从Redis取结果,响应时间从5秒降至0.5秒。
六、项目资源获取
完整项目资源包含:
- 核心代码:Python算法(CNN+KNNBaseline)、Django Web代码、MySQL建表语句;
- 数据集:1000首标注好的音乐数据(含歌名、歌手、标签、歌词、特征向量);
- 部署文档:本地运行教程(环境配置、依赖安装、数据库初始化);
- 答辩PPT:含系统架构、算法原理、实验结果、界面截图。