简介:面向期末大作业、毕业设计与课程设计场景的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.csv;train.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_on="id", right_on="movie_id", how="left") print(df.shape, df.columns.tolist())逻辑说明:movies表的主键是id,credits表里叫movie_id,所以合并时用left_on和right_on分别指定。how="left"表示以电影表为主体,即使个别电影没有演员信息,也保留这一行。合并后可以立刻打印形状和列名,看看是不是预期的 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"], errors="coerce") df["runtime"] = pd.to_numeric(df["runtime"], errors="coerce") df["release_date"] = pd.to_datetime(df["release_date"], errors="coerce") 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", regex=True ).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"], errors="coerce") 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(normalize=True))固定阈值可以由自己决定,不必非用中位数。比如你希望模型更聚焦于“大卖”,可以用revenue >= 100_000_000;但阈值越高,正样本越少,模型可能学成一个“永远预测 0”的废模型。所以我的建议是先用中位数,以保证类别均衡,后面再在报告里做一组不同阈值的敏感性测试。
生成特征矩阵后,立刻切分训练集和测试集。重点:切分放在一切特征构造之后、模型训练之前。代码:
from sklearn.model_selection import train_test_split y = df["is_hit"].values X = X.fillna(X.median(numeric_only=True)).fillna("Unknown") X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) print(X_train.shape, X_test.shape, y_train.mean(), y_test.mean())stratify=y是这里最关键的参数。它保证训练集和测试集中热门片比例与原始数据一致,避免切分后测试集全是冷门片,导致评估失真。random_state=42固定随机种子,让每次运行结果一致,期末报告里的数字也才能复现。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_unknown="ignore"), cat_cols) ]) lr = make_pipeline(pre, LogisticRegression(max_iter=1000, C=1.0)) lr.fit(X_train, y_train)逻辑说明:ColumnTransformer对数值特征做标准化,让不同量纲不干扰逻辑回归的收敛;对类别特征做 one-hot,因为逻辑回归不能像树模型那样直接把main_genre_code当有序数字。handle_unknown="ignore"防止测试集出现训练集没见过的类别时报错。C=1.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_estimators=300, max_depth=10, min_samples_leaf=4, random_state=42, 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(f"AUC: {auc:.3f}") print(classification_report(y_test, rf.predict(X_test), target_names=["cold", "hit"])) joblib.dump(rf, "models/movie_model.pkl")参数说明:n_estimators=300让模型有足够多棵树,但也不至于把训练时间拖到分钟级;max_depth=10防止单棵树过深;min_samples_leaf=4要求叶子节点至少 4 个样本,这是控制过拟合最有效的参数。如果训练时发现 AUC 很高但测试 AUC 低,说明过拟合,把max_depth降到 6、min_samples_leaf升到 8 再试。n_jobs=-1让随机森林用全部 CPU 核。
LightGBM 版本类似:
from lightgbm import LGBMClassifier lgb = LGBMClassifier( n_estimators=500, learning_rate=0.05, max_depth=5, subsample=0.8, colsample_bytree=0.8, random_state=42, verbose=-1, ) lgb.fit(X_train, y_train, eval_set=[(X_test, y_test)], eval_metric="auc") print(f"LightGBM AUC: {roc_auc_score(y_test, lgb.predict_proba(X_test)[:, 1]):.3f}") joblib.dump(lgb, "models/movie_model_lgb.pkl")subsample=0.8和colsample_bytree=0.8分别是行采样和列采样,这两个参数让每棵树看到的样本和特征都不同,能明显降低过拟合。learning_rate=0.05配合n_estimators=500,是一个稳定的组合;如果想更快,把learning_rate提到 0.1,n_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, label=f"AUC={auc:.3f}") plt.plot([0, 1], [0, 1], linestyle="--", color="gray") plt.xlabel("False Positive Rate") plt.ylabel("True Positive Rate") plt.legend() plt.savefig("models/roc.png", dpi=150)参数说明: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, cv=5, scoring="roc_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(subset="movie_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(revenue+1),评估时再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 返回了首页 HTML,JS 解析失败。Flask 会把模板目录和静态目录区分开,如果你写的是templates/echarts.min.js,就会按模板解析,返回 HTML 而不是 JavaScript。
解决:模板里把<script src="/static/echarts.min.js"></script>写对,并确认app.py里Flask(__name__, static_folder="static")的目录名和实际文件夹一致。还有一个常见坑是文件用编辑器保存为UTF-8时带了 BOM,导致 JavaScript 第一行报错;用文本编辑器另存为无 BOM 的 UTF-8 即可。
如果本地没有 ECharts 库文件,也可以从官方打包里把dist/echarts.min.js复制到项目目录。不要用在线地址,因为答辩教室很可能无法访问外网资源。本地库文件是保证现场演示不翻车的前提。
6. 把模型做成可视化页面:Flask 本地大屏和验收技巧
6.1 Flask 接口:最小可用的/api/overview
import 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(debug=False)先实现一个能返回 JSON 的统计接口。实际项目里可以扩展成返回预算中位数、类型分布、月份票房均值等列表,前端按需渲染。注意debug=False,答辩演示时一旦代码报错,不会把调试页面暴露出来。
6.2 dashboard.html 里最关键的 10 行
<script src="/static/echarts.min.js"></script> <div id="chart" style="width: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再训练。可视化大屏的坑也类似,记得把接口路径固定成不易记错的单词,前端页面尽量使用本地静态资源。希望你不用踩同一个坑,希望这些经验能帮到你。
本文还有配套的精品资源,点击获取