)
Python实战从零构建线性回归模型预测房价在数据科学领域线性回归就像学习骑自行车时的辅助轮——它简单却强大是理解更复杂算法的基础。今天我们将抛开抽象的理论直接动手用Python实现一个完整的线性回归模型并用它来预测波士顿地区的房价。这个教程特别适合那些刚接触机器学习、渴望看到代码实际运行效果的新手。1. 环境准备与数据加载在开始之前我们需要确保工作环境配置正确。推荐使用Jupyter Notebook或Google Colab进行实验这些交互式环境非常适合数据探索和模型调试。首先安装必要的库pip install numpy pandas matplotlib scikit-learn波士顿房价数据集是机器学习领域的经典数据集我们可以直接从scikit-learn加载from sklearn.datasets import load_boston import pandas as pd import numpy as np # 加载数据集 boston load_boston() data pd.DataFrame(boston.data, columnsboston.feature_names) data[PRICE] boston.target print(f数据集包含 {len(data)} 条记录) print(特征示例:\n, data.head())这个数据集包含506条房屋记录每个房屋有13个特征包括CRIM城镇人均犯罪率ZN住宅用地比例INDUS非零售业务用地比例CHAS是否靠近查尔斯河1表示是0表示否NOX氮氧化物浓度RM平均房间数AGE1940年前建造的自住单位比例DIS到波士顿五个就业中心的加权距离RAD辐射状公路可达性指数TAX每10,000美元的全额财产税税率PTRATIO城镇师生比例B黑人比例LSTAT低收入人群比例PRICE房屋价格目标变量2. 数据探索与预处理在构建模型前我们需要先了解数据的特点。良好的数据探索能帮助我们做出更明智的建模决策。2.1 数据可视化让我们先看看房价的分布情况import matplotlib.pyplot as plt plt.figure(figsize(10,6)) plt.hist(data[PRICE], bins30, edgecolork, alpha0.7) plt.xlabel(房价 (千美元)) plt.ylabel(频数) plt.title(波士顿房价分布) plt.show()从直方图中我们可以看到大多数房屋价格集中在20-25千美元区间分布呈现右偏态。2.2 特征相关性分析了解哪些特征与房价最相关非常重要correlation data.corr()[PRICE].sort_values(ascendingFalse) print(特征与房价的相关性:\n, correlation)输出可能显示RM房间数与房价正相关最强LSTAT低收入人群比例与房价负相关最强2.3 处理缺失值与异常值虽然波士顿数据集已经过清洗但在实际项目中这是关键步骤# 检查缺失值 print(缺失值统计:\n, data.isnull().sum()) # 处理异常值示例 data data[data[PRICE] 50] # 移除极端高房价3. 手写线性回归实现现在进入核心部分——从零实现线性回归算法。我们将使用正规方程法Normal Equation来求解模型参数。3.1 正规方程原理线性回归的目标是找到参数θ使得预测值hθ(x) θ^T·x与实际值y之间的均方误差最小。正规方程给出了θ的解析解θ (X^T·X)^-1·X^T·y其中X是特征矩阵添加了x01的截距项y是目标向量3.2 Python实现让我们把这个数学公式转化为Python代码def linear_regression(X, y): 使用正规方程实现线性回归 参数: X: 特征矩阵 (m x n) y: 目标向量 (m x 1) 返回: theta: 模型参数 (n x 1) # 添加截距项x01 X_b np.c_[np.ones((len(X), 1)), X] # 计算正规方程 theta np.linalg.inv(X_b.T.dot(X_b)).dot(X_b.T).dot(y) return theta # 准备数据 X data.drop(PRICE, axis1).values y data[PRICE].values # 训练模型 theta linear_regression(X, y) print(模型参数:\n, theta)3.3 模型预测有了模型参数我们可以对新数据进行预测def predict(X_new, theta): 使用训练好的模型进行预测 参数: X_new: 新样本特征 (k x n) theta: 模型参数 (n1 x 1) 返回: 预测值 (k x 1) X_new_b np.c_[np.ones((len(X_new), 1)), X_new] return X_new_b.dot(theta) # 示例预测 sample_house X[0:1] # 取第一个样本 predicted_price predict(sample_house, theta) print(f预测价格: {predicted_price[0]:.2f}千美元, 实际价格: {y[0]}千美元)4. 模型评估与优化构建模型只是开始评估其性能同样重要。我们将实现两个常用指标均方误差(MSE)和R²分数。4.1 实现评估指标def mean_squared_error(y_true, y_pred): 计算均方误差(MSE) 参数: y_true: 真实值 y_pred: 预测值 返回: MSE值 return np.mean((y_true - y_pred) ** 2) def r2_score(y_true, y_pred): 计算R²分数 参数: y_true: 真实值 y_pred: 预测值 返回: R²值 ss_res np.sum((y_true - y_pred) ** 2) ss_tot np.sum((y_true - np.mean(y_true)) ** 2) return 1 - (ss_res / ss_tot) # 在整个数据集上评估 y_pred predict(X, theta) print(fMSE: {mean_squared_error(y, y_pred):.2f}) print(fR²: {r2_score(y, y_pred):.4f})4.2 特征工程改进原始特征不一定是最佳选择我们可以尝试# 创建新特征 data[RM_SQUARED] data[RM] ** 2 # 房间数的平方 data[LSTAT_TAX] data[LSTAT] * data[TAX] # 交互特征 # 重新训练模型 X_improved data.drop(PRICE, axis1).values theta_improved linear_regression(X_improved, y) # 评估改进后的模型 y_pred_improved predict(X_improved, theta_improved) print(\n改进后模型:) print(fMSE: {mean_squared_error(y, y_pred_improved):.2f}) print(fR²: {r2_score(y, y_pred_improved):.4f})4.3 正则化与交叉验证为防止过拟合可以引入岭回归(Ridge Regression)def ridge_regression(X, y, alpha1.0): 岭回归实现 参数: X: 特征矩阵 y: 目标向量 alpha: 正则化强度 返回: 模型参数 X_b np.c_[np.ones((len(X), 1)), X] I np.identity(X_b.shape[1]) I[0, 0] 0 # 不惩罚截距项 theta np.linalg.inv(X_b.T.dot(X_b) alpha * I).dot(X_b.T).dot(y) return theta # 使用岭回归 theta_ridge ridge_regression(X, y, alpha10) y_pred_ridge predict(X, theta_ridge) print(\n岭回归结果:) print(fMSE: {mean_squared_error(y, y_pred_ridge):.2f}) print(fR²: {r2_score(y, y_pred_ridge):.4f})5. 模型部署与应用现在我们已经有了一个可用的房价预测模型如何将它应用到实际场景中呢5.1 保存与加载模型import pickle # 保存模型 with open(boston_lr_model.pkl, wb) as f: pickle.dump(theta, f) # 加载模型 with open(boston_lr_model.pkl, rb) as f: theta_loaded pickle.load(f)5.2 构建预测函数def predict_price(features, model_pathboston_lr_model.pkl): 预测房价的实用函数 参数: features: 字典形式特征值 model_path: 模型路径 返回: 预测价格 # 加载模型 with open(model_path, rb) as f: theta pickle.load(f) # 将特征字典转换为数组 feature_order [CRIM, ZN, INDUS, CHAS, NOX, RM, AGE, DIS, RAD, TAX, PTRATIO, B, LSTAT] X_new np.array([features[feat] for feat in feature_order]).reshape(1, -1) return predict(X_new, theta)[0] # 示例使用 house_features { CRIM: 0.1, ZN: 18.0, INDUS: 2.5, CHAS: 0, NOX: 0.5, RM: 6.5, AGE: 65.0, DIS: 4.5, RAD: 3, TAX: 300, PTRATIO: 15.0, B: 390.0, LSTAT: 10.0 } predicted predict_price(house_features) print(f\n预测房价: ${predicted*1000:,.2f})5.3 模型局限性讨论虽然我们的模型表现不错但线性回归有几个固有局限假设特征与目标呈线性关系对异常值敏感当特征高度相关时可能出现多重共线性问题在实际项目中我们可能需要考虑尝试多项式特征扩展使用更鲁棒的回归方法如Huber回归采用特征选择技术减少冗余特征