ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

波士顿房价预测实战:线性回归全流程与模型诊断详解

波士顿房价预测实战:线性回归全流程与模型诊断详解 简介基于线性回归实现波士顿房价预测的项目源码是一份面向机器学习初学者的完整期末作业。项目已获得导师指导并通过审核最终得分九十七分适合作为课程设计、期末大作业或线性回归算法练手参考。压缩包共十二个文件核心是三个Python脚本分别完成线性回归模型的训练、评估与预测另含六张过程可视化图片、一个已训练好的joblib模型文件以及README说明文档整体大小约为733千字节解压后即可运行。读者可结合代码与说明文档复现波士顿房价预测流程学习从数据读取、特征处理到利用sklearn构建线性回归模型并完成评估的完整思路同时通过可视化图像直观理解特征与房价之间的线性关系。目前已有1123人学习/下载目录结构清晰实用性和可操作性都比较强。1. 波士顿房价预测为什么绕不开线性回归期末大作业拿 97 分靠的不是调参而是把线性回归算法在波士顿房价预测这条链路上每一步都做对了。这个数据集虽然 2019 年后被 scikit-learn 移出但作为课程设计和期末大作业的标配它依然是最适合讲透回归问题的素材。原因是特征有真有假、有连续有离散目标变量 MEDV 接近正态但不完全正态样本量只有 506 条恰好能暴露过拟合、共线性、离群点三类经典问题。本文不是纸上谈兵而是按一份可直接运行的源码包拆解从数据清洗、模型训练到评估诊断全部给命令和代码。适合两类人一是正在做期末大作业的学生可以直接对着复现二是写过分类模型但没系统做过回归项目的开发者重点看第四章的交叉验证和第五章的残差诊断能补上你平时容易忽略的环节。2. 数据来源与特征工程先搞清楚 13 个特征里哪些能信2.1 波士顿房价数据集的前世今生与读取方式波士顿房价数据集由 Harrison 和 Rubinfeld 于 1978 年收集包含 506 条样本每条样本记录波士顿地区不同街区的房产信息。原始数据里有 14 个字段其中 MEDV自有住房价格中位数单位千美元是目标变量其余 13 个是特征。值得注意的是sklearn 1.2 版本已经移除了load_boston()接口理由是数据集包含可能引发伦理讨论的特征 B非裔美国人比例所以现在的项目源码里一般会内置一份 CSV 文件直接pd.read_csv读取。常见做法是项目根目录下放一份 boston_housing.csv列名已改成可读形式。读取和数据探查的代码很固定import pandas as pd import numpy as np df pd.read_csv(boston_housing.csv) print(df.shape) # (506, 14) print(df.head()) # 检查缺失值 print(df.isnull().sum()) # 目标变量分布 print(df[MEDV].describe())这段代码做了三件事确认样本量和列数、检查是否有空值、观察目标变量的分布形态。describe()输出的四分位数和均值中位数对比能快速判断数据是否偏态——如果均值明显大于中位数说明存在右偏或离群值这在 MEDV 上确实存在因为部分高价位街区把均值拉高了。2.2 特征含义速查表13 个特征里有连续型、离散型、二值型理解它们的物理含义比直接扔进模型更重要尤其是答辩时老师大概率会逐个问。特征名含义数据类型对房价的预期影响CRIM城镇人均犯罪率连续负向ZN占地面积超过 25000 平方英尺的住宅用地比例连续弱正向INDUS城镇非零售商业用地比例连续负向CHAS是否邻近查理斯河1/0二值正向NOX一氧化氮浓度连续负向RM平均房间数离散强正向AGE1940 年前建成的自住单位比例连续负向DIS到波士顿五个就业中心的加权距离连续正向RAD径向高速公路可达性指数离散负向TAX每 10000 美元的不动产税率连续负向PTRATIO城镇学生与教师比例连续负向B黑人比例的变换值连续弱正向LSTAT低收入人群占比连续强负向2.3 相关性分析与离群点处理在做线性回归之前先看特征与目标的相关性矩阵这一步能筛掉明显无关的特征也能预判多重共线性问题。一般情况下用corr()和热力图完成import matplotlib.pyplot as plt import seaborn as sns corr_matrix df.corr().abs() # 与 MEDV 相关性最高的特征 print(corr_matrix[MEDV].sort_values(ascendingFalse)) plt.figure(figsize(10, 8)) sns.heatmap(corr_matrix, annotTrue, fmt.2f, cmapRdBu_r) plt.show()从相关系数排序可以看到LSTAT低收入人群占比与 MEDV 的负相关系数通常达到 -0.74 左右RM平均房间数则在 0.70 左右这两个特征贡献了模型绝大部分解释力。而特征之间的高相关则需要警惕RAD 与 TAX 的相关系数通常超过 0.9NOX 与 INDUS 也高度相关这意味着标准线性回归的系数估计会不稳定第五章会进一步验证。离群点处理有一个我常用的判断方法对每个特征计算 Z-score绝对值超过 3 的样本标记为离群点。但要注意线性回归对目标变量 MEDV 的离群点比特征离群更敏感尤其当 MEDV 大于 50 时这些值在原始数据里实际是被截断的capped。因此实践中不直接删除而是先建模看残差如果某条样本的残差超过 3 倍标准差再决定是否剔除。3. 线性回归算法原理与 sklearn 实现从最小二乘法到可运行模型3.1 最小二乘法的几何意义和解析解线性回归假设目标值与特征之间满足线性关系y w_1 x_1 w_2 x_2 ... w_13 x_13 b写成矩阵形式为 y Xw b。模型训练的目标是最小化残差平方和L(w) |y - Xw|^2_2对 w 求导并让导数为零得到闭式解w (X^T X)^{-1} X^T y这个解析解在小样本n506下非常高效因为 X^T X 是 13×13 的矩阵求逆的计算量几乎可以忽略。但如果特征之间存在严重共线性X^T X 接近奇异求逆会出现数值不稳定的问题这是线性回归算法在真实数据集上最常见的一个坑。所以某些实现里会加入正则项变成岭回归目的就是让 X^T X \lambda I 可逆且稳定。3.2 sklearn 实现完整代码基于线性回归实现波士顿房价预测的源码包中核心训练文件 LinearRegression_2.py 的结构如下这是可以直接复现的版本import pandas as pd from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.preprocessing import StandardScaler from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error # 1. 加载数据 df pd.read_csv(boston_housing.csv) X df.drop(MEDV, axis1) y df[MEDV] # 2. 划分训练集和测试集 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42 ) # 3. 特征标准化只拟合训练集 scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 4. 创建并训练模型 model LinearRegression() model.fit(X_train_scaled, y_train) # 5. 预测与评估 y_pred model.predict(X_test_scaled) print(R2 Score:, r2_score(y_test, y_pred)) print(MSE:, mean_squared_error(y_test, y_pred)) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) print(MAE:, mean_absolute_error(y_test, y_pred)) # 6. 输出特征系数 feature_names X.columns coef_dict dict(zip(feature_names, model.coef_)) for name, coef in sorted(coef_dict.items(), keylambda x: abs(x[1]), reverseTrue): print(f{name}: {coef:.4f})3.3 关键参数与代码逻辑说明train_test_split的random_state42是为了固定随机种子保证每次运行划分结果一致期末大作业答辩时可能被问到为什么是这个数字——它的作用是让实验可复现换成其他整数也可以关键是固定。StandardScaler对线性回归不是必须的因为线性回归的系数可以吸收特征的尺度差异。但标准化有两种实际收益一是让每个特征的系数直接反映其重要性量级方便解释二是当后续切换到岭回归或 Lasso 时正则化项要求所有特征在相同尺度下才有意义。注意这里只对训练集调用fit_transform对测试集只调用transform这是防止数据泄露的标准做法。LinearRegression()的核心参数只有两个参数默认值作用何时修改fit_interceptTrue是否计算截距项 b数据已中心化时可设为 FalsepositiveFalse是否强制所有系数为正特征与目标关系先验已知为正时可用模型拟合完成后输出的特征系数排序非常直观LSTAT 和 RM 的系数绝对值最大对应相关系数分析中这两个特征对房价的强影响。第 3.1 节提到的共线性问题在系数上表现为 TAX 和 RAD 的系数可能一正一负互相矛盾这时就要做第五章的方差膨胀因子诊断。4. 评估指标、交叉验证与正则化调优从跑通到结果可信4.1 回归模型四大评估指标的使用边界波士顿房价预测的输出是连续数值不能套用准确率常用的四个指标分别回答了不同问题指标公式要点适合场景本项目典型值R²1 - SS_res / SS_tot模型解释力越接近 1 越好0.70 ~ 0.75MSE残差平方的均值惩罚大误差对离群敏感50 ~ 70千美元²RMSEMSE 开根号误差尺度与 y 同单位可解释性强7 ~ 9千美元MAE绝对误差均值离群影响更小稳健5 ~ 6千美元RMSE 与 MAE 的差值能提示离群点的存在如果 RMSE 明显大于 MAE比如大 1.5 倍以上说明测试集中存在预测误差非常大的样本。本项目预测目标 MEDV 的均值在 20 千美元左右RMSE 达到 7~8 千美元意味着平均误差接近 40%这个精度在教学场景下是合理的工作场景中会考虑引入非线性模型但大作业答辩讲清楚误差来源比盲目堆模型更值钱。4.2 K 折交叉验证标准流程单次 train_test_split 的结果受随机划分影响很大尤其当样本量只有 506 条时某次划分可能恰好把高价房都分进测试集导致 R² 剧烈波动。所以跟导师汇报时要补充交叉验证的结果。from sklearn.model_selection import cross_val_score, KFold kf KFold(n_splits5, shuffleTrue, random_state42) cv_scores cross_val_score(model, X_train_scaled, y_train, cvkf, scoringr2) print(CV R2 scores:, cv_scores) print(CV R2 mean: {:.4f}, std: {:.4f}.format(cv_scores.mean(), cv_scores.std())) cv_mse cross_val_score( model, X_train_scaled, y_train, cvkf, scoringneg_mean_squared_error ) print(CV RMSE mean: {:.4f}.format((-cv_mse.mean()) ** 0.5))这段代码的KFold设置了shuffleTrue目的同样是保证划分的随机性。输出里 R² 的均值和标准差是两个关键观察点标准差超过 0.1 说明模型对训练集划分敏感也就是方差偏大需要正则化或增加数据均值明显低于单次测试集上的 R²说明单次划分运气好真实泛化性能没那么强。交叉验证结果里还有一个常见坑是scoringneg_mean_squared_errorsklearn 的cross_val_score默认遵循「越大越好」的约定所以 MSE 被取负手动开根号前要先加负号还原。如果直接对负数开根会在 numpy 中得到 NaN。4.3 岭回归与 Lasso 的调参实验当交叉验证发现模型方差大、或者特征系数不稳定时加入 L2 正则化的岭回归Ridge是线性回归最直接的升级路径。泛化性能对比实验代码如下from sklearn.linear_model import Ridge, Lasso # 候选正则化强度 alphas [0.001, 0.01, 0.1, 1.0, 10.0, 100.0] for alpha in alphas: ridge Ridge(alphaalpha) ridge_scores cross_val_score(ridge, X_train_scaled, y_train, cvkf, scoringr2) lasso Lasso(alphaalpha) lasso_scores cross_val_score(lasso, X_train_scaled, y_train, cvkf, scoringr2) print(falpha{alpha:.3f}, Ridge R2{ridge_scores.mean():.4f}, fLasso R2{lasso_scores.mean():.4f})在这里alpha 控制惩罚力度alpha 越大系数被压缩得越狠模型偏差增加、方差降低。调参观察到的典型现象是alpha 从 0.001 加到 1.0 时 R² 略有波动但总体稳定继续加大到 100 时 R² 明显下降说明模型被过度正则化进入了欠拟合区间。Lasso 的另一个特性是会把不重要的特征系数压缩到 0所以还可以观察哪些特征被清零——在波士顿数据集里通常 ZN 和 CHAS 的系数最先归零说明它们对房价的解释力在控制其他变量后贡献很小。5. 残差诊断与答辩技巧用验证性分析让模型不露破绽5.1 残差图与 Q-Q 图以 sklearn 自带模型为对象期末大作业拿到好分数的关键不只是跑通代码而是能主动说出模型的缺陷和改进方向。线性回归的四个经典假设是线性关系、误差独立、误差同方差、误差正态分布。逐一用残差验证import scipy.stats as stats # 计算残差 residuals y_test - y_pred # 1. 残差 vs 预测值散点图 plt.figure(figsize(8, 5)) plt.scatter(y_pred, residuals, alpha0.6) plt.axhline(y0, colorred, linestyle--) plt.xlabel(Predicted MEDV) plt.ylabel(Residuals) plt.show() # 2. Q-Q 图检验残差正态性 stats.probplot(residuals, distnorm, plotplt) plt.show() # 3. 查看残差统计量 print(Residual mean:, np.mean(residuals)) print(Residual std:, np.std(residuals))残差图如果呈现明显的扇形预测值增大时残差扩散说明存在异方差性此时标准误差估计和置信区间都不可信常见补救方法是对目标变量取对数即把 y 替换为np.log1p(y)模型的目标就变成预测房价的对数值。Q-Q 图里如果残差在两端偏离直线说明厚尾分布这时可以报告 MAE 而不是只依赖 RMSE。这两项验证放在项目报告里就是预答辩时最好的防守答案。5.2 共线性定量判断计算方差膨胀因子 VIF第 2.1 节已经发现 RAD 与 TAX 高度相关这里用 VIF 定量确认from statsmodels.stats.outliers_influence import variance_inflation_factor X_with_const sm.add_constant(X_train_scaled) vif_data pd.DataFrame() vif_data[feature] X.columns vif_data[VIF] [ variance_inflation_factor(X_with_const, i 1) for i in range(X.shape[1]) ] print(vif_data.sort_values(VIF, ascendingFalse))VIF 大于 10 通常认为存在严重共线性RAD 和 TAX 会最先爆掉通常超过 20。处理方式有三选一直接删除其中一个特征将两个特征合并改用岭回归。实践中更推荐在报告中直接展示 VIF 结果然后说明最终模型选择保留 TAX、删除 RAD理由有两个——TAX 的物理含义更清晰且保留 RAD 时系数不稳定。这一步体现的是对线性回归算法假设的理解程度也是最容易被提问的环节。5.3 手动验证一次预测让答辩演示更完整最后在代码里保留一个预测示例输出方便答辩时当场演示sample X_test_scaled[0].reshape(1, -1) sample_true y_test.iloc[0] sample_pred model.predict(sample)[0] print(f真实房价: {sample_true:.2f} 千美元) print(f预测房价: {sample_pred:.2f} 千美元)这段代码的作用是让答辩现场直接从测试集取一条样本验证真实值与预测值的差距就是你要向导师解释误差来源的起点。配合上面的残差分析结果你可以说清哪些街区被高估、哪些被低估以及这与 LSTAT 特征的关系。这样整个基于线性回归实现波士顿房价预测的项目源码就从「模型跑通」升级成了「模型可解释、局限可验证」的完整作业。本文还有配套的精品资源点击获取
返回列表