
很多做 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 制作了一個“猜你喜歡”頁面并能切換兩套算法查看效果梳理了冷啟動、相似度計算、接口順序等問題并給出排查方法。如果這個項目用作畢業設計接下來的優化方向我建議按你論文的定位來從算法對比角度切入的同學可以增加評價指標比如把推薦集合與用戶真實喜好進行比對從系統實現角度切入的同學可以把評分矩陣的構造做成動態采集并把播放行為變成可量化的隱式反饋對前端展示有追求的同學可以在列表里加入收藏、歌詞滾動、播放歷史形成更完整的用戶閉環。整理代碼和文檔時建議專門把“推薦算法如何從模型設計一路走到前端展示”這條主線寫清楚。畢設答辯最看重的是主線清晰所有模塊最好都能像鏈條一樣銜接自然。代碼跑通后再回頭給自己的軟件配上一份可復現的操作說明這份工作對未來的項目復盤和改進都會有明顯幫助。