机器学习实践项目(二)- 房价预测增强篇 - 模型训练与评估:从多模型对比到小网格微调
好,前述的代码,我们处理了特征工程,下面就正式进入了模型训练阶段了。
我们的策略是——用多个模型进行交叉验证并选出最佳模型。
下面来分析对应的代码。
一、评测指标函数:rmse_on_log_space
def rmse_on_log_space(y_true, y_pred):try:return mean_squared_error(np.log1p(y_true), np.log1p(y_pred), squared=False)except TypeError:return np.sqrt(mean_squared_error(np.log1p(y_true), np.log1p(y_pred)))
- 把真实值/预测值先做
log1p再算 RMSE —— 这是竞赛中常见的做法,能缓解右偏分布。 - 兼容旧版 sklearn:旧版
mean_squared_error没有squared参数,所以兜底用sqrt(MSE)。
二、核心交叉验证函数:rmse_cv(model)
pipe = Pipeline([("prep", preprocess),("reg", TransformedTargetRegressor(regressor=model, func=np.log1p, inverse_func=np.expm1))
])
...
for tr_idx, va_idx in stratified_splits:X_tr, X_va = X.iloc[tr_idx], X.iloc[va_idx]y_tr, y_va = y[tr_idx], y[va_idx]pipe.fit(X_tr, y_tr)pred_va = pipe.predict(X_va)rmses.append(rmse_on_log_space(y_va, pred_va))
- 把预处理(
preprocess)和模型串成一个Pipeline,
这样每折都只在训练折上拟合 imputer / 标准化 / One-Hot,避免数据泄漏。
这个和前面介绍的“分层目标编码”一个概念。 - 用
TransformedTargetRegressor(TTR)让模型在 log 空间 里训练,预测时自动逆变换。
可以理解成:TTR 是一个小“批处理器”,定义一组操作,执行时顺序完成。 - 遍历“第0步”生成的
stratified_splits(分层 K 折),
确保每次试验用同一套折分,各模型结果可公平比较。 - 最后返回该模型的 平均 RMSE 和标准差(越小越好)。
三、候选模型与“稳收敛”设置
candidates = {"RidgeCV": RidgeCV(...),"LassoCV": LassoCV(..., max_iter=200_000, tol=1e-3, selection="random"),"ElasticNetCV": ElasticNetCV(..., max_iter=200_000, tol=1e-3, selection="random"),"HistGBR": HistGradientBoostingRegressor(...)
}
- 同时用
RidgeCV / LassoCV / ElasticNetCV / HistGBR四类模型进行评估,选出最优者。 - 线性稀疏模型给了更大的
max_iter、略放宽的tol、加上selection="random",
是为了减少“不收敛”或“收敛慢”的告警。 - HistGBR 则采用一组「保守但好用」的参数:
learning_rate=0.06、max_leaf_nodes=31、min_samples_leaf=20、l2_regularization=1e-3,
强调稳健泛化。
🌱 一句话先讲明白:
“我想比较几种不同的模型,看哪个预测房价最准。
为了防止它们训练不收敛或报错,我给每个模型设置了比较稳定的参数。”
四、模型字典与参数的通俗理解
🧩 四个模型是谁?
candidates = {"RidgeCV": RidgeCV(...),"LassoCV": LassoCV(...),"ElasticNetCV": ElasticNetCV(...),"HistGBR": HistGradientBoostingRegressor(...)
}
这其实就是一个“模型字典”:
| 模型 | 简单理解 | 作用 |
|---|---|---|
| RidgeCV | 岭回归 | 线性模型,防止过拟合 |
| LassoCV | 套索回归 | 线性模型,自动选特征 |
| ElasticNetCV | 弹性网 | 综合 Ridge 和 Lasso |
| HistGBR | 树模型(梯度提升树) | 捕捉非线性关系 |
五、模型参数逐个解释(配比喻更好记)
1️⃣ max_iter
训练时最多迭代多少次。
想象模型在不断“试探”参数,直到误差够小。
太少会“没学完”,所以给大一点(如 200000)更稳。
2️⃣ tol
容忍度(容错值)。
越小越严格,模型学得更久。稍放宽(1e-3)就不会老警告。
3️⃣ selection="random"
对 Lasso/ElasticNet 来说,这表示“随机挑选参数更新”。
随机顺序能防止算法卡住,更容易收敛。
4️⃣ learning_rate=0.06
树模型的学习速度。
太大会过拟合,太小会学不完。0.06 是稳健中间值。
5️⃣ max_leaf_nodes=31
每棵树的最大分支数。
分支越多模型越复杂,31 是“刚刚好”的中等复杂度。
6️⃣ min_samples_leaf=20
每个叶子节点至少多少样本。
样本太少容易记住噪声,20 可以防过拟合。
7️⃣ l2_regularization=1e-3
给模型一个“惩罚项”,让它别太激进。
值越大越“温和”,泛化更好。
🧠 类比:模型就像学生
| 参数 | 比喻 | 调节作用 |
|---|---|---|
| max_iter | 给学生复习的次数 | 多学几遍才彻底掌握 |
| tol | 老师标准 | 放宽点别太抠细节 |
| selection | 学习顺序 | 随机复习更灵活 |
| learning_rate | 学习速度 | 太快出错,太慢浪费时间 |
| max_leaf_nodes | 笔记条数 | 太多=啰嗦,太少=粗糙 |
| min_samples_leaf | 每条笔记至少几句 | 保证总结有依据 |
| l2_regularization | 老师的管束 | 防止学得太极端 |
✅ 一句话总结:
这些参数的目的就是:让模型稳稳当当地“学完”,不乱跑、不炸分。
所以叫“稳收敛设置”。
六、模型对比与最优选择
results = {}
for name, model in candidates.items():m, s = rmse_cv(model)results[name] = (m, s)print(f"{name:12s}: {m:.5f} +/- {s:.5f}")best_name = min(results, key=lambda k: results[k][0])
- 对每个模型都跑一次
rmse_cv,输出“均值 ± 标准差”; - 选 RMSE 最小 的模型作为
best_name; - 后续可用它进行全量训练或进一步调参。
七、小网格微调(仅当 HistGBR 最优时)
DO_HGB_TUNING = (best_name == "HistGBR")
...
hgb_pipe = Pipeline([("prep", preprocess),("reg", TransformedTargetRegressor(regressor=HistGradientBoostingRegressor(random_state=42),func=np.log1p, inverse_func=np.expm1))
])
param_grid = {"reg__regressor__learning_rate": [0.03, 0.06, 0.1],"reg__regressor__max_leaf_nodes": [31, 63, 127],"reg__regressor__min_samples_leaf": [10, 20, 40],"reg__regressor__l2_regularization": [1e-3, 3e-3, 1e-2]
}
继续沿用同一个管道结构,只在少数关键超参上做“小网格”搜索。
时间可控,却往往能涨一点分。
😵💫 什么是“小网格微调”?
好,到这里估计又要懵了。
什么是“网格”?什么是“小网格微调”?难道还有“大网格”?
好吧,我也是懵的,只好让“爱老师”继续上场 😄。
🧩 一句话解释
“小网格微调”就是:
模型已经不错了,再拿出几组可能更好的参数组合,
让电脑帮我们试试,看哪组最好。
🌰 举个简单例子
假设你在调一台风扇:
| 参数 | 档位 |
|---|---|
| 风速 | [1, 2, 3] |
| 风向 | [固定, 摇头] |
3 × 2 = 6 种组合,你挨个试试,看哪种风最舒服。
这,就是“小网格”搜索。
💡 在模型里怎么做?
HistGBR 也一样:
我们给它四个主要“旋钮”:
| 参数 | 含义 | 可能的值 |
|---|---|---|
| learning_rate | 学习速度 | [0.03, 0.06, 0.1] |
| max_leaf_nodes | 每棵树最多叶子数 | [31, 63, 127] |
| min_samples_leaf | 每个叶子最少样本 | [10, 20, 40] |
| l2_regularization | 正则强度 | [1e-3, 3e-3, 1e-2] |
让电脑“自动试” → 共 3×3×3×3 = 81 种组合。
这就是“小网格(Grid)”。
⚙️ GridSearchCV 做的事
- 挨个尝试参数组合;
- 每组都做交叉验证;
- 记录结果(RMSE);
- 选出最优组合;
- 返回“调好参数的最优模型”。
🧮 为什么叫“小网格”
参数太多会组合爆炸:
| 参数数 | 每个取值 | 组合数 |
|---|---|---|
| 2 | 3 | 9 |
| 3 | 3 | 27 |
| 4 | 3 | 81 |
| 5 | 3 | 243 |
所以我们只在关键参数上取少量值,形成“小网格”,
节省计算,又能有效微调。
✅ 一句话总结:
“小网格微调 = 在模型已经不错的情况下,自动帮你试几组最重要的参数,让它更稳、更高分。”
八、最终产物
results:每个模型在分层 CV 下的 RMSE 均值/方差。best_name:最好的基础模型名。best_estimator(可选):若做了 HistGBR 微调,则为最优管道(含预处理 + TTR + 最优超参)。
九、一句话总结
这段代码把**“公平、稳定、可复现”**三件事落到实处:
- 同一套分层折评所有模型(公平);
- 在 log 空间评 RMSE,且每折重拟合预处理(稳定);
- 固定随机种子、重用折分、必要时小网格微调(可复现 + 提升)。
