☰
基于Python深度学习的音乐流行趋势预测系统:LSTM时间序列与Django/Flask双部署实战
2026/10/3 3:25:18 网站建设 项目流程

简介:这份资源是面向计算机相关专业学生与开发者的深度学习实战项目包,围绕音乐流行趋势预测这一典型时间序列任务展开,可用于毕业设计、课程设计、项目立项演示或自学进阶。包内共64个文件,以36个ipynb实验笔记为核心,配合13个csv与3个xlsx数据集、3份pdf与2份caj参考文献、3份md部署文档及html、png等辅助材料,压缩包约14.52MB,结构清晰便于按模块查阅。项目已通过导师评审并取得95分答辩成绩,代码经测试可正常运行,涵盖Pandas、Matplotlib、Seaborn等数据分析工具与深度学习建模流程,并附Django与Flask两套系统部署文档,方便读者复现实验、理解算法原理并在此基础上修改扩展功能。目前已有243人学习下载,适合希望系统掌握音乐趋势预测方法、快速完成毕设或课设的读者参考使用。

1. 从一份 95 分毕设拆开看:音乐流行趋势预测到底在预测什么

音乐流行趋势预测这个题目,第一次接触的人容易想歪,以为要预测某首歌会不会火。实际拆开这份基于 Python 深度学习音乐流行趋势预测系统的设计与实现资源后会发现,它预测的是时间序列维度上的播放量走势——给定阿里音乐过去若干天的艺人播放数据,推断未来一段时间的数值区间。这本质是一个回归问题,不是分类问题,更不是推荐系统。

这份资源适合三类人:正在找课程设计或毕业设计题目的计算机相关专业学生,想拿一个完整 Django/Flask 双部署方案练手的后端初学者,以及需要时间序列预测 baseline 做对比实验的研究者。包里带了设计报告、部署文档、论文参考和阿里音乐大赛的赛题数据说明,等于把「选题背景—数据处理—模型训练—系统部署—论文写作」整条链路都铺好了。下面按我实际拆包的顺序,把能复现的部分和容易翻车的地方一条条讲清楚。

2. 数据管线与模型选型:为什么用 LSTM 而不是 ARIMA

2.1 阿里音乐赛题数据的结构长什么样

这份资源的核心数据集来自阿里音乐流行趋势预测大赛。原始数据通常是两张表:一张是艺人基本信息(artist_id、name、gender、album_count 等),另一张是每日播放量流水(date、artist_id、play_count)。预测目标是给定前 30 天(或前 60 天)的播放量,输出未来 30 天(或 60 天)的每日播放量。

先看数据加载和初步探查的代码,这是所有后续步骤的地基:

import pandas as pd import numpy as np import matplotlib.pyplot as plt # 加载播放量流水表,日期列解析为 datetime df = pd.read_csv('data/music_play.csv', parse_dates=['date']) # 按艺人分组后按日期排序,时间序列建模的前提是顺序不能乱 df = df.sort_values(['artist_id', 'date']).reset_index(drop=True) # 查看每个艺人的记录条数分布,判断数据是否均衡 counts = df.groupby('artist_id')['play_count'].count() print(counts.describe()) # 典型输出:mean 约 60~180,说明不同艺人历史长度差异很大 # 画一个艺人的播放量曲线,肉眼确认趋势和周期性 sample = df[df['artist_id'] == df['artist_id'].iloc[0]] plt.plot(sample['date'], sample['play_count']) plt.title('Sample Artist Play Count Trend') plt.show()

这段代码做了三件事:解析日期、按艺人和时间排序、用 describe 和折线图做初步探查。参数上要注意parse_dates必须指定,否则 date 列是字符串,后面做滑窗会出错。sort_values的顺序不能反,先 artist_id 再 date,保证同一艺人的记录连续且按时间递增。

常见做法是进一步做缺失日期填充。阿里赛题数据里有些艺人不是每天都有记录,直接丢进 LSTM 会导致时间步错位。我一般会先构建完整的日期索引,再 left join 原始数据,缺失的 play_count 用前向填充或插值补上。

2.2 滑动窗口构造监督学习样本

时间序列要喂给深度学习模型,必须转成 (样本数, 时间步, 特征数) 的三维张量。这一步是整条管线里最容易写错的地方:

def create_sequences(data, window=30, horizon=30): """ data: 一维数组,单个艺人的播放量序列 window: 输入时间步,用过去多少天预测 horizon: 输出时间步,预测未来多少天 """ X, y = [], [] for i in range(len(data) - window - horizon + 1): X.append(data[i : i + window]) y.append(data[i + window : i + window + horizon]) return np.array(X), np.array(y) # 对单个艺人做归一化后再滑窗,避免量级差异影响收敛 from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler() values = sample['play_count'].values.reshape(-1, 1) scaled = scaler.fit_transform(values).flatten() X, y = create_sequences(scaled, window=30, horizon=30) print(X.shape, y.shape) # 例如 (100, 30) (100, 30)

window和horizon是最关键的两个参数。window 太小(比如 7),模型看不到长期趋势;太大(比如 90),样本数骤减且容易过拟合。这份资源里设计报告推荐的是 window=30、horizon=30,和赛题要求对齐。归一化必须用 MinMaxScaler 而不是 StandardScaler,因为播放量是非负的,MinMax 能保留这个边界。注意 scaler 只能在训练集上 fit,验证集和测试集用 transform,否则就是数据泄漏——这是答辩时老师最爱问的点。

2.3 LSTM 与 BP 神经网络的取舍

资源包里同时收了 BP 神经网络和 LSTM 相关的参考论文(颜家康的 BP 应用研究、吕倩倩的机器学习预测),这不是凑数,而是让你做对比实验用的。BP 网络把时间序列当成普通特征向量,丢失了时序依赖;LSTM 通过门控机制保留长期记忆,在播放量这种有明显惯性的数据上通常更稳。

我实际跑下来的经验是:BP 在短窗口(window≤14)时差距不大,但 window 拉到 30 以上,LSTM 的验证集 MAE 能低 15%~25%。如果答辩要求有对比章节,把两者放在同一份数据、同一套滑窗参数下跑,结论很有说服力。

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout model = Sequential([ LSTM(64, input_shape=(30, 1), return_sequences=False), Dropout(0.2), Dense(30) # 直接输出未来 30 天 ]) model.compile(optimizer='adam', loss='mse', metrics=['mae']) model.summary()

LSTM 层 64 个单元是常见起点,数据量大可以加到 128。Dropout 0.2 用来抑制过拟合,如果训练 loss 和验证 loss 差距大就往上调。输出层 Dense(30) 对应 horizon=30,如果改成预测 60 天,这里和滑窗参数要同步改,漏改一个就会维度报错。

3. Django 与 Flask 双部署:从模型文件到可访问接口

3.1 两种部署方案的适用场景

资源里给了两份部署文档,Django 和 Flask 各一套。这不是让你二选一随便挑,而是对应两种不同的使用场景。Django 自带 ORM、Admin 和完整的 MVC 结构,适合要把预测系统做成带用户管理、历史记录、数据看板的正经 Web 应用;Flask 轻量,几十行就能起一个预测接口,适合快速演示或嵌入已有系统。

如果你的毕设要求有「系统」的样子——登录页、数据管理页、图表展示页——走 Django。如果只是答辩时演示「输入一段历史数据,返回预测结果」,Flask 足够,而且部署踩坑少。

3.2 Flask 最小预测接口的落地步骤

先看 Flask 方案,因为它能最快验证模型文件是否可用:

from flask import Flask, request, jsonify import numpy as np import joblib from tensorflow.keras.models import load_model app = Flask(__name__) model = load_model('model/lstm_music.h5') scaler = joblib.load('model/scaler.pkl') @app.route('/predict', methods=['POST']) def predict(): # 接收前端传来的历史播放量数组,长度必须等于 window data = request.json['history'] arr = np.array(data).reshape(-1, 1) scaled = scaler.transform(arr).flatten() X = scaled.reshape(1, 30, 1) pred = model.predict(X).flatten() # 反归一化回真实播放量量级 result = scaler.inverse_transform(pred.reshape(-1, 1)).flatten() return jsonify({'prediction': result.tolist()}) if __name__ == '__main__': app.run(host='0.0.0.0', port=5000)

逻辑链条是:接收历史数据 → 用训练时的 scaler 归一化 → reshape 成模型要求的 (1, 30, 1) → 预测 → 反归一化 → 返回 JSON。参数上,host='0.0.0.0'是为了让同局域网的其他机器能访问,只在本机测试用127.0.0.1就行。scaler 必须和训练时是同一个对象,重新 fit 会导致预测值完全错乱,这是新手最常翻的车。

3.3 Django 集成中的模型加载时机

Django 方案里,模型加载不能放在视图函数里每次请求都 load_model,那样第一个请求会卡好几秒,并发上来直接崩。正确做法是在 apps.py 的 ready() 方法里做全局加载:

# music_predict/apps.py from django.apps import AppConfig from tensorflow.keras.models import load_model import joblib class MusicPredictConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'music_predict' model = None scaler = None def ready(self): # 应用启动时加载一次,后续请求复用 self.model = load_model('model/lstm_music.h5') self.scaler = joblib.load('model/scaler.pkl')

然后在视图里通过apps.get_app_config('music_predict').model取用。这样模型只在进程启动时加载一次,请求响应时间从秒级降到毫秒级。注意 Django 开发服务器有自动重载机制,改代码会触发 ready() 重新执行,生产环境用 gunicorn 就不存在这个问题。

数据库层面,Django 方案一般会建两张表:一张存艺人信息,一张存预测记录(艺人 ID、预测日期、预测值、创建时间)。预测记录表方便做历史回溯和图表展示,答辩演示时能直接翻出「上周预测的这周实际是多少」的对比。

4. 避坑与排查:那些让系统跑不起来的细节

4.1 现象:模型预测值全是同一个数

原因通常是归一化出了问题。如果 scaler 在全体数据上 fit 后再划分训练测试集,或者预测时用了新的 scaler,模型看到的输入分布和训练时不一致,输出就会坍缩到均值附近。

解决:严格按「训练集 fit → 验证测试集 transform → 保存 scaler → 预测时加载同一个 scaler」的流程走。保存用 joblib.dump,加载用 joblib.load,不要用 pickle 混着来。

4.2 现象:训练 loss 一直不降,MAE 卡在很高位

先查学习率。adam 默认 0.001 在大多数情况够用,但如果数据没归一化,梯度会爆炸或消失。其次查滑窗构造,如果 X 和 y 错位了(比如 y 取了和 X 重叠的区间),模型学到的就是「复制输入」,验证时自然崩。

解决:打印几组 X 和 y 的实际值,肉眼确认 y 确实是 X 之后的时间段。再确认输入已经归一化到 [0,1]。这两步做完,loss 一般就能正常下降。

4.3 现象:Flask 接口返回 500,日志显示 shape 不匹配

模型训练时 input_shape 是 (30, 1),但前端传过来的数组长度不是 30,或者 reshape 成了 (30, 1) 而不是 (1, 30, 1)。Keras 的 predict 要求第一维是 batch size。

解决:在接口里加长度校验,if len(data) != 30: return jsonify({'error': 'history length must be 30'}), 400。reshape 固定写X = scaled.reshape(1, 30, 1),不要用 -1 让 numpy 自己猜。

4.4 现象:Django 启动报 ModuleNotFoundError: No module named 'tensorflow'

虚拟环境没激活,或者 tensorflow 装在了全局 Python 里。这份资源用的是 TensorFlow/Keras 路线,不是 PyTorch,装错框架也会报类似的错。

解决:确认pip list | grep tensorflow有输出,且版本和训练模型时一致。TensorFlow 2.x 保存的 .h5 模型在 1.x 下加载会报错,反过来也一样。部署文档里如果写了版本号,严格按那个版本装。

4.5 现象:预测结果整体偏高或偏低一个固定倍数

反归一化时用错了 scaler 的 data_min_ 或 data_range_。MinMaxScaler 的 inverse_transform 依赖 fit 时记录的 min 和 max,如果预测时加载的 scaler 是重新 fit 过的,这两个值就变了。

解决:把训练时的 scaler 和模型文件放在同一个目录,用相对路径加载,确保部署时不会拿错文件。可以在保存模型后立刻做一次「保存→加载→预测」的自检,确认结果一致再往下走。

5. 把预测结果做成能看的图表:Matplotlib 与 Seaborn 的实战技巧

模型跑通只是第一步,答辩和演示时真正加分的是可视化。资源里带了 Pandas、Matplotlib、Seaborn 三个库,下面讲一个我常用的「历史+预测」对比图做法,以及一个容易被忽略的细节。

import matplotlib.pyplot as plt import seaborn as sns import pandas as pd sns.set_style('whitegrid') plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文标签不乱码 plt.rcParams['axes.unicode_minus'] = False # history: 过去 30 天真实值, prediction: 未来 30 天预测值 days_history = pd.date_range(end='2024-01-30', periods=30) days_future = pd.date_range(start='2024-01-31', periods=30) fig, ax = plt.subplots(figsize=(12, 5)) ax.plot(days_history, history, label='历史播放量', color='#2E86AB', linewidth=2) ax.plot(days_future, prediction, label='预测播放量', color='#E94F37', linewidth=2, linestyle='--') # 用填充色标出预测区间,视觉上区分历史和未来 ax.axvspan(days_future[0], days_future[-1], alpha=0.08, color='#E94F37') ax.set_xlabel('日期') ax.set_ylabel('播放量') ax.set_title('音乐流行趋势预测结果') ax.legend() plt.tight_layout() plt.savefig('static/prediction_chart.png', dpi=150)

这段代码的关键点有三个。第一,中文字体必须设SimHei,否则标题和标签全是方框,这是 Linux 服务器上部署时的高频翻车点,服务器没装中文字体的话要先把字体文件放进 matplotlib 的字体目录再清缓存。第二,axvspan给预测区间加了一层浅色背景,比单纯换线型更直观,答辩时老师一眼就能看出哪段是预测。第三,dpi=150保存的图在网页上清晰度够用,再高文件会很大,Django 静态文件加载会变慢。

如果要展示多个艺人的对比,用 Seaborn 的lineplot配合hue参数更省事:

# df_multi 包含 columns: date, play_count, artist_name, type(history/prediction) sns.lineplot(data=df_multi, x='date', y='play_count', hue='artist_name', style='type', markers=False)

style='type'会自动把历史和预测用不同线型区分,hue按艺人分色。数据量大的时候记得先聚合或采样,否则图会糊成一团。

还有一个实战习惯:把每次预测的结果连同时间戳存进数据库或 CSV,过一段时间拿实际数据回测。资源里的设计报告提到了模型评估,但真正让答辩有说服力的是「我预测了,而且我用后来的真实数据验证了误差」。MAE 和 RMSE 两个指标都算出来,MAE 解释直观(平均差多少播放量),RMSE 对大误差更敏感,两个一起报显得严谨。

从那以后我每次做完预测系统,都会强制走一遍「保存模型→重启服务→用同一组输入预测两次→对比结果是否一致」的流程。这个习惯帮我抓到过好几次 scaler 没保存、模型路径写死绝对路径的问题。希望这份拆解能帮你少走点弯路,把这份资源真正跑起来、改起来。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询