ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

协同过滤音乐推荐系统:Python+Django+Vue全栈实现与算法解析

协同过滤音乐推荐系统:Python+Django+Vue全栈实现与算法解析 这次分享的是计算机毕业设计里出现频率很高的一个方向基于协同过滤的音乐推荐系统。项目技术组成很典型后端用 Python Django前端用 Vue推荐核心用协同过滤算法整体是一套前后端分离的全栈系统。对打算选推荐系统方向的毕设学生来说这个题目的价值在于推荐算法能讲清楚、前端页面有得展示、后端接口可以演示配合完整源码、文档报告和代码讲解答辩时的主线会很完整。这篇内容不打算只讲概念会沿着一条实际开发主线走一遍从系统架构、协同过滤算法落地、Django 模型设计、Vue 页面请求到推荐结果验证和常见问题排查。文章里出现的目录结构、接口路径和命令都属于毕设开发中的常见写法不是某个平台上不可变的标准实际使用时可以按你的项目包名、应用名和文档要求调整。1. 核心能力速览能力项说明项目类型前后端分离全栈毕设项目后端技术Python Django前端技术Vue Axios推荐算法User-Based CF、Item-Based CF、热门兜底策略主要功能用户登录注册、音乐浏览搜索、评分/播放行为记录、个性化推荐、热门榜单、管理后台硬件要求不需要 GPU普通开发机能跑数据库SQLite 起步可切换 MySQLAPI 形态RESTful JSON 接口批量能力离线重算相似度、批量生成 Top-N 推荐列表交付内容参考源码、数据库设计、课程设计/毕设文档报告、代码讲解适合场景毕业设计、课程设计、推荐系统入门实践这样的技术组合适合大多数本科毕设需求。原因很直接Django 负责用户体系和业务数据管理开发效率高Vue 负责页面展示和交互做出“音乐平台”的观感协同过滤算法则作为论文和答辩中的核心创新点。2. 系统整体架构与边界这个系统可以分成三层来理解。第一层是 Vue 前端。用户打开浏览器访问页面执行登录、搜索、点击歌曲、收藏、评分等操作。前端不直接操作数据库而是通过 Axios 发送 HTTP 请求到后端。第二层是 Django 后端。Django 接收请求后先把请求路由到对应视图函数再由视图函数调用 ORM 完成数据库读写。推荐相关请求会进入推荐引擎模块由协同过滤算法计算后返回结果。第三层是数据库。用户表、音乐表、评分行为表、推荐结果表和相似度表都存到这里。本地开发阶段使用 SQLite 最省事零配置、单文件、方便提交给老师评审如果文档报告中想强调 MySQL可以切换。从边界角度来看前端不应该直接访问推荐算法内部数据结构后端也不应该把相似度矩阵计算逻辑堆在 view 函数里。推荐引擎放到独立模块业务视图只负责接收参数和返回 JSON是便于讲解也便于测试的结构。一条真实的推荐请求流程如下Vue 页面发起登录 - 拿到 Token - 用户点击某首歌 - 前端上报行为数据 - Django 保存评分记录 - 用户打开个人推荐页 - Django 调用协同过滤引擎 - 根据用户历史行为生成 Top-N 歌曲列表 - JSON 返回前端渲染。3. 协同过滤推荐算法怎么落地协同过滤是推荐系统里最经典的算法思路基本逻辑很简单如果你和我过去喜欢的歌曲很相似或者你喜欢的歌和我过去喜欢的歌存在相似关系那我就把那些歌推荐给你前提是你还没听过。3.1 两种协同过滤怎么选基于用户的协同过滤 User-Based CF重点找相似用户。先通过用户对歌曲的评分计算两个用户的相似度再找一个目标用户最相似的 K 个用户把这 K 个用户喜欢且目标用户没听过的歌曲推荐过去。适合社区属性强、用户数量少于歌曲数量的场景。缺点是用户量增长后两两用户之间的相似度计算成本会快速上升。基于物品的协同过滤 Item-Based CF重点找相似歌曲。先根据所有用户对歌曲的行为计算歌曲和歌曲之间的相似度当用户对某首歌产生评分或播放行为时找出和这首歌最相似的歌作为推荐候选。音乐推荐系统里 Item-Based CF 用得更多因为歌曲集合相对稳定新增歌曲时只需要重算与它的相似度实时推荐响应更快。在毕设实现中更稳妥的设计是同时保留两种算法但默认走 Item-Based CF 主链路保留一名用户对比实验的空间。3.2 相似度计算两首歌之间的相似度可以用它们共同被用户评分的行为来衡量。最常用的是余弦相似度。两首歌 A 和 B 的评分向量分别为 a 和 b相似度公式是similarity (A · B) / (||A|| * ||B||)这个公式的含义是把歌手、歌词、封面这些属性放到一边只看用户行为。如果大多数给 A 歌打过分的用户也给了 B 歌高分那么 A 和 B 就可能被推断为相似。用 Python 实现一个简化的 Item-Based 相似度计算可以这样写import math def cosine_similarity(vec_a, vec_b): common_users set(vec_a.keys()) set(vec_b.keys()) if not common_users: return 0.0 dot sum(vec_a[u] * vec_b[u] for u in common_users) norm_a math.sqrt(sum(value ** 2 for value in vec_a.values())) norm_b math.sqrt(sum(value ** 2 for value in vec_b.values())) if norm_a 0.0 or norm_b 0.0: return 0.0 return dot / (norm_a * norm_b)这里 vec_a 和 vec_b 是字典key 是用户 IDvalue 是该用户对当前歌曲的评分。代码量不大却是整个推荐算法的地基论文里可以直接引用这一段做核心算法说明。3.3 冷启动和兜底策略协同过滤最大的问题是冷启动。新用户还没有评分历史找不到和他相似的用户也缺少他感兴趣的歌曲直接跑算法会得到空列表。新歌曲刚上架时没有用户行为同样无法被推荐。针对新用户可以设计一个热门兜底策略把歌曲按播放次数、收藏数或综合得分排序用户在没有任何行为记录时推荐页展示热门歌曲榜。等到用户产生至少 5 到 10 条有效评分行为后再自动切换为协同过滤推荐。针对新歌曲可以给它一个临时的新歌推荐位或使用内容相似策略将其挂在同歌手、同专辑、同风格的歌曲下面。答辩时如果被问到冷启动能回答出这两层设计项目完整度会明显提升。真实使用中评分矩阵会非常稀疏。大多数用户只会给少量歌曲评分直接计算所有歌曲两两相似度会浪费资源。实用的做法是只计算那些至少有共同评分用户的歌曲对提前过滤掉没有交集的歌曲避免大量无效计算。4. 数据库设计与 Django 模型推荐系统里最重要的数据不是用户资料而是用户与歌曲之间的行为关系。三条核心数据链路必须有用户、音乐、评分行为。评分行为表是协同过滤算法的数据来源。设计时需要记录哪个用户、对哪首歌曲、打了多少分或做了什么操作、在什么时间。为了让系统能支撑播放、收藏等不同行为可以额外存一个 behavior_type 字段。Django 模型参考如下from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): avatar models.URLField(blankTrue) class Meta: verbose_name 用户 verbose_name_plural verbose_name class Music(models.Model): title models.CharField(max_length128, verbose_name歌曲名) singer models.CharField(max_length128, verbose_name歌手) album models.CharField(max_length128, blankTrue, verbose_name专辑) cover_url models.URLField(blankTrue, verbose_name封面) audio_url models.URLField(blankTrue, verbose_name音频链接) play_count models.IntegerField(default0, verbose_name播放次数) created_at models.DateTimeField(auto_now_addTrue, verbose_name添加时间) class Meta: verbose_name 音乐 verbose_name_plural verbose_name class Rating(models.Model): BEHAVIOR_TYPE ( (play, 播放), (collect, 收藏), (score, 评分), ) user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name用户) music models.ForeignKey(Music, on_deletemodels.CASCADE, verbose_name歌曲) score models.PositiveSmallIntegerField(default0, verbose_name评分, help_text1-5分) behavior_type models.CharField(max_length10, choicesBEHAVIOR_TYPE, defaultplay) created_at models.DateTimeField(auto_now_addTrue, verbose_name操作时间) class Meta: verbose_name 用户行为 verbose_name_plural verbose_name unique_together (user, music, behavior_type)这里的 Rating 表既保存评分也保存播放和收藏行为。不同行为可以理解为不同强度的反馈。计算时给评分赋最高权重收藏次之播放最后这样可以避免用户只播放一次某首歌就让推荐结果发生剧烈变化。unique_together 约束能保证同一用户对同一首歌的同一种行为只存在一条记录后期做增量更新时不会出现重复累加问题。实际开发中如果想用 MySQL只需要在 Django settings.py 中替换 DATABASES 配置并安装对应数据库驱动。5. 本地开发环境准备开发这套系统不需要特殊硬件桌面版 CPU 就够。以下是毕设开发中最常见的一套环境组合版本号按当时稳定版本理解即可不用刻意追求最新。环境建议操作系统Windows 10/11、macOS、Ubuntu 均可Python3.8 及以上推荐 3.10/3.11Django3.2 LTS 或 4.xNode.js16 以上推荐 18 LTS前端工程Vue 3 Vite 或 Vue CLI开发工具VS Code、PyCharm、WebStorm推荐先创建 Python 虚拟环境再把后端依赖装进去。python -m venv venvWindows 下激活虚拟环境venv\Scripts\activatemacOS / Linux 下激活虚拟环境source venv/bin/activate后端依赖清单可以参考下面的写法具体版本建议按实际项目锁定Django4.2,5.0 djangorestframework3.14 django-cors-headers4.0安装依赖pip install -r requirements.txt前端部分先确认 Node.js 已安装然后创建 Vue 工程。用 Vite 创建npm create vitelatest music-frontend -- --template vue cd music-frontend npm install npm run devVue 开发服务器默认运行在 5173 端口Django 后端默认运行在 8000 端口。开发阶段需要处理跨域后端安装 django-cors-headers 并配置 CORS或在 Django 的响应中间件中开放允许来源。前者更省事毕设阶段推荐直接用 django-cors-headers。需要提醒的一点是如果本机已经安装过其他版本 Python建议在虚拟环境内部统一使用 python 命令不要混用多个 Python 路径否则会出现 pip 安装的依赖在后端启动时找不到的问题。6. Django 后端用户、歌曲、评分与推荐模块6.1 项目创建与目录划分后端项目建议按模块拆分不要把所有逻辑写进一个 app。一个适合扩展的结构是music-recommend-backend/ ├── manage.py ├── config/ # Django 项目配置 │ ├── settings.py │ ├── urls.py │ └── ... ├── apps/ │ ├── users/ # 用户与认证 │ ├── music/ # 歌曲管理与搜索 │ ├── behavior/ # 用户行为上报 │ └── recommend/ # 推荐引擎与推荐接口 ├── recommender/ # 协同过滤算法核心独立 Python 包 │ ├── matrix.py │ ├── similarity.py │ ├── item_cf.py │ └── user_cf.py ├── scripts/ │ ├── rebuild_similarity.py │ └── evaluate.py └── db.sqlite3Django 支持多个 app采用上面的结构后推荐算法的代码可以独立成 recommender 包不依赖 Django 的 ORM。负责算法实现的是纯 Python 模块负责数据读写的是 app 中的 service 层。这样在论文里解释“算法与业务解耦”会更容易。创建配置示例django-admin startproject config . python manage.py startapp users python manage.py startapp music python manage.py startapp behavior python manage.py startapp recommend然后在 config/settings.py 中 INSTALLED_APPS 注册这些应用。6.2 推荐服务与接口代码推荐视图层不应该直接写相似度计算循环而应该调用 recommender 包提供的接口。下面是一个简化的推荐服务调用方式# recommend/services.py from recommender.item_cf import ItemCFRecommender from music.models import Music, Rating def get_personal_recommend(user_id, top_n10): # 1. 从数据库取出该用户全部行为 ratings Rating.objects.filter(user_iduser_id).order_by(music_id) if len(ratings) 5: # 2. 行为太少走热门兜底 return list( Music.objects.order_by(-play_count)[:top_n] ) # 3. 行为足够走 ItemCF recommender ItemCFRecommender() candidates recommender.recommend(user_id, top_ntop_n) return candidates上面这个逻辑是“有行为且行为足够多协同过滤才有意义”的直接体现。推荐接口返回时需要把歌曲对象转成 JSON 需要的字典结构# recommend/views.py from django.http import JsonResponse from .services import get_personal_recommend def personal_recommend_api(request, user_id): top_n int(request.GET.get(top_n, 10)) results get_personal_recommend(user_id, top_ntop_n) data [] for item in results: if hasattr(item, music_id): data.append({ music_id: item.music_id, title: item.title, singer: item.singer, cover_url: item.cover_url, }) else: data.append({ music_id: item.id, title: item.title, singer: item.singer, cover_url: item.cover_url, }) return JsonResponse({code: 0, data: data, msg: success})这只是一个示例。如果项目使用了 Django REST framework可以把 JsonResponse 换成 drf 的 Response再加入序列化器做法是等价的。推荐算法的核心不放在 view 里是我比较建议的工程习惯将来答辩被提问时也可以直接讲 recommender 包里的相似度计算和矩阵构造过程。7. Vue 前端页面结构与数据请求前端页面建议至少覆盖以下几个视图。用户端需要登录注册页面、首页音乐大厅、搜索列表、歌曲详情页、我的评分/行为记录页面、个人推荐页面。管理端需要用户管理、歌曲管理、评分行为管理、推荐结果展示页面。前端请求逻辑可以统一封装一个 axios 实例自动携带 Token。下面是一个封装示例// src/utils/request.js import axios from axios; const service axios.create({ baseURL: /api, timeout: 10000 }); service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization Token ${token}; } return config; }); service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token); window.location.href /login; } return Promise.reject(error); } ); export default service;页面调用推荐接口时可以这样写// src/api/recommend.js import request from /utils/request; export function getPersonalRecommend(userId) { return request({ url: /recommend/${userId}, method: get, params: { top_n: 10 } }); }!-- 个人推荐页面组件片段 -- script setup import { ref, onMounted } from vue; import { getPersonalRecommend } from /api/recommend; const recommendList ref([]); const userId localStorage.getItem(user_id); onMounted(async () { const res await getPersonalRecommend(userId); recommendList.value res.data || []; }); /script template div h2猜你喜欢/h2 div v-ifrecommendList.length 0 classempty-tip 你还没有足够的行为记录先去听几首歌并评分吧。 /div div v-foritem in recommendList :keyitem.music_id classmusic-card img :srcitem.cover_url altcover / p{{ item.title }} - {{ item.singer }}/p /div /div /template这里的关键是前端展示“猜你喜欢”的前提是用户产生了真实行为。因此歌曲详情页里必须提供评分、播放、收藏按钮并且要正确上报到后端。只做展示而缺少行为入口会导致推荐结果永远为空这是前后端联调时最容易忽视的问题。常规页面还需要引入 Vue Router 做页面路由跳转例如 /login、/home、/search、/recommend、/admin 等页面。前端项目整理完毕后可以用 npm run build 构建出静态文件再由 Nginx 或 Django 托管也可在答辩演示时直接用开发服务器运行。8. 接口 API 与推荐任务更新推荐系统在接口层面主要涉及下面几个核心接口请求方式路径功能POST/api/users/register用户注册POST/api/users/login用户登录返回 TokenGET/api/music/歌曲列表GET/api/music/search?keywordxxx歌曲搜索POST/api/music/{music_id}/behavior上报播放、收藏、评分行为GET/api/recommend/{user_id}获取个人推荐列表POST/api/admin/recommend/rebuild管理员触发全量相似度重算接口路径只是为了讲解清晰实际项目里可按 Django 路由规则调整。需要特别注意的是一般要写成 /api/ 前缀方便后来用 Nginx 做同一域名的反向代理避免跨域配置。如果使用 Django REST framework行为上报接口可以写成这样# behavior/views.py from rest_framework.decorators import api_view from rest_framework.response import Response from music.models import Music, Rating api_view([POST]) def report_behavior(request, music_id): user request.user music Music.objects.filter(idmusic_id).first() if not music: return Response({code: 1, msg: 音乐不存在}, status404) behavior_type request.data.get(behavior_type, play) score request.data.get(score, 0) Rating.objects.update_or_create( useruser, musicmusic, behavior_typebehavior_type, defaults{score: score}, ) if behavior_type play: music.play_count 1 music.save(update_fields[play_count]) return Response({code: 0, msg: success})从批量角度考虑推荐不是每次请求都实时全量重算而是分成“实时推荐”和“离线更新”两条链路。实时推荐读取已经算好的相似度表或近邻结果离线更新脚本定期重算全部歌曲的相似度再把结果写进数据库或缓存。可以设计几个 Django management command方便后台定期执行# 全量重算歌曲相似度表建议低峰期执行 python manage.py rebuild_item_similarity # 为全量用户生成 Top-N 推荐记录 python manage.py generate_topn_for_all_users --top_n 20 # 执行离线效果评估脚本打印 Precision / Recall python manage.py offline_evaluate --test_size 0.2这三个命令名字可理解为推荐系统项目里比较通用的示例。管理员在后端执行后可以看到终端输出相似度计算耗时、推荐覆盖用户数、评估指标等日志。对于文档报告中“批量任务”的描述这一块是重要支撑。9. 推荐效果怎么验证推荐系统的效果验证不能只看“页面能不能打开”。答辩时老师更关心算法是否有效、如何评估。所以至少准备两个层面的验证。第一层是不需要写复杂公式就能做的规则验证。构造三个测试账号。账号 A 注册后不做任何操作打开推荐页应该看到热门歌曲榜账号 B 给某歌手多首歌打高分账号 C 有同样的收听偏好分别在两个账号上查看推荐结果确认 B 和 C 收到的推荐存在一定相似度随后给账号 B 增加完全不同类型的歌曲偏好再查看推荐结果变化。第二层是离线实验。把历史评分行为按比例切分成训练集和测试集训练集构造相似度测试集验证推荐结果。以每个用户为维度如果被推荐的前 N 首歌里包含测试集中用户真实听过的歌就判定一次命中。整体计算 PrecisionN、RecallN 和覆盖率。给出一个离线评估脚本框架可按自己的数据表结构调整# scripts/evaluate.py import random from collections import defaultdict def split_ratings(rating_list, test_ratio0.2, seed42): random.seed(seed) test random.sample(rating_list, int(len(rating_list) * test_ratio)) train [r for r in rating_list if r not in test] return train, test def precision_recall(recommend_fn, test_ratings, top_n10): user_hit defaultdict(int) user_total defaultdict(int) for record in test_ratings: user_id record[user_id] music_id record[music_id] rec_list recommend_fn(user_id, top_ntop_n) user_total[user_id] len(rec_list) if music_id in rec_list: user_hit[user_id] 1 precision sum(user_hit.values()) / max(1, sum(user_total.values())) recall sum(user_hit.values()) / max(1, len(test_ratings)) return precision, recall实际执行时需要注意评分行为越稀疏离线指标可能越低这是正常的不代表算法没有效果。评估报告里把冷启动用户和活跃用户分组统计会显得更有说服力。10. 常见问题与排查问题现象可能原因排查方式解决方案前端访问后端接口跨域报错后端未配置 CORS查看浏览器 Network 请求错误安装并配置 django-cors-headers登录后刷新页面状态丢失Token 只存在内存中查看 localStorage将 Token 与用户信息保存到 localStorage推荐页面列表始终为空用户行为数据不足查看数据库评分表记录数量增加热门兜底并引导用户完成评分django migrate 找不到数据表app 未注册到 INSTALLED_APPS检查 settings.py注册 app 后执行 makemigrationsPython 包安装后启动仍报模块不存在多个 Python 环境混用执行 which python、pip list在虚拟环境内重新安装依赖npm run dev 端口被占用5173 已被其他进程占用看终端报错提示使用 npm run dev -- --port 5174推荐结果全是同一歌手歌曲相似度计算未区分范围观察历史行为数据增加候选集的多样性过滤行为上报接口提示 401请求未携带 Token查看请求 Header在 axios 拦截器中附加 Authorization中文数据入库后乱码数据库字符集问题检查 MySQL 字符集建库时指定 utf8mb4表内字段对齐大批量用户推荐接口响应变慢每次推荐实时重算后端打印耗时日志预计算相似度表推荐时只查结果在这些问题里推荐结果为空和推荐实时计算耗时长是出现概率最高的两个。前者可以在用户行为不足时返回热门歌曲榜保证页面永远有内容后者可以通过一张推荐结果表来实现离线任务跑完后接口直接查表只返回 Top-N 歌曲 ID。11. 版权边界与工程化建议音乐推荐系统天然涉及音乐资源。做毕设时要注意一个边界系统是学习演示用途不是音乐下载平台。代码仓库和提交包里建议只保留歌曲元数据以及一段合法的试听链接不附带完整音乐文件、歌词文件、专辑封面等受版权保护的媒体资源。演示时可以自行使用无版权或已获授权的音乐数据不要从商业音乐平台批量抓取音源后随源码公开发布。工程化方面有几个建议。第一次接触这类全栈项目的同学不要先追求把推荐界面做得很好看。优先跑通一条最小链路注册用户给歌曲打评分管理端能看见评分数据推荐接口返回 Top-N 结果。这个链路跑通后再补页面细节。所有数据需要分目录管理。建议约定datasets/ # 原始数据集与预处理脚本 media/ music/ # 本地试听音频或封面 covers/ logs/ # 接口调用和推荐日志 outputs/ # 离线评估结果推荐引擎的日志要记录完整上下文包括用户 ID、行为来源、生成时间、候选集大小和返回 Top-N 结果。不要只是终端打印要落到文件。后续如果要做推荐策略调整这些日志是难得的判断依据。文档报告在毕设里同样重要。建议把以下内容写进文档系统需求分析、用例图、E-R 图、数据库表结构说明、协同过滤算法公式与实现细节、离线评估结果截图、核心接口说明、系统测试记录。如果已配有源码、文档报告和代码讲解这套结构已经比较适合直接作为毕业设计的起点。我建议先从 Item-Based CF 开始实现只使用评分向量和余弦相似度跑通后再加用户实时行为权重。协同过滤算法虽然经典但完整做完环境搭建、算法实现、接口联调、指标评估这四步后对推荐系统整体的理解会比直接看完结论深刻得多。下一步如果你想扩展最容易加的几项是用户行为时间衰减、歌曲热门度惩罚、物品相似度离线更新调度、前端“相似歌曲”区块。每加一项都可以在文档报告里增加一个小节也让答辩时可讲的内容更充实。
返回列表