简介:这份资源面向计算机相关专业的毕业生与课程设计学习者,提供一套基于Python机器学习算法的电影推荐系统与票房预测系统完整方案,可用于毕业设计、期末大作业等高分场景。压缩包共59个文件,约30.95MB,包含16个py源码文件、16个csv数据集、23张png图表、2个md说明文档及1份pdf报告,覆盖数据预处理、特征工程、模型训练与结果可视化全流程。推荐部分涉及协同过滤、基于内容的推荐及KNN、SVD等混合模型,票房预测部分则采用线性回归、随机决策树等方法,并配有代码注释,新手也能理解。项目目录按data、prediction、ensemble_recommender、personal_recommender、report等模块划分,结构清晰,便于按需查阅与二次开发。目前已有343人学习下载,适合希望快速掌握推荐与预测算法实践、需要完整可运行项目参考的读者。
1. 从一份 98 分毕设拆起:电影推荐与票房预测到底怎么落地
很多同学做毕设时最头疼的不是写不出代码,而是不知道一个「完整系统」该长什么样。这份资源给了一个很具体的答案:一个用 Python 和机器学习算法搭起来的电影推荐系统加票房预测系统,附带源码、文档和 PDF 报告。它不是那种只跑一个模型的 demo,而是把推荐和预测两条线都做全了,从数据预处理、特征工程到模型训练、结果输出,整条链路都有对应文件。适合正在做毕业设计、期末大作业或者课程设计的同学,也适合想拿一个真实项目练手 Python 机器学习的人。你拿到手之后,最直接的价值是:不用从零想架构,照着目录就能理解一个合格系统该有哪些模块,每个模块之间怎么衔接。
2. 推荐系统模块拆解:从协同过滤到内容推荐的代码结构
2.1 推荐系统为什么拆成这么多文件
打开recommender文件夹,你会看到Demographic.py、Content.py、Keyword.py这几个文件,它们分别对应不同的推荐策略。这种拆分方式不是随便起的名字,而是按推荐算法的主流分类来的。常见做法是:基于人口统计学的推荐(Demographic)负责处理用户画像和基础分组,基于内容的推荐(Content)负责分析电影本身的特征,基于关键词的推荐(Keyword)则是在内容基础上做更细粒度的匹配。这样拆的好处是,每个文件只解决一类问题,调试的时候能快速定位是哪个策略出了问题,而不是在一个几千行的脚本里大海捞针。
naive_recommender文件夹里放的是最基础的推荐实现,适合新手先跑通流程。ensemble_recommender则是把多种推荐结果融合在一起,比如KNN_SVD_ensemble.py就是把 KNN 和 SVD 两种协同过滤方法做了集成。personal_recommender更偏向个性化,里面有KNN_movie.py、Personal_SVD.py、KNN_user.py,分别从电影维度、用户维度和矩阵分解维度做推荐。这种分层设计在毕设里很加分,因为导师一眼就能看出你理解不同算法的适用场景,而不是随便调个库就交差。
2.2 协同过滤的核心代码逻辑
以KNN_user.py为例,基于用户的协同过滤核心思路是:找到和目标用户兴趣相似的一批用户,把他们喜欢但目标用户没看过的电影推荐出来。下面是一个简化后的代码骨架,你可以对照源码理解参数含义。
import numpy as np from sklearn.neighbors import NearestNeighbors # 假设 user_movie_matrix 是用户-电影评分矩阵,行是用户,列是电影 # 缺失评分用 0 填充,实际项目中会做均值中心化处理 user_movie_matrix = np.array([ [5, 3, 0, 1], [4, 0, 0, 1], [1, 1, 0, 5], [0, 0, 5, 4], [0, 1, 5, 4], ]) # n_neighbors=2 表示找最相似的 2 个用户 # metric='cosine' 用余弦相似度,适合评分矩阵稀疏的场景 model_knn = NearestNeighbors(metric='cosine', algorithm='brute', n_neighbors=2) model_knn.fit(user_movie_matrix) # 对第 0 个用户做推荐 target_user_index = 0 distances, indices = model_knn.kneighbors( user_movie_matrix[target_user_index].reshape(1, -1), n_neighbors=2 ) # indices 里包含目标用户自己和最相似的邻居 # 实际推荐时要把目标用户已经看过的电影排除掉 print("相似用户索引:", indices) print("相似度距离:", distances)这段代码里,n_neighbors控制找几个相似用户,一般设 5 到 20 之间,太小容易推荐不准,太大又会引入不相关的人。metric='cosine'是常见选择,因为电影评分矩阵通常很稀疏,余弦相似度对缺失值不那么敏感。algorithm='brute'表示暴力搜索,数据量小的时候没问题,数据量大了要换成kd_tree或ball_tree。跑完拿到相似用户后,下一步就是加权计算推荐分数,把邻居看过而目标用户没看过的电影按分数排序输出。
2.3 矩阵分解在推荐里的位置
Personal_SVD.py用的是 SVD 矩阵分解。它的逻辑是把用户-电影评分矩阵拆成用户隐向量和电影隐向量,用隐向量的内积来预测缺失评分。相比 KNN,SVD 能更好地处理稀疏数据,而且预测结果更平滑。常见做法是先用surprise库或者scipy.sparse.linalg.svds做分解,然后取前 N 个隐因子。参数上,隐因子数量一般从 20 到 200 之间调,太少欠拟合,太多容易过拟合。学习率和正则化系数也要根据验证集表现来定,不能拍脑袋。
ensemble_recommender里的KNN_SVD_ensemble.py把两种方法的结果做了加权融合。融合权重可以按验证集上的 RMSE 来定,比如 KNN 权重 0.4、SVD 权重 0.6,哪个在验证集上误差小就给它更高权重。这种集成思路在毕设里属于加分项,因为单一算法往往有局限,集成能互补。
3. 票房预测模块实战:特征工程与模型训练怎么接
3.1 票房预测的数据从哪来
prediction文件夹和master data里的tmdb_5000_movies.csv、tmdb_5000_credits.csv是票房预测的数据基础。TMDB 5000 数据集包含电影预算、票房、类型、演员、导演、上映日期等字段,足够做特征工程。FeatureEDA文件夹里的single_feature_visual.py是用来做单特征可视化的,帮你快速看某个特征和票房的关系,比如预算和票房是不是线性相关,上映月份有没有季节性规律。
票房预测的特征工程比推荐系统更依赖领域知识。常见做法是:把类型字段做 one-hot 编码,把演员和导演的知名度用历史作品平均票房来衡量,把上映日期拆成月份和季度,把预算取对数来缓解长尾分布。calculate.py里应该包含了这些特征计算逻辑,你可以对照源码看每个特征是怎么构造出来的。
3.2 线性回归和决策树在票房预测上的对比
票房预测常用的模型有线性回归、随机森林、梯度提升树。线性回归可解释性强,能直接看出每个特征对票房的影响方向和大小,但假设特征和票房是线性关系,实际中往往不成立。随机森林能捕捉非线性关系,对异常值也不敏感,但可解释性差一些。下面是一个用随机森林做票房预测的代码示例,你可以对照prediction文件夹里的实现来理解。
import pandas as pd from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score import numpy as np # 读取特征工程后的数据 df = pd.read_csv('master data/tmdb_5000_movies.csv') # 选取数值型特征和编码后的类别特征 # budget 取对数,因为票房和预算都是长尾分布 df['log_budget'] = np.log1p(df['budget']) df['log_popularity'] = np.log1p(df['popularity']) feature_cols = ['log_budget', 'log_popularity', 'runtime', 'vote_average'] # 实际项目中还会加入类型 one-hot、演员知名度等特征 X = df[feature_cols].fillna(0) y = df['revenue'].fillna(0) # 取对数让目标变量更接近正态分布 y = np.log1p(y) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # n_estimators=100 表示 100 棵树 # max_depth=10 控制树深,防止过拟合 # min_samples_split=5 表示节点最少 5 个样本才分裂 rf = RandomForestRegressor( n_estimators=100, max_depth=10, min_samples_split=5, random_state=42, n_jobs=-1 ) rf.fit(X_train, y_train) y_pred = rf.predict(X_test) rmse = np.sqrt(mean_squared_error(y_test, y_pred)) r2 = r2_score(y_test, y_pred) print(f"RMSE: {rmse:.4f}, R2: {r2:.4f}")这段代码里,np.log1p是处理长尾分布的常用手段,票房和预算的原始分布偏斜很严重,取对数后模型更容易学到稳定关系。n_estimators设 100 是起点,实际可以试 200 到 500,看验证集误差有没有下降。max_depth控制模型复杂度,太深会过拟合,太浅会欠拟合,一般从 5 到 15 之间调。min_samples_split也是防过拟合的参数,样本量小的时候可以设大一点。跑完看 RMSE 和 R2,R2 能到 0.6 以上就算不错了,票房预测本身难度大,不要期望太高。
3.3 训练集和测试集怎么切
personal_recommender里有train.csv和test.csv,说明作者已经把数据切好了。常见做法是按时间切,比如用 2015 年之前的电影做训练,2015 年之后的做测试,这样更接近真实预测场景。如果随机切,可能会把同一部电影的信息泄露到测试集里,导致评估结果虚高。test.py应该是用来跑测试集评估的脚本,你可以直接运行看输出结果。
4. 避坑与排查:跑这份源码时最容易翻车的几个地方
4.1 路径问题导致文件读不到
现象:运行recommender.py或test.py时报FileNotFoundError,提示找不到tmdb_5000_movies.csv或train.csv。原因:代码里用的是相对路径,而你的工作目录和作者当时的不一样。解决:在脚本开头加import os; os.chdir(os.path.dirname(os.path.abspath(__file__))),把工作目录切到脚本所在目录,或者把所有路径改成绝对路径。我一般会在项目根目录放一个config.py,把所有数据路径集中管理,换环境时只改一个文件。
4.2 依赖库版本不匹配
现象:ImportError或AttributeError,比如sklearn某个模块找不到,或者surprise库装不上。原因:作者用的库版本和你本地的不一致,或者surprise在 Windows 上编译需要 C 编译器。解决:先看README.md里有没有写依赖版本,没有的话按报错信息逐个装。surprise装不上可以换scikit-surprise的预编译版本,或者直接用scipy的 SVD 替代。常见做法是建一个虚拟环境,python -m venv venv,然后在虚拟环境里按需安装,避免污染全局环境。
4.3 数据缺失值处理不当导致模型报错
现象:训练模型时提示Input contains NaN或could not convert string to float。原因:TMDB 数据集里budget、revenue、runtime都有缺失值,有些字段还是字符串格式。解决:在特征工程阶段统一做fillna(0)或dropna(),字符串字段用pd.to_numeric(errors='coerce')转成数值。票房预测里revenue为 0 的记录要小心,可能是缺失而不是真的零票房,直接当 0 处理会拉低模型效果。
4.4 推荐结果全是已经看过的电影
现象:跑完推荐脚本,输出的电影用户早就看过了。原因:没有过滤掉用户已经评分过的电影。解决:在生成推荐列表后,用用户历史记录做差集,只保留没看过的。这个逻辑在KNN_user.py和Personal_SVD.py里都应该有,如果源码里漏了,自己补上就行。常见做法是维护一个seen_movies集合,推荐时排除掉。
4.5 票房预测 R2 为负数
现象:模型评估时 R2 是负的,说明模型比直接预测均值还差。原因:特征工程没做好,或者训练集和测试集分布差异太大。解决:先检查特征和票房的相关性,把不相关的特征去掉;再检查是不是用了未来信息,比如用上映后的评分去预测票房;最后看数据量够不够,TMDB 5000 只有 5000 条,去掉缺失值后可能只剩 3000 多条,模型容易过拟合。可以试试交叉验证,看不同折上的表现是否稳定。
5. 进阶技巧:把推荐和预测串起来做一个完整演示
5.1 用 Flask 搭一个最小可用的接口
毕设答辩时,如果能现场演示一个网页界面,效果会比只跑脚本好很多。常见做法是用 Flask 写两个接口:一个接收用户 ID 返回推荐电影列表,一个接收电影特征返回预测票房。下面是一个最小示例。
from flask import Flask, request, jsonify import pandas as pd import numpy as np from sklearn.ensemble import RandomForestRegressor app = Flask(__name__) # 加载训练好的模型和数据 # 实际项目中模型应该提前训练好并保存,这里只做演示 df = pd.read_csv('master data/tmdb_5000_movies.csv') df['log_budget'] = np.log1p(df['budget'].fillna(0)) feature_cols = ['log_budget', 'runtime', 'vote_average'] X = df[feature_cols].fillna(0) y = np.log1p(df['revenue'].fillna(0)) rf = RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42) rf.fit(X, y) @app.route('/predict', methods=['POST']) def predict(): data = request.get_json() # 接收预算、片长、评分三个特征 budget = float(data.get('budget', 0)) runtime = float(data.get('runtime', 0)) vote_average = float(data.get('vote_average', 0)) features = np.array([[np.log1p(budget), runtime, vote_average]]) log_revenue = rf.predict(features)[0] revenue = np.expm1(log_revenue) return jsonify({'predicted_revenue': round(revenue, 2)}) if __name__ == '__main__': app.run(debug=True, port=5000)这个接口接收 JSON 格式的请求,返回预测票房。np.expm1是np.log1p的逆运算,把对数结果还原成原始票房。实际部署时要把模型用joblib保存下来,接口里直接加载,不用每次重新训练。推荐接口类似,接收用户 ID,返回推荐列表的 JSON。
5.2 验证推荐效果的一个实用指标
推荐系统不像票房预测有明确的 RMSE,评估起来更麻烦。一个实用做法是留一法:对每个用户,把他评分最高的那部电影藏起来,用剩下的数据训练,看推荐列表里有没有藏起来的那部。命中率越高,推荐效果越好。这个逻辑可以在test.py里实现,跑一遍就能得到一个可量化的指标,答辩时拿出来很有说服力。
5.3 我踩过的一个坑
有一次我直接把train.csv和test.csv按随机方式切,结果推荐效果虚高,因为同一个用户的行为同时出现在训练集和测试集里。后来改成按用户切,保证一个用户要么在训练集要么在测试集,评估结果才真实。从那以后我每次做推荐评估都强制走一遍用户维度的切分,再也不敢图省事用随机切了。希望帮到你。
本文还有配套的精品资源,点击获取