
简介这是一份面向计算机相关专业学生与机器学习初学者的房价预测实战项目源自经导师指导并获得98分评价的课程大作业。项目覆盖二手房房价数据采集、特征分析与模型构建等完整流程压缩包内共26个文件包含15个Python脚本用于数据处理与模型实现、Jupyter Notebook及HTML分析报告、爬虫抓取脚本与CSV数据文件还配有使用说明文档整体大小约1.28MB结构清晰便于按步骤运行和复现。目前已有111人学习下载。通过该资源读者可掌握从数据清洗、特征工程到房价回归预测的完整思路获得可直接运行的源码、可视化图表与配套讲解说明适合用于课程设计、期末大作业参考或项目实战练习。1. 为什么人工智能大作业都爱选房价预测一个能串起完整机器学习流程的入口每年人工智能大作业选题时总有人翻着教材挑一个数据集好找、模型能跑的方向。房价与二手房价格预测几乎是这类作业里的“标准答案”数据容易获取任务是典型回归问题而且天然覆盖数据清洗、特征工程、模型训练、评估可视化这条完整机器学习应用流程。我见过不少同学拿到“房价与二手房价格预测源码使用说明”后直接跑通、换数据、改参数就交差结果答辩时被老师问住“特征为什么这么处理”“为什么用R²而不是MAE做结论”这其实不是代码的问题而是对整个流程缺一个整体认知。我下面按平时做这类回归任务的顺序从数据准备讲到模型评估再把使用说明里最容易藏坑的地方全摊开适合正在做机器学习大作业、需要快速落地房价预测项目的同学。2. 数据准备拿到二手房价格原始数据后先做的三件事数据决定了模型的上限这句话在房价预测里体现得非常直接。很多公开的二手房数据集并不是为了机器学习准备的里面会混着“暂无数据”“业主急售”这类文本还有同一套房在不同渠道重复出现的记录。你后面所有特征工程和模型调参都要建立在干净的数据上。2.1 字段梳理先别急着跑模型把每一列都问一遍“能不能用”拿到数据的第一件事不是写 pd.read_csv 然后立刻 train_test_split而是打开字段清单逐个确认含义。常见二手房数据一般包含小区名称、所在区域、户型室厅卫、建筑面积、单价、总价、朝向、装修情况、楼层、建筑年代、是否有电梯。其中“总价”和“单价”有强线性关系单价总价/面积这两个字段不能同时当作原始特征否则模型会把这种恒等关系当作最大的规律导致看似 R² 很高实际没学到任何真实模式。我一般会先跑一段探查代码看清每个字段的类型和缺失比例。import pandas as pd df pd.read_csv(house_price.csv, encodingutf-8-sig) print(df.shape) print(df.info()) # 对数值字段做分布统计重点看有没有离群值 num_cols df.select_dtypes(include[int64, float64]).columns print(df[num_cols].describe().T.to_string()) # 查看类别字段的唯一值数量判断是否适合直接编码 for col in df.select_dtypes(include[object]).columns: print(col, df[col].nunique(), df[col].unique()[:10])这段代码有几个地方值得说明。encodingutf-8-sig是为了兼容带 BOM 的 CSV很多从网页导出或 Excel 另存的文件都是这种格式不加的话第一列列名会变成“\ufeff小区名称”。select_dtypes(include[object])会把所有字符串列挑出来方便你一眼看出哪些是需要编码的类别特征哪些其实是文本备注比如“满五唯一”“有抵押”这类备注通常不适合直接进模型。字段梳理阶段最容易被忽略的是“区域”字段。北京的“朝阳区”和上海的“浦东新区”出现在同一份数据里时单独做独热编码没有意义因为地域之间没有可比性。更合理的做法是把区域拆成“城市”和“市内片区”两级或者干脆按项目要求只看单一城市的数据。常见的做法是只保留一个城市的房源避免模型学到城市间的价格水平差异而掩盖了真正的特征关系。2.2 缺失值与异常值删除、填充还是打个标记房价数据的缺失值很有特点低总价房源往往户型字段缺失老小区“电梯”字段缺失刚开盘的新楼盘“建筑年代”缺失。这些缺失不是随机的背后往往有业务原因。如果直接把缺失行删掉可能会删掉大量低价样本让模型只见过偏贵的房子。# 缺失率超过50%的列直接删除 df df.drop(columns[col for col in df.columns if df[col].isnull().mean() 0.5]) # 数值列用中位数填充避免被极端的豪宅总价拉偏 for col in [面积, 建筑年代]: df[col] df[col].fillna(df[col].median()) # 类别列填充未知并保留是否缺失的标记 for col in [朝向, 装修]: df[col _is_missing] df[col].isnull().astype(int) df[col] df[col].fillna(未知)这里的关键参数是isnull().mean()它算的是每列缺失比例。我一直用的阈值是 50%高于这个阈值说明该字段已经不具备可用性留着只会引入噪声。中位数填充比均值安全因为房价数据里“总价 5 亿的独栋别墅”会把均值拉得很离谱中位数还能反映大多数样本的水平。给类别列加_is_missing标记是我比较喜欢的技巧模型可以从中学到“这个房源信息不全”这个事实有时反而能提升预测精度。异常值处理要单独说。面积 1 平方米的房子、总价 1 万元的“车位”这些要查一下是不是数据录入错误。先用箱线图看一轮再用业务经验过滤。import matplotlib.pyplot as plt # 先看面积分布有没有极端尾巴 plt.boxplot(df[面积]) plt.show() # 按业务常识过滤住宅建筑面积一般在20到500平之间 df df[(df[面积] 20) (df[面积] 500)] df df[df[总价] 0] print(df.shape)这里的过滤阈值不要照搬要结合你所在城市的实际情况。比如一线城市的老破小可能只有 20 平而二线城市的大平层可能到 300 平。你可以先打印describe()里的 min 和 max再决定上下限。2.3 目标变量分布为什么总价要取对数再训练直接拿总价做回归目标会有两个问题一是总价呈长尾分布几套高总价豪宅会把损失函数的主导权抢走二是模型预测出的价格恒为正但线性回归没有这个限制可能预测出负价。取对数可以同时解决这两个问题。import numpy as np df[log_price] np.log1p(df[总价]) # 对比变换前后的分布直方图 ax1 plt.subplot(1, 2, 1) plt.hist(df[总价], bins50) ax1.set_title(总价原始分布) ax2 plt.subplot(1, 2, 2) plt.hist(df[log_price], bins50) ax2.set_title(log1p之后分布) plt.show()np.log1p在 x 趋近 0 时等价于 log(x1)它比 log(x) 更稳健避免出现 log(0) 的警告。训练完成后预测值要记得用np.expm1还原这是log1p的逆变换。为什么大作业里很多源码都默认做对数变换因为评估指标 RMSE 在原始空间和 log 空间是不同的。如果对总价取 log 训练而评估时直接用原始总价算 RMSE那你其实是在比较两个不同尺度的误差。规范做法是训练目标用 log_price评估时把预测值 expm1 还原成真实总价再用原始总价算指标。3. 特征工程把地址、朝向和房龄编码成模型能理解的数值特征工程是房价预测里最能让手工特征发光的一步。同一个模型不处理朝向和处理好朝向线上测试集可能差出几十万。这一章讲的是把表格里的文本和数值整理成模型能吃下的格式同时避免一些常见的编码陷阱。3.1 类别特征编码独热编码与顺序编码的适用边界朝向、装修、楼层低中高这类特征看似是类别但有部分存在顺序语义。比如装修情况“毛坯、简装、精装、豪装”明显是从差到好直接用整数 1、2、3、4 编码比独热编码更合适因为模型可以学到“数值越大装修越好房价越高”的单调趋势。而朝向“东、南、西、北”没有这种顺序用梯度提升树时可以直接做标签编码但线性模型必须用独热编码否则“南3、北4”这种编码顺序会让线性模型误以为北方朝向价格更高。我在处理这类项目时的准则是对树模型类别特征直接用整数编码即可独热编码反而会让树的分裂算子难以发挥对线性模型和神经网络独热编码是必须的。下面分别给出两种做法。# 顺序编码装修情况按档位映射 order_map {未知: 0, 毛坯: 1, 简装: 2, 精装: 3, 豪装: 4} df[decorate_rank] df[装修].map(order_map) # 独热编码朝向没有顺序关系线性模型用get_dummies df pd.get_dummies(df, columns[朝向], prefixtoward)get_dummies默认会生成 K 列如果原始列里没有缺失值建议加上drop_firstTrue把 K 列变成 K-1 列避免多重共线性。但对决策树类模型drop_first不是必须的树的分裂不依赖线性独立。另一个容易被忽视的类别特征是“区域”。同一城市内片区之间的价格差异非常大但片区数量可能有一两百个直接独热编码会生成上百列让矩阵变得稀疏。常见的做法是先用目标编码把片区映射为“该片区平均 log_price”但这很容易造成数据泄露必须结合交叉验证来做。大作业里更稳妥的办法是保留原始区域字段用标签编码给树模型用不要自己做目标编码除非你有时间解决过拟合。3.2 数值特征组合面积、单价、总价之间的隐藏关系二手房数据里“单价 总价 / 面积”是一个恒等式。但真正有预测能力的不是这个恒等式而是房龄、楼层、是否电梯与面积之间的交互。比如同一面积下有电梯的房子比没电梯的贵但这部分差异会被面积和总价本身吸收吗不一定因为电梯和楼龄有关。我一般会构造这几个衍生特征房龄 当前年份 - 建筑年代房龄比建筑年代更直观且数值是连续递增的。每平方价差总价/面积 - 小区均价这个需要先按小区分组计算均值然后再做差。它能捕捉同一小区内部不同房源的溢价。户型紧凑度面积 / 房间数用于衡量“同样面积是两居还是四居”小户型大房龄在北京西城会有特殊性。下面是一段派生特征代码。current_year 2025 df[house_age] current_year - df[建筑年代] # 按小区计算平均单价再用当前单价减小区均价得到相对溢价 community_mean df.groupby(小区名称)[单价].transform(mean) df[price_premium] df[单价] - community_mean # 紧凑度面积除以室数厅数卫数注意避免除以0 df[rooms] df[室] df[厅] df[卫] df[density] df[面积] / df[rooms].replace(0, np.nan)transform(mean)返回与 df 相同长度的 Series不会改变行数这比先groupby再merge更简单。replace(0, np.nan)处理了部分数据“室厅卫都为 0”的异常这样 density 缺失的行会在后续模型里被处理而不是参与训练制造无穷值。注意单价和总价只能保留一个作为特征。如果同时保留模型会发现总价/面积就是单价然后把这个恒等式当成最重要的特征导致模型在其他真实因素上学不到东西。这个坑在答辩时几乎必被问到提前把“我们只保留总价把单价当作衍生特征计算”写进使用说明。3.3 标准化与数据集划分不同城市的数据别混在一起切特征标准化只对线性模型和距离类模型有效。对线性回归面积范围在 20 到 500 之间房龄范围在 0 到 100 之间如果不标准化面积会主导梯度更新。对树模型标准化不影响分裂点所以随机森林和 XGBoost 不要求标准化。from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 只对线性模型使用scaler num_features [面积, house_age, density, price_premium] scaler StandardScaler() X_train_num scaler.fit_transform(X_train[num_features]) X_test_num scaler.transform(X_test[num_features])这里唯一的要点是fit_transform只能用在训练集上测试集必须用同一套scaler.transform。如果你对全量数据先做了标准化再去切分测试集的信息已经在均值里透出来了这叫数据泄露。大作业里这种错误很常见代码表面上没报错但换一份数据后分数就崩。数据划分还有个细节如果数据来自不同城市直接随机切分会导致同一城市的房源同时出现在训练集和测试集模型会记住城市均价测试集成绩虚高。更严谨的做法是按城市分组切分或者至少保证同一小区不在两个集合里出现。这个在源码里往往是一行train_test_split(df, test_size0.2, random_state42)的事但理解为什么这样做比跑通代码更重要。4. 模型选型与训练从线性回归到 XGBoost 的落地对比大作业里最贪心的想法就是“把 SVM、随机森林、XGBoost、神经网络全部跑一遍哪个分高用哪个”。结果通常是被随机森林和 XGBoost 碾压但答辩时讲不清楚为什么。模型选型的正确路线是先拿一个简单模型做 baseline确认特征工程有效再逐步升级到复杂模型。4.1 先用线性回归跑通最小流程再谈复杂模型线性回归是房价预测的最小可行模型它假设特征与房价对数呈线性关系。虽然现实中不成立但它的价值在于快速验证数据流水线有没有 bug。如果线性回归的 R² 异常低比如只有 0.1那大概率是特征编码错了或者目标变量没取 log而不是模型不行。from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score model LinearRegression() model.fit(X_train, y_train) y_pred_log model.predict(X_test) y_pred np.expm1(y_pred_log) y_true np.expm1(y_test) print(MAE:, mean_absolute_error(y_true, y_pred)) print(RMSE:, mean_squared_error(y_true, y_pred, squaredFalse)) print(R2:, r2_score(y_true, y_pred))这里我用y_test存的是 log_price所以评估时先expm1还原回总价再计算指标。很多人会忘记这一步直接在 log 空间里算 MAE得到的数字比如 0.2会让人误以为误差只有两毛钱实际换算成总价误差是几千几万。线性回归的参数几乎不用调。真正要检查的是特征共线性。你可以看模型的系数如果某个特征的系数异常大且符号违背常识比如面积系数为负多半是共线性或目标变量没处理好。这个阶段的“排查”价值远大于“预测”价值。4.2 随机森林与 XGBoost集成模型不是无脑上随机森林是房价预测大作业最常用的模型因为它对特征尺度不敏感、能捕捉非线性交互、不易过拟合。XGBoost 通常分数更高但它对参数更敏感调不好会比随机森林还差。下面表格是我平时给新手用的对比维度随机森林 RandomForestRegressorXGBoost XGBRegressor对特征缩放的依赖无无默认参数表现较好直接可用一般需要调参过拟合风险低高需要早停或正则训练速度慢快特征重要性基于不纯度基于分裂增益可解释性较好较好随机森林需要调的参数主要是n_estimators树的数量、max_depth树深、min_samples_leaf叶子最少样本数。我一般把树数固定到 300重点调max_depth从 5 到 15。from sklearn.ensemble import RandomForestRegressor rf RandomForestRegressor( n_estimators300, max_depth12, min_samples_leaf3, random_state42, n_jobs-1 ) rf.fit(X_train, y_train)n_jobs-1用满所有 CPU 核能明显加快训练。min_samples_leaf设成 3 而不是默认的 1可以避免模型记住单个极端样本。随机森林的随机性来自random_state固定它才能在多次实验之间做公平对比。XGBoost 的调参更讲究。学习率learning_rate默认 0.3但大作业里更适合 0.01 到 0.05配合n_estimators1000和早停让树一棵一棵地充分学习。from xgboost import XGBRegressor from sklearn.model_selection import train_test_split # 从训练集再切出验证集用于早停观察测试集仍然不参与 X_tr, X_val, y_tr, y_val train_test_split( X_train, y_train, test_size0.2, random_state42 ) xgb XGBRegressor( learning_rate0.03, max_depth6, n_estimators1000, subsample0.8, colsample_bytree0.8, reg_lambda1.0, random_state42, tree_methodhist ) xgb.fit( X_tr, y_tr, eval_set[(X_val, y_val)], verboseFalse )subsample0.8表示每棵树只用 80% 的样本colsample_bytree0.8表示每棵树只用 80% 的特征这两个参数都能降低过拟合。tree_methodhist在大数据集上比默认的近似法快很多对一万行左右的二手房数据差距不大但习惯上我会加上。注意eval_set里的验证集只用于观察训练曲线不要基于它的分数反复挑参数否则验证集又变成了测试集。4.3 交叉验证与超参搜索拿验证集调参测试集只碰一次很多初学者的错误是把训练集再切成小训练集和验证集用验证集调参最后测试集跑一次。这样已经算规范了。更规范的写法是使用 K 折交叉验证让每一折数据都有机会当验证集。from sklearn.model_selection import KFold, cross_val_score kf KFold(n_splits5, shuffleTrue, random_state42) scores cross_val_score(rf, X_train, y_train, cvkf, scoringr2) print(5折R2均值:, scores.mean(), ±, scores.std())cross_val_score返回的是每折的 R²最终看均值和方差。如果均值高但方差大说明模型对数据划分很敏感可能需要减少树深度或者增加数据。这个现象也是答辩时可以主动讲出来的亮点。超参搜索不要用网格搜索全遍历房价数据特征维度不高但树模型参数组合非常多全遍历会跑一个小时。我用RandomizedSearchCV做随机搜索配合n_iter50十分钟内能出结果。from sklearn.model_selection import RandomizedSearchCV param_dist { max_depth: [6, 8, 10, 12], min_samples_leaf: [2, 3, 5], n_estimators: [200, 300] } search RandomizedSearchCV( RandomForestRegressor(random_state42), param_distributionsparam_dist, n_iter20, cv5, scoringr2, n_jobs-1, random_state42 ) search.fit(X_train, y_train) print(最佳参数:, search.best_params_) print(最佳得分:, search.best_score_)n_iter20表示随机抽 20 组参数组合不是 20 次完整网格遍历。best_score_是交叉验证的平均分数不是测试集分数。这一点一定要在论文或实验记录里写清楚模型选择用的是训练集内部的交叉验证最终验收才用测试集。5. 评估、可视化与避坑答辩前必须检查的三张图和四个坑评估函数能算出分数但不代表你理解了模型。答辩时老师更看重的是“你能不能解释分数背后的含义”。这一章先讲指标和图再讲我从源码运行到答辩踩过的四个坑。5.1 回归指标MAE、RMSE、R² 到底该怎么向老师解释MAE 是平均绝对误差单位是万元直观但无法区分小误差和大误差。RMSE 是均方根误差对大误差更敏感如果预测中混进一两个离群样本RMSE 会比 MAE 大很多。R² 是模型解释的方差比例范围通常在 0 到 1 之间0.8 意味着模型解释了 80% 的房价波动。指标意义适合场景房价大作业里的注意点MAE平均绝对误差关注典型误差单位与房价一致适合向非技术背景的人解释RMSE均方根误差放大较大误差与 MAE 差异大时说明预测存在极端失效样本R²决定系数模型整体解释力不是越大越好0.99 反而要怀疑数据泄露代码计算很简单但要额外关注“在哪个空间算”。我们训练的是 log_price所以from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score y_pred_log xgb.predict(X_test) y_pred np.expm1(y_pred_log) y_true np.expm1(y_test.values) mae mean_absolute_error(y_true, y_pred) rmse mean_squared_error(y_true, y_pred, squaredFalse) r2 r2_score(y_true, y_pred) print(fMAE: {mae:.1f} 万元, RMSE: {rmse:.1f} 万元, R²: {r2:.3f})如果 RMSE 比 MAE 大出两倍以上我通常会回去查预测误差最大的几条样本看看是不是有面积特别大的房子、或者标注有误的总价。这比盲目调参有用得多。5.2 两张答辩必备图残差图和预测对比图老师一看图就知道你模型做到了什么程度。第一张是残差图横轴是预测值纵轴是真实值减预测值。理想状态是残差均匀分布在 0 附近不随预测值增大而发散。第二张是预测值和真实值的散点图如果点都落在 yx 这条线上说明预测接近完美。import matplotlib.pyplot as plt plt.figure(figsize(12, 5)) # 左图预测值 vs 真实值 plt.subplot(1, 2, 1) plt.scatter(y_true, y_pred, alpha0.3) plt.plot([y_true.min(), y_true.max()], [y_true.min(), y_true.max()], r--) plt.xlabel(真实总价万元) plt.ylabel(预测总价万元) plt.title(预测值 vs 真实值) # 右图残差分布 plt.subplot(1, 2, 2) residual y_true - y_pred plt.scatter(y_pred, residual, alpha0.3) plt.axhline(y0, colorr, linestyle--) plt.xlabel(预测总价万元) plt.ylabel(残差真实-预测万元) plt.title(残差图) plt.tight_layout() plt.show()残差图里如果看到明显的“喇叭口”也就是预测值越大残差越分散说明模型在高端房源上不稳定。这个现象也常见因为高价房样本少模型史料不足。答辩时你可以主动说“我们注意到高端房源预测偏差偏大原因是该区间样本占比不足 5%后续可以通过采集更多高总价样本或对高总价区域单独建模来改进。”这比被老师逼问出来体面得多。5.3 坑一中文路径和编码让源码一跑就崩现象按使用说明运行python main.py控制台第一行就报UnicodeDecodeError或FileNotFoundError。原因Windows 下默认编码是 GBK而大部分数据集和源码都用 UTF-8 保存。如果你的项目文件夹路径里带中文比如“大作业/房价预测”某些旧版 sklearn 的模型序列化文件按相对路径加载时也会出问题。解决文件全部换成英文路径在 read_csv 里显式指定encodingutf-8-sig如果代码里用open()读文本也要加上encodingutf-8。这个坑十有八九会出现在大作业源码里你的使用说明里应该一开始就写清楚环境要求而不是让用户自己猜。5.4 坑二数据泄露——把未来信息喂给了模型现象测试集 R² 有 0.97但换一批数据后只有 0.5而且特征重要性第一名居然是“户型”。原因数据里包含了不该出现的字段。比如“总价”和“单价”同时作为特征模型只要记住“单价×面积总价”就能赢又比如用“挂牌时间”做特征而目标“成交价”发生在挂牌之后数据里的时间差本身就是答案。更隐蔽的是先对整个数据集做标准化或填充缺失值再切分训练测试集。解决特征清单里如果有单价删除时间类特征要么作为排序泄露直接去掉要么转换成滞后变量所有预处理都放在train_test_split之后。判断泄露的简单办法是看模型分数是否高得异常再检查特征是否包含了目标变量的某个线性变换。5.5 坑三拿测试集调参成绩虚高但答辩被问穿现象反复用同一份测试集试参数最后测试集 R² 比训练集还高。原因测试集的信息通过你的调参过程泄漏到了模型里。你“看过”测试集误差很多次模型选择已经在隐式拟合测试集了。这不是数据泄露但会让线下分数失真。解决固定一份“never-touch”测试集放在另一个文件里平时只用训练集做交叉验证只在最后提交前跑一次。如果你已经不小心用过多次就重新按 random_state 换一份测试集。在实验记录里写明最终结果只来自一次测试集评估。5.6 坑四可视化中文乱码与坐标轴看不清现象图上的中文全变成方块或者图例被挤到看不见。原因matplotlib 默认字体不支持中文。坐标轴刻度太密也会导致标签重叠。解决在绘图前设置字体和坐标轴格式。plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots() ax.scatter(y_true, y_pred, s6) ax.tick_params(labelsize10) ax.set_title(预测总价与真实总价对比)axes.unicode_minusFalse是必须的否则负号会显示成方框。如果你用的是 Mac把 SimHei 换成PingFang SC如果服务器上没有中文字库干脆把图上文字都统一改成英文避免答辩时演示环境没字体。6. 进阶验证用学习曲线判断模型状态用 SHAP 解释单套预测如果你还有一周时间不要急着换更深的模型先做这两件事学习曲线和 SHAP。它们能让你的大作业从“跑通源码”变成“讲清楚模型”。6.1 学习曲线欠拟合和过拟合不是猜出来的学习曲线是训练集大小与训练/验证得分的折线图。如果训练分和验证分都很低是欠拟合如果训练分很高而验证分低是过拟合。from sklearn.model_selection import learning_curve train_sizes, train_scores, val_scores learning_curve( xgb, X_train, y_train, train_sizes[100, 500, 1000, 2000, 4000], cv5, scoringr2, n_jobs-1 ) train_mean train_scores.mean(axis1) val_mean val_scores.mean(axis1) plt.plot(train_sizes, train_mean, label训练得分) plt.plot(train_sizes, val_mean, label交叉验证得分) plt.xlabel(训练样本数) plt.ylabel(R²) plt.legend() plt.show()如果两条曲线始终没有收敛说明数据量不够或者模型太复杂。这时最简单的做法不是加数据而是降低max_depth、增大min_samples_leaf。6.2 SHAP 值告诉老师“为什么这套房子预测 836 万”SHAP 是当前解释机器学习模型的主流方法它把预测值分解成每个特征的贡献。答辩时老师问“你凭什么预测这套房八百多万”你可以画出这个。import shap explainer shap.TreeExplainer(xgb) shap_values explainer.shap_values(X_test[:100]) shap.summary_plot(shap_values, X_test[:100], plot_typedot)TreeExplainer专门用于树模型随机森林也可以。summary plot 的每个点代表一个样本颜色代表特征值大小横坐标为 SHAP 值。如果“面积”的 SHAP 值沿着正方向明显扩散说明面积对价格的影响最大而且面积越大贡献越正向。这两个工具加起来能让你在答辩时占据主动。我记得有一次预答辩老师直接指着测试集里一套房子问“它为什么贵”我现场用 SHAP 算了一张分解图把面积、区域、房龄的贡献列出来老师反而开始跟我讨论数据质量而不是挑论文毛病了。从那以后我每次做回归类项目都固定加这两步先把验证图讲清楚再做预测演示。这个习惯帮我省了不少修改时间也希望帮到你。本文还有配套的精品资源点击获取