别再死记硬背了!用Python手撸线性回归,从波士顿房价预测看透算法本质

发布时间:2026/6/26 12:46:29

别再死记硬背了!用Python手撸线性回归,从波士顿房价预测看透算法本质 用Python拆解线性回归从波士顿房价预测看算法本质最近在技术社区看到一个有趣的现象很多初学者能熟练调用sklearn的LinearRegression完成预测任务但当被问到权重矩阵如何计算或为什么用MSE作为损失函数时却突然语塞。这就像会开车但不了解发动机原理——短期可行长期却限制发展。今天我们就用NumPy手写线性回归在波士顿房价数据集上通过代码反推数学本质。1. 线性回归的代码化表达1.1 数据准备的艺术波士顿房价数据集包含13个特征如人均犯罪率、房间数等和1个目标值房价中位数。原始数据需要经过关键处理import numpy as np from sklearn.datasets import load_boston boston load_boston() X, y boston.data, boston.target # 添加偏置项x01 X np.concatenate([np.ones((len(X), 1)), X], axis1)注意添加全1列是为了将偏置项b融入权重矩阵简化公式表达。这一步对应数学表达式中的$w_0$1.2 正规方程的核心实现线性回归的闭式解closed-form solution通过正规方程给出$$ \theta (X^TX)^{-1}X^Ty $$用NumPy实现仅需三行def normal_equation(X, y): XT X.T theta np.linalg.inv(XT X) XT y return theta为什么这能工作实际上这是在求解损失函数对$\theta$的偏导为零时的最优解。当特征矩阵$X$列满秩时$X^TX$可逆这个解是全局最优。2. 损失函数的可视化理解2.1 MSE的数学本质均方误差MSE定义为$$ MSE \frac{1}{m}\sum_{i1}^m(y_i - \hat{y}_i)^2 $$Python实现揭示其计算逻辑def mse(y_true, y_pred): residuals y_true - y_pred return np.dot(residuals, residuals) / len(y_true)关键特性凸函数性质保证梯度下降能找到全局最小值平方放大对离群点更敏感可导性便于使用梯度优化方法2.2 损失曲面可视化选取两个特征如房间数RM和低收入比例LSTAT观察损失函数变化import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # 生成参数网格 w0 np.linspace(-10, 10, 100) w1 np.linspace(-1, 1, 100) W0, W1 np.meshgrid(w0, w1) loss np.array([mse(y, W0_*1 W1_*X[:,1]) for W0_, W1_ in zip(W0.ravel(), W1.ravel())]).reshape(W0.shape) # 绘制3D曲面 fig plt.figure() ax fig.add_subplot(111, projection3d) ax.plot_surface(W0, W1, loss, cmapviridis)这个曲面直观展示了为什么线性回归有唯一最优解——碗形结构没有局部最小值。3. 梯度下降的实战实现3.1 批量梯度下降算法即使有了正规方程实现梯度下降仍很有教学意义def gradient_descent(X, y, lr0.01, epochs1000): m, n X.shape theta np.zeros(n) losses [] for _ in range(epochs): y_pred X theta gradient X.T (y_pred - y) / m theta - lr * gradient losses.append(mse(y, y_pred)) return theta, losses关键参数影响参数影响典型值学习率(lr)过大导致震荡过小收敛慢0.01-0.1迭代次数(epochs)与学习率共同决定收敛500-5000特征缩放加速收敛StandardScaler3.2 学习率的动态调整实践中可采用学习率衰减策略def adaptive_lr(epoch): initial_lr 0.1 decay 0.95 return initial_lr * (decay ** epoch)这种调整能在初期快速接近最优解后期精细调整。4. 模型评估的多维度视角4.1 R²系数的实现R平方指标衡量模型解释的方差比例def r2_score(y_true, y_pred): 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解读指南0-1之间越接近1越好负数表示模型比均值预测还差对特征数量敏感需调整4.2 残差分析实战健康的线性回归应满足残差随机分布无模式近似正态分布同方差性检查代码residuals y_test - y_pred plt.figure(figsize(12,4)) plt.subplot(131) plt.scatter(y_pred, residuals) # 检查异方差 plt.subplot(132) stats.probplot(residuals, plotplt) # Q-Q图 plt.subplot(133) plt.hist(residuals, bins30) # 分布检查5. 从代码到数学的逆向思考当我们在代码中写下np.linalg.inv(X.T X) X.T y时实际上是在求解最小二乘问题的解析解。这行代码背后隐藏着投影理论寻找y在X列空间的正交投影矩阵求导$\frac{\partial}{\partial\theta}J(\theta) X^T(X\theta - y)$凸优化Hessian矩阵$X^TX$的正定性保证通过代码调试如打印中间矩阵形状可以直观理解这些抽象概念。例如当特征存在多重共线性时X.T X的行列式接近0求逆会出现数值不稳定——这正好解释了为什么需要正则化。在波士顿房价预测中我发现一个有趣现象当只使用房间数RM特征时手动实现的梯度下降与sklearn结果在小数点后第5位才开始出现差异。这种细微差别正源于浮点数计算精度的累积效应而理解这点需要对算法实现有透彻认识。

相关新闻