拓冰建站拓冰建站
首页 / 资讯中心 / 正文

波士顿房价预测:线性回归算法从原理到实践的高分毕设指南

简介这是一份基于线性回归实现波士顿房价预测的毕业设计源码及说明文档使用 Python 语言结合 sklearn 库完成模型构建适合计算机、通信、人工智能、自动化等相关专业学生作为课程设计、期末大作业或毕业设计参考也适合机器学习初学者理解经典回归流程。项目围绕波士顿房价数据集展开包含数据加载、特征处理、模型训练与预测评估的完整代码并配有可视化图像和训练好的模型文件便于读者对照理解各特征与房价之间的关系。压缩包共 12 个文件、约 732KB内含 3 个 Python 脚本、1 个 joblib 模型文件、6 张可视化图像及 Markdown 说明文档结构简洁代码经调试测试可直接运行和二次修改。项目源自个人毕业设计答辩评审分达 98 分学习借鉴价值较高目前已有 680 人学习下载。读者可从源码中掌握线性回归的 sklearn 实现方式与项目组织思路也可参考文档结构完成自己的课程设计与毕设基础较好者还可在此基础上扩展功能实现更多房价预测应用场景。1. 从“被移除的数据集”开始为什么波士顿房价还能做成高分毕设先说一个反直觉的事实波士顿房价数据集已经被新版 scikit-learn 移除了原因是当年采集的某些字段带有社会层面的敏感色彩。但这不妨碍它成为机器学习入门生态里最经典的一份回归数据更不妨碍你把它做成一份高分毕业设计。真正的理由是这份数据“小而不简单”506 个样本、13 个特征既有连续变量、离散变量还有二值变量足够你把线性回归算法的完整链路走一遍又不会被数据量淹没在调参里。毕业设计拿高分的关键往往不在模型多先进而在你能不能讲清楚“为什么用线性回归、数据怎么处理、结果怎么验证”。本文从数据获取一路写到模型评估和答辩展示每一段都有可复现的代码和参数说明。无论你是用 python 手写梯度下降还是直接调 sklearn都能找到对应的落地点。接下来我们先从回归原理和数据本身说起。2. 线性回归算法的本质与波士顿房价数据摸底2.1 从损失函数到求解方式线性回归算法在拟合什么线性回归算法做的事情非常直接找一组系数让预测值y_pred w1*x1 w2*x2 ... b与真实房价y之间的误差平方和最小。这个目标函数写作MSE (1/m) * Σ(y_pred - y_true)^2m 是样本数。之所以用平方误差而不是绝对误差是因为平方误差对大误差的惩罚更重也让目标函数变成一个凸函数保证能找到全局最优解。求解这个最优化问题有两条路线。一条是正规方程直接对损失函数求导并令导数为零得到闭式解另一条是梯度下降迭代地沿负梯度方向更新参数。实际工程里数据量在万级以下时正规方程非常快因为只需要一次矩阵乘法和一次求逆数据量变大后矩阵求逆的 O(n^3) 复杂度会拖慢速度此时梯度下降或它的变体是更常见的选择。在毕业设计的文档里这两条路线都值得写清楚。下面这张表可以在论文或文档说明里直接使用求解方式优点缺点适用场景正规方程一次求解、无学习率参数矩阵求逆慢特征多时不稳定特征数小于 1 万的小数据集梯度下降批量可扩展、内存可控需要调学习率和迭代次数大多数回归任务随机梯度下降收敛快、适合流式数据收敛路径有波动数据量大或在线学习场景2.2 加载波士顿房价数据13 个特征背后的编码含义虽然新版 sklearn 移除了内置的load_boston()但数据本身仍可以从 OpenML 拉取。用fetch_openml是最稳妥的路径它会返回一个 DataFrame特征名和原始论文保持一致。代码只有几行import pandas as pd from sklearn.datasets import fetch_openml # data_id531 是波士顿房价数据在 OpenML 上的编号 data fetch_openml(data_id531, as_frameTrue) df data.frame print(df.shape) # (506, 14) print(df.dtypes.value_counts()) # 看特征类型分布 print(df.head())这段代码会把 506 条样本、13 个特征加 1 个目标列MEDV修正后的房价中位数一起装载进来。as_frameTrue很关键它让返回结果直接是 pandas DataFrame便于后续做探索性分析。注意MEDV这一列本身是修正过的原始数据中房价超过 50 的样本被截断为 50所以数据里会有一批恰好等于 50 的值这是数据集的已知缺陷答辩时主动提出来反而是加分项。13 个特征的含义值得在文档里逐个列表说明重点是以下这几个RM平均房间数、LSTAT低收入人群占比、DIS到就业中心的加权距离、TAX房产税率、CHAS是否紧邻查尔斯河二值变量。其中RM和LSTAT与房价的相关性最大这一点在做相关性分析时很快就能验证。2.3 描述统计与相关分析先看 LSTAT 和 RM 再动手建模拿到数据后不要急着训练模型先用describe()和相关性矩阵做一次快速体检。这一步在文档说明里叫探索性数据分析它能帮你发现数据的取值范围、缺失情况和特征之间的关系。# 只挑几个有代表性的特征做统计 cols [RM, LSTAT, DIS, TAX, MEDV] print(df[cols].describe().T[[mean, std, min, max]]) # 计算特征与目标变量的相关性 corr df.corr(numeric_onlyTrue)[MEDV].sort_values(ascendingFalse) print(corr.head(8))从输出结果能观察到两个核心事实RM与MEDV呈较强的正相关房间数越多房价越高LSTAT与MEDV呈明显的负相关低收入人群占比越高的区域房价越低。这两个特征几乎决定了线性回归模型的上限后续做特征选择的对比实验时可以以它们为基线。还有一个容易被忽略的细节CHAS是二值变量它的相关性解读要格外小心。它只有 0 和 1 两种取值相关系数只能说明“河景房”与房价的线性关系不能理解为“每增加 1 单位 CHAS 房价就上涨多少”。文档里如果直接引用系数做解释很可能被答辩老师追问。建议在处理时把它明确标记为分类变量而不是普通的连续数值。3. 不依赖框架用 python 手写线性回归源码3.1 梯度下降前的最后一步标准化为什么必须做如果直接拿原始特征做梯度下降会遇到一个非常实际的问题TAX的数值范围在 200 到 700 之间而RM的数值范围只有 3 到 9。两个特征的量纲差一个数量级会导致梯度下降在数值大的特征方向上震荡收敛速度慢且不稳定。标准化Z-score是解决这个问题的标准手段对每个特征减去均值再除以标准差。代码实现如下import numpy as np def zscore(X): mu X.mean(axis0) sigma X.std(axis0) # 分母加 1e-8 防止零方差特征导致除零 return (X - mu) / (sigma 1e-8), mu, sigma这段代码返回标准化后的数据和两个统计量mu、sigma。保留这两个值很重要训练集上计算出来的均值和标准差必须保存下来用于转换测试集而不是在测试集上重新计算。原因很简单测试集扮演的是“未来数据”的角色我们不能让它提前参与统计量的计算否则就是典型的数据泄漏。3.2 带偏置的梯度下降线性回归算法完整实现手写线性回归的核心代码并不长但要写得规范。下面是一个支持批量梯度下降的完整实现训练集的特征矩阵X已经经过标准化目标值y也建议做标准化方便观察损失曲线def add_bias(X): # 在特征矩阵最左边拼接一列 1对应偏置项 w0 return np.hstack([np.ones((X.shape[0], 1)), X]) def mse_loss(y_pred, y_true): m len(y_true) return np.mean((y_pred - y_true) ** 2) def gradient_descent(X, y, lr0.05, epochs1000): X_b add_bias(X) m, n X_b.shape theta np.zeros(n) loss_history [] for epoch in range(epochs): pred X_b theta # MSE 对 theta 的梯度是 (2/m) * X_b.T (pred - y) gradient (X_b.T (pred - y)) / m theta - lr * gradient loss_history.append(mse_loss(pred, y)) return theta, loss_history参数说明lr是学习率控制每次参数更新的步长epochs是迭代轮数m是样本量梯度里除以m是为了让梯度大小与数据量解耦这样学习率的取值不会因为样本量不同而需要大幅调整。梯度方向的推导不复杂MSE 对 theta 的梯度是 (2/m) * X_b^T (pred - y)。代码里没有显式写求导过程但注释保留了这个公式答辩时如果被问到可以直接推导。如果你的电脑上还没有 python 环境需要先完成 python 安装再跑这段代码。step 顺序很简单官网下载 python 3.10 以上版本安装时勾选“Add to PATH”然后打开终端验证python --version最后用pip install numpy pandas matplotlib scikit-learn补齐依赖库。这一步完成后上面的代码就能直接运行。3.3 对比正规方程稳定与效率的选择点梯度下降需要调学习率可能陷入学习率过大不收敛、过小收敛慢的困境。作为对比正规方程一步到位直接求解参数。代码实现非常短# 解法二正规方程直接求解最优参数 X_b add_bias(X) theta_norm np.linalg.pinv(X_b.T X_b) X_b.T y这里使用pinv而不是inv是有讲究的。pinv是伪逆矩阵当X_b.T X_b不可逆比如存在高度相关的特征时伪逆依然能给出一个数值稳定的解而inv会直接报错。在线性回归算法中这种细节最能体现对数值计算的理解。两份源码跑完后对比损失曲线和最终参数。通常你会看到正规方程解出的参数与梯度下降收敛后的参数几乎一致但正规方程不需要调lr。在 506 条样本的场景下正规方程在速度上完胜。所以如果你的毕业设计目标是“稳妥”直接用正规方程如果希望展示“对优化算法的理解”用梯度下降并画损失曲线效果更好。4. 用 scikit-learn 提升预测效果Pipeline 与正则化4.1 拆分与归一化的先后顺序一个经典的数据泄漏陷阱手写部分讲的是算法原理真正提交毕业设计时代码的工程规范性同样重要。最容易踩的坑是数据拆分与归一化的顺序问题。正确做法是先把数据集拆成训练集和测试集再在训练集上计算均值和标准差然后用同一组统计量转换测试集。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error X df.drop(columnsMEDV) y df[MEDV] 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) # 在训练集上 fit X_test_scaled scaler.transform(X_test) # 在测试集上只用 transform model LinearRegression() model.fit(X_train_scaled, y_train) y_pred model.predict(X_test_scaled) print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse))需要强调的是fit_transform和transform的区分使用。fit_transform只在训练集上出现一次后续所有数据包括测试集和将来可能的新数据都只能调用transform。如果对测试集也做fit_transform测试集的均值方差信息会被模型间接学到测试误差会偏小答辩时一旦被指出整个模型的可信度都会打折扣。4.2 用 Pipeline 统一封装从线性回归到多项式回归上面的代码已经可以跑出基线 RMSE但每次新增预处理步骤都要手动拼接容易出错。更规范的写法是用Pipeline把标准化、模型训练串联起来from sklearn.pipeline import Pipeline from sklearn.preprocessing import PolynomialFeatures # 线性回归基线 linear_pipeline Pipeline([ (scaler, StandardScaler()), (reg, LinearRegression()) ]) # 多项式回归引入二次项捕捉非线性关系 poly_pipeline Pipeline([ (scaler, StandardScaler()), (poly, PolynomialFeatures(degree2, include_biasFalse)), (reg, LinearRegression()) ]) linear_pipeline.fit(X_train, y_train) poly_pipeline.fit(X_train, y_train) for name, pipe in [(linear, linear_pipeline), (poly, poly_pipeline)]: rmse mean_squared_error(y_test, pipe.predict(X_test), squaredFalse) print(f{name} RMSE: {rmse:.3f})为什么引入多项式特征对波士顿数据有效因为LSTAT与房价的关系并不是严格线性的低收入比例较低的区域房价对LSTAT的敏感度明显不同加入平方项后模型可以拟合这种弯曲关系。PolynomialFeatures(degree2)会生成所有特征的二次组合13 个特征就会膨胀为上百个新特征所以必须在它之后接标准化否则特征尺度差异会拖垮线性回归。这里选择degree2而不是更高是为了控制过拟合风险——训练集只有 400 来个样本三次项很容易记住噪声。4.3 正则化参数调节Ridge 与 Lasso 的系数变化表多项式回归的 RMSE 通常会比普通线性回归低几个点但代价是特征数量暴涨过拟合风险升高。此时需要引入正则化Ridge加 L2 惩罚Lasso加 L1 惩罚。它们的核心区别是 Lasso 会把一部分系数压缩到 0天然具备特征选择能力Ridge 则只会让系数变小不会完全置零。from sklearn.linear_model import Ridge, Lasso alphas [0.01, 0.1, 1.0, 10.0, 100.0] results [] for a in alphas: ridge Pipeline([ (scaler, StandardScaler()), (poly, PolynomialFeatures(degree2, include_biasFalse)), (reg, Ridge(alphaa)) ]) ridge.fit(X_train, y_train) rmse mean_squared_error(y_test, ridge.predict(X_test), squaredFalse) # 记录非零系数数量观察稀疏性 n_nonzero np.sum(np.abs(ridge.named_steps[reg].coef_) 1e-6) results.append([a, round(rmse, 3), n_nonzero]) print(pd.DataFrame(results, columns[alpha, RMSE, nonzero_coef]))从输出可以看到一个典型的规律alpha从 0.01 增大到 100 的过程中测试集 RMSE 先下降后上升形成一个 U 形曲线。alpha太小时正则化形同虚设太大时模型被过度约束偏差主导了误差。最优值通常落在 0.1 到 10 之间。答辩时可以画出 alpha 与 RMSE 的关系折线图这个图本身就是“调参能力”的直观证明。5. 模型评估与可视化验证不只盯 R25.1 四个回归指标MSE、RMSE、MAE、R2 该看哪个很多毕设项目只报告一个 R2容易被质疑评估不全面。完整的回归评估至少应覆盖这四个指标指标公式含义关注点MSEmean( (y_pred - y_true)^2 )平方误差均值对大误差敏感RMSEsqrt(MSE)与 y 同量纲最常报告的主指标MAEmean( abs(y_pred - y_true) )绝对误差均值对大误差不敏感R21 - SS_res / SS_tot模型解释的方差比例须结合残差图解读只看 R2 最大的问题在于它会被离群点拉高。波士顿数据里那几个MEDV50的截断值很可能成为高杠杆点模型为了拟合它们会牺牲中间样本的精度。所以建议同时报告 RMSE 和 MAE如果二者差距明显说明预测误差中存在离群值。from sklearn.metrics import mean_absolute_error, r2_score print(RMSE:, mean_squared_error(y_test, y_pred, squaredFalse)) print(MAE :, mean_absolute_error(y_test, y_pred)) print(R2 :, r2_score(y_test, y_pred))实际应用中RMSE 约在 4 到 5 之间R2 约在 0.7 到 0.8 之间都是合理范围因为数据集本身存在噪声且没有引入外部数据。如果你的 R2 超过 0.9反而要警惕是否发生了数据泄漏或测试集污染。5.2 残差图模型违背假设的四种信号残差是预测值与真实值的差。绘图代码简单但解读信息量很大import matplotlib.pyplot as plt residuals y_test - y_pred fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].scatter(y_pred, residuals, alpha0.6) ax[0].axhline(y0, colorred, linestyle--) ax[0].set_xlabel(Predicted MEDV) ax[0].set_ylabel(Residuals) ax[1].hist(residuals, bins30, edgecolorwhite) ax[1].set_xlabel(Residuals) plt.tight_layout() plt.show()左图是预测值与残差的散点图右图是残差直方图。四种异常信号需要逐一检查其一残差随预测值增大呈喇叭状发散说明模型存在异方差性可以考虑对目标变量取对数其二残差呈弯曲的弧线而不是随机分布说明遗漏了非线性关系需要增加多项式特征或换用非线性模型其三存在远离零线的孤立点对应数据集的截断值其四残差直方图明显偏态说明误差不满足正态性假设。答辩时把这张图画出来并逐条解释比单纯堆叠指标有说服力得多。5.3 K 折交叉验证跑分必须写成 Mean ± Std单次train_test_split的结果受随机种子影响很大。换一个random_stateR2 可能从 0.72 跳到 0.78。为了让评估结果可复现、可对比交叉验证是必选项from sklearn.model_selection import cross_val_score scores cross_val_score( linear_pipeline, X, y, cv5, scoringneg_root_mean_squared_error ) print(RMSE: {:.3f} ± {:.3f}.format(-scores.mean(), scores.std()))cv5表示五折交叉验证数据被分成 5 份轮流取 1 份做验证、4 份做训练最终报告 5 次分数的均值和标准差。输出结果的正确书写方式是RMSE: 5.132 ± 0.73这种格式标准差能反映模型稳定性。不同模型做对比实验时务必保证cv的折数相同、随机种子一致否则对比没有意义。6. 高分毕业设计的最后一步对比实验与文档组织毕业设计评分看重的不是“跑通”而是“科学对比”。建议在论文里做一个三模型的横向对比线性回归sklearn 版本、决策树回归、支持向量回归全部使用相同的五折交叉验证和相同的评估指标。预期结果是线性回归的 RMSE 在 4.5 到 5.5 之间决策树通常略差且方差更大支持向量回归如果做特征缩放可能略优。这个结果是理想的讨论素材——线性回归的可解释性最好SVR 在非线性拟合上更强但参数更多、调参成本更高。文档说明部分建议按五个章节组织数据来源与特征含义、探索性分析与数据预处理、线性回归算法原理含手写推导、基于 sklearn 的实验与对比、模型评估与结论。代码仓库里每一个.py文件都要配 README 说明运行顺序建议顺序是0_download_data.py、1_eda.py、2_manual_lr.py、3_sklearn_compare.py、4_evaluate.py。答辩时把2_manual_lr.py中的损失曲线和正规方程参数一起展示比任何口头解释都有力。最后一个提升答辩观感的技巧把测试集的预测结果按真实值升序排列用散点图画一条预测值 vs 真实值的对角线参考线。预测点越贴近对角线整体预测越准对角线下方的点是低估的房价上方是高估的。这张图直观、信息量大可以作为 PPT 的第一张结果图。配合残差图讲解评委能看到你对模型边界有完整的认知。整个项目不需要复杂的网络结构或浩大的数据集把线性回归算法每一步的执行细节、每一个参数的选择理由、每一种评估方法的偏旁都讲透就已经是一份足够有含金量的毕业设计。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门