多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Keras回归实战:波士顿房价预测从数据加载到模型调参

Keras回归实战:波士顿房价预测从数据加载到模型调参 简介这份PDF面向具备Python与机器学习基础、希望用Keras实战回归问题的开发者与学习者以波士顿房价数据集为案例讲解如何构建神经网络预测连续型房价。内容围绕14个特征与506条1978年统计数据展开涵盖数据标准化、基准模型搭建、交叉验证与K折评估、KerasRegressor包装、GridSearchCV超参数搜索以及通过调整隐藏层结构与优化器将均方误差从约14降至10左右的调优思路。资源包共1个PDF文件约366KB篇幅紧凑适合作为回归项目入门与代码复现的参考。目前已有1349人学习下载可帮助读者掌握从数据预处理到模型评估的完整流程理解深度学习在价格预测等趋势类问题上的应用方法。1. 波士顿房价预测一个被低估的回归入门项目很多人第一次接触 Keras 和深度学习都是从波士顿房价预测这个数据集开始的。它不像 MNIST 那样被讲烂了也不像 ImageNet 那样门槛高得吓人13 个特征、506 条样本、一个连续值输出刚好卡在「能跑通」和「能讲清楚」之间。但我要说句实话这个项目真正难的地方从来不是搭网络而是数据本身有一堆历史遗留问题——比如那个著名的 B 特征取值 0 到 396.9含义是「城镇黑人比例」涉及伦理争议后来 scikit-learn 从 1.2 版本开始已经把它从load_boston里移除了。所以你现在照着老教程跑大概率第一行就报错。这篇文章面向两类人一是刚装完 Python 和 Keras想找个完整项目练手的二是做过分类任务但没认真处理过回归问题的。我会从数据加载的替代方案讲起一路走到模型调参、特征标准化、损失函数选择最后给一个能直接复现的完整流程。不堆概念每一步都告诉你为什么这么做、参数改了会怎样、报错先看哪里。2. 数据加载与预处理老 API 失效后的三条替代路径2.1 为什么load_boston不能用了scikit-learn 在 1.2 版本正式移除了load_boston原因是数据集中包含一个基于种族假设构造的特征社区认为继续分发不合适。如果你用的是 1.0 到 1.1 之间的版本还能用但会收到FutureWarning1.2 以上直接ImportError。这不是你环境装错了是官方主动砍掉的。那数据从哪来常见做法有三种方案来源优点缺点原始 CSV公开镜像站或本地存档字段完整可自定义需要自己处理缺失和编码fetch_openmlOpenML 平台官方推荐替代首次下载慢需网络内置合成数据make_regression无需网络可控不是真实房价数据我一般会优先用fetch_openml因为它是 scikit-learn 官方给出的迁移路径字段和原来一致只是多了一步下载。如果你在离线环境就提前把 CSV 存好。2.2 用fetch_openml拉取数据的完整代码# 需要 scikit-learn 1.2 from sklearn.datasets import fetch_openml import pandas as pd import numpy as np # 拉取波士顿房价数据集版本 1 # as_frameTrue 返回 DataFrame方便后续处理 data fetch_openml(nameboston, version1, as_frameTrue) # data.frame 包含特征 目标列MEDV df data.frame print(df.shape) # (506, 14) print(df.columns.tolist()) print(df.isnull().sum().sum()) # 检查缺失值正常为 0逻辑说明fetch_openml第一次调用会从网络下载并缓存到~/scikit_learn_data目录之后离线也能用。as_frameTrue是关键参数不加的话返回的是 numpy 数组列名和索引信息会丢失后面做特征分析很麻烦。参数说明nameboston是 OpenML 上的数据集标识version1对应原始版本。如果你不加 version默认取最新版但波士顿这个数据集只有 version 1 是标准的。2.3 特征标准化回归任务里最容易被跳过的一步波士顿房价的 13 个特征量纲差异极大CRIM 犯罪率在 0.006 到 89 之间NOX 一氧化氮浓度在 0.38 到 0.87 之间TAX 税率在 187 到 711 之间。如果你直接喂给神经网络梯度更新会被大量纲特征主导小量纲特征几乎不起作用。血泪经验不标准化loss 曲线会抖得像心电图。from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split # 分离特征和目标 X df.drop(columns[MEDV]).astype(np.float32) y df[MEDV].astype(np.float32) # 先划分训练集和测试集再对训练集 fit scaler # 这一步顺序不能反否则测试集信息会泄漏到训练过程 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意测试集只 transform不 fit print(X_train_scaled.shape, X_test_scaled.shape)逻辑说明fit_transform在训练集上计算均值和方差并转换transform在测试集上只用训练集的均值和方差做转换。这个顺序如果搞反测试集的信息就泄漏了模型评估结果会虚高上线后翻车。参数说明test_size0.2是常见划分比例506 条数据里约 101 条做测试。random_state42固定随机种子保证每次划分一致方便复现。3. 用 Keras 搭回归网络层数、激活、损失函数怎么定3.1 回归任务的输出层和损失函数选择分类任务输出层用 softmax损失用交叉熵回归任务输出层不加激活函数线性输出损失用均方误差 MSE。这是最基本的区别但新手最容易在这里照搬分类模板。import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers def build_model(input_dim): model keras.Sequential([ # 输入层 第一个隐藏层 layers.Dense(64, activationrelu, input_shape(input_dim,)), # 第二个隐藏层神经元减半 layers.Dense(32, activationrelu), # 输出层1 个神经元线性激活默认就是线性 layers.Dense(1) ]) model.compile( optimizerkeras.optimizers.Adam(learning_rate0.001), lossmse, metrics[mae] ) return model model build_model(X_train_scaled.shape[1]) model.summary()逻辑说明两层隐藏层、64 和 32 个神经元对于 506 条样本、13 个特征的数据量来说已经够用。再深容易过拟合再浅拟合不足。输出层Dense(1)不加激活函数因为房价是连续值不需要压缩到 0-1 或做概率映射。参数说明learning_rate0.001是 Adam 的默认值通常不用改。metrics[mae]加上平均绝对误差因为 MSE 的数值尺度是房价的平方不直观MAE 直接反映平均预测偏差多少千美元。3.2 训练过程中的 batch_size 和 epochs 怎么调history model.fit( X_train_scaled, y_train, validation_split0.2, # 从训练集里再切 20% 做验证 epochs200, batch_size16, verbose0 ) # 看最终几轮的 loss print(history.history[loss][-5:]) print(history.history[val_loss][-5:])逻辑说明validation_split0.2是从训练集里再切一部分做验证不碰测试集。batch_size16对于 400 条左右的训练数据每个 epoch 约 25 个 batch梯度更新频率适中。epochs200看起来多但回归任务收敛慢配合 EarlyStopping 更稳。参数说明batch_size 太小如 4会导致梯度噪声大loss 震荡太大如 128则更新次数少收敛慢。16 到 32 是这个数据量下的常见区间。3.3 加 EarlyStopping 和 ReduceLROnPlateau 回调callbacks [ # 验证 loss 连续 20 轮不下降就停并恢复最优权重 keras.callbacks.EarlyStopping( monitorval_loss, patience20, restore_best_weightsTrue ), # 验证 loss 连续 10 轮不降就把学习率砍半 keras.callbacks.ReduceLROnPlateau( monitorval_loss, factor0.5, patience10, min_lr1e-6 ) ] history model.fit( X_train_scaled, y_train, validation_split0.2, epochs500, batch_size16, callbackscallbacks, verbose0 )逻辑说明EarlyStopping 防止过拟合和无意义训练restore_best_weightsTrue保证模型回到验证 loss 最低的那一轮而不是最后一轮。ReduceLROnPlateau 在 loss 停滞时降低学习率帮助跳出局部最优。参数说明patience20表示容忍 20 轮不改善这个值太小会过早停止太大浪费算力。factor0.5是每次砍半min_lr1e-6是下限防止学习率降到 0。4. 模型评估与特征分析MAE 多少算合格4.1 在测试集上算 MAE 和 R²from sklearn.metrics import mean_absolute_error, r2_score y_pred model.predict(X_test_scaled).flatten() mae mean_absolute_error(y_test, y_pred) r2 r2_score(y_test, y_pred) print(fMAE: {mae:.2f} 千美元) print(fR2: {r2:.4f})逻辑说明MAE 直接告诉你预测房价平均偏了多少千美元。波士顿房价中位数约 21.2 千美元MAE 在 2.5 到 3.5 之间算正常低于 2.5 说明模型拟合不错。R² 衡量模型解释了多少方差0.8 以上算合格0.85 以上算好。参数说明flatten()把(n, 1)的预测输出压成(n,)否则 sklearn 的指标函数会报维度不匹配。4.2 看哪些特征对预测影响最大# 用 Permutation Importance 看特征重要性 from sklearn.inspection import permutation_importance result permutation_importance( model, X_test_scaled, y_test, n_repeats10, random_state42, scoringneg_mean_absolute_error ) # 按重要性排序 importance_df pd.DataFrame({ feature: X.columns, importance: result.importances_mean }).sort_values(importance, ascendingFalse) print(importance_df.head(8))逻辑说明Permutation Importance 的思路是逐个打乱某个特征的值看模型误差增加多少。增加越多说明该特征越重要。这比看神经网络权重直观得多因为权重在多层的非线性变换后很难解释。参数说明n_repeats10表示每个特征重复打乱 10 次取平均减少随机性。scoringneg_mean_absolute_error用负 MAE因为 sklearn 的约定是分数越大越好。5. 避坑与排查波士顿房价预测里最容易翻车的 5 个地方5.1 报错ImportError: cannot import name load_boston现象照着老教程写from sklearn.datasets import load_boston直接报 ImportError。原因scikit-learn 1.2 版本移除了这个函数原因是数据集中 B 特征涉及伦理争议。解决改用fetch_openml(nameboston, version1, as_frameTrue)或者提前下载 CSV 用pd.read_csv加载。不要试图降级 scikit-learn 到 1.1那样会引入其他兼容问题。5.2 训练 loss 下降但验证 loss 上升现象训练集 loss 一路降到很低验证集 loss 先降后升两条曲线分叉。原因模型过拟合。506 条数据对神经网络来说偏少两层 6432 的参数量已经可能记住训练集。解决加 Dropout 层layers.Dropout(0.2)或者减小隐藏层神经元数量或者加 L2 正则化kernel_regularizerkeras.regularizers.l2(0.01)。EarlyStopping 也能缓解但它是事后止损不是事前预防。5.3 预测结果全是同一个值现象模型预测出来的房价几乎一样MAE 很大但 loss 看起来在降。原因学习率太大导致模型卡在某个局部最优或者特征没有标准化大量纲特征主导了梯度。解决先检查标准化是否做了再检查学习率。把learning_rate从 0.001 降到 0.0001 试试或者换用ReduceLROnPlateau自动降。5.4 测试集 MAE 远大于验证集 MAE现象验证集 MAE 2.5测试集 MAE 5.0差距明显。原因数据划分不均匀或者标准化时测试集用了自己的均值和方差信息泄漏的反向问题。解决检查train_test_split的random_state是否固定检查scaler是否只在训练集上fit。如果数据量允许用 K 折交叉验证代替单次划分结果更稳定。5.5 用 accuracy 做回归评估现象模型编译时写了metrics[accuracy]训练时 accuracy 一直是 0 或者毫无意义的值。原因accuracy 是分类指标回归任务不能用。Keras 不会报错但结果没有意义。解决回归任务用metrics[mae]或metrics[mse]。如果想看百分比误差可以自定义MAPE但要注意房价中有接近 0 的值会导致除零。6. 进阶技巧用 Keras Tuner 自动搜超参数手动调层数、神经元数、学习率很费时间而且容易陷入局部尝试。Keras Tuner 是 TensorFlow 官方提供的超参数搜索工具能自动遍历你定义的搜索空间。我一般会在手动跑通一版之后用 Tuner 做一轮系统搜索通常能再降 5% 到 10% 的 MAE。# 需要先安装pip install keras-tuner import keras_tuner as kt def build_tunable_model(hp): model keras.Sequential() # 搜索隐藏层数量1 到 3 层 for i in range(hp.Int(num_layers, 1, 3)): model.add(layers.Dense( unitshp.Int(funits_{i}, min_value16, max_value128, step16), activationrelu )) # 每层后可选加 Dropout if hp.Boolean(fdropout_{i}): model.add(layers.Dropout(hp.Float(fdrop_rate_{i}, 0.1, 0.5, step0.1))) model.add(layers.Dense(1)) model.compile( optimizerkeras.optimizers.Adam( hp.Float(lr, 1e-4, 1e-2, samplinglog) ), lossmse, metrics[mae] ) return model tuner kt.RandomSearch( build_tunable_model, objectiveval_mae, max_trials20, executions_per_trial2, directorytuner_dir, project_nameboston_housing ) tuner.search( X_train_scaled, y_train, validation_split0.2, epochs200, batch_size16, callbacks[keras.callbacks.EarlyStopping(monitorval_mae, patience15)], verbose0 ) best_model tuner.get_best_models(num_models1)[0] best_hp tuner.get_best_hyperparameters(num_trials1)[0] print(best_hp.values)逻辑说明hp.Int(num_layers, 1, 3)搜索隐藏层数量hp.Int(funits_{i}, ...)搜索每层神经元数hp.Boolean决定是否加 Dropouthp.Float(lr, ..., samplinglog)在对数尺度上搜索学习率。max_trials20表示尝试 20 组不同配置executions_per_trial2表示每组配置跑 2 次取平均减少随机初始化带来的波动。参数说明objectiveval_mae表示以验证集 MAE 为优化目标Keras Tuner 默认取最小值。directory和project_name决定搜索结果存哪里下次可以断点续搜。一个实际跑下来的经验波士顿房价这个数据量最优配置通常落在 1 到 2 层隐藏层、每层 32 到 64 个神经元、学习率 0.001 到 0.005 之间。再复杂的网络在这个数据上几乎必然过拟合。所以如果你用 Tuner 搜出来最优是 3 层 128 神经元先检查验证集划分是不是有问题。最后说个习惯我每次跑完回归项目都会把y_test和y_pred的散点图画出来看一眼。如果点均匀分布在 yx 对角线两侧说明模型没有系统性偏差如果低房价段预测偏高、高房价段预测偏低说明模型对非线性关系的捕捉不够可以考虑加特征交叉项或者换用树模型做对比。这个图比任何指标都直观希望帮到你。本文还有配套的精品资源点击获取
返回列表