FEATURED · 精选文章

Python实战:线性回归与决策树构建房价预测模型

发布时间 / 2026/8/29 12:15:18
来源 / 创域科博编辑部
栏目 / 资讯中心
Python实战:线性回归与决策树构建房价预测模型 1. 项目概述从数据到预测的实战旅程房价这个牵动无数人心弦的数字背后其实是一系列复杂因素共同作用的结果。作为一名长期和数据打交道的从业者我经常被问到“能不能用代码预测房价” 答案是肯定的而且入门门槛远比想象中低。今天我们就抛开复杂的理论堆砌直接上手用 Python 构建两个最经典、也最实用的机器学习模型——线性回归和决策树来亲手揭开房价预测的神秘面纱。这不仅仅是一个模型训练的过程更是一次完整的数据分析思维演练。无论你是刚学完 Python 语法想找项目练手的新人还是希望夯实机器学习基础的数据爱好者这篇内容都将带你走完从数据清洗、特征理解、模型构建到评估优化的全流程。你会发现预测模型并非黑盒通过合适的工具和清晰的思路我们完全可以从数据中挖掘出有价值的洞见。2. 核心思路与工具选型解析2.1 为什么选择线性回归和决策树在开始敲代码之前明确“为什么选这两个模型”至关重要这决定了我们后续分析问题的角度。线性回归模型的核心优势在于其可解释性。它试图找到一系列特征如房屋面积、房间数量、地理位置等与目标变量房价之间的线性关系。最终模型会给出每个特征的系数这个系数直接代表了“在其他特征不变的情况下该特征每增加一个单位房价平均变化多少”。例如面积系数为3000就意味着面积每增加一平米房价预计上涨3000元。这种白盒特性让我们能直观理解各个因素对房价的影响力度非常适合于关系探索和基线模型建立。决策树模型则采用了完全不同的思路。它通过一系列“是/否”问题如“面积是否大于100平米”、“是否位于市中心”对数据进行递归分割最终将数据划分到不同的“叶子节点”每个叶子节点对应一个预测的房价通常是该节点内样本房价的平均值。它的优势在于捕捉非线性关系和交互效应。房价的影响因素往往不是简单的叠加比如“大面积”和“好学区”结合带来的溢价可能远超两者单独贡献之和决策树能很好地刻画这种复杂模式。将两者结合使用构成了一个稳健的分析策略先用线性回归建立可解释的基线理解主要线性趋势再用决策树捕捉残差中的非线性模式提升预测精度。这种组合拳在实战中非常有效。2.2 环境与工具准备工欲善其事必先利其器。我们选择 Python 作为实现语言主要是因为其丰富且成熟的科学生态系统。以下是核心库及其作用Pandas NumPy数据分析的基石。Pandas 的DataFrame结构是处理表格数据的利器而 NumPy 提供高效的数值计算。Scikit-learn机器学习的神兵利器。我们构建线性回归和决策树模型、进行数据预处理、模型评估都将依赖它。它 API 设计统一文档完善是入门和实践的首选。Matplotlib Seaborn数据可视化库。用于探索性数据分析EDA绘制分布图、关系散点图等帮助我们肉眼观察数据规律和问题。Jupyter Notebook / Lab交互式编程环境。强烈推荐使用它能将代码、运行结果、图表和文字说明结合在一起非常适合这种分步演示和探索的数据分析项目。安装非常简单通常一行命令即可搞定建议使用虚拟环境pip install pandas numpy scikit-learn matplotlib seaborn jupyter3. 数据理解与预处理实战没有高质量的数据再优秀的模型也是空中楼阁。数据预处理通常占据一个数据分析项目80%的时间这一步的细致程度直接决定模型的成败。3.1 数据加载与初步窥探我们使用一个经典的公开数据集例如波士顿房价数据集由于某些考虑后续版本已移除但原理相通或加州房价数据集。这里以更复杂的加州房价数据集为例它包含更多特征和样本。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.datasets import fetch_california_housing # 加载数据 housing fetch_california_housing() df pd.DataFrame(housing.data, columnshousing.feature_names) df[‘MedHouseVal‘] housing.target # 中位数房价单位是十万美元 print(“数据形状”, df.shape) print(“\n前5行数据”) print(df.head()) print(“\n数据基本信息”) print(df.info()) print(“\n描述性统计”) print(df.describe())运行后你会看到数据包含约20640个样本8个特征如收入中位数、平均房间数、房屋年龄等和1个目标变量。df.info()帮你查看是否有缺失值这里应该没有和各列数据类型。df.describe()则展示了数值特征的分布情况均值、标准差、分位数等这是发现异常值的第一站。3.2 探索性数据分析与特征工程接下来我们要用可视化工具深入理解数据。1. 目标变量分布plt.figure(figsize(10, 5)) plt.subplot(1, 2, 1) sns.histplot(df[‘MedHouseVal‘], kdeTrue, bins50) plt.title(‘房价分布直方图‘) plt.subplot(1, 2, 2) sns.boxplot(xdf[‘MedHouseVal‘]) plt.title(‘房价箱线图‘) plt.tight_layout() plt.show()观察房价是否呈正态分布很多模型假设误差正态分布以及是否存在极端离群值。加州房价数据中你会发现房价分布有右偏且存在一个上限截断超过5.0的值被设为5.0这是数据收集时设定的上限需要在建模时心里有数。2. 特征与目标的关系# 绘制特征与房价的散点图 fig, axes plt.subplots(2, 4, figsize(16, 8)) axes axes.ravel() for i, col in enumerate(housing.feature_names): axes[i].scatter(df[col], df[‘MedHouseVal‘], alpha0.3, s1) axes[i].set_xlabel(col) axes[i].set_ylabel(‘MedHouseVal‘) # 尝试添加趋势线 z np.polyfit(df[col], df[‘MedHouseVal‘], 1) p np.poly1d(z) axes[i].plot(df[col], p(df[col]), “r--“, linewidth2) plt.tight_layout() plt.show()这个图至关重要。你可以直观看到哪些特征与房价有明显的线性关系如MedInc收入中位数哪些关系复杂如AveOccup平均入住率以及是否有异常点某个特征值极小或极大区域的散点。3. 特征间相关性分析plt.figure(figsize(10, 8)) correlation_matrix df.corr() sns.heatmap(correlation_matrix, annotTrue, cmap‘coolwarm‘, center0, fmt‘.2f‘) plt.title(‘特征相关性热力图‘) plt.show()热力图能清晰展示特征之间的多重共线性。例如AveRooms平均房间数和AveBedrms平均卧室数很可能高度相关。如果两个特征相关性极高如 0.9在线性回归中考虑剔除其中一个以避免共线性问题影响系数稳定性。4. 特征工程初步基于以上观察我们可以进行一些简单的特征工程处理非线性对于与房价呈现明显非线性关系的特征可以考虑创建多项式特征如面积的平方或进行分箱处理。组合特征例如创建“房间总数”总房间数 * 户数或“人均房间数”等可能更有意义的特征。缩放线性回归和决策树虽然对特征尺度不敏感决策树完全不受影响但为了后续可能使用正则化或比较系数大小通常会对数值特征进行标准化StandardScaler或归一化MinMaxScaler。实操心得EDA 阶段不要急于求成。花时间反复观察这些图表提出假设并验证。例如看到HouseAge房龄与房价关系较弱可以进一步思考是不是新房和旧房的价值差异在高端和低端市场表现不同可以尝试按收入分组后再看房龄与房价的关系。这个思考过程本身比模型结果更有价值。4. 模型构建、训练与评估数据准备就绪现在进入核心环节建模。4.1 数据分割与预处理流水线首先必须将数据分割为训练集和测试集绝对禁止用全部数据训练后又用同样的数据测试那会得到极其乐观的虚假结果。from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 定义特征X和目标y X df.drop(‘MedHouseVal‘, axis1) y df[‘MedHouseVal‘] # 分割数据80%训练20%测试 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_transform这里的关键点是scaler的拟合fit只发生在训练集上然后用同样的参数去转换transform测试集。这样才能模拟模型遇到全新数据时的真实情况。4.2 线性回归模型from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score # 创建并训练模型 lr_model LinearRegression() lr_model.fit(X_train_scaled, y_train) # 在训练集和测试集上进行预测 y_train_pred_lr lr_model.predict(X_train_scaled) y_test_pred_lr lr_model.predict(X_test_scaled) # 评估模型 def evaluate_model(y_true, y_pred, set_name): mse mean_squared_error(y_true, y_pred) mae mean_absolute_error(y_true, y_pred) r2 r2_score(y_true, y_pred) print(f“{set_name}评估结果“) print(f“ 均方误差(MSE): {mse:.4f}“) print(f“ 平均绝对误差(MAE): {mae:.4f}“) print(f“ 决定系数(R²): {r2:.4f}“) return mse, mae, r2 print(“线性回归模型“) lr_train_metrics evaluate_model(y_train, y_train_pred_lr, “训练集“) lr_test_metrics evaluate_model(y_test, y_test_pred_lr, “测试集“) # 查看模型系数特征重要性 lr_coef pd.DataFrame({ ‘feature‘: X.columns, ‘coefficient‘: lr_model.coef_ }).sort_values(by‘coefficient‘, ascendingFalse) print(“\n特征系数影响力排序”) print(lr_coef)结果解读MSE/MAE衡量预测值与真实值的平均误差大小。MSE 对大的误差惩罚更重。关注测试集上的值。R²表示模型能解释的目标变量方差的比例。越接近1越好。重点对比训练集和测试集的 R²。如果训练集 R² 很高如0.9而测试集 R² 很低如0.6说明模型过拟合了。系数正系数表示特征与房价正相关。例如MedInc收入的系数很可能最大且为正这符合常识。标准化后的系数大小可以在一定程度上比较特征的重要性。4.3 决策树回归模型from sklearn.tree import DecisionTreeRegressor, plot_tree # 创建并训练决策树模型先使用默认参数 dt_model DecisionTreeRegressor(random_state42) dt_model.fit(X_train_scaled, y_train) # 决策树不需要特征缩放这里用缩放后的数据也无妨 # 预测与评估 y_train_pred_dt dt_model.predict(X_train_scaled) y_test_pred_dt dt_model.predict(X_test_scaled) print(“决策树回归模型默认参数“) dt_train_metrics evaluate_model(y_train, y_train_pred_dt, “训练集“) dt_test_metrics evaluate_model(y_test, y_test_pred_dt, “测试集“) # 可视化其中一棵树深度限制为3以便查看 plt.figure(figsize(20, 10)) plot_tree(dt_model, max_depth3, feature_namesX.columns, filledTrue, roundedTrue, fontsize10) plt.title(‘决策树结构前3层‘) plt.show()运行后你很可能会发现一个典型现象决策树在训练集上的 R² 接近 1.0完美拟合MSE 接近 0但在测试集上的表现却比线性回归还差。这就是决策树模型最容易出现的严重过拟合问题。默认的决策树会一直生长直到每个叶子节点只剩下一个样本它完全记住了训练数据的所有噪声导致泛化能力极差。4.4 决策树剪枝与超参数调优为了解决过拟合我们必须对决策树进行“剪枝”即通过调整超参数来控制模型的复杂度。from sklearn.model_selection import GridSearchCV # 定义要搜索的参数网格 param_grid { ‘max_depth‘: [3, 5, 10, 15, 20, None], # 树的最大深度 ‘min_samples_split‘: [2, 5, 10, 20], # 内部节点再划分所需最小样本数 ‘min_samples_leaf‘: [1, 2, 4, 8], # 叶子节点最少样本数 ‘max_features‘: [‘auto‘, ‘sqrt‘, ‘log2‘, None] # 寻找最佳分割时考虑的特征数 } # 创建网格搜索对象以负均方误差-MSE作为评分标准sklearn默认最大化评分 grid_search GridSearchCV(DecisionTreeRegressor(random_state42), param_grid, cv5, # 5折交叉验证 scoring‘neg_mean_squared_error‘, # 负MSE越大越好 n_jobs-1, # 使用所有CPU核心 verbose1) grid_search.fit(X_train_scaled, y_train) # 输出最佳参数和最佳模型 print(“最佳参数组合”, grid_search.best_params_) print(“最佳交叉验证分数负MSE:”, grid_search.best_score_) # 使用最佳模型进行最终评估 best_dt_model grid_search.best_estimator_ y_train_pred_best_dt best_dt_model.predict(X_train_scaled) y_test_pred_best_dt best_dt_model.predict(X_test_scaled) print(“\n优化后的决策树模型“) best_dt_train_metrics evaluate_model(y_train, y_train_pred_best_dt, “训练集“) best_dt_test_metrics evaluate_model(y_test, y_test_pred_best_dt, “测试集“) # 查看优化后模型的特征重要性 dt_feature_importance pd.DataFrame({ ‘feature‘: X.columns, ‘importance‘: best_dt_model.feature_importances_ }).sort_values(by‘importance‘, ascendingFalse) print(“\n优化决策树特征重要性“) print(dt_feature_importance)关键参数解释max_depth限制树的最大深度是防止过拟合最直接有效的参数。min_samples_split节点必须至少有这么多样本才会继续分裂。min_samples_leaf叶子节点必须至少包含这么多样本。max_features分裂时随机考虑的特征子集大小引入随机性有助于提升泛化能力。GridSearchCV通过交叉验证的方式在给定的参数组合中寻找在验证集上表现最好的那一组。这个过程计算量较大但能系统性地找到更优的模型配置。5. 模型对比、诊断与优化5.1 模型性能对比现在让我们将两个优化后的模型放在一起对比。# 汇总结果 results pd.DataFrame({ ‘Model‘: [‘Linear Regression‘, ‘Decision Tree (Tuned)‘], ‘Train R²‘: [lr_train_metrics[2], best_dt_train_metrics[2]], ‘Test R²‘: [lr_test_metrics[2], best_dt_test_metrics[2]], ‘Test MSE‘: [lr_test_metrics[0], best_dt_test_metrics[0]], ‘Test MAE‘: [lr_test_metrics[1], best_dt_test_metrics[1]] }).round(4) print(“模型性能对比“) print(results) # 可视化预测值与真实值的散点图 fig, axes plt.subplots(1, 2, figsize(14, 6)) models [(y_test_pred_lr, ‘Linear Regression‘), (y_test_pred_best_dt, ‘Tuned Decision Tree‘)] for ax, (pred, title) in zip(axes, models): ax.scatter(y_test, pred, alpha0.3) ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], ‘r--‘, lw2) # 理想对角线 ax.set_xlabel(‘True Values‘) ax.set_ylabel(‘Predicted Values‘) ax.set_title(f‘{title}: True vs Predicted‘) plt.tight_layout() plt.show() # 可视化残差分布 fig, axes plt.subplots(1, 2, figsize(14, 5)) for ax, (pred, title) in zip(axes, models): residuals y_test - pred ax.scatter(pred, residuals, alpha0.3) ax.axhline(y0, color‘r‘, linestyle‘--‘) ax.set_xlabel(‘Predicted Values‘) ax.set_ylabel(‘Residuals‘) ax.set_title(f‘{title}: Residual Plot‘) plt.tight_layout() plt.show()对比分析R²看哪个模型在测试集上解释的方差更多。散点图点越紧密地分布在红色对角线附近说明预测越准。可以观察是否存在系统性偏差如预测值普遍偏高或偏低。残差图这是诊断模型假设的利器。理想的残差图应该是围绕0水平线随机、均匀地分布没有任何明显的模式如漏斗形、曲线形。如果出现模式说明模型未能捕捉到数据中的某些规律可能需要进行更复杂的特征工程或尝试其他模型。5.2 线性回归模型诊断与改进如果线性回归的残差图显示出非线性模式我们可以尝试改进添加多项式特征捕捉特征与目标之间的非线性关系。from sklearn.preprocessing import PolynomialFeatures from sklearn.pipeline import make_pipeline # 创建多项式回归流水线例如2次多项式 poly_pipeline make_pipeline( StandardScaler(), PolynomialFeatures(degree2, include_biasFalse), # 添加二次项和交互项 LinearRegression() ) poly_pipeline.fit(X_train, y_train) # 评估...注意多项式特征会急剧增加特征数量可能引发过拟合需要配合正则化如 Ridge 或 Lasso 回归。使用正则化回归当特征间存在多重共线性或特征较多时正则化可以防止系数过大提升模型稳定性。from sklearn.linear_model import Ridge, Lasso # Ridge回归 (L2正则化) ridge_model Ridge(alpha1.0) # alpha是正则化强度 ridge_model.fit(X_train_scaled, y_train) # Lasso回归 (L1正则化)可以产生稀疏系数用于特征选择 lasso_model Lasso(alpha0.01, max_iter10000) lasso_model.fit(X_train_scaled, y_train) # 查看Lasso筛选后的特征系数不为0的特征 print(“Lasso选中的特征”, X.columns[lasso_model.coef_ ! 0])5.3 决策树模型的局限与进阶即使调优后单棵决策树可能仍然存在稳定性问题数据微小变化可能导致树结构巨变。为此我们可以考虑集成学习方法随机森林构建多棵决策树通过投票分类或平均回归得到最终结果能有效降低过拟合提升泛化能力。from sklearn.ensemble import RandomForestRegressor rf_model RandomForestRegressor(n_estimators100, random_state42, n_jobs-1) rf_model.fit(X_train_scaled, y_train) # 评估... 通常效果会比单棵决策树好很多梯度提升树如 XGBoost、LightGBM以串行方式构建多棵树每一棵都在纠正前一棵树的残差是目前结构化数据竞赛中的霸主。# 需要先安装 xgboost: pip install xgboost import xgboost as xgb xgb_model xgb.XGBRegressor(n_estimators100, learning_rate0.1, random_state42) xgb_model.fit(X_train_scaled, y_train)6. 常见问题与排查技巧实录在实际操作中你几乎一定会遇到下面这些问题。这里是我踩过坑后总结的排查清单。问题现象可能原因排查与解决思路线性回归 R² 为负数模型预测结果比简单使用均值预测还要差。1.检查数据泄露确保测试集数据没有以任何形式参与训练如缩放时误用fit_transform。2.检查特征与目标关系散点图可能显示根本没有线性关系考虑非线性模型或特征变换。3.检查异常值极端异常值会严重扭曲线性回归的拟合线。决策树训练集完美测试集极差严重的过拟合。1.进行剪枝使用GridSearchCV系统调整max_depth,min_samples_leaf等参数。2.使用集成方法直接切换到随机森林或梯度提升树它们内置了防止过拟合的机制。模型预测结果出现不合理的极端值1. 特征中存在极端异常值。2. 模型尤其是树模型外推能力差预测了训练数据范围之外的值。1.可视化检查绘制特征与目标的散点图定位并处理异常值盖帽、截断或删除。2.谨慎对待预测向业务方说明模型的预测范围对超出训练集特征范围的新数据预测结果不可信。运行网格搜索非常慢参数网格太大数据量多。1.先粗调后精调先用大范围、少步长搜索锁定大致区间后再在小范围细调。2.减少cv折数例如从5折降到3折。3.使用随机搜索RandomizedSearchCV在参数空间随机采样效率更高有时效果相近。特征重要性显示某个重要特征权重很低1. 该特征与目标确实无关。2. 该特征与其它特征高度相关其重要性被“分摊”了。3. 模型不适合捕捉该特征的影响模式。1.检查相关性查看热力图如果该特征与另一强特征高度相关可以尝试剔除其中一个再看。2.尝试不同模型线性回归和决策树计算重要性的方式不同可以对比观察。3.业务理解结合领域知识判断该特征是否真的应该重要。核心避坑技巧永远先从简单的模型开始。线性回归就是最好的基线模型。它训练快可解释性强。用它先跑通全流程得到一个基准分数。然后再尝试更复杂的模型如决策树、随机森林。每次只改变一个变量比如换模型或增加新特征并记录测试集性能的变化。这样你才能清楚地知道是什么导致了性能的提升或下降。一上来就堆砌复杂模型和特征只会让你在问题出现时无从下手。最后我想分享一点个人体会房价预测项目是一个绝佳的机器学习沙盒。它数据相对规整业务目标明确。通过这个项目你实践的不只是fit()和predict()这两个函数更重要的是掌握了“数据思维”的完整工作流从理解问题、获取和探索数据、清洗转换、构建基线模型、诊断改进、到最终评估和解释结果。这个流程适用于绝大多数预测性数据分析任务。当你下次面对销售预测、用户流失分析或设备故障预警时你会发现核心的步骤和思考方式是相通的。真正的价值不在于模型本身有多复杂而在于你能否用数据清晰地定义问题并用合适的工具一步步逼近答案。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻