资讯动态

波士顿房价预测全流程:多元线性回归从入门到模型优化

发布时间:2026/10/21 18:13:56 来源:尧图企业网站定制
波士顿房价预测是多元线性回归入门绕不开的题目。我在不少技术社区里看到初学者拿它当第一个实战项目用它理解“用一堆特征去预测一个连续数值”这件事到底是怎么发生的。项目本身并不复杂一份包含若干个区域统计指标的数据集目标列是那一带房屋的中位价格我们训练一个多元线性回归模型让它学会从输入特征映射到房价。别看这个题小它能串起数据探索、预处理、模型训练、评估诊断、特征优化一整套机器学习流程非常适合刚踩进这个领域的人系统地走一遍。这个项目适合谁来刷呢刚学过线性代数、打算入门机器学习的人已经在跑分类任务、想补一补回归短板的人甚至是想练一手数据可视化和特征工程基本功的人。它不像图像、文本项目那样需要昂贵的算力一份几十KB的表格用笔记本就能跑完。这篇文章把我踩过的坑和最终沉淀下来的方法一起写出来按我实际动手的顺序来从数据探索一路做到模型优化方便你对着操作。1. 项目整体思路拆解1.1 这个项目到底在做什么先把这个项目的底细弄清楚。这是一个典型的回归任务输入特征有13个维度从社区治安相关的统计值、住宅用地占比、非零售商业用地占比到平均房间数、老房子比例、房产税率、区域师生比、区域经济水平相关的统计指标等等输出目标是房价中位数单位是千美元。数据集整体规模不大一共506条样本放在今天的深度学习场景里连热身都算不上但对线性回归来说样本量完全足够因为线性模型的复杂度很低本身就不需要海量数据去喂。训练这个模型的重心不只是把一个损失函数数值调低而是搞清楚每个特征和房价之间的关系方向与强度房间数多一点房价大概往高走区域经济水平相关的指标差一些房价往下压空气污染浓度低一些的区域往往价格更高。这些基于生活常识的关系模型会用一组系数精确地量化出来。换句话说跑完这个项目之后你不光能得到一个预测房价的模型还能得到一张“特征如何影响房价”的说明书。1.2 为什么选多元线性回归而不是一上来就上复杂模型作为入门项目选多元线性回归有几个非常实际的理由。第一可解释性强。决策树也能预测房价但看完那棵树之后你能直观说出来的往往是“哪个属性被切分了、被切到什么程度重要”而线性回归直接给到“某个特征每变化一个标准差房价会变化多少千美元”这种立刻能消化的数字。第二实现成本低。scikit-learn里一个LinearRegression就能完成拟合不会像神经网络那样折腾一两个小时调参。第三它天然是后续很多方法的地基。正则化的Ridge、Lasso都是线性回归的扩展逻辑回归虽然名字里带“回归”本质上也是线性模型的延伸。先把多元线性回归的系数、残差、评估指标弄明白后面学这些东西会顺畅得多。当然这个选择也有代价。线性模型对特征与目标之间的非线性关系表达能力有限如果数据里明明存在明显的曲线关系线性回归会欠拟合。这个问题我在后面的优化对比实验里会专门讨论这里先按下不表。2. 环境准备与数据探索2.1 起步条件与数据获取别死在load_boston这个坑里这个项目对环境要求不高Python 3.8以上装好pandas、numpy、matplotlib、seaborn和scikit-learn就够。我个人的使用习惯是普通编辑器加终端或者直接用notebook逐格执行完全看个人偏好。数据获取这里有一个新手很容易踩的坑网上很多老教程直接写from sklearn.datasets import load_boston然后一行load_boston()就把数据加载出来了但在较新版本的scikit-learn里这个接口已经被移除了。原因是这个经典数据集包含部分按社区人口结构统计的敏感字段社区经过讨论后决定不再默认提供。所以现在的标准做法是自己准备一份CSV文件从开源镜像仓库下载一份历史版本的数据副本放到项目目录下命名为boston_housing.csv。import pandas as pd df pd.read_csv(boston_housing.csv) print(df.shape) print(df.head())如果你下载到的文件里多了一列Unnamed: 0之类的行号列加载后直接执行df df.drop(columns[Unnamed: 0])丢掉即可。2.2 每个特征的基本盘数据加载后别急着训练先花十分钟摸清每个特征的长相。我用df.info()检查缺失值用df.describe()看均值、标准差、最小值、最大值这些统计量。这份数据整体质量还不错没有缺失值但特征之间的分布形态差异很大。这13个特征各有各的脾气特征列大致含义典型特点CRIM社区治安相关的统计值严重右偏多数接近0个别极大ZN大面积住宅用地占比大量值为0INDUS非零售商业用地占比分布相对均匀CHAS是否临近河流的虚拟变量0/1取值NOX空气污染浓度指标分布偏斜且与其他特征相关RM住宅平均房间数与房价正相关很强AGE老房子占比大量高值接近100DIS到就业中心的加权距离与NOX相关明显RAD到公路可达性指数取值范围跨度很大TAX房产税率分布偏窄偶有高组值PTRATIO区域师生比取值范围较集中B区域人口结构统计值大多数集中在高值区间LSTAT区域经济水平相关指标右偏与房价负相关很强其中CRIM、NOX、DIS、LSTAT这些列的偏态都很明显最大值和均值差出好几个量级。这种分布对线性模型的拟合是有影响的后面处理时要留意。2.3 散点图与热力图里的三个关键线索看表格只能建立模糊印象真正的线索要靠画图。我画了两张图一张是用seaborn挑RM、LSTAT、NOX、TAX这四列和目标一起看的散点图矩阵另一张是全量特征与目标列的相关系数热力图。这两张图给我三个信息量很大的结论。第一RM和房价有明显的正相关离散点虽然不少但整体趋势非常清楚散点图几乎就是一条向右上倾斜的带子。第二LSTAT和房价的负相关同样扎眼而且关系不是笔直的直线带有一定弧度这个弧度暗示后面可以考虑对这类特征做变换给线性模型增加一点非线性表达能力。第三特征内部存在明显的共线性迹象TAX和RAD的相关系数很高DIS和NOX之间的关系也接近线性。这意味着后面做系数解释时要留个心眼多个高度相关的特征同时进模型权重会相互拉扯符号和大小都可能变得不稳定。3. 数据预处理与特征工程3.1 切分数据是第一优先级很多人习惯先把数据清洗一遍再做切分这里有一个隐患。正确的顺序是先把特征和目标切出来再用train_test_split按7:3或者8:2拆成训练集和测试集并且固定random_state保证实验可复现。from sklearn.model_selection import train_test_split features df.drop(columns[MEDV]) target df[MEDV] X_train, X_test, y_train, y_test train_test_split( features, target, test_size0.2, random_state42 )为什么切分要排在前面因为后面所有基于训练集计算出来的均值、标准差、归一化参数本质上都携带了训练集的信息。如果先用全量数据算这些参数测试集的信息就提前渗进了预处理流程模型在测试集上的评估结果就会虚高。这在行业里叫数据泄露是新手最容易犯但最难察觉的错误。它的表现是训练集和测试集的指标都很好看但模型一到真实场景就露馅。3.2 标准化不是可选项顺序不对等于白做多元线性回归在scikit-learn里的实现严格来说不标准化也能算出系数但一旦下面两个场景出现不标准化就会出问题一是对特征做L1、L2正则化时量纲大的特征会被优先惩罚导致正则化完全失效二是如果后续要做交叉项、多项式扩展数值范围不一致会让模型训练变得病态。所以我这里统一先标准化。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)注意测试集只调用transform不要fit理由和上一小节提到的数据泄露完全一致。如果你觉得每次都手动处理太容易出错建议直接使用Pipeline把这几个环节串起来代码更干净也不容易漏掉这个细节。提示测试集只能用transform()千万不能对它调用fit()或fit_transform()。3.3 偏态特征与异常值处理我在探索阶段观察到CRIM、LSTAT这类特征右偏得很严重。右偏意味着大量样本挤在低值区间少数几个样本拉到极高的位置线性模型在这种分布上容易把小概率样本当成主要矛盾导致整体拟合被带偏。我的处理方式对CRIM、LSTAT、DIS做log1p变换把右偏拉回来一些。np.log1p是log(1x)这样即使x等于0也不会出现负无穷非常实用。这里的顺序有讲究log变换只对单个列做数学运算不依赖全样本的统计信息所以放在切分之前或之后都行而标准化需要全体样本的均值、方差必须放在切分之后。import numpy as np # 放在切分之前也没问题只对单列做数学变换没有跨样本信息泄露 for col in [CRIM, LSTAT, DIS]: df[col] np.log1p(df[col]) # 然后再执行3.1的切分和3.2的标准化按照我反复试验的经验LSTAT和CRIM做对数变换后模型对这两个特征的敏感度会稳定不少R²往往也会有小幅提升。异常值方面CRIM的极值样本中确实有一些离群点。直接删除样本会丢掉信息而且异常值在线性回归里对系数的拉动作用很大我建议先用稳健的统计量观察一下比如把超过中位数3倍四分位距的样本找出来数量若占比很小再决定是剔除还是用RANSAC这类稳健回归做对比。这个数据集里异常样本占比不大我保留了它们让模型自己学习这样更接近真实工作中“数据不完美”的常见状态。4. 多元线性回归模型训练与参数解读4.1 模型算法原理速览多元线性回归的数学形式其实特别朴素对于每个样本预测值等于一组系数和特征值的线性组合加上一个截距。训练过程就是寻找一组系数让预测值和真实值之间的均方误差最小。求解这个最优系数有两条主流路线。一条是用正规方程直接通过矩阵运算一步解出闭式解scikit-learn中LinearRegression的默认求解器就是这样样本量不大时速度非常快另一条是梯度下降通过迭代朝着误差减小的方向更新系数在样本量极大或特征维度极高时更实用。波士顿房价这份数据只有五百多条样本正规方程毫无压力。因为误差函数对系数是凸函数正规方程得到的解就是全局最优几乎不用考虑陷入局部最优的问题这也是线性模型最适合入门的原因之一。4.2 训练代码与容易忽略的参数细节from sklearn.linear_model import LinearRegression model LinearRegression() model.fit(X_train_scaled, y_train) theta model.coef_ intercept model.intercept_ print(系数:, theta) print(截距:, intercept)这里有两个容易被忽略的细节。第一训练数据必须用上一轮标准化后的版本不要拿原始缩放的数据。第二LinearRegression默认fit_interceptTrue它会自动拟合一个截距你不用自己往数据里加一列全1再去拟合那样反而会把参数空间搞乱。训练耗时在这个数据集上几乎可以忽略不计肉眼可见就是一瞬的事。真正花时间的部分在后面的评估和结果解读上。4.3 读懂系数背后的业务含义训练完之后如果你直接打印coef_看到的是十三个数字这些数字的大小和符号组合在一起才是模型给出的完整答案。因为特征已经标准化每个系数可以解释为“该特征变化一个标准差预测房价会变化多少千美元”。在我实验的那组结果里RM的系数最大且为正这和散点图的直觉完全一致房间数每多一个标准差的量预测房价就往上涨几万美元。LSTAT的系数为负且绝对值很大说明区域经济水平指标每变差一个标准差房价就被明显拉低。PTRATIO的系数也为负师生比高的区域房价偏低这同样符合常理。有一个细节值得严查TAX和RAD因为存在高相关性两者的系数在单独训练时会相互拉扯绝对值偏大且符号可能不稳定。这个现象本身就是多重共线性的典型症状。进一步拓展到跨行业场景时你会更深刻地理解为什么可解释性分析不能只看系数还要结合共线性诊断。5. 评估指标与模型诊断5.1 回归模型该看哪些指标MAE、RMSE、R2怎么配合训练完只看损失函数数值是不够的我用三个指标从不同角度评估测试集表现。from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score y_pred model.predict(X_test_scaled) mae mean_absolute_error(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) r2 r2_score(y_test, y_pred) print(fMAE: {mae:.4f}) print(fRMSE: {rmse:.4f}) print(fR2: {r2:.4f})MAE反映平均绝对误差单位是千美元直观好理解RMSE因为先平方再开根会放大那些预测偏差很大的样本对异常值更敏感R²衡量模型相比“直接用平均值预测”提升了多少0.7意味着模型解释了七成的方差剩下的三成还有待其他因素或非线性关系去解释。三个指标同时看才能对模型的长短板有完整认识。我在测试集上得到的典型结果是R²在0.7以上RMSE在4到5之间千美元MAE在3左右千美元。对这个数据集的规模来说这个水平在意料之中线性模型已经抓到主要趋势但显然还有信息没被利用。5.2 残差分析暴露了什么很多新人到这里就宣布项目完成我会建议再往下走一步做残差分析。残差就是预测值减真实值把残差画在y轴上、预测值画在x轴上能看出不少刚才指标里看不到的内容。我看到的残差图有两个明显信号第一残差带在预测值大的区间略微变宽说明高房价段的误差有放大趋势模型对高价位区域的预测稳定性不如中低价位第二存在几个残差绝对值特别大的点去对照原始数据会发现基本都是高房价样本或者某几个特征特别极端的样本。这个发现其实提示了一个可能的改进方向——线性模型假设误差方差恒定如果实际数据里存在异方差性可以考虑对目标也做对数变换再回归。我在优化阶段尝试了这个思路确实会让高价位段的残差收敛一些但代价是模型输出变成了对数房价解释起来绕了一层。5.3 交叉验证把结果打回原形单次切分得到的结果依赖random_state同一个模型换个切分方式可能差异不小所以要再看交叉验证。from sklearn.model_selection import cross_val_score cv_scores cross_val_score( LinearRegression(), X_train_scaled, y_train, cv5, scoringr2 ) print( 交叉验证R2均值: {:.4f} (/- {:.4f}).format( cv_scores.mean(), cv_scores.std() ) )5折交叉验证把训练集切成5份轮流让模型在其中4份上学习、在剩下1份上评估得到的分数比单次测试集评估更可靠。我跑出来的结果R²均值落在0.7附近标准差大概在0.05到0.1之间这告诉我模型的泛化能力基本稳定没有明显的过拟合虚高。6. 优化方向与高频问题排查6.1 用正则化模型做对比实验纯线性回归是理解模型的基础但拿它做最终版本时我会加一个正则化的对比实验。正则化在损失函数里多加一项系数惩罚限制系数不要无限膨胀能缓解过拟合和共线性带来的系数不稳定。Ridge用的是L2惩罚适合处理特征之间高度相关的场景Lasso用的是L1惩罚一方面做正则另一方面会把不重要的特征系数压到零天然起到特征选择作用。我直接在标准化后的数据上分别试了这两者from sklearn.linear_model import Ridge, Lasso ridge Ridge(alpha1.0) ridge.fit(X_train_scaled, y_train) lasso Lasso(alpha0.1) lasso.fit(X_train_scaled, y_train)alpha的选择是个超参数问题。我用一个简单的手写循环在0.01到100的范围内按数量级取几个候选值再用5折交叉验证对比R²选出表现最好的那个。实测下来Ridge在合理alpha范围内与纯线性回归表现相当甚至略优Lasso会筛掉一部分系数为零的特征这在把模型简化成只需要少数几个关键指标的场景中特别有价值。这个实验让我意识到优化阶段的目标不是无脑追求提升几个点的R²而是搞清楚哪些方法在什么条件下有效。这些对比记录本身才是项目最有含金量的产出。6.2 特征筛选与多项式扩展如果说正则化是从系数上做减法特征筛选就是从输入上做减法和加法。减法方向最直接的方式是看Lasso的稀疏系数把被压成零的特征直接剔除重新训练纯线性回归。我按这个思路试过删掉对预测几乎不贡献的特征后模型测试集R²与全特征版本基本持平说明剔除是安全的。加法方向则是对数值特征做多项式扩展让线性模型有机会拟合曲线关系。scikit-learn里有PolynomialFeatures原理是把原始特征的平方项、交互项全部加入训练集。from sklearn.preprocessing import PolynomialFeatures poly PolynomialFeatures(degree2, include_biasFalse) X_poly_train poly.fit_transform(X_train_scaled) X_poly_test poly.transform(X_test_scaled)但亲自跑完一圈之后我的结论是在波士顿房价数据上直接上degree2的完整多项式很容易把维度从13爆到上百并且立刻过拟合训练集R²飙升而测试集不升反降。正确的姿势是先用Lasso选特征再对少数几个重要特征做低阶交互。比如只对RM和LSTAT生成平方项甚至三次项配合交叉验证看是否真有提升。这个数据集的线性部分很强多项式扩展带来的收益非常有限搞不好还拖后腿建议把主要精力放在更稳的正则化和合理的特征选择上。6.3 高频踩坑问题速查一张表解决80%的事我把实操中见过的高频问题整理成一张速查表遇到问题直接对着找现象原因处理方法load_boston找不到或报错新版库已移除该接口改用读CSV文件方式加载数据测试集R²比训练集低很多过拟合或数据泄露检查scaler、特征选择步骤是否放在切分之前系数符号和常识相反特征之间存在强共线性计算特征相关矩阵结合业务判断是否剔除正则化后效果不升反降alpha取值过大把系数压死了缩小alpha数量级配合交叉验证重新选择对房价极高样本预测偏差大线性模型对极端值天然拟合不足尝试对目标做log变换或采用稳健回归对比训练数据没标准化就直接用Ridge量纲差异让L2惩罚失效必须先用StandardScaler标准化这几个坑里数据泄露是最隐蔽的。它不会直接报错指标还很好看但一旦你意识到这个问题就会对整个实验流程的可靠性打个问号。建议把“切分→标准化→建模”固化成模板每次动手前先检查顺序。这个项目我前后带过不少新人跑几乎每个人都会在数据预处理和模型评估这两个环节有一种朴素的冲动总想快点跑出第一个结果然后赶紧调参。我的建议是反过来把探索、切分和标准化老老实实做完让第一个结果出现在一个完全干净的数据管线上哪怕它只是0.7的R²也比在脏数据上强行调到0.8有价值。另一个小技巧是我个人非常喜欢用的每一个预处理步骤都把变换参数存下来用Pipeline或者pickle保存scaler和模型这样下次拿到同结构的新数据可以直接用同一套流程跑预测不用重新摸索。很多人做完这个入门项目就把它扔在一边其实把这个项目整理成一套可复用的回归模板后面不管遇到销量预测、租金预测还是其他回归问题都能直接套骨架省下大量重复劳动。以上是个人在实操中的一些经验总结。多动手多画图多问一句“为什么”比跑通十遍代码都管用把这个基础项目吃透后面的路会好走很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑