ARTICLE DETAIL

资讯详情

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

基于协同过滤的Python音乐推荐系统实现与工程落地

基于协同过滤的Python音乐推荐系统实现与工程落地 很多做 Python 毕业设计的同学看到“推荐系统”四个字第一反应是先去找算法公式第二反应是担心“数学不好能不能做”。实际上毕设里的推荐系统并没有那么神秘。它最核心的协同过滤思想翻译成大白话就是一句话把跟你口味相似的人喜欢的歌推荐给你或者把你平时听过的歌的“同类歌”推荐给你。这个课题能在本科毕设里持续热门原因是它天然具备“算法有亮点、工程可落地、界面能展示、论文有素材”四个特点。你可以用 Python 独立完成后端推荐引擎用 Django 暴露接口再用 Vue 做前端页面。整个过程没有一项技术是超纲的但它合成的效果却足以撑起一份完整的毕设项目。这篇文章会把整个系统从零到一拆开讲重点解决三类问题协同过滤到底是什么在音乐场景里应该怎么建模Django 如何描述用户、歌曲和评分行为算法如何写进业务代码Vue 前端如何把“为你推荐”变成可点击、可播放、可收藏的真实页面。内容末尾还整理了常见异常和答辩高频问题方便直接对照复盘。1. 先想清楚一件事你做的不是算法 Demo而是一个系统很多人做毕设容易走偏花两周调一个 Python 脚本打印出几个相似度数字就以为推荐系统做完了。但答辩时老师看重的往往不是你的相似度公式写得多细而是你有没有把推荐这个动作完整放进业务流程里。推荐系统在真实产品中应该具备三块联动能力行为采集用户收藏了哪首歌、听了多少秒、是否评分。没有行为数据算法就是无源之水。算法计算维护一张“用户-歌曲-评分”关系表并在此基础上完成相似度计算和 TopN 排序。前端触达推荐结果最终要出现在页面里以列表形式呈现比如“猜你喜欢”“相似歌曲”。论文里可以写“本系统基于协同过滤算法实现个性化推荐”但工程里真正要落地的是上面这串完整链路。接下来的内容会以这个链路为主线展开。1.1 这个系统的技术选型为什么是 Django Vue先用一句话总结结论Django 负责把“数据模型、推荐计算、API 接口”包成后端服务Vue 负责把后端计算结果渲染成用户能看懂的界面。这个组合在本科毕设里流行有三个直接原因Django 自带 ORM 和 Admin 后台建表、加数据、管理歌曲不需要额外开发一套后台页面Django REST Framework 可以快速把 Python 对象转换成 JSON 接口刚好喂给前端Vue 单页组件写法直观用来做“播放器 推荐列表 收藏按钮”这类交互开发量可控。数据库方面如果是在本机演示使用 Django 默认的 SQLite 即可。SQLite 单文件部署对毕设最友好。如果导师明确要求“必须使用 MySQL”你只需要在项目中替换DATABASES配置然后重新执行迁移命令模型层代码不需要大改。2. 基础概念协同过滤的两种核心玩法在写代码之前需要用“场景化”的方式把协同过滤讲明白。如果你答辩时能用自己的话复述这个东西老师基本不会再刁难。2.1 基于用户的协同过滤User-Based CF假设用户 A 喜欢《晴天》《七里香》《夜曲》用户 B 喜欢《晴天》《七里香》《以父之名》系统会认为 A 和 B 的听歌口味高度相似于是把 B 听过的《以父之名》推给 A。这种玩法更关心“人与人之间的关系”适合社交属性强、用户量中等的场景。缺点是系统刚上线时用户行为太少很难找到相似用户也就是常说的冷启动问题。2.2 基于物品的协同过滤Item-Based CF假设你在听《七里香》系统发现听过《七里香》的人有很大概率也收藏了《简单爱》于是把《简单爱》推荐给你。它不关心你和别人像不像它关心歌曲与歌曲之间被同时消费的关联性更适合网易云音乐、QQ 音乐这类曲库相对稳定的产品。在实际项目中更稳的做法是同时实现这两种算法然后在 API 层留一个参数指定当前走哪套策略。这样不仅论文里能多写一个对比分析点答辩时也会有更多可以讲的内容。3. 环境准备与前置条件下面这组环境清单按当前主流适配情况整理。具体到某个同学的机器版本可能略有差异但这组配置基本可以稳定跑通。依赖版本建议用途说明Python3.10推荐算法与 Django 运行环境Django4.x LTSWeb 后端框架djangorestframework3.14快速生成 JSON APIdjango-cors-headers4.x解决前后端分离的跨域问题Vue CLI / ViteVue 3.x前端开发工具链Node.js18前端脚手架运行环境SQLiteDjango 内置本地默认数据库如果还没装 Python建议先确认 Python 能正常执行python --version pip --version然后在虚拟环境中安装后端依赖mkdir music-recommend cd music-recommend python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activate pip install django djangorestframework django-cors-headers安装完成后创建 Django 项目django-admin startproject config . python manage.py startapp music python manage.py startapp recommend项目结构初步规划如下music-recommend/ ├── config/ # Django 主配置 │ ├── settings.py │ └── urls.py ├── music/ # 歌曲与用户行为模块 │ ├── models.py │ └── views.py ├── recommend/ # 推荐算法模块 │ ├── utils.py │ └── views.py ├── manage.py └── db.sqlite3 # 默认数据库文件接下来把music和recommend两个应用注册到INSTALLED_APPS中。然后在全局路由里配置 API 前缀# config/urls.py from django.contrib import admin from django.urls import path, include urlpatterns [ path(admin/, admin.site.urls), path(api/music/, include(music.urls)), path(api/recommend/, include(recommend.urls)), ]这样设计的好处是后端业务按模块拆分推荐算法独立成应用。以后想替换算法只需要改业务逻辑不需要改路由配置。4. 后端模型设计用数据表描述用户行为在音乐推荐系统里模型设计直接决定推荐算法的质量。我们以“歌手Singer— 歌曲Song— 评分Rating”三张核心表为例来进行搭建。打开music/models.py完整代码如下from django.db import models from django.conf import settings class Singer(models.Model): 歌手表 name models.CharField(max_length100, verbose_name歌手名称) avatar models.URLField(blankTrue, verbose_name封面地址) description models.TextField(blankTrue, verbose_name歌手简介) class Meta: verbose_name 歌手 verbose_name_plural verbose_name def __str__(self): return self.name class Song(models.Model): 歌曲表 title models.CharField(max_length200, verbose_name歌曲名称) singer models.ForeignKey( Singer, on_deletemodels.CASCADE, related_namesongs, verbose_name歌手 ) cover models.URLField(blankTrue, verbose_name封面地址) audio_url models.URLField(verbose_name音频播放地址) duration models.IntegerField(default0, verbose_name时长秒) play_count models.IntegerField(default0, verbose_name播放次数) publish_date models.DateField(nullTrue, blankTrue, verbose_name发行日期) class Meta: verbose_name 歌曲 verbose_name_plural verbose_name ordering [-play_count] def __str__(self): return f{self.title} - {self.singer.name} class Rating(models.Model): 用户评分表 user models.ForeignKey( settings.AUTH_USER_MODEL, on_deletemodels.CASCADE, related_nameratings, verbose_name用户 ) song models.ForeignKey( Song, on_deletemodels.CASCADE, related_nameratings, verbose_name歌曲 ) score models.IntegerField( default5, verbose_name评分1-5 ) created_at models.DateTimeField(auto_now_addTrue, verbose_name评分时间) class Meta: verbose_name 用户评分 verbose_name_plural verbose_name unique_together (user, song) def __str__(self): return f{self.user.username} 评 {self.song.title}{self.score}这里需要解释几个关键设计决策Rating使用unique_together (user, song)目的是避免用户对同一首歌重复评分。如果用户再次评分应该走更新逻辑而不是再插入一条记录。audio_url和cover使用 URLField前端播放器可以直接使用该地址方便后续接入实际音频资源。每张表都定义了verbose_name这会让 Django Admin 后台的显示更规范同时处理较为美观。完成模型后执行迁移命令python manage.py makemigrations python manage.py migrate python manage.py createsuperuser启动开发服务器后登录后台http://127.0.0.1:8000/admin/可以录入几组真实数据。推荐数据条数大致为歌手 10 个左右每名歌手关联 5-10 首歌然后注册 3-5 个测试用户分别对歌曲进行评分。这些测试数据是算法展示效果的关键基础。5. 协同过滤推荐算法的核心实现打开recommend/utils.py以下代码实现的逻辑属于教学中较常用的“基于物品的协同过滤 基于用户的协同过滤”双策略。from math import sqrt from collections import defaultdict from music.models import Rating, Song def get_user_item_matrix(): 将评分数据转换为 用户 - {歌曲ID: 评分} 的字典结构 user_item defaultdict(dict) ratings Rating.objects.all().select_related(user, song) for r in ratings: user_item[r.user_id][r.song_id] r.score return dict(user_item) def get_item_user_matrix(user_item): 转换为 歌曲ID - {用户ID: 评分} 的字典结构方便计算歌曲相似度 item_user defaultdict(dict) for user_id, songs in user_item.items(): for song_id, score in songs.items(): item_user[song_id][user_id] score return dict(item_user) def cosine_similarity(vec1, vec2): 基于共同评分用户的余弦相似度计算 common set(vec1.keys()) set(vec2.keys()) if not common: return 0.0 dot sum(vec1[u] * vec2[u] for u in common) norm1 sqrt(sum([v ** 2 for v in vec1.values()])) norm2 sqrt(sum([v ** 2 for v in vec2.values()])) if norm1 0 or norm2 0: return 0.0 return dot / (norm1 * norm2) def user_similarity_matrix(user_item): 计算用户之间的相似度矩阵 users list(user_item.keys()) sim_matrix defaultdict(dict) for i in range(len(users)): for j in range(i 1, len(users)): sim cosine_similarity(user_item[users[i]], user_item[users[j]]) if sim 0: sim_matrix[users[i]][users[j]] sim sim_matrix[users[j]][users[i]] sim return dict(sim_matrix) def recommend_by_user_based(user_id, top_n10): 基于用户的协同过滤推荐 user_item get_user_item_matrix() if user_id not in user_item: return [] sims user_similarity_matrix(user_item) target_user_songs user_item[user_id] score_dict defaultdict(float) # 找到与当前用户最相似的若干个用户 sorted_sims sorted(sims.get(user_id, {}).items(), keylambda x: x[1], reverseTrue)[:5] for other_user_id, sim_value in sorted_sims: for song_id, score in user_item[other_user_id].items(): if song_id in target_user_songs: continue score_dict[song_id] sim_value * score ranked_songs sorted(score_dict.items(), keylambda x: x[1], reverseTrue)[:top_n] return [song_id for song_id, _ in ranked_songs] def build_item_similarity_matrix(): 预计算歌曲之间的相似度矩阵返回 歌曲A - 歌曲B - 相似度 user_item get_user_item_matrix() item_user get_item_user_matrix(user_item) item_ids list(item_user.keys()) sim_matrix defaultdict(dict) for i in range(len(item_ids)): for j in range(i 1, len(item_ids)): sim cosine_similarity(item_user[item_ids[i]], item_user[item_ids[j]]) if sim 0: sim_matrix[item_ids[i]][item_ids[j]] sim sim_matrix[item_ids[j]][item_ids[i]] sim return dict(sim_matrix) def recommend_by_item_based(user_id, top_n10): 基于物品的协同过滤推荐 user_item get_user_item_matrix() if user_id not in user_item: return [] item_sim build_item_similarity_matrix() user_history user_item[user_id] score_dict defaultdict(float) # 遍历用户听过的歌找出每首歌的相似歌曲 for listened_song_id, rating in user_history.items(): for similar_song_id, sim in item_sim.get(listened_song_id, {}).items(): if similar_song_id in user_history: continue score_dict[similar_song_id] sim * rating ranked_songs sorted(score_dict.items(), keylambda x: x[1], reverseTrue)[:top_n] return [song_id for song_id, _ in ranked_songs]代码里需要重点掌握三个细节相似度计算现在采用的方法是先找出两个向量里的共同元素再做余弦相似度计算。这里的“向量”可以是一个用户在不同歌曲上的评分序列也可以是一首歌在不同用户下的评分序列它不影响代码复用。两级过滤相似度矩阵计算完成之后生成候选集时会显式跳过用户已经听过的歌这样可以避免“推荐用户已经听过的歌”这种低体验的场景。计算时机在数据量很小的毕设场景里接口每次临时计算相似度是可行的。但如果你演示时有几百个用户甚至更多需要在论文里写明“在线计算成本高”进而选择预先算好相似度矩阵、离线缓存相似度结构的方法。答辩时老师很可能会顺着这个问题往下问。6. 用 Django REST Framework 暴露推荐接口推荐算法算完后前端需要的不是一个歌曲 ID 列表而是完整的歌曲信息例如歌名、歌手、封面和音频地址。所以接口层负责拼接数据返回。在recommend/views.py中编写from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from music.models import Song from music.serializers import SongSerializer from .utils import recommend_by_item_based, recommend_by_user_based class RecommendSongView(APIView): 获取推荐歌曲列表 permission_classes [IsAuthenticated] def get(self, request): algorithm request.query_params.get(algorithm, item) user request.user if algorithm user: song_ids recommend_by_user_based(user.id, top_n10) else: song_ids recommend_by_item_based(user.id, top_n10) # 防止数据库里还没有评分数据导致报错 songs Song.objects.filter(id__insong_ids) if song_ids else Song.objects.none() # 保持推荐顺序filter(id__in...) 不会按列表顺序返回这里手动排序 song_map {song.id: song for song in songs} ordered_songs [song_map[sid] for sid in song_ids if sid in song_map] serializer SongSerializer(ordered_songs, manyTrue, context{request: request}) return Response({ code: 0, message: success, data: serializer.data })这里需要说明一个隐藏问题Song.objects.filter(id__insong_ids)返回的顺序和song_ids列表的顺序是不一致的。为了让前端展示顺序和算法打分顺序保持一致我做了一次手动映射排序。这个细节在面试或答辩中如果主动说出来属于“实战经验型亮点”。music/serializers.py中定义嵌套的歌曲序列化器from rest_framework import serializers from .models import Singer, Song, Rating class SingerSerializer(serializers.ModelSerializer): class Meta: model Singer fields [id, name, avatar, description] class SongSerializer(serializers.ModelSerializer): singer SingerSerializer(read_onlyTrue) class Meta: model Song fields [id, title, singer, cover, audio_url, duration, play_count, publish_date]然后在recommend/urls.py中配置路由from django.urls import path from .views import RecommendSongView urlpatterns [ path(songs/, RecommendSongView.as_view(), namerecommend-songs), ]同时在music/urls.py里提供歌曲列表、评分、收藏等常用接口接口。评分接口代码如下# music/views.py from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated from django.shortcuts import get_object_or_404 from .models import Song, Rating class RateSongView(APIView): 给歌曲评分重复评分则更新分数 permission_classes [IsAuthenticated] def post(self, request): song_id request.data.get(song_id) score request.data.get(score, 5) try: score int(score) if score 1 or score 5: raise ValueError except (TypeError, ValueError): return Response({code: 400, message: 评分需为 1-5 的整数}) song get_object_or_404(Song, idsong_id) rating, created Rating.objects.update_or_create( userrequest.user, songsong, defaults{score: score} ) return Response({ code: 0, message: 评分成功 if created else 评分已更新, data: {rating_id: rating.id, score: rating.score} })上述代码用到了 Django ORM 中非常实用的一招update_or_create。它把“有则更新、无则新建”的逻辑压缩成了一行代码避免先filter().exists()再update()的繁琐写法。7. Vue 前端把推荐结果变成可交互页面前端采用 Vue 3 Axios 开发。这里不展开脚手架创建细节核心思路是页面加载时向后端推荐接口发起请求拿到data后渲染到“推荐歌曲”列表中。template div classcontainer h2猜你喜欢/h2 div classtoolbar button clickloadRecommend(item) :class{ active: algorithm item }相似歌曲推荐/button button clickloadRecommend(user) :class{ active: algorithm user }相似用户推荐/button /div div v-ifloading classloading推荐加载中.../div div v-else classsong-list div classsong-card v-forsong in songs :keysong.id img :srcsong.cover || defaultCover alt封面 classcover / div classinfo div classtitle{{ song.title }}/div div classsinger{{ song.singer.name }}/div audio :srcsong.audio_url controls/audio /div /div /div p v-if!loading songs.length 0 classempty 暂无推荐结果请先给几首歌曲打分 /p /div /template script import axios from axios; export default { name: RecommendList, data() { return { songs: [], loading: false, algorithm: item, defaultCover: https://via.placeholder.com/100, }; }, methods: { async loadRecommend(algorithm) { this.algorithm algorithm; this.loading true; try { const response await axios.get(/api/recommend/songs/, { params: { algorithm }, headers: { Authorization: Bearer ${localStorage.getItem(token)} }, }); this.songs response.data.data; } catch (error) { console.error(加载推荐失败, error); this.songs []; } finally { this.loading false; } }, }, mounted() { this.loadRecommend(item); }, }; /script style scoped .container { max-width: 900px; margin: 0 auto; padding: 20px; } .toolbar { margin-bottom: 20px; } .toolbar button { margin-right: 10px; padding: 8px 16px; border: 1px solid #ddd; border-radius: 4px; background: #fff; cursor: pointer; } .toolbar button.active { background: #409eff; color: #fff; border-color: #409eff; } .song-card { display: flex; align-items: center; background: #f9f9f9; border-radius: 8px; padding: 12px; margin-bottom: 12px; } .cover { width: 80px; height: 80px; border-radius: 8px; margin-right: 16px; object-fit: cover; } .info { flex: 1; } .title { font-size: 18px; font-weight: bold; } .singer { color: #888; margin: 4px 0 8px; } audio { width: 100%; } .empty { text-align: center; color: #999; padding: 40px 0; } /style这个组件里隐藏了一个工程化问题开发环境里 Vue 页面通过 Axios 请求 Django 后端涉及“跨域”问题。为了顺利联调需要在 Django 的settings.py中增加INSTALLED_APPS [ # ... corsheaders, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, # ... ] CORS_ALLOW_ALL_ORIGINS True生产环境不允许CORS_ALLOW_ALL_ORIGINS True这种做法只适合本地开发验证。更稳妥的做法是配置具体的前端域名白名单例如CORS_ALLOWED_ORIGINS [ http://localhost:5173, http://127.0.0.1:5173, ]8. 运行验证与观察推荐效果完成以上步骤后按顺序做一轮全链路验证。8.1 准备测试数据通过 Django Admin 或命令行录入测试数据这里给出三条模拟评分数据。用 shell 代码添加测试用户并评分python manage.py shellfrom django.contrib.auth.models import User from music.models import Singer, Song, Rating singer Singer.objects.create(name某知名歌手) song1 Song.objects.create(title歌曲A, singersinger, audio_urlhttps://example.com/a.mp3) song2 Song.objects.create(title歌曲B, singersinger, audio_urlhttps://example.com/b.mp3) song3 Song.objects.create(title歌曲C, singersinger, audio_urlhttps://example.com/c.mp3) u1, _ User.objects.get_or_create(usernameu1, passwordpbkdf2_sha256$) u2, _ User.objects.get_or_create(usernameu2, passwordpbkdf2_sha256$) Rating.objects.create(useru1, songsong1, score5) Rating.objects.create(useru1, songsong2, score4) Rating.objects.create(useru2, songsong1, score5) Rating.objects.create(useru2, songsong3, score3)执行完成后退出 shell。注意上面的password只是占位在实际登录验证时需要对这些测试用户设置真实密码u1.set_password(123456) u1.save() u2.set_password(123456) u2.save()8.2 通过接口验证推荐结果启动 Djangopython manage.py runserver使用 Token 或 JWT 获取用户身份后请求curl -H Authorization: Bearer token \ http://127.0.0.1:8000/api/recommend/songs/?algorithmitem预期返回的 JSON 结构如下{ code: 0, message: success, data: [ { id: 3, title: 歌曲C, singer: { id: 1, name: 某知名歌手 }, audio_url: https://example.com/c.mp3, play_count: 0 } ] }8.3 怎么判断推荐是否正常不要只看“有没有返回数据”要检查以下三个维度推荐列表里是否排除了用户已评分的歌曲用户 A 和用户 B 的历史行为不同调换用户身份请求接口时推荐结果是否存在差异把测试用户历史行为修改为完全离群数据后推荐结果是否随之改变。如果两组不同用户的推荐结果完全相同大概率是评分矩阵没有按用户区分需要回查get_user_item_matrix()里r.user_id取到的是不是同一个用户。9. 常见问题与排查思路做这类系统的过程中大家容易遇到的问题我做了一个总结式的清单。它本身也是论文“系统测试与问题分析”章节的直接素材。问题现象可能原因排查方式解决方案推荐接口报 401前端请求头没有携带 Token打开浏览器 Network 面板查看 Authorization 字段在 Axios 拦截器中统一注入 Token推荐结果为空当前用户没有任何评分行为调接口前检查数据库中 Rating 表无行为数据时返回热门歌曲兜底而不是返回空列表推荐列表顺序与打分不一致filter(id__in...)不保证顺序打印song_ids列表与接口返回列表对比建立id - Song映射后手动排序用户相似度全是 0两个用户没有共同评分过的歌曲检查余弦相似度函数中共同元素集合是否为空补充测试用户之间的共同评分记录接口请求跨域前后端端口不同查看浏览器 Console 的 CORS 错误配置django-cors-headers并在白名单里添加前端地址item 推荐与 user 推荐结果相同用户相似度矩阵或物品相似度矩阵存在退化打印矩阵查看相似度较大值是否集中在少数歌曲上增加差异化数据检查是否误传入同一张矩阵用户重复评分的记录出现多条没有为user song建唯一约束查看 Django Admin 的 Rating 记录在 Meta 中添加unique_together使用update_or_create保证幂等新用户登录后没有推荐冷启动问题检查该用户是否产生过行为记录冷启动阶段用“热门歌曲榜”做替代推荐方案音频地址无法播放在线音频资源本身跨域或失效用浏览器直接打开audio_url测试替换为可公开访问的音频直链或在前端配置代理算法返回极慢在线计算所有相似度未做缓存统计接口耗时时长离线预计算相似度矩阵并缓存到文件或数据库9.1 冷启动怎么在代码头解决“冷启动”是答辩时老师最常问的问题之一。实际产品中面对新用户没有行为数据推荐系统无法建模。毕设 Demo 可以采用常见工程解法新用户注册后默认展示全站播放量最高的 Top 10 歌曲作为热门推荐只有在用户产生足够评分之后系统才启用协同过滤推荐在接口层的判断逻辑为先检查用户是否存在评分记录不存在则走热门兜底逻辑。这样不存在“推荐结果为空”的问题可以直接跨过数据不足带来的体验尴尬。热门兜底的推荐接口可简单实现为from music.models import Song from music.serializers import SongSerializer def recommend_hot_songs(top_n10): 冷启动阶段没有行为数据时返回全站热门 songs Song.objects.order_by(-play_count)[:top_n] return songs9.2 “评分预测”和“TopN 推荐”有什么区别很多教材会把协同过滤分成两个问题方向评分预测目标是预测用户会给某首歌打几分常用评估指标是 RMSE、MAETopN 推荐目标是生成一个用户可能喜欢的歌曲列表常用评估指标是准确率、召回率。本系统采用的方式是 TopN 推荐因此返回结果的接口设计用列表形式呈现推荐候选歌曲。很多同学在论文里把这两类问题混着写导致“问题定义不清”直接被导师打回。建议在第 4 章就明确写下这一段本系统完成的是 TopN 推荐因此评估指标不采用 RMSE而是重点分析推荐结果是否贴合用户兴趣。10. 最佳实践与工程建议如果你的目标不只是一份“能跑过的代码”而是希望导师觉得你具备工程能力和安全规范可以把下面的建议内化进代码与文档中。10.1 算法层相似度矩阵“离线计算在线读取”项目数据量较小时接口内实时计算相似度完全没问题。但如果是答辩现场演示点击一次推荐按钮要卡住三四秒体验会很糟糕。更规范的做法是写一个manage.pycommand 或定时任务每隔一段时间预计算一次相似度矩阵把矩阵结果缓存进数据库或内存例如使用 JSON 字段或 Redis用户请求时直接读取缓存好的相似度矩阵只对当前用户执行打分求和 TopN 排序。这样可大幅优化响应时间。在论文系统设计部分你可以给出这样的说明“系统采用离线计算、在线推荐的服务架构”。老师听了会觉得你有生产环境意识。10.2 冷启动与数据稀疏问题要写进论文更完整的协同过滤毕设都需要回应“数据稀疏”的经典质疑。你的论文里最好有一小节专门说明当用户-歌曲评分矩阵非常稀疏时基于共同评分的余弦相似度可能不准确当前系统使用了“热门歌曲兜底”策略保证数据不足时也能返回可用推荐未来改进方向可以加入基于内容特征的 Bandit 策略或 Embedding 化召回。10.3 数据与代码安全既然是系统开发建议遵循与实际工业一致的部署意识使用 Djangocreatesuperuser录入的测试密码不要在代码中明文保存管理员口令。生成接口返回歌曲详情时只返回与前端展示有关的字段涉及内部状态字段的处理建议单独设计。批量写测试数据时先备份好原始数据再执行 Django 的 shell 脚本。推荐接口需要登录态验证不推荐将接口完全公开。10.4 前后端分离配置建议如果你的前端使用的是 Vite 开发服务器Proxy 方式比跨域更自然。在vite.config.js中增加export default { server: { proxy: { /api: { target: http://127.0.0.1:8000, changeOrigin: true, }, }, }, };配置好代理之后前端代码里的请求地址可以统一写成相对路径/api/xxx不需要在多个组件里硬编码完整域名。10.5 包管理和版本锁定毕设提交时建议统一锁定版本避免换一台电脑跑不起来。可使用pip freeze requirements.txt提交的代码包里保留requirements.txt并附上版本说明。11. 总结与后续学习方向至此你已经有能力完整地搭建一个前后端分离的音乐推荐系统使用 Django 描述了歌手、歌曲和用户评分三类核心数据基于用户评分矩阵实现了基于物品与基于用户两种协同过滤算法通过 Django REST Framework 把推荐结果转换成 JSON 接口用 Vue 制作了一个“猜你喜欢”页面并能切换两套算法查看效果梳理了冷启动、相似度计算、接口顺序等问题并给出排查方法。如果这个项目用作毕业设计接下来的优化方向我建议按你论文的定位来从算法对比角度切入的同学可以增加评价指标比如把推荐集合与用户真实喜好进行比对从系统实现角度切入的同学可以把评分矩阵的构造做成动态采集并把播放行为变成可量化的隐式反馈对前端展示有追求的同学可以在列表里加入收藏、歌词滚动、播放历史形成更完整的用户闭环。整理代码和文档时建议专门把“推荐算法如何从模型设计一路走到前端展示”这条主线写清楚。毕设答辩最看重的是主线清晰所有模块最好都能像链条一样衔接自然。代码跑通后再回头给自己的软件配上一份可复现的操作说明这份工作对未来的项目复盘和改进都会有明显帮助。
返回列表