尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

电影大数据预测与可视化:Python机器学习项目实战全解析

电影大数据预测与可视化:Python机器学习项目实战全解析 简介面向期末大作业、毕业设计与课程设计场景的Python机器学习电影大数据预测分析及可视化项目源码包含完整可运行的前后端与预测分析流程。项目围绕电影大数据展开涵盖数据清洗、特征处理、模型训练、指标评估以及可视化展示难度适中且经过助教审定评审得分98分适合希望在真实题目中快速上手机器学习项目的学生直接复现或二次开发。压缩包共2000个文件大小21.72MB以svg图标、html/css/js前端页面、py脚本与csv数据集为主另有map、xml等配置和扩展文件。前端相关文件占多数用于构建交互式可视化面板py脚本为核心建模与预测代码csv提供实验数据目录结构清晰便于按模块查阅。目前已有179人学习/下载。资源可直接运行的特性减少了环境配置与排错成本使用者可获得一套从数据到预测再到可视化的完整项目参考覆盖选题、编码、讲解与答辩所需材料尤其适合课程设计或期末大作业的快速落地。1. 期末大作业拍板电影大数据预测与可视化到底在交什么期末大作业里最稳的组合就是这个Python 把数据处理干净机器学习模型给出预测可视化把结果讲成人话。电影大数据预测分析及可视化项目源码简单说就是拿公开的电影数据训练一个能预测票房的模型再配一个能展示图表的大屏页面。老师想看到的是一条能跑通的链路数据读取、清洗、特征工程、模型训练、评估、页面展示。这个方向适合正在选期末题目的学生也适合刚学完 Python 基础和 sklearn 的开发者。它最大的好处是不用自己爬数据公开电影数据集里预算、类型、演员、上映时间、评分、票房都有天然适合做分析和可视化。后面的方案会按数据清洗、特征工程、模型训练、可视化四步展开每章都带能直接复现的源码和参数说明。2. 选型先于编码数据源、库版本和项目目录这样搭2.1 数据源用 TMDB 还是 MovieLens按作业时长选我一般会把课程项目的数据源分成两类能用 API 实时拉的和直接下载 CSV 的。期末大作业不要碰前者因为 API 密钥有效期、限流、字段变更都会让项目在答辩前一天突然挂掉。常见做法是直接选用公开的 CSV 数据集。如果你的预测目标是票房优先用 TMDB 5000 Movie Dataset。它是最常出现在电影分析课设里的那份数据常见的文件结构是两张表一张tmdb_5000_movies.csv包含预算、收入、发行日期、类型、热门度、评分和评分人数另一张tmdb_5000_credits.csv包含演员和制作人员。两张表通过id关联样本量在 5000 部左右。对课设来说刚好数据量不会大到把普通笔记本耗尽又足够撑起“电影大数据”的表述。如果你的预测目标是用户评分用 MovieLens 更合适。它按规模分为 100K、1M、25M 等版本文件是标准的三张表movies.csv、ratings.csv、tags.csv。但 MovieLens 的电影表里没有预算和收入字段所以不适合做票房预测。期末展示时如果只画评分分布又容易显得单薄。我的选择是主数据用 TMDB 做票房分类预测MovieLens 只在报告里提一句“可以迁移到推荐系统”不实际用它训练。为什么这个选择很重要因为标题叫“电影大数据预测分析”老师大概率会问“你的特征是什么”“你预测的是什么目标”。TMDB 数据集的字段让这两个问题都有现成答案预算、票房、演员阵容、类型都可以变成特征票房能否过线可以作为标签。反过来如果用 MovieLens 又想做票房就需要再找外部数据合并数据对齐工作会占用很多时间。补充一句TMDB 数据集中有一些字段是字符串形式的 JSON比如genres是整个电影的类型数组cast是演员数组。这个部分会在第三章专门处理。下面先给项目目录。2.2 项目目录五份源码文件一次到位movie_project/ ├── data/ │ ├── tmdb_5000_movies.csv │ ├── tmdb_5000_credits.csv │ └── processed/ │ └── features.csv ├── src/ │ ├── clean.py │ ├── features.py │ ├── train.py │ └── app.py ├── models/ │ └── movie_model.pkl ├── static/ │ └── echarts.min.js └── templates/ └── dashboard.html目录结构为什么这样拆clean.py只负责把原始 CSV 读进来、去重、处理缺失值和 JSON 列features.py负责构造模型特征并输出processed/features.csvtrain.py读取特征文件训练模型并保存到models/app.py是 Flask 服务加载模型并提供一个/api/overview接口templates/dashboard.html是可视化页面。这个拆分让每一份源码都可以单独运行期末报告里也能配合流程图讲清楚数据流向。四个文件的依赖顺序要记住先跑clean.py再跑features.py再跑train.py最后跑app.py。如果中间某一步报错错误范围会被限制在单个文件内这是课设项目最需要的可调试性。有些同学喜欢把所有代码堆在一个 Jupyter Notebook 里演示确实方便但答辩现场涉及“从零跑通”时Notebook 的单元格顺序错乱很容易翻车。static/echarts.min.js是本地放的 ECharts 库文件。这里强调本地化不要在dashboard.html里写 CDN 链接因为答辩教室的可用资源不可控。离线可运行是可视化项目的最低要求。2.3 环境准备先跑通 Python 再装机器学习库常见做法是创建一个虚拟环境避免把系统 Python 弄乱。我一般在项目根目录执行python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate python -m pip install --upgrade pip pip install pandas numpy scikit-learn lightgbm flask joblib参数说明pandas和numpy是数据处理基础scikit-learn里包含了随机森林、逻辑回归、交叉验证和评价指标lightgbm是提升树模型flask用来起本地服务joblib用来保存和加载模型。如果lightgbm在你的 Python 版本上安装失败不要纠结直接把训练代码里的 LightGBM 部分注释掉只保留RandomForestClassifier也能完整交作业。就最终效果而言随机森林在这个数据规模下和 LightGBM 的差距并不大。装好后可以验证版本python -c import pandas, sklearn, flask; print(pandas.__version__, sklearn.__version__)。这一步不是为了显摆版本号而是确认所有依赖都已经可导入。常见失败是pip命令装到了全局环境但编辑器用的解释器是虚拟环境外的最后运行train.py时报ModuleNotFoundError。所以后面所有脚本一律在虚拟环境下运行命令行里能看到(.venv)前缀才是对的。准备环境最忌一上来就装一堆“看起来会用到”的包。我的建议是只装上面六个最后如果要在报告里画 ROC 曲线再补matplotlib。否则依赖越多答辩前环境损坏的概率越大。3. 数据清洗和特征工程预测准不准七成在这3.1 读取并合并两张表JSON 字段先转成 Python 结构clean.py里的第一件事是读表并去掉明显没有预算或收入的样本。代码import pandas as pd movies pd.read_csv(data/tmdb_5000_movies.csv) credits pd.read_csv(data/tmdb_5000_credits.csv) df movies.merge(credits, left_onid, right_onmovie_id, howleft) print(df.shape, df.columns.tolist())逻辑说明movies表的主键是idcredits表里叫movie_id所以合并时用left_on和right_on分别指定。howleft表示以电影表为主体即使个别电影没有演员信息也保留这一行。合并后可以立刻打印形状和列名看看是不是预期的 5000 行左右、字段是否都进来。紧接着把字符串形式的 JSON 列解析成 Python 列表。TMDB 数据集中genres、keywords、production_companies、cast、crew都是字符串。用ast.literal_eval而不是eval因为前者更安全只解析文本字面量。示例from ast import literal_eval json_cols [genres, keywords, production_companies, cast, crew] for col in json_cols: df[col] df[col].apply(lambda x: literal_eval(x) if isinstance(x, str) else []) df[main_genre] df[genres].apply(lambda g: g[0][name] if g else Unknown) df[actor_count] df[cast].apply(lambda c: len(c) if c else 0) df[company_count] df[production_companies].apply(lambda c: len(c) if c else 0)参数说明main_genre只取第一个元素作为主类型因为电影类型本身可能有多个取第一个能让类别数量受控避免出现几百个稀罕组合。actor_count不只是“演员人数”它间接反映电影投资规模company_count同理。解析完成后df里每一列应该是正常的 Python 列表或整数而不是字符串。在意数据泄漏的同学会问cast和crew里包含上映后的信息吗严格说演员阵容是上映前确定的可以当特征。但后面的vote_count、popularity是上映后统计的用于预测票房就带“未来信息”需要单独说明我通常会在报告里把它标注为“事后特征”并做一版去掉它们之后的对比模型。这个边界要自己心里有数。3.2 构造 14 个特征数值特征和类别特征怎么选features.py会读取上一步合并后的结果继续加工。我常用的特征集分成四个组预算结构与时间特征、文本与人员规模、评价热度、主类型与语言。代码import numpy as np import pandas as pd df pd.read_csv(data/processed/cleaned.csv) df[budget] pd.to_numeric(df[budget], errorscoerce) df[runtime] pd.to_numeric(df[runtime], errorscoerce) df[release_date] pd.to_datetime(df[release_date], errorscoerce) df[release_year] df[release_date].dt.year df[release_month] df[release_date].dt.month df[budget_log] np.log1p(df[budget]) df[runtime_missing] df[runtime].isna().astype(int) df[runtime] df[runtime].fillna(df[runtime].median()) df[is_franchise] df[title].str.lower().str.contains( part|ii|iii|iv|v|vi, regexTrue ).astype(int) df[title_len] df[title].str.len()参数说明budget里可能有空值或非数字pd.to_numeric统一转成浮点数并让非法值变成NaN。release_date用to_datetime解析后面的year和month对票房有明显影响暑期档、圣诞档的电影天然更容易卖座。budget_log用log1p是因为预算分布偏态严重几部大制作会把数值拉得很高取对数后模型更容易学。is_franchise粗暴地通过标题里是否出现续集序号判断不是完美的判断但在课设里够用如果希望更严谨可以手工整理一个续集名单但收益不大。在此基础上把类别特征做数值化。main_genre和original_language可以用pandas.Categorical转码或者用 sklearn 的LabelEncoder。注意类别不需要 one-hot树模型可以直接吃整数编码。代码from sklearn.preprocessing import LabelEncoder feature_cols [ budget_log, runtime, release_year, release_month, actor_count, company_count, title_len, is_franchise, vote_count, vote_average, popularity, ] df[main_genre_code] LabelEncoder().fit_transform(df[main_genre].fillna(Unknown)) df[language_code] LabelEncoder().fit_transform(df[original_language].fillna(Unknown)) X df[feature_cols [main_genre_code, language_code]].copy()LabelEncoder在这里没有任何排序含义只是把字符串映射成整数供树模型使用。如果用逻辑回归做 baseline则需要对这些类别再做 one-hot我在第四章会让逻辑回归配合OneHotEncoder这里先给树模型用标签编码。为什么不用 one-hot 作为整个项目的统一方案因为随机森林和 LightGBM 对高基数稀疏类别并不友好标签编码在树模型里更快也不会损失太多信息。3.3 制造训练目标把「票房是否热门」变成二分类标签回归问题直接预测票房在期末展示时非常吃亏因为票房的长尾分布会让 RMSE 大得没法看。改成二分类就清晰多了。常见做法是用票房中位数或一个业务阈值比如revenue 5000 万美元记为 1否则为 0。代码df[revenue] pd.to_numeric(df[revenue], errorscoerce) df df.dropna(subset[revenue]) median_revenue df[revenue].median() df[is_hit] (df[revenue] median_revenue).astype(int) print(df[is_hit].value_counts(normalizeTrue))固定阈值可以由自己决定不必非用中位数。比如你希望模型更聚焦于“大卖”可以用revenue 100_000_000但阈值越高正样本越少模型可能学成一个“永远预测 0”的废模型。所以我的建议是先用中位数以保证类别均衡后面再在报告里做一组不同阈值的敏感性测试。生成特征矩阵后立刻切分训练集和测试集。重点切分放在一切特征构造之后、模型训练之前。代码from sklearn.model_selection import train_test_split y df[is_hit].values X X.fillna(X.median(numeric_onlyTrue)).fillna(Unknown) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) print(X_train.shape, X_test.shape, y_train.mean(), y_test.mean())stratifyy是这里最关键的参数。它保证训练集和测试集中热门片比例与原始数据一致避免切分后测试集全是冷门片导致评估失真。random_state42固定随机种子让每次运行结果一致期末报告里的数字也才能复现。fillna对数值列用中位数对类别列用Unknown这是简单有效的兜底不引入太多噪声。4. 建模与评分分类任务比回归任务更适合期末展示4.1 为什么先跑逻辑回归给答辩一个可解释基线很多同学一上来就调 LightGBM结果答辩时被问“为什么这个特征重要”答不上来。我一般会让项目同时包含一个逻辑回归作为基线和一个随机森林作为主模型。逻辑回归的结构简单系数能直接看出影响方向比如预算每增加一个对数单位热门概率的变化是多少这些可以写进报告。from sklearn.pipeline import make_pipeline from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder, StandardScaler from sklearn.linear_model import LogisticRegression numeric_cols feature_cols cat_cols [main_genre_code, language_code] pre ColumnTransformer([ (num, StandardScaler(), numeric_cols), (cat, OneHotEncoder(handle_unknownignore), cat_cols) ]) lr make_pipeline(pre, LogisticRegression(max_iter1000, C1.0)) lr.fit(X_train, y_train)逻辑说明ColumnTransformer对数值特征做标准化让不同量纲不干扰逻辑回归的收敛对类别特征做 one-hot因为逻辑回归不能像树模型那样直接把main_genre_code当有序数字。handle_unknownignore防止测试集出现训练集没见过的类别时报错。C1.0是正则化强度默认即可想要更稳可以把C调小到0.5但通常差别不大。评估逻辑回归之前先别急着看准确率。期末答辩中经常出现“代码跑完打印 accuracy 0.72”这种回答可一旦正样本只占 30%一个预测全部为 0 的模型也能有 0.7 的准确率。所以一定要用混淆矩阵和 AUC。4.2 随机森林和 LightGBM树模型的主场随机森林在几千条样本上训练很快表现也很稳。LightGBM 能提供更好的特征重要性展示两个都可以放到项目里。我提供一份组合代码你按课设时间选一个训练即可from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import roc_auc_score, classification_report import joblib rf RandomForestClassifier( n_estimators300, max_depth10, min_samples_leaf4, random_state42, n_jobs-1, ) rf.fit(X_train, y_train) y_pred_proba rf.predict_proba(X_test)[:, 1] auc roc_auc_score(y_test, y_pred_proba) print(fAUC: {auc:.3f}) print(classification_report(y_test, rf.predict(X_test), target_names[cold, hit])) joblib.dump(rf, models/movie_model.pkl)参数说明n_estimators300让模型有足够多棵树但也不至于把训练时间拖到分钟级max_depth10防止单棵树过深min_samples_leaf4要求叶子节点至少 4 个样本这是控制过拟合最有效的参数。如果训练时发现 AUC 很高但测试 AUC 低说明过拟合把max_depth降到 6、min_samples_leaf升到 8 再试。n_jobs-1让随机森林用全部 CPU 核。LightGBM 版本类似from lightgbm import LGBMClassifier lgb LGBMClassifier( n_estimators500, learning_rate0.05, max_depth5, subsample0.8, colsample_bytree0.8, random_state42, verbose-1, ) lgb.fit(X_train, y_train, eval_set[(X_test, y_test)], eval_metricauc) print(fLightGBM AUC: {roc_auc_score(y_test, lgb.predict_proba(X_test)[:, 1]):.3f}) joblib.dump(lgb, models/movie_model_lgb.pkl)subsample0.8和colsample_bytree0.8分别是行采样和列采样这两个参数让每棵树看到的样本和特征都不同能明显降低过拟合。learning_rate0.05配合n_estimators500是一个稳定的组合如果想更快把learning_rate提到 0.1n_estimators降到 200。LightGBM 的verbose-1是为了减少终端输出避免答辩现场刷屏。4.3 评估指标AUC 优先顺便给报告画 ROC 曲线期末答辩不能只有数字还要有图。ECharts 可以画 ROC 曲线但更快的方案是用matplotlib生成roc.png放进报告。代码import matplotlib.pyplot as plt from sklearn.metrics import roc_curve fpr, tpr, _ roc_curve(y_test, y_pred_proba) plt.plot(fpr, tpr, labelfAUC{auc:.3f}) plt.plot([0, 1], [0, 1], linestyle--, colorgray) plt.xlabel(False Positive Rate) plt.ylabel(True Positive Rate) plt.legend() plt.savefig(models/roc.png, dpi150)参数说明fpr和tpr来自测试集在不同概率阈值下的表现AUC 不是某一个阈值下的指标而是所有阈值下综合的区分能力所以曲线越靠近左上角模型越好。报告里写“AUC 0.82说明模型对热门片的排序能力优于随机猜测”这句话比“准确率 0.78”高级得多。此外用cross_val_score做一次 5 折验证能证明模型稳定from sklearn.model_selection import cross_val_score scores cross_val_score(rf, X, y, cv5, scoringroc_auc) print(scores, scores.mean(), scores.std())如果 5 轮 AUC 的波动超过 0.05就要怀疑特征或切分不稳定。这时可以先看特征重要性rf.feature_importances_能告诉你哪些特征在预测中权重最高。一般budget_log和popularity会靠前这是合理的如果某个奇怪特征排第一比如title_len就要检查是不是标签写错了。5. 避坑记录电影数据预测最容易翻车的五个点5.1 现象模型 AUC 高达 0.98答辩一追问就露馅原因把revenue自己当成特征放进X了。TMDB 数据里有revenue很多人清洗时顺手把所有列都拿来做特征模型实际上是在用答案猜答案。表现就是 AUC 异常高测试集上几乎完美。解决在feature_cols里明确排除revenue只保留budget_log、runtime、类型等。另外popularity和vote_count虽然是上映后数据但它们不是目标本身使用时要主动说明“这是在用已知热度预测票房属于事后特征”。如果希望更干净可以跑一版只含上映前特征的模型把 AUC 对比写进报告。我踩过的实际经历是把预算对数特征和目标列同时进入模型AUC 从 0.86 跳到 0.99当时还以为自己调参出了神模型。后来打印X.columns才发现有一列叫revenue删掉后指标降到正常范围。从此每次训练前我都会先检查特征列里有没有目标列的影子。5.2 现象pd.read_csv一跑内存就炸原因TMDB 原始文件不大但merge后如果键不是唯一的会产生笛卡尔积行数膨胀几十倍。尤其credits里movie_id不是唯一键时Left Join 会把主表重复多行。解决读入后先df[df[id].duplicated()]检查重复合并前用credits.drop_duplicates(subsetmovie_id)去重。数据加载时也可以指定dtype{budget: float32, runtime: float32}把不必要的int64降成float32内存占用直接减半。这个坑在课设里特别隐蔽因为你只看df.shape可能觉得行数还行但内存已经被重复行占满。我在给一个朋友排查时发现他的合并结果从 5000 行变成 40 万行原因就是 credits 表里同一个movie_id出现了多次最后用drop_duplicates解决。5.3 现象回归模型 R2 为负数预测票房全部靠近均值原因直接拿原始revenue做回归但这列是长尾分布几部超级大片占了绝大多数收入普通电影收入很低。平方误差被少数样本主导模型宁愿全部预测均值来减少整体误差导致 R2 为负。解决最简单的方法是改做二分类把目标变成“是否热门”如果老师坚持要回归就对revenue做log1p转换训练目标变成log(revenue1)评估时再expm1还原。这样 RMSE 会变得可读但答辩时要说清楚这是在优化对数空间误差。我一般会建议同学分组展示先给回归的 log 空间 RMSE再给分类的 AUC两张图放一起既回答了“能不能预测票房”又避免了长尾分布带来的尴尬。千万不要把负 R2 藏起来老师一问反而扣分。5.4 现象可视化大屏页面能打开但图表一动不动原因dashboard.html里请求的接口路径写错了或者app.py没有正确加载模型。常见的是 Ajax 请求到了http://127.0.0.1:5000/api但 Flask 路由写的是/api/overview。解决先单独在浏览器打开/api/overview看返回的 JSON 是否正常再打开浏览器开发者工具 Network 面板看接口状态码。最简单的方式是让接口不依赖参数直接返回一批聚合结果前端只管展示避免每刷新一次都要传一堆参数。期末演示时最怕遇到接口路径不对导致白屏。我已经养成习惯在app.py里加一条/api/health路由返回{status: ok}页面加载前先请求它如果拿不到就直接在前端显示错误信息。这样即使出了问题也不是整个页面空白而是能看到具体原因。5.5 现象ECharts 图不渲染控制台报Unexpected token 原因static/echarts.min.js没有正确放到项目目录HTML 引用它时被 Flask 返回了首页 HTMLJS 解析失败。Flask 会把模板目录和静态目录区分开如果你写的是templates/echarts.min.js就会按模板解析返回 HTML 而不是 JavaScript。解决模板里把script src/static/echarts.min.js/script写对并确认app.py里Flask(__name__, static_folderstatic)的目录名和实际文件夹一致。还有一个常见坑是文件用编辑器保存为UTF-8时带了 BOM导致 JavaScript 第一行报错用文本编辑器另存为无 BOM 的 UTF-8 即可。如果本地没有 ECharts 库文件也可以从官方打包里把dist/echarts.min.js复制到项目目录。不要用在线地址因为答辩教室很可能无法访问外网资源。本地库文件是保证现场演示不翻车的前提。6. 把模型做成可视化页面Flask 本地大屏和验收技巧6.1 Flask 接口最小可用的/api/overviewimport joblib import pandas as pd from flask import Flask, jsonify, render_template app Flask(__name__) model joblib.load(models/movie_model.pkl) app.route(/) def index(): return render_template(dashboard.html) app.route(/api/overview) def overview(): data pd.read_csv(data/processed/features.csv) hit_rate float(data[is_hit].mean()) return jsonify({hit_rate: hit_rate, sample_count: len(data)}) if __name__ __main__: app.run(debugFalse)先实现一个能返回 JSON 的统计接口。实际项目里可以扩展成返回预算中位数、类型分布、月份票房均值等列表前端按需渲染。注意debugFalse答辩演示时一旦代码报错不会把调试页面暴露出来。6.2 dashboard.html 里最关键的 10 行script src/static/echarts.min.js/script div idchart stylewidth:100%;height:400px/div script fetch(/api/overview).then(r r.json()).then(data { const chart echarts.init(document.getElementById(chart)); chart.setOption({ series: [{ type: pie, data: [ { name: 热门, value: Math.round(data.hit_rate * data.sample_count) }, { name: 非热门, value: Math.round((1 - data.hit_rate) * data.sample_count) } ] }]}); }); /script逻辑说明fetch获取准备好的数据ECharts 的setOption接收一个普通对象就能渲染出饼图。这个骨架可以扩展成多图表大屏原理都一样后端给 JSON前端绘图。6.3 验收的三个技巧第一测试集 AUC 低于 0.7 就说明模型没有学到有效信号这时候宁可改进特征也不要继续调参。第二随机抽 20 部电影把模型预测概率和真实票房打印成表格人眼检查一下明显不合理的地方比如把低成本冷门片预测成热门。第三写一个“全部预测为多数类”的基线把基线的准确率或 AUC 和你的模型放在同一张表里证明模型确实有提升。我踩过的最大教训是在交作业前把revenue当特征用了重跑后指标掉了 0.15从此每版代码都先打印X.columns再训练。可视化大屏的坑也类似记得把接口路径固定成不易记错的单词前端页面尽量使用本地静态资源。希望你不用踩同一个坑希望这些经验能帮到你。本文还有配套的精品资源点击获取
返回列表