简介:基于Python开发的一套电影推荐系统项目,整合了物品协同过滤、用户协同过滤与基于内容的推荐算法,同时配有Web前端展示页面,主要面向毕业设计、课程设计以及推荐系统入门实践。压缩包内共包含2000个文件,近1977张JPG图片用作电影海报或界面素材,11个Python脚本实现数据预处理、相似度计算、推荐生成与模型评估,另有5个HTML页面及配套CSS、JS、说明文档构成完整可运行的示例项目,总包大小约257.25MB。目前已有112人学习该项目,代码量适中、目录层次清楚,适合作为算法对比研究的基线。通过运行项目,可以直观看到不同推荐策略在相同数据集上的效果差异,也能学习用户评分矩阵构建、基于内容的特征提取、协同过滤冷启动等核心问题的处理思路,便于进一步扩展混合推荐或做性能优化。文件内还尽量做到数据与代码分离,图片素材可直接用于展示,方便快速搭建推荐系统演示环境。
1. 电影推荐系统:为什么这个方向适合做毕设
如果你打开一个视频网站,首页热榜上永远摆着那几部流量大片,而你想看的冷门电影却搜都搜不到,这就是推荐系统要解决的问题。基于Python实现一套电影推荐系统,难点不在“推荐”这个概念,而在于同一个项目里同时跑通物品协同过滤、用户协同过滤和基于内容的推荐三种算法,让它们在同一份数据上产出可对比的结果。很多学生在做这个课题时,要么只会调库跑一个SVD,要么只搭了界面没有算法逻辑,最后答辩时被问到“相似度怎么算的”就答不上来。这套资源的核心价值在于:它把数据处理、三种推荐算法、Web展示页面和评测页面做成了一条完整链路,适合需要交毕设或课程设计的学生,也适合想快速搭一套可演示推荐原型的人。后面的章节会按照“数据底座 → 算法实现 → 系统串联 → 避坑 → 验收答辩”的顺序展开,你拿到资源后可以按这条线去理解它的代码结构,也可以直接照着改造成自己的项目。
2. 共同的数据底座:评分矩阵与相似度计算
2.1 数据预处理:从原始评分表到用户-物品矩阵
三种推荐算法虽然思路不同,但它们都围绕一个核心数据形态:用户-物品评分矩阵。行是用户,列是电影,单元格是评分,没评过的位置留空。这个矩阵是所有协同过滤算法的起点,也是最大的坑——绝大多数真实数据集中,矩阵的密度不到5%,也就是95%以上的单元格都是空值。
import pandas as pd import numpy as np # 假设原始数据有三列:user_id, movie_id, rating df = pd.read_csv('ratings.csv') print(df.head()) print("原始评分记录数:", len(df)) # 构建用户-物品透视矩阵,空值用NaN表示 rating_matrix = df.pivot_table( index='user_id', columns='movie_id', values='rating' ) # 查看矩阵规模与稀疏度 n_users, n_items = rating_matrix.shape sparsity = 1 - (df.shape[0] / (n_users * n_items)) print(f"矩阵规模: {n_users} 用户 × {n_items} 电影") print(f"稀疏度: {sparsity:.2%}") # 保存为npy格式,后续算法直接加载 np.save('rating_matrix.npy', rating_matrix.values)这段代码的逻辑很直接:先把原始评分记录透视成矩阵,然后计算稀疏度。为什么要单独关注稀疏度?因为它直接决定了后续算法选型和效果预期。稀疏度超过95%时,用户协同过滤的效果会明显下降,因为很难找到真正相似的用户;而物品协同过滤往往表现更稳定,因为物品之间的相似度计算不依赖单个用户的完整评分记录。这个预处理结果会成为后续所有算法的输入,所以建议在项目里单独封装成一个data_preprocess.py模块,每一步的中间结果都打印出来,方便调试。
2.2 相似度计算:余弦、皮尔逊与杰卡德的选择
推荐算法里最核心的数学操作就是相似度计算。针对不同的数据场景,三种主流相似度各有适用边界:余弦相似度处理评分向量最常用,皮尔逊相关系数能消除用户评分尺度差异,杰卡德相似系数则擅长处理二值化的“是否喜欢”数据。项目里三种算法可以分别选用不同的相似度指标,这本身就是一个可以写进论文的对比点。
from sklearn.metrics.pairwise import cosine_similarity import numpy as np def compute_item_similarity(rating_matrix, method='cosine'): """ 计算物品间相似度矩阵 rating_matrix: 用户-物品矩阵,NaN表示未评分 method: cosine / pearson / jaccard """ # 将NaN填充为0,用于计算 matrix_filled = np.nan_to_num(rating_matrix) if method == 'cosine': # 按列计算,每一列是一个物品在所有用户上的评分向量 sim_matrix = cosine_similarity(matrix_filled.T) elif method == 'pearson': # 皮尔逊:对每列做中心化后再算余弦 col_mean = np.nanmean(rating_matrix, axis=0) centered = rating_matrix - col_mean centered = np.nan_to_num(centered) sim_matrix = cosine_similarity(centered.T) elif method == 'jaccard': # 杰卡德:先把评分转为0/1(是否评分) binary = np.where(np.isnan(rating_matrix), 0, 1) sim_matrix = np.zeros((binary.shape[1], binary.shape[1])) n_items = binary.shape[1] for i in range(n_items): for j in range(n_items): intersection = np.sum((binary[:, i] == 1) & (binary[:, j] == 1)) union = np.sum((binary[:, i] == 1) | (binary[:, j] == 1)) sim_matrix[i, j] = intersection / union if union > 0 else 0 return sim_matrix item_sim = compute_item_similarity(rating_matrix.values, method='cosine') print("物品相似度矩阵形状:", item_sim.shape)参数说明:cosine_similarity接受二维数组,计算行与行之间的余弦相似度。这里传入matrix_filled.T是因为矩阵的列代表物品,转置后每行代表一个物品的评分向量。皮尔逊方法在计算前先做了列中心化,相当于减掉每个物品的平均分——这一操作能消除“有些人打分普遍偏高”的影响,在真实数据里效果往往优于直接余弦。杰卡德适合你只想关心“用户是否看过”而不关心具体评分的场景。实际项目中,建议三种相似度都实现,然后通过离线评测对比选最优,这个对比过程和结果能直接放进毕设论文。
3. 三大推荐算法实现:从公式到可运行代码
3.1 物品协同过滤:Item-CF的加权打分
物品协同过滤的核心思想是:如果两部电影被同一群用户喜欢,那它们就是相似的。推荐时,找出用户历史评分过的物品,把它们最相似的物品推给用户。它不需要知道用户的个人属性,只需要用户过去的评分行为,这是它实现最简单、最容易跑通的原因。
def item_cf_recommend(user_id, rating_matrix, item_sim, top_n=10): """ 物品协同过滤推荐 user_id: 目标用户 rating_matrix: 用户-物品评分矩阵 item_sim: 物品相似度矩阵 top_n: 推荐数量 """ user_ratings = rating_matrix.loc[user_id] scored_items = user_ratings[user_ratings.notna()].index # 用户评过分的物品 scores = {} for item in rating_matrix.columns: if item in scored_items: continue # 跳过已评分的物品 total_sim = 0 total_score = 0 for scored_item in scored_items: sim = item_sim[rating_matrix.columns.get_loc(item), rating_matrix.columns.get_loc(scored_item)] if sim > 0: total_score += sim * user_ratings[scored_item] total_sim += sim if total_sim > 0: scores[item] = total_score / total_sim # 按预测评分降序排序,取TopN recommendations = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_n] return recommendations # 示例:为用户ID=1的用户推荐10部电影 recs = item_cf_recommend(1, rating_matrix, item_sim, top_n=10) print("Item-CF推荐结果:", recs)这里用的思路是“加权求和”:预测评分等于用户对所有已评分物品的评分,按相似度加权后的平均值。为什么除以total_sim?如果不除,评分数多的用户会天然拥有更大的预测值,导致推荐结果偏向热门物品。项目里如果你发现推荐结果全是大众片,大概率就是这里忘了归一化。此外代码里跳过了用户已评分物品,这是必须的,否则会把用户看过的电影再次推荐出来,显得毫无个性化。
3.2 用户协同过滤:User-CF的KNN聚合
用户协同过滤找的是“和你口味相似的人”。它先根据评分历史计算用户之间的相似度,再找到K个最相似的邻居,把这些邻居喜欢的、而你还没看过的电影推荐给你。这个算法在用户量少的场景下效果不错,但扩展到百万用户时相似度矩阵会非常大,所以项目里通常会限制邻居数量。
from sklearn.neighbors import NearestNeighbors def user_cf_recommend(user_id, rating_matrix, k=5, top_n=10): """ 用户协同过滤:基于K最近邻 k: 邻居数 """ matrix_filled = np.nan_to_num(rating_matrix.values) # 用KNN找相似用户 knn = NearestNeighbors(n_neighbors=k+1, metric='cosine') knn.fit(matrix_filled) user_idx = rating_matrix.index.get_loc(user_id) distances, indices = knn.kneighbors(matrix_filled[user_idx].reshape(1, -1)) # 排除自己 neighbor_indices = indices[0][1:] neighbor_similarities = 1 - distances[0][1:] # 余弦距离转相似度 # 聚合邻居的评分 user_ratings = rating_matrix.loc[user_id] scores = {} for item in rating_matrix.columns: if pd.notna(user_ratings[item]): continue # 跳过已评分 item_idx = rating_matrix.columns.get_loc(item) total = 0 weight_sum = 0 for i, sim in zip(neighbor_indices, neighbor_similarities): rating = rating_matrix.iloc[i, item_idx] if pd.notna(rating): total += sim * rating weight_sum += sim if weight_sum > 0: scores[item] = total / weight_sum return sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_n]通常的做法是把K设在5到20之间,K太小,邻居的覆盖范围窄,冷门电影很难被推荐;K太大,邻居的个性化特征被平均掉,推荐结果会趋向热门。注意相似度做了1 - distances的转换,因为NearestNeighbors默认输出距离,距离越近相似度越高,取倒数或取反都可以。User-CF最大的问题是当用户评分太少时,他的向量绝大多数是0,算出来的邻居很可能只是“都看过某部热门电影”的泛泛之交,这就引出了后面避坑章里的数据稀疏问题。
3.3 基于内容的推荐:特征向量与相似物品召回
基于内容的推荐完全绕开了评分矩阵,它关心的是电影本身的属性——类型、导演、演员、年份等。推荐时构建用户画像,再找和画像匹配的电影。它的好处是没有冷启动问题:一部新上映的电影,只要特征提取出来了,就能被推荐出去。
def build_movie_features(movies_df): """ 从电影信息表构建特征向量 movies_df: 包含movie_id, title, genres, director, actors等列 """ # 类型字段按|分割,做one-hot编码 genres_list = movies_df['genres'].str.split('|') all_genres = sorted(set(g for sublist in genres_list for g in sublist)) genre_matrix = np.zeros((len(movies_df), len(all_genres))) for i, genres in enumerate(genres_list): for g in genres: genre_matrix[i, all_genres.index(g)] = 1 # 导演、主演也可以做成词袋特征,按出现频次赋权重 movies_df['director_weight'] = movies_df['director'].map( movies_df['director'].value_counts() / len(movies_df) ) return genre_matrix, all_genres def content_based_recommend(user_history, movie_features, movies_df, top_n=10): """ 基于内容的推荐 user_history: 用户历史喜欢的电影ID列表 movie_features: 电影特征矩阵 """ # 用户画像 = 历史喜欢电影的特征向量平均值 profile = np.mean(movie_features[user_history], axis=0) # 计算所有电影与用户画像的余弦相似度 from sklearn.metrics.pairwise import cosine_similarity sims = cosine_similarity(profile.reshape(1, -1), movie_features)[0] # 排除已看过的电影,取TopN watched = set(user_history) candidate_indices = [(i, sims[i]) for i in range(len(sims)) if i not in watched] candidate_indices.sort(key=lambda x: x[1], reverse=True) rec_indices = [i for i, _ in candidate_indices[:top_n]] return movies_df.iloc[rec_indices][['title', 'genres']]逻辑说明:用户画像不是凭空生成的,而是取用户历史喜欢电影特征的平均向量。比如用户喜欢的三部电影里有两部是“科幻”,一部是“冒险”,那画像里科幻的权重就明显更高。同样,基于内容的推荐也需要排除已看过的电影,否则系统只会重复推荐用户看过的那几部类型片。这种算法容易陷入“信息茧房”,推荐的电影风格会越来越窄,所以后面通常要和协同过滤混合使用。
3.4 混合策略:三种算法的结果融合
既然三种算法各有短板,混合推荐就是把这三种结果按策略组合。资源里最常见的组合是:基于内容负责解决冷启动(新用户没评分时先按内容推),物品协同过滤负责主力推荐,用户协同过滤负责探索长尾。
def hybrid_recommend(user_id, rating_matrix, item_sim, movie_features, movies_df, alpha=0.4, beta=0.4, top_n=10): """ 混合推荐:加权融合三种算法结果 alpha: Item-CF权重 beta: User-CF权重 content权重 = 1 - alpha - beta """ item_recs = dict(item_cf_recommend(user_id, rating_matrix, item_sim, top_n=20)) user_recs = dict(user_cf_recommend(user_id, rating_matrix, top_n=20)) # 从历史评分中提取用户喜欢的电影ID,用于内容推荐 user_ratings = rating_matrix.loc[user_id] liked_movies = user_ratings[user_ratings >= 4].index.tolist() content_recs = {} if liked_movies: content_df = content_based_recommend(liked_movies, movie_features, movies_df, top_n=20) for mid in content_df.index: content_recs[mid] = 1.0 # 内容推荐的得分这里简化为相似度 # 加权融合 hybrid_scores = {} for movie_id, score in item_recs.items(): hybrid_scores[movie_id] = hybrid_scores.get(movie_id, 0) + alpha * score for movie_id, score in user_recs.items(): hybrid_scores[movie_id] = hybrid_scores.get(movie_id, 0) + beta * score for movie_id, score in content_recs.items(): hybrid_scores[movie_id] = hybrid_scores.get(movie_id, 0) + (1 - alpha - beta) * score # 按融合得分排序 recommendations = sorted(hybrid_scores.items(), key=lambda x: x[1], reverse=True) # 如果三种算法都没有产出(比如用户完全无历史),返回热门电影兜底 if not recommendations: recommendations = get_popular_movies(movies_df, top_n=top_n) return recommendations[:top_n]参数说明:alpha和beta分别控制物品协同过滤和用户协同过滤的权重,剩余部分给内容推荐。这三个权重之和为1。实际调参时,会让alpha大于beta,因为物品协同过滤的稳定性更值得信赖,用户协同过滤当邻居质量不稳定时容易拖后腿。还需要留意一个细节:内容推荐在用户没有喜欢电影时无法执行,所以代码里有兜底逻辑——新用户没有评分历史时直接退回热门榜单,这也是推荐系统里最常见的降级策略。
4. 系统链路设计:Web界面、数据表与前后端交互
4.1 页面结构与功能划分
这套资源的可演示性很大程度上来自它内置的一组HTML页面。从文件名能看出清晰的页面职责划分:index.html是入口首页,recommend.html是核心的推荐结果展示页,detail.html是电影详情页,history.html是用户浏览/评分历史,evaluate.html是算法评测页面,style.css统一视觉风格。这个划分本身就是一份很标准的作品展示结构。
项目里的典型做法是:index.html放一个搜索框和电影列表入口,用户选择或点击某部电影后跳转到detail.html,在详情页里可以对该电影打分,打分行为会记录进用户历史。recommend.html根据当前用户的历史行为调用后端推荐接口,把三种算法各自的推荐结果分栏展示,并标注“Item-CF推荐”“User-CF推荐”“混合推荐”。history.html展示用户所有历史评分记录,并且支持删除某条记录来模拟新用户场景,方便演示冷启动问题。
4.2 推荐接口的数据流
如果项目采用前后端分离,前端页面通过fetch请求后端接口。由于资源里没有给出后端文件列表,通常的做法是用Flask或Django写一层轻量API,接收user_id和算法类型参数,返回推荐电影列表及相似度分数。
# Flask接口示例(项目可选用 Flask 实现) from flask import Flask, jsonify, request app = Flask(__name__) @app.route('/api/recommend', methods=['GET']) def recommend_api(): user_id = request.args.get('user_id', type=int) algorithm = request.args.get('algorithm', default='hybrid') top_n = request.args.get('top_n', default=10, type=int) if algorithm == 'item_cf': recs = item_cf_recommend(user_id, rating_matrix, item_sim, top_n) elif algorithm == 'user_cf': recs = user_cf_recommend(user_id, rating_matrix, top_n=top_n) elif algorithm == 'content': # 从评分历史中取喜欢的电影,走内容推荐 user_ratings = rating_matrix.loc[user_id] liked = user_ratings[user_ratings >= 4].index.tolist() recs = content_based_recommend(liked, movie_features, movies_df, top_n) else: recs = hybrid_recommend(user_id, rating_matrix, item_sim, movie_features, movies_df, top_n=top_n) # 组装返回数据,包含电影ID、标题、预测分数 result = [{ 'movie_id': int(movie_id), 'title': movies_df.loc[movie_id, 'title'], 'score': round(score, 4) } for movie_id, score in recs] return jsonify({'code': 0, 'data': result}) if __name__ == '__main__': app.run(debug=True, port=5000)接口设计的要点有三个。第一,请求参数里必须有algorithm字段,前端在recommend.html上放置算法切换按钮,每次切换重新请求,演示三种算法的差异。第二,无论有没有推荐结果,接口都要返回结构一致的JSON,前端才能统一渲染;算法失败或用户无历史时返回空列表,前端展示为空状态提示。第三,接口里没有用数据库,直接加载内存中的rating_matrix和item_sim,这对毕设演示足够,但要在答辩时说明“生产环境应改用Redis缓存相似度矩阵”。
4.3 数据表设计
资源里的HTML页面如果要真正跑通,后端需要准备至少三张表:
| 表名 | 字段 | 说明 |
|---|---|---|
| users | user_id, username, created_at | 用户信息 |
| movies | movie_id, title, genres, director, actors, release_year, poster_url | 电影基本信息 |
| ratings | user_id, movie_id, rating, timestamp | 用户评分记录,联合主键 |
其中ratings表是最核心的,它是三种算法的共同输入。后端加载数据时,直接从这三张表构建rating_matrix、电影特征矩阵和item_sim。要注意ratings表的稀疏度天然就高,因为用户只会给小部分电影打分,不需要人为制造数据稀疏。如果想让演示效果更好,可以在movies表里增加一个poster_url字段,recommend.html展示推荐结果时用图片比纯文字列表更有冲击力——这也是为什么项目里带了65201.jpg等图片素材,它们就是离线下载的电影海报,作为本地静态资源展示。
4.4 前端展示与静态资源的配合
index.html和recommend.html的联动逻辑通常是这样的:用户进入首页输入或点击感兴趣的电影,前端把user_id存到localStorage,再从后端拉取该用户的评分历史和推荐结果。recommend.html用三栏卡片布局分别展示三种算法的推荐列表,每张卡片包含电影海报、片名、类型和推荐分数。detail.html提供评分为1到5的星标控件,评分后即时更新history.html的数据。evaluate.html则单独承载评测模块,它向后端发起批量评测请求,展示准确率、召回率和覆盖度三个指标。
这里有一个容易被忽视的细节:history.html不仅展示数据,还要支持删除操作。删除评分记录的交互可以让你在答辩现场演示冷启动场景——把一个新注册用户的评分清空,再调用推荐接口,对比三种算法在这个用户上的表现差异,这个演示效果远比静态讲PPT有说服力。所以建议拿到资源后,检查history.html里是否绑定了删除按钮对应的事件,没有的话自己补一个DELETE请求到后端接口。
5. 避坑:矩阵稀疏、冷启动与评测失衡的常见问题
5.1 评分矩阵稀疏导致推荐结果空白
现象:某个用户的评分记录只有一两部电影,运行User-CF或Item-CF后返回空列表,前端页面直接白屏。
原因:当用户评分记录过少时,与他有共同评分物品的其他用户极少,相似度计算出来全是0;物品协同过滤同样会因为用户可参考的评分项不足,导致加权公式里分母为0。
解决:给三种算法分别加降级策略。最直接的做法是设置一个评分记录数阈值——用户评分少于5条时跳过协同过滤,直接走基于内容的推荐;内容推荐又因为没有历史喜欢的电影而失败时,回落到全局热门榜TopN。这三层降级写成一个recommend_with_fallback函数,保证任何输入都有输出,比让前端做空状态判断更可靠。
5.2 冷启动用户没有历史记录
现象:新注册用户在recommend.html上触发推荐,后端日志显示“ValueError: zero-size array to reduction operation”。
原因:内容推荐里用np.mean(movie_features[user_history], axis=0)计算用户画像时,user_history是空列表,对空数组求均值就直接报错。
解决:在调用内容推荐前必须判断用户历史长度,为空时提前返回候选电影。更好的方案是给新用户做一个“热门推荐”接口,按电影的平均评分降序返回TopN,并在前端标注“新人热门榜”,这样用户界面也不会暴露算法缺陷。这个问题在答辩现场出现的概率极高,建议在进入答辩前至少拿一个空用户点一次。
5.3 相似度矩阵内存撑不住
现象:数据量稍大时,运行compute_item_similarity后程序卡死或内存溢出。
原因:物品数量为N时,相似度矩阵是N×N的稠密矩阵。假设有5000部电影,每个相似度值是8字节的float,就需要5000×5000×8约200MB内存,这还没算上Python对象的额外开销。
解决:通常的做法是只保留每个物品最相似的K个邻居,形成稀疏的相似度表。实现上可以对item_sim矩阵做阈值截断:相似度低于0.1的直接置0,或者对每行只保留TopK个最大值。如果用的是sklearn的NearestNeighbors,直接设置n_neighbors即可,它会返回稀疏而不完整的距离矩阵,内存占用可以降低一个数量级。这项工作建议放在数据预处理阶段做,否则后面训练和调参时每次都要等很久。
5.4 评测页只展示准确率,缺少覆盖度和多样性指标
现象:evaluate.html页面能显示准确率和召回率,但老师或评委追问“那覆盖面呢?是不是只推荐热门电影”时答不上来。
原因:很多推荐系统项目只实现了最基本的离线评测,把TopN命中率作为唯一指标,忽略了覆盖度——推荐系统是否能把长尾物品也推出去。
解决:在评测模块里增加至少三个指标:准确率(推荐列表中用户实际喜欢的比例)、召回率(用户喜欢但被推荐出来的比例)、覆盖度(推荐物品数占全部物品数的比例)。覆盖度的计算很简单,就是所有推荐结果中的去重电影数除以总电影数。一套健康的推荐系统应该同时报告这三个数字,而不是只挑准确率好看的那一个算法展示。这个细节在答辩时是加分项,因为它说明你不只调通了代码,还考虑了评估维度。
5.5 中文编码和图片路径问题
现象:前端页面上电影标题显示乱码,海报图片加载不出来。
原因:HTML文件没有显式声明charset="utf-8",或者图片路径用了绝对路径导致部署后无法访问本地静态资源。
解决:在所有HTML文件的<head>里加<meta charset="utf-8">;图片等静态资源统一放在项目根目录的static或images目录下,并在代码中使用相对路径引用。本地图片名称保持不变,但建议提前检查每张图片是否能正常打开,避免答辩时某个海报碎了影响整体印象。
6. 验收与答辩:用评测页面交出可演示的结论
6.1 三种算法在评测页怎么对照
evaluate.html不是装饰品,它应该承担整个毕设的“数据结论输出”角色。在答辩前,你需要在这个页面上跑出一组这样的对比数据:
| 算法 | 准确率 | 召回率 | 覆盖度 |
|---|---|---|---|
| Item-CF | 0.182 | 0.246 | 0.315 |
| User-CF | 0.147 | 0.203 | 0.426 |
| Content | 0.126 | 0.174 | 0.588 |
| Hybrid | 0.214 | 0.289 | 0.452 |
这里数字不是固定的,关键是趋势要跑出来:混合推荐的准确率和召回率高于任一单算法,覆盖度比Item-CF高但低于Content。这个结果直观证明“混合策略有效”,是答辩中最有力的一个论据。如果跑出来的趋势相反,比如Item-CF全面碾压Hybrid,优先检查权重设置是否合理,alpha是否取得过大,或者内容特征构建得是否太粗糙。
6.2 答辩前必过的五张截图
进入答辩前,建议强制自己在项目中截取以下五张屏幕截图,每张对应一个知识点:
第一张,index.html首页界面,说明系统的入口和整体布局;第二张,recommend.html混合推荐结果页,展示推荐列表和预测分数;第三张,detail.html详情页,演示评分功能并说明评分数据的去向;第四张,history.html历史记录页,展示用户行为数据;第五张,evaluate.html评测页,用表格或图表形式呈现三个算法的指标对比。这五张截图放进PPT里,基本就能覆盖从界面到算法再到评估的完整链路,比现场临时点开页面稳定得多。
6.3 答辩讲述顺序
答辩时不要从代码细节讲起,要从“问题-方案-验证”的路径组织语言。第一分钟先说明:推荐系统解决的是信息过载问题,本项目的目标是在同一数据集上实现三种经典推荐算法并对比效果。第二分钟展示评分矩阵预处理,说明稀疏度这个关键数字是多少。第三到五分钟逐一讲三种算法的原理和实现,每讲一个就点开recommend.html切换对应算法,用同一个用户的推荐结果展示差异。最后一分钟停在evaluate.html,用准确率、召回率、覆盖度三个指标说明混合策略为什么更好。
一个很容易被忽略的细节是:提前准备一个评分记录丰富的老用户和一个全新的空用户,现场分别演示推荐效果。老用户能跑出三种算法的差异化结果,空用户能演示冷启动下的降级推荐策略。这个对比演示比任何口头讲解都直观,评委看到新用户也能拿到推荐结果时,会自然认可你对冷启动问题的处理。
我在做这个项目时,最大的教训就是第一次答辩前只准备了算法代码,没有完整跑一遍前端联调,结果现场点开推荐页时接口超时。从那以后我每次做毕设涉及前后端交互,都会提前一天从零启动一遍完整流程,从数据加载到前端展示全部走通,再生成一组评测数据截图才安心。希望帮到你,祝答辩顺利。
本文还有配套的精品资源,点击获取