
简介这是一份面向计算机专业毕业设计的高分项目——基于机器学习算法实现电影票房预测源自大四学生经导师认可的98.5分优秀设计适合正在选题或需要实战参考的毕设学生也可用于课程设计与期末大作业。压缩包共59个文件约30.95MB内含16个Python源码、16个CSV数据、23张PNG图表、2份Markdown分析文档和1份PDF报告覆盖数据读取、特征可视化、K近邻/SVD及集成推荐等核心模块。内容预览显示目录结构清晰主数据来自TMDB 5000电影与演员数据集并分别实现基础推荐、集成推荐和个性化推荐三套方案附带电影数据分析报告与单特征可视化脚本。目前已有267人学习下载。文件类型分工明确Python脚本便于直接运行与扩展CSV数据支持复现实验图表与报告完整呈现建模过程及结论可帮助读者快速掌握从数据处理到模型评估的完整流程。1. 基于机器学习算法的电影票房预测这个毕设选题为什么值得做毕设答辩现场老师问“你的电影票房预测模型比直接猜一个数字强在哪”这是基于机器学习算法的票房预测项目里最容易被问倒的问题。很多人花了大量时间调模型最后却只能拿出几个误差指标原因往往不在模型上而在数据口径和评估方式出了差错。这篇笔记把完整的落地方案掰开讲清楚适合正在做 Python 毕业设计的学生也适合想评估这个方向是否值得投入的开发者数据怎么来、特征怎么造、算法怎么选、参数怎么调、报告怎么写并把中间那些容易翻车的坑单独拎出来说。2. 票房预测的数据从哪来数据清洗与特征工程的落地路线2.1 先定数据结构影片特征、市场环境、历史绩效三张表电影票房预测不是拿到一张表就能开跑而是需要围绕“一部电影上映前能拿到的信息”来构建数据。我一般会把数据拆成三张表来整理这样做的好处是清洗逻辑清晰、后续特征工程也不容易乱。第一张表是影片特征表记录电影本身的属性片名、导演、主演、类型、时长、制片国家、制作成本、是否为续集。第二张表是上映环境表记录上映日期、所处档期、上映周次、同期竞争影片数量。第三张表是历史绩效表记录导演和主演过往作品的票房表现这部分信息在票房预测里往往比影片自身的属性更有解释力。先把这三张表的读取和初始拼接写好后续的清洗和特征构造都在这个基础上进行。import pandas as pd films pd.read_csv(films.csv) # 影片特征表 releases pd.read_csv(releases.csv) # 上映环境表 history pd.read_csv(history_perf.csv) # 历史绩效表 df films.merge(releases, onmovie_id, howleft) df df.merge(history, ondirector, howleft) print(df.shape, df.columns.tolist())这里用merge做表关联on指定关联键。影片特征表和上映环境表通过movie_id关联历史绩效表通过导演名字关联。需要注意howleft表示以影片特征表为主表避免因为某个导演没有历史数据而丢掉整部电影。很多同学的初始数据只有一张表字段少、样本少到后面特征工程阶段会很被动。起步阶段宁可多花两天整理历史绩效数据也别急着跑模型。票房预测这个题目强特征往往就藏在“人”的历史表现里。2.2 数据采集与清洗先把脏数据这块硬骨头啃下来数据来源方面常见做法是爬取豆瓣、猫眼、淘票票等公开页面或者直接使用 TMDB 这类开放电影数据库。采集方式可以用 requests 加 BeautifulSoup 解析静态页面遇到动态加载的内容就要换成 Playwright 或 Selenium。这个项目的数据量不需要很大三百部到五百部影片的历史数据足够完成一篇毕业设计关键在于字段齐整、口径一致。清洗环节比采集更容易消耗时间。一个典型的脏数据问题是同一部电影在不同平台上的票房口径不一致有的包含服务费有的不含单位也不统一有的用“万元”有的用“亿元”。这类问题一旦混进训练集模型会学到错误的比例关系后期排查非常痛苦。def clean_film_data(raw_df): df raw_df.copy() df[release_date] pd.to_datetime(df[release_date], errorscoerce) df df.dropna(subset[release_date]) # 日期缺失的电影直接丢弃 df[budget] df[budget].fillna(df[budget].median()) # 成本缺失用中位数填充 df[budget_missing] df[budget].isna().astype(int) # 记录缺失标记 df[genre] df[genre].fillna(未知) df df.drop_duplicates(subset[title, release_date]) return df这段代码的关键点有三个errorscoerce会把无法解析的日期转成NaT随后用dropna剔除成本缺失用中位数填充同时加一列缺失标记让模型自己决定是否要利用“缺失”这个信息drop_duplicates按片名加日期去重防止同一部电影因为重映或数据重复而出现多行。单位不统一的问题需要在进入数据框之前就处理完。我的习惯是写一个单位换算函数把所有票房数据在入库阶段统一成“万元”。否则到模型阶段才发现单位混乱还得回炉清洗属于典型的返工。2.3 特征工程把导演、主演和档期变成模型能吃的数字原始数据里有大量文本信息导演和主演是字符串类型是逗号分隔的多个标签上映日期是时间戳。模型没法直接处理这些内容特征工程就是把它们转换成数值的过程。这也是决定票房预测项目质量上限的关键一步。日期特征可以拆出星期几、是否档期、距离最近大档期的天数。主演信息不能直接做 one-hot因为演员数量太多做出来会变成上千列的稀疏矩阵。常见做法是计算演员历史票房均值作为“演员号召力”特征。def build_features(df, actor_stats, director_stats): feats df.copy() feats[weekday] feats[release_date].dt.dayofweek feats[is_holiday] feats[release_date].apply(is_holiday_flag) feats[actor_power] feats[actors].apply( lambda x: np.mean([actor_stats.get(a, 0) for a in x.split(,)]) ) feats[director_power] feats[director].map(director_stats).fillna(0) genre_dummies feats[genre].str.get_dummies(sep,) return pd.concat([feats, genre_dummies], axis1)actor_stats是一个字典键是演员名值是演员历史电影的票房均值。director_power同理。get(a, 0)保证了某个演员没有历史数据时返回 0而不是报错。类型列用str.get_dummies做多标签展开会生成“剧情”“喜剧”“动作”等独立的 0/1 列。这里有一个容易被忽略的细节actor_stats和director_stats必须只用训练集统计。如果把全量数据的票房信息拿去算演员均值和导演均值等于把未来信息混进特征这是数据泄漏的一种隐蔽形式。后面避坑章节会专门展开。3. 算法选型与训练参数从线性回归到 LightGBM 的切换路线3.1 先跑通一个可解释的基线模型岭回归票房预测本质上是一个回归问题输出是连续的票房数值。很多人一上来就用 XGBoost 或深度学习结果预测结果和业务解释对不上答辩时被问得哑口无言。成熟做法是先跑一个线性基线把逻辑打通再逐步换更强的模型。线性模型在这里的价值是提供可解释性。岭回归是线性回归加了 L2 正则可以缓解特征之间的多重共线性。票房特征里导演号召力与演员号召力往往高度相关岭回归对这种情况比普通线性回归更稳。from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import Ridge from sklearn.model_selection import train_test_split import numpy as np features df.select_dtypes(include[np.number]).drop(columns[box_office]) target np.log1p(df[box_office]) # 对票房取对数压缩长尾 X_train, X_val, y_train, y_val train_test_split( features, target, test_size0.2, random_state42 ) model Pipeline([ (scaler, StandardScaler()), (ridge, Ridge(alpha1.0)), ]) model.fit(X_train, y_train)np.log1p是log(1x)解决票房分布高度右偏的问题。如果不做这一步少数几部高票房电影会主导损失函数模型会牺牲绝大多数影片的预测精度去拟合那几部爆款。StandardScaler对特征做标准化消除量纲影响毕竟“票价”“时长”和“演员号召力”的单位差异很大。alpha1.0是正则强度值越大模型越保守系数越趋近于 0。跑完这个基线后建议立即查看特征的系数。系数的大小和正负能帮你判断哪些特征是正向影响、哪些是负向影响这个结果直接可以写进毕业设计报告里做业务分析。如果连线性模型的系数都解释不通后面的复杂模型也大概率有问题。3.2 换树模型时哪些参数真正值得调线性模型对非线性关系的拟合能力有限。档期效应、类型组合、续集效应这些因素与票房之间的关系往往不是线性的这时候就需要切换到树模型。实际项目里用得最多的是 XGBoost 和 LightGBM两个库在 sklearn 风格接口下接入非常方便。树模型的好处是自带特征交互不需要做特征标准化对缺失值也有原生处理逻辑。它不太好的地方是容易过拟合尤其当样本量只有几百条时需要严格控制树的复杂度。import xgboost as xgb dtrain xgb.DMatrix(X_train, labely_train) dval xgb.DMatrix(X_val, labely_val) params { objective: reg:squarederror, max_depth: 4, eta: 0.05, subsample: 0.8, colsample_bytree: 0.8, eval_metric: rmse, } model_xgb xgb.train( params, dtrain, num_boost_round1000, evals[(dval, val)], early_stopping_rounds50, )这段代码里最需要关注的是max_depth和eta。max_depth4在样本量不大的场景下是一个比较安全的深度刻意加大到 8 甚至 10训练集误差会快速下降但验证集会迅速恶化。eta0.05是学习率学习率越低需要越多的num_boost_round配合early_stopping_rounds50可以自动在验证集不再下降时停止训练。subsample和colsample_bytree都是防止过拟合的随机采样参数。subsample0.8表示每棵树随机使用 80% 的样本colsample_bytree0.8表示每棵树随机使用 80% 的特征。两者同时设置为 0.8 是经验值问题不大时可以不用再动。很多同学在这个阶段会掉进调参陷阱用网格搜索把所有参数翻来覆去地试。实际上几百条样本的票房数据参数对结果的提升远不如特征工程来得多。如果时间和算力都紧张只需要调max_depth、eta和subsample三个参数就够了其余参数保持默认不会对结果有致命影响。3.3 评估指标与训练集切分要跟着业务走票房预测项目里最常犯的评估错误是不加思考地使用随机切分。电影的票房与上映时间高度相关同一时期的电影共享相似的市场环境。如果随机切分训练集和验证集里都会出现同一时期的电影模型等于看到了一部分“未来”的规律验证集上的表现自然好看但真实场景中根本做不到。正确做法是按时间顺序切分。比如按上映日期排序取前 70% 的影片作为训练集后 30% 作为验证集。这也符合业务直觉用过去的电影预测未来的电影。评估指标方面MSE 和 RMSE 受极端值影响太大一部《流浪地球》级别的爆款就能让整体 RMSE 飙升。我会同时报告 MAE、MAPE 和 MAD。MAD 的全称是 Mean Absolute Deviation指预测值与真实值之间绝对误差的中位数它比平均数更抗极端值干扰。from sklearn.metrics import mean_absolute_error, mean_squared_error pred_log model_xgb.predict(xgb.DMatrix(X_val)) pred np.expm1(pred_log) true np.expm1(y_val) mae mean_absolute_error(true, pred) mad np.median(np.abs(true - pred)) mape np.mean(np.abs((true - pred) / true)) * 100 print(fMAE: {mae:.1f} 万元) print(fMAD: {mad:.1f} 万元) print(fMAPE: {mape:.1f}%)np.expm1是np.log1p的反变换把预测值从对数空间还原回万元单位。MAD 用中位数而不是平均值意思是“一半的电影预测误差在多少万元以内”这个指标在课题报告里更有说服力。MAPE 对低票房影片非常敏感一部预测误差两百万的小成本电影MAPE 就能被拉到很高所以它更适合用来发现极端问题而不是作为唯一衡量标准。4. 票房预测避坑清单数据泄漏、冷启动与评估失真的排查记录4.1 特征泄漏用了上映后才有的信息现象模型在验证集上的预测表现非常好MAE 不到两千万但拿到新片做预测时结果一塌糊涂误差上亿。原因特征构建时把上映后才产生的信息混了进去。最典型的例子是用“首日排片率”预测首日票房。排片率虽然对票房有极强的解释力但在真实业务里排片率在电影上映前只有预售阶段才能拿到计算方式也并不稳定。另一个隐蔽场景是前面提到过的用全量数据计算演员历史票房均值导致训练阶段已经看过验证集的票房信息。解决统一特征截断时间点。每一条样本的特征必须严格限定在上映日之前可获得的信息范围内。我一般会在特征清单里单独标注每个特征的“可得时间”只有在上映前一周内能拿到的数据才被允许进入模型。排查方法也简单把训练集和验证集上的特征分布分别画出来如果某个特征在验证集上的分布与训练集异常接近就要怀疑是否包含了未来信息。4.2 小成本影片预测集体失准冷启动问题怎么处理现象验证集上总体 MAE 尚可但把预测结果按制作成本分桶后发现成本低于 3000 万的影片误差非常大。大制作电影预测误差几千万小制作电影误差也能到几千万相对误差差了好几倍。原因小成本电影在训练样本中占比低树模型很容易把它们的预测结果拉到样本量更大的中等成本电影附近。另外小成本电影的主演和导演通常缺乏历史票房数据actor_power和director_power很可能都是 0模型根本找不到有效信号。解决在训练时按成本分组评估不要只看总体指标。如果小成本影片误差过大可以在损失函数里对低预算影片加大权重或者给演员和导演的历史特征增加缺失标记列让模型能区分“号召力差”和“没有数据”这两种情况。答辩时主动提出这个边界会比被老师问出来好得多。4.3 票房分布高度右偏回归目标必须做变换现象用原始票房数值直接训练模型预测出来的票房几乎都落在均值附近。遇到爆款电影预测值严重偏低遇到冷门电影预测值又偏高。RMSE 高得离谱。原因票房分布呈明显的长尾形态。少数头部电影占据了大部分票房份额线性回归和树模型在拟合时都会倾向于把预测值往样本均值方向压以降低整体损失。在长尾分布下这个小数目的极端样本会主导梯度。解决把回归目标从原始票房换成log1p(box_office)让分布更接近正态。评估时再通过expm1还原。这个做法对线性模型和树模型都有效。需要注意的是有些同学只对特征做了 log 变换而忘记对目标变量做变换这相当于问题只解决了一半。4.4 票房口径不一致训练集内部互相打架现象特征与目标之间的关系看起来没有规律同一个导演、类似类型的两部电影票房却相差几十倍模型怎么调都学不出稳定规律。原因数据来源不统一。爬取的猫眼数据、豆瓣数据和 TMDB 数据对“总票房”的定义并不一致。有的平台统计的是含服务费票房有的统计的是分账票房有的统计的是上映期票房有的统计的还包括重映。单位上有的是美元有的是人民币不入库时统一换算后面每一步都是错的。解决在数据入库前制定统一口径表。我习惯把所有票房统一为“中国大陆地区含服务费总票房单位万元”。上映日期统一为公映首日不取点映日期。这个口径表要写进毕业设计报告作为数据说明的一部分。检查口径的方法也很直接随机抽 10 部已知票房的电影看自己的数据是否与公开数据一致误差应该在 5% 以内。5. 回测与报告收尾让答辩老师信服模型有效的两个技巧模型训练完成后很多人直接拿 RMSE 和 MAE 写在报告里就结束了。但答辩老师真正想确认的是你的模型是不是一个有效的方法而不是碰巧拟合了训练数据。第一个技巧是滚动时间窗回测。把数据按时间排序后第一天用前三年数据训练预测第四年第二天用前四年数据训练预测第五年依次滚动。每一次预测都应得到一组误差值最终报告每组误差的均值与方差这样可以看出模型在不同市场环境下的稳定性。这一步虽然增加工作量却是区分“真做了项目”和“跑完一遍代码”的最好证据。第二个技巧是把预测误差拆到业务维度。按类型、制作成本、档期三个维度分别计算 MAD然后在报告里解释每个维度下误差的来源比如“喜剧片误差较大推测原因是喜剧效果高度依赖口碑而上映前可获得的特征无法捕捉这种主观感受”。这种有业务解释的误差分析比单纯贴指标更能体现对项目的理解。我给自己挖过的最大一个坑是花了两周时间调模型参数最后发现业绩提升远不如花一天修复数据泄漏。从那以后我的习惯是先建评估体系再跑任何模型。希望这篇笔记能帮你少走一段弯路也祝你的毕业设计答辩顺利。本文还有配套的精品资源点击获取