Python机器学习实战:豆瓣电影数据分析与评分预测全流程

发布时间:2026/10/12 21:41:35
Python机器学习实战:豆瓣电影数据分析与评分预测全流程
简介基于Python与机器学习的豆瓣电影数据分析项目包含完整源码、实验报告和可视化结果适合计算机、人工智能、大数据等专业学生用于课程设计、期末大作业或毕业设计也适合数据分析学习者作为练手项目参考。资源共16个文件约28.18MB主要包括Python主程序、HTML可视化报告、PDF实验报告、Markdown说明文档以及评论文本、停用词表、名称列表等txt数据文本和词云jpg图片并附带RAR格式的原始评论数据包覆盖数据获取、文本预处理、关键词提取、情感分析与图表展示等完整流程。代码经严格调试可直接运行读者可结合源码与实验报告理解豆瓣电影评论数据的清洗、分词、统计分析和可视化方法报告中还包含实现思路与结果讨论可作为撰写课程报告或论文的参考。目前已有351人学习下载适合需要快速获取完整可运行项目并系统学习Python数据分析与机器学习流程的读者。1. 基于 python 与机器学习的豆瓣电影数据分析拿到 zip 后先搞清它解决什么问题你手头这个名为“基于python与机器学习的豆瓣电影数据分析源码报告.zip”的压缩包是近两年最常见的课程设计和数据分析入门项目形态之一。压缩包内通常有三样东西可运行的 Python 源码Jupyter Notebook 或 .py 文件、一份图文报告以及一份或多份豆瓣电影 CSV 数据集。它解决的问题非常明确用一个人人都看得懂的电影评分场景演示数据清洗、特征工程、机器学习建模到报告输出的完整闭环。它适合三种用途理解机器学习落地流程、直接作为课程作业复现、改造后作为面试作品。接下来我会按“解压复现 → 清洗与特征 → 建模评估 → 排错避坑 → 报告输出”的顺序展开把源码变成你能自由控制的项目而不是只在别人电脑上能跑的黑匣子。2. 拆包与复现从 zip 源码压缩包到本地跑出第一张图表拿到一个以“源码报告”命名的压缩包第一步不是解压而是先建立“项目地图”。我拆过太多课程项目 zip 包发现一个完整的豆瓣电影分析压缩包几乎固定包含四层内容数据层CSV/JSON/pkl 文件、代码层.py 脚本或 .ipynb、报告层Word/Markdown/PDF、输出层figures 图表和模型结果。这四层在实际项目中并不一定在同一目录下但缺了任何一层你后面都会卡住。数据层缺失时代码能跑但“无米下锅”输出层缺失时对照报告里的图表你会非常吃力。所以我的习惯是解压之前先记下压缩包里有哪些顶层目录再对照下面的典型项目结构做比对。2.1 解压时的第一个坑中文路径与编码先不急着双击解压。这个 zip 的文件名和内部文件名很可能是中文的Windows 自带解压工具遇到 UTF-8 编码的中文文件名时经常解压出一堆乱码文件夹名Linux 和 macOS 的默认解压行为也有差异。我的习惯是把它解压到一个纯英文路径下比如D:\douban_project或~/douban_project。这个动作看起来无所谓实际影响很大——不少旧代码用相对路径data/douban_movie.csv读文件如果项目目录名带“豆瓣”两个汉字Windows 控制台默认代码页GBK和 Python 字符串的 UTF-8 编码一旦发生碰撞pd.read_csv就会直接抛 FileNotFoundError。如果解压后文件名仍是乱码常见原因是压缩包制作时用了 GBK 编码的 zip 格式。用系统自带工具解压 Windows 下制作的 zip 多数没问题反过来在 macOS 或者 Linux 下解压就容易乱。这时候用 7-Zip 重新解压在选项中把编码切到 UTF-8 或者 GB18030绝大多数乱码都能恢复。解压完成后先不要急着读代码用文件管理器或命令行把目录结构看一遍tree /F douban_project如果系统没有 tree 命令Windows 下用dir /smacOS 或 Linux 下用find . -maxdepth 2 -type f。这一步重点关注三个入口数据目录是否存在、入口文件是 .py 还是 .ipynb、有没有依赖清单 requirements.txt。许多 zip 包里会附带一两个加_new或_final后缀的数据文件比如douban_movie_final.csv这类文件一般是作者清洗后的结果优先使用它而不是从原始抓取文件重新走一遍清洗流程这样可以节省大量调试时间。提示如果压缩包里有.pkl或.npy文件说明作者把预处理后的数据以二进制格式保存了。从这些文件加载数据通常只要几秒而且能绕开 CSV 编码问题是你复现最快的一条路径。2.2 环境搭建Python 版本、虚拟环境与依赖安装这个项目几乎必然依赖 pandas、numpy、scikit-learn、matplotlib、seaborn、jupyter 六个基础库。先用命令行确认 Python 版本python --version如果是 3.10 而且拿到的是若干年前的项目代码先做心理准备Python 3.12 移除了distutils模块部分旧依赖会直接安装失败。课程项目最常见的安全版本区间是 Python 3.8 到 3.9建议优先用这个版本搭建虚拟环境python -m venv venv venv\Scripts\activatemacOS 或 Linux 下的命令是另外两行python3 -m venv venv source venv/bin/activate激活成功时终端提示符前方会出现(venv)前缀告诉你现在所有 pip 操作都进独立环境里。这个习惯在机器学习项目里的价值怎么强调都不过分——scikit-learn 半年一次的大版本更新足以让老源码直接运行失败虚拟环境相当于项目专用的“保险箱”环境坏了删掉重建即可不影响机器上的其他项目。依赖安装优先看有没有requirements.txtpip install -r requirements.txt这里我特别想提醒你在pip install之前先检查requirements.txt里的版本约束。有的源码包写死pandas0.24.2、scikit-learn0.20.0这类版本在 Python 3.9 下可能装不上需要你把等号后的版本改宽松一点再安装。缺少 requirements 文件时按项目实际需求手动安装pip install pandas numpy scikit-learn matplotlib seaborn jupyter豆瓣文本分析还会用到 jieba 分词和 wordcloud 词云按需加装pip install jieba wordcloud安装过程如果出现网络问题临时换成国内镜像源即可-i参数只对当前命令生效不改全局配置pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后别急着跑代码先确认版本组合pip list | findstr pandas scikit-learn matplotlibLinux/macOS 把findstr换成grep。一个相对稳妥的组合是 pandas 1.3.x、scikit-learn 0.24.x 或 1.0.x、matplotlib 3.5.x。这个组合兼容绝大多数课程代码也覆盖了后面要讲到的 API 路径问题。2.3 第一次运行从入口文件验证数据与指标是否对齐环境就绪后先跑入口文件。如果项目是.py脚本直接python main.py如果是 Jupyter Notebook启动服务jupyter notebook然后在浏览器里打开analysis.ipynb用菜单中的 Kernel → Restart Kernel and Run All 从头到尾完整运行一遍而不是只运行当前单元格。这一点非常关键因为 Notebook 的单元格共享内存前面某个单元格没执行后面会连续报name df is not defined让人误以为源码本来就有错。跑通后的代码会输出数据规模和基础统计信息。典型的第一个单元格通常是这样的import pandas as pd df pd.read_csv(data/douban_movie.csv) print(样本量:, df.shape) print(df.head()) print(df.info())df.shape会给出(行数, 列数)比如(1792, 11)。立刻把这个行数和报告里写的样本量对比如果对不上说明数据文件被替换过或者清洗步骤没有完整运行这是判断源码包是否自带完整数据的第一个信号。df.info()能看出每个字段的非空数量与 dtype比如rating有 1792 个非空值而actors只有 1400 个说明后者缺失较多后续特征工程必须处理。另外如果运行时的报错信息指向No module named sklearn.cross_validation这类问题直接跳到最后一部分的避坑清单看解决方案。这类版本兼容问题在旧课程项目里是头号杀手。到这里你已经跨过了“拆包 → 搭环境 → 跑通”这道门槛可以从代码层面理解这个项目了。接下来真正决定模型效果好坏的不是算法而是数据清洗和特征工程的质量。3. 数据清洗与特征工程把豆瓣电影里的原始字段变成模型能用的数值特征3.1 豆瓣数据集的常见字段与类型陷阱典型的豆瓣电影 CSV 会包含以下列title片名、rating豆瓣评分、rating_people评分人数、director导演、actors演员、genre类型、year年份、region地区、language语言、length片长、date上映日期。如果包里有多份 CSV字段名往往并不统一比如有的文件叫votes有的叫rating_count合并前必须统一口径。字段名常见 dtype脏数据形态处理方向ratingfloat64个别字符串“暂无评分”强制转数值或剔除yearobject/int64混入“2021(中国大陆)”正则抽取四位年份genreobject“剧情/爱情/历史”或“剧情,爱情”拆分后独热lengthobject/int64“120分钟”或缺失提取数字、中位数填充actorsobjectNaN 或导演与演员混写缺失占位统计人数rating_peopleint64/object可能带千分位逗号去除逗号再转 int读入后的第一件事永远是统一列名和检查 dtypeimport pandas as pd df pd.read_csv(data/douban_movie.csv, encodingutf-8) df.columns [c.strip().lower() for c in df.columns] print(df.dtypes)df.dtypes的结果里rating应该是float64year可能是int64也可能是object这个差异决定了后续要不要做类型转换。object类型的年份极可能是混入了“2021(中国大陆)”这类脏文本直接sort_values会按字符串排序得到 1099 在 2020 之前的诡异结果。3.2 缺失值处理不能一把dropna()了事处理缺失值最忌讳的做法是df df.dropna()因为这会把“演员缺失”和“语言缺失”这些不同业务含义的缺失一视同仁地删掉样本量瞬间缩水三分之一模型在剩下的数据上学到的分布也会偏离真实情况。规范的流程是先看缺失比例再决定策略missing_ratio df.isnull().mean().sort_values(ascendingFalse) print(missing_ratio)拿到比例后按业务含义分三种处理超过 80% 的列比如很多数据集里的imdb_link直接删20%~80% 之间的列按字段语义填充低于 5% 的列少量填充即可。# 按业务含义填充 df[length] df[length].fillna(df[length].median()) df[language] df[language].fillna(未知) df[actors] df[actors].fillna(暂无) df[director] df[director].fillna(未知导演)这里length用中位数而不是均值是有讲究的电影片长分布右偏个别长纪录片会把均值拉高中位数更能代表典型片长。language、director这类字符串字段用独立的占位值后面做get_dummies时会自动生成一列“未知”类别既保住了样本量又不会引入错误的数值语义。3.3 特征工程年份提取、类型展开与人物特征year字段如果混有文本用正则提取四位年份df[year_text] df[year].astype(str) df[year_num] df[year_text].str.extract(r(\d{4})) df[year_num] pd.to_numeric(df[year_num], errorscoerce)str.extract只提取第一个匹配的四个连续数字errorscoerce把无法转成数字的值变成 NaN最后统一dropna(subset[year_num])。这种处理比直接int(df[year])健壮得多——后者碰到“1997(中国香港)”会立刻崩溃。类型字段genre在豆瓣数据里常见两种格式一种是“剧情/爱情/历史”另一种是“剧情,爱情,历史”。先统一分隔符再拆分、展开、独热编码df[genre] df[genre].fillna(未知) df[genre] df[genre].str.replace(/, ,) # 拆分并切成列表 genres df[genre].str.split(,, expandTrue) df[main_genre] genres[0] # 只保留出现频次前 8 的类型防止维度爆炸 top_genres df[main_genre].value_counts().head(8).index df[main_genre] df[main_genre].where(df[main_genre].isin(top_genres), 其他) df pd.get_dummies(df, columns[main_genre], prefixgenre)get_dummies把main_genre中的每个类别映射成 0/1 列。限制前 8 个类型是关键参数因为豆瓣类型字段可能有几十种全部展开会生成大量低频列不但增加维度还会稀释模型的特征重要性。genre_剧情这类列名的含义是“此电影是否属于剧情片”取值只有 0 和 1。人物特征是这类项目里最容易做出区分度的地方。导演和演员对评分有较强的业务相关性但直接独热编码会产生几百列稀疏特征。更经济的做法是构造“导演历史平均分”和“主演人数”这类聚合特征# 主演人数 df[actor_count] df[actors].fillna().apply(lambda x: len(str(x).split(,))) # 导演历史平均分注意这里用了全量数据正式建模要先切分再算 director_stats df.groupby(director)[rating].mean().rename(director_avg_rating) df df.merge(director_stats, left_ondirector, right_indexTrue, howleft)这里有一个重要的数据泄露问题用全量数据计算导演平均分再作为特征喂给模型测试集信息会悄悄溜进训练集。严谨做法是先切分训练集和测试集再在训练集上做groupby统计把统计结果 merge 回测试集。这一点在避坑章节会展开讲。merge的howleft表示以左边的主表为基准没有匹配到的导演得到 NaN处理时填充全局均值即可。3.4 探索性分析报告里的那几张图是怎么来的几乎每份报告都会有以下四张图评分分布直方图、评分随时间变化的折线图、不同类型电影的平均评分对比图、评分与评分人数的散点图。用 seaborn 很快就能复现import matplotlib.pyplot as plt import seaborn as sns plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, axes plt.subplots(2, 2, figsize(12, 9)) sns.histplot(df[rating], bins30, kdeTrue, axaxes[0, 0]) sns.lineplot(xyear_num, yrating, datadf, axaxes[0, 1]) sns.barplot(xmain_genre, yrating, datadf, axaxes[1, 0]) sns.scatterplot(xrating_people, yrating, datadf, alpha0.4, axaxes[1, 1]) plt.tight_layout() plt.savefig(figures/eda_overview.png, dpi150) plt.show()plt.rcParams[font.sans-serif]设置中文字体这是中文图表不显示豆腐块的固定操作。bins30是直方图分箱数kdeTrue在直方图上叠加核密度曲线。sns.scatterplot里的alpha0.4是透明度参数数据点密集时能看出叠加程度避免一张图全被黑色点糊住。看完图和统计描述就具备了进入建模的前提。下面把评分当作回归目标完整走一遍机器学习流程。4. 机器学习建模从评分预测到报告里那套可复现的结果4.1 回归还是分类先看报告的目标字段拿到源码先做一个判断这个项目建模目标是回归还是分类。如果报告里出现mean_squared_error、R2、RMSE这些指标就是回归出现accuracy、precision、recall、F1就是分类。两种问题的特征处理和评估方法差别非常大混用会让整份报告失去意义。回归场景里最直接的问题是“根据电影特征预测豆瓣评分”。下面是一段最小可复现的回归代码from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_squared_error, r2_score feature_cols [year_num, length, rating_people, actor_count, director_avg_rating] X df[feature_cols].copy() y df[rating].astype(float) # 特征里的缺失值统一用中位数填充 for c in feature_cols: X[c] X[c].fillna(X[c].median()) X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) model RandomForestRegressor(n_estimators300, max_depth10, min_samples_leaf3, random_state42) model.fit(X_train, y_train) pred model.predict(X_test) print(RMSE:, mean_squared_error(y_test, pred, squaredFalse)) print(R2:, r2_score(y_test, pred))逐参数解释这段代码背后的考虑。train_test_split的test_size0.2表示 20% 的数据做测试random_state42固定随机种子让每次运行的结果一致这是可复现性的基础。n_estimators300是随机森林中决策树的数量300 棵比 100 棵的方差更小但超过一定阈值后计算量线性增长、收益趋于饱和。max_depth10限制单棵树深度防止对训练集死记硬背。min_samples_leaf3要求叶子节点至少 3 个样本是一个控制过拟合的强力旋钮值越大模型越平滑。这组超参数如果换成你手上另一份源码不要机械照抄因为特征数量和数据量不同最佳取值都会变。4.2 特征选择用特征重要性砍掉无效维度训练完模型后第一件事永远是查看feature_importances_feat_imp pd.Series(model.feature_importances_, indexfeature_cols).sort_values(ascendingFalse) print(feat_imp)输出是一个从高到低排列的重要性向量。我处理特征重要性的经验分三步先把重要性低于 0.02 的特征删掉重新训练并对比测试集 RMSE如果 RMSE 没有明显恶化说明删掉的确实是噪声特征如果 RMSE 显著上升说明该特征虽然单看贡献小但与其他特征存在协同效应要保留它。还有一个常见误用必须提醒不要把特征重要性当成因果关系的证据。rating_people的重要性高只能说明在随机森林的分裂过程中它与评分关联性强不能直接解读为“评分人数越多评分越高”。因果判断需要另外做控制变量分析报告里如果要讨论“影响评分的主要因素”措辞上建议写“强相关特征”而不是“决定性因素”。4.3 模型对比与交叉验证单次切分结果不靠谱一份合格的报告不会只比较一个模型。最常见的做法是同时跑线性回归、决策树和随机森林对比它们在测试集上的表现from sklearn.linear_model import LinearRegression from sklearn.tree import DecisionTreeRegressor from sklearn.ensemble import RandomForestRegressor models { LinearRegression: LinearRegression(), DecisionTree: DecisionTreeRegressor(max_depth10, random_state42), RandomForest: RandomForestRegressor(n_estimators300, max_depth10, min_samples_leaf3, random_state42), } for name, m in models.items(): m.fit(X_train, y_train) p m.predict(X_test) print(name, RMSE:, mean_squared_error(y_test, p, squaredFalse), R2:, r2_score(y_test, p))单次随机切分的问题在于如果数据只有一千条左右测试集只有两百条RMSE 的差异完全可能是采样波动。更高置信度的做法是用交叉验证取平均指标from sklearn.model_selection import cross_val_score scores cross_val_score( RandomForestRegressor(n_estimators300, max_depth10, min_samples_leaf3, random_state42), X, y, cv5, scoringr2 ) print(CV R2:, scores, mean:, scores.mean(), std:, scores.std())cv5做五折交叉验证把数据分成 5 份每次取 4 份训练、1 份评估循环 5 次后取均值和标准差。标准差大说明模型在不同数据子集上表现不稳定通常指向数据量不足或特征噪声高。这时候与其继续调参不如回到特征工程——机器学习的经典经验是“好的特征比好的模型更值钱”在豆瓣电影这种小数据集上尤其适用。4.4 模型调参用网格搜索找更合理的参数组合如果交叉验证结果稳定但指标不理想可以做一个简单网格搜索from sklearn.model_selection import GridSearchCV param_grid { n_estimators: [200, 300, 400], max_depth: [6, 10, 14], min_samples_leaf: [2, 3, 5], } grid GridSearchCV( RandomForestRegressor(random_state42), param_grid, cv5, scoringr2, n_jobs-1 ) grid.fit(X_train, y_train) print(grid.best_params_) print(grid.best_score_)n_jobs-1让所有 CPU 核心并行训练cv5表示对每组参数做五折交叉验证。GridSearchCV的输出best_params_是评分最高的一组参数best_score_是它的平均交叉验证分数。不建议把搜索范围设置得过大比如给n_estimators设 [50, 500, 5000] 会导致训练时间成倍增加而且在小数据集上最终差异往往只有零点几个百分点不划算。到这里一个完整的建模流程已经走通。接下来这一段是整篇内容里含金量最高的部分那些在源码和报告里几乎看不到但你实际跑起来一定会撞上的问题。5. 避坑与排查豆瓣电影分析项目里最常见的 5 个翻车点5.1 数据泄露特征里混进了评论、短评或评分人数现象训练阶段 R2 高达 0.95测试集 R2 却只有 0.3或者某特征的feature_importances_异常地高比第二名高出三倍以上。原因特征列表里混入了与评分强相关的“评分人数”“评论内容”“点赞数”等字段或者在构造“评论情感得分”这类特征时正负向标签本身来自测试集样本。模型相当于提前看到了答案训练指标是假的换到真实新数据立刻露馅。解决建模前先打印feature_cols人工检查有没有不该出现的列把所有short_comment、review相关列排除。更严谨的做法是把训练测试集划分提前到特征构造之前先切分训练集和测试集再在训练集内部计算导演均值、演员均值这类统计特征并保存映射表最后把映射表应用到测试集。这样测试集的信息就不会反向流入训练集。这个点如果能在答辩或面试时主动讲出来会是明显的加分项。5.2 中文编码CSV 读取乱码与 UnicodeDecodeError现象pd.read_csv(data/douban_movie.csv)报UnicodeDecodeError: utf-8 codec cant decode byte 0xd0...或费劲读进来了打印出来全是乱码。原因豆瓣导出的 CSV 可能是 GBK 或 GB18030 编码存储而 pandas 和 Jupyter 默认用 UTF-8 解码。Windows 简体中文环境的系统默认编码恰好是 GBK所以 Excel 打开正常Python 里一旦不encoding参数就报错。解决读文件时显式指定编码df pd.read_csv(data/douban_movie.csv, encodinggb18030)如果仍是报错说明文件里混入了非法字节可以用 Notepad 或 VS Code 打开看右下角编码提示先另存为 UTF-8 with BOM再换回encodingutf-8读取。处理中文文本时还有另一个细节在 Windows 控制台打印中文字符可能触发UnicodeEncodeError解决办法是在代码开头执行import sys sys.stdout.reconfigure(encodingutf-8)5.3 训练集测试集划分忽略了时间维度现象报告里的测试集准确率或 R2 很好看但把模型拿到新的电影数据上预测值完全不靠谱。原因很多源码用的是默认train_test_split(..., random_state42)随机打乱后训练集和测试集同时含过去和未来的电影。模型在时间上“穿越”了测试评估的是记忆历史数据的能力而不是预测未来的能力。这在时间序列性质的业务场景里是设计错误。解决当数据里有year_num字段并且业务场景是预测未来电影评分时按时间顺序划分df df.sort_values(year_num).reset_index(dropTrue) split_point int(len(df) * 0.8) train_df df.iloc[:split_point].copy() test_df df.iloc[split_point:].copy() X_train train_df[feature_cols] y_train train_df[rating] X_test test_df[feature_cols] y_test test_df[rating]切分完再建模测试集里的电影年份整体晚于训练集符合“预测未来”的语义。写进报告时这个方法比随机划分更经得起追问。5.4 scikit-learn 版本变动导致老代码报错现象按 requirements.txt 装完依赖后源码第一行就报ModuleNotFoundError: No module named sklearn.cross_validation或者train_test_split不存在。原因scikit-learn 在 0.20 版本把cross_validation模块重构成model_selection里面的train_test_split、cross_val_score都换了位置。旧代码按老路径导入自然失败这是机器学习生态中常见的 API 迁移问题不是你的环境坏了。解决把导入语句改成新路径from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV如果源码里还用了更老的sklearn.datasets.load_boston这类在最新版中已被移除的功能有两种处理方案一是把 scikit-learn 降级到 0.22~0.24 区间这个版本兼容大多数课程老代码二是按新 API 重写对应调用。注意warnings.filterwarnings(ignore)只能屏蔽警告不能修复已经移除的函数。排查时先确认当前环境版本pip show scikit-learn然后对着报错里的模块路径去官方迁移文档改写虽然稍花时间但能彻底搞清楚“原来为什么会报错”。5.5 样本量太小导致建模结果不稳定现象同一份代码用不同 random_state 运行RMSE 差异超过 0.2或者交叉验证的均值和单次切分结果相差很大。原因豆瓣这类项目数据量经常只有几百到两千条评分预测任务本身噪声高复杂的树模型很容易过拟合单次切分的偶然性远大于模型间的真实差异。解决优先减少模型复杂度把max_depth从默认的 None 调到 8 到 10把min_samples_leaf升到 3 到 5。其次考虑把回归问题改造成分类问题例如把评分分成“高分≥7.5/中分6~7.5/低分6”三段用准确率和 F1 作为报告指标。稳定性比绝对值更重要报告里明确写出“交叉验证标准差”比只写一个漂亮的单次 R2 更有说服力。6. 交付前最后一步报告结构、结果验证与可复现性检查很多同学拿到源码包以后有个误区代码能跑、图能出就默认项目可以交付了。实际上最后一步更关键让报告和代码之间形成闭环。一份合格的豆瓣电影分析报告应该由三层结论构成——描述层数据规模、字段构成、评分分布、关联层年份与评分、类型与平均分、评分人数与评分的关系、模型层比起基准模型机器学习模型的增量是否值得付出复杂度。在写报告的模型结论之前我有一个固定习惯先跑一个基线模型。这里指“永远预测训练集评分的均值”。用 sklearn 的 DummyRegressor 一行就能跑from sklearn.dummy import DummyRegressor dummy DummyRegressor(strategymean) dummy.fit(X_train, y_train) dummy_rmse mean_squared_error(y_test, dummy.predict(X_test), squaredFalse)然后对比随机森林的 RMSE 和这个基线值。如果随机森林只比基线改善不到 5%报告里就不应该写“模型显著优于基准”而应该把重心转向特征工程分析哪些特征对评分解释力更强。这个对比做下来报告的价值会提升一个档次因为你说清楚了“模型到底带来了多少增量”而不是只粘贴一个孤零零的 R2 数字。关于报告中的每张图表我强烈建议写清对应的数据来源和运行路径。图表下方加一行说明比如“基于清洗后 1792 条豆瓣电影样本统计源码入口 analysis.ipynb 第 3 节”。这样做最大的好处是别人拿到你的 zip 包后能按图索骥地复现如果跑出来的数字对不上也能快速定位原因。最后分享我的交付习惯在写报告之前先运行一句df.to_csv(final_dataset.csv, indexFalse, encodingutf-8-sig)把最终建模用的数据集固化成一份快照。utf-8-sig会在文件开头写入 BOM 字节Excel 打开 CSV 时中文不乱码其他人拿到这份 CSV 也方便直接用 pandas 复现。这个动作相当于给了项目一个后悔药机制哪怕后续调参把数据集改乱了随时可以回到这份快照重来。我见过太多人省掉这一步结果报告写完发现数据被中间逻辑改坏只能返工重跑得不偿失。把“解压复现 → 清洗特征 → 建模评估 → 排错避坑 → 报告输出”这条链路完整走一遍之后你手里的这个 zip 包就不再是黑匣子了。它能跑出什么水平的结论、指标差是数据还是特征或者参数造成的你都能在几分钟内定位到原因。希望这份笔记帮到你。本文还有配套的精品资源点击获取