ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

最小二乘法原理与实战:从线性回归基础到Python实现

2026/8/22 20:32:16 拓冰建站 浏览量
最小二乘法原理与实战:从线性回归基础到Python实现 1. 从“猜”到“算”为什么我们需要最小二乘法在机器学习的入门路上很多人会卡在一个看似简单的问题上给你一堆数据点怎么找到一条最合适的直线来描述它们之间的关系比如我们收集了历史上房屋面积和售价的数据想预测一个新面积的房子大概能卖多少钱。最直观的想法是画一条直线穿过这些点但你会发现几乎不可能有一条直线能完美地穿过所有点因为现实数据总有误差和噪声。于是问题变成了在无数条可能的直线中哪一条才是“最好”的这就是最小二乘法要解决的核心问题。它不是一个高深莫测的数学魔术而是一种朴素又强大的思想让预测值和真实值之间的“差距”最小。这个“差距”在数学上我们用“误差的平方和”来衡量。为什么是平方而不是直接加总误差因为误差有正有负直接相加会相互抵消让你误以为一条很差的直线“误差为零”。而平方操作一方面消除了正负号的影响另一方面对较大的误差给予了更重的“惩罚”这使得最终找到的直线对异常值不那么敏感结果更稳健。我第一次接触这个概念时觉得它太“数学”了离编程很远。直到自己动手用Python拟合一个简单的线性模型看着那条蓝色的回归线稳稳地穿过散乱的数据点中心才恍然大悟最小二乘法是连接数据现象与模型规律最坚实的那座桥。无论是你期末复习《机器学习》时头疼的公式推导还是工作中构建预测模型它都是你绕不开的基石。接下来我们就抛开教科书式的陈述从原理、推导、实现到避坑完整地走一遍最小二乘法的实战之路。2. 最小二乘法的直观理解与数学本质2.1 一个生活中的类比如何把书桌摆正想象一下你要把一张长方形的书桌靠墙摆放希望桌边与墙完全平行。但你手头没有量角器只有一把卷尺。你会怎么做一个实用的方法是测量书桌两个桌角到墙壁的距离。如果桌子完全平行于墙那么这两个距离应该相等。但实际上你测得的距离一个是10厘米一个是12厘米。这时你不会去硬掰桌子直到某个距离变成0因为那会让另一个距离变得更大。最合理的做法是轻微旋转桌子让这两个距离都发生一点变化最终使它们尽可能接近。比如调整后两个距离变成了11.1厘米和10.9厘米虽然都不为零但它们的“不一致程度”大大降低了。更进一步如果我们定义“不一致程度”为两个距离差值的平方(d1 - d2)^2那么我们的目标就是通过调整桌子角度让这个平方值最小化。最小二乘法做的就是类似的事情。只不过我们的“书桌”是那条假设的直线y wx bw是斜率b是截距“距离”是每个数据点的真实值y_i和直线上预测值(wx_i b)之间的差值即残差。我们要找的w和b就是能让所有残差的平方和最小的那个“桌子角度”。2.2 从目标函数到“最小二乘”的命名让我们把上面的思想数学化。假设我们有n个数据点(x_1, y_1), (x_2, y_2), ..., (x_n, y_n)。 我们假设它们之间存在线性关系可以用y wx b来近似。 对于第i个点预测值为ŷ_i wx_i b真实值为y_i则残差为e_i y_i - ŷ_i y_i - (wx_i b)。如果直接求和Σ e_i正负残差会抵消。取绝对值Σ |e_i|在数学上不好处理不可导。于是我们选择对残差进行平方再求和这就构成了我们的目标函数损失函数L(w, b) Σ (y_i - (wx_i b))^2 其中求和i从1到n。“最小二乘法”这个名字就此清晰最小我们的目标是让某个量最小化。二乘这个量是误差残差的平方二次方的和。所以最小二乘法的本质就是寻找一组模型参数w, b使得基于该模型的预测值与观测值之间差值的平方和达到最小。这是一个典型的无约束优化问题。2.3 与最大似然估计的隐秘联系频率学派视角如果你在机器学习课程中已经接触过概率论这里有一个更深层的洞察在误差服从正态分布的假设下最小二乘法等价于最大似然估计。这是什么意思我们假设每个观测值y_i与其真实关系(wx_i b)之间的误差ε_i是一个随机噪声并且这个噪声服从均值为0、方差为σ²的正态分布高斯分布ε_i ~ N(0, σ²)。那么y_i就可以写成y_i wx_i b ε_i 因此y_i本身也服从一个正态分布y_i ~ N(wx_i b, σ²)。最大似然估计的思想是找到一组参数w, b使得我们观测到眼前这组数据(y_1, y_2, ..., y_n)的概率似然最大。在数据点独立同分布的假设下这个联合概率就是每个点概率的乘积。通过数学推导取对数似然函数并求最大值你会发现最大化这个似然函数恰好等价于最小化残差平方和Σ (y_i - (wx_i b))^2。注意这个等价关系非常重要。它赋予了最小二乘法一个概率论上的坚实解释。当我们使用最小二乘法时我们实际上隐含地假设了数据误差是独立同分布的高斯白噪声。如果数据中有大量异常值误差分布不是高斯而是重尾分布最小二乘法的效果就会变差此时可能需要考虑如Huber损失等更稳健的损失函数。3. 手撕推导闭式解是怎么来的知道了要最小化L(w, b)下一步就是怎么找到使L最小的w和b。对于线性回归这个特例我们可以通过求导并令导数为零的方法得到一个解析解也叫闭式解。这意味着我们不需要复杂的迭代优化算法直接用公式就能算出最优参数。3.1 单变量线性回归的推导我们的目标函数是L(w, b) Σ_{i1}^{n} (y_i - wx_i - b)^2这是一个关于w和b的二元二次函数。因为平方项的存在它是一个凸函数有唯一的全局最小值点。我们分别对w和b求偏导数并令其等于0。对 b 求偏导∂L/∂b Σ 2*(y_i - wx_i - b)*(-1) -2 Σ (y_i - wx_i - b)令∂L/∂b 0得到Σ (y_i - wx_i - b) 0Σ y_i - w Σ x_i - n*b 0n*b Σ y_i - w Σ x_ib (Σ y_i)/n - w*(Σ x_i)/n我们记ȳ (Σ y_i)/n为y的均值x̄ (Σ x_i)/n为x的均值。则上式可写为b ȳ - w * x̄——公式(1)这个公式非常直观最优的直线必然穿过所有数据点的中心(x̄, ȳ)。对 w 求偏导∂L/∂w Σ 2*(y_i - wx_i - b)*(-x_i) -2 Σ x_i(y_i - wx_i - b)令∂L/∂w 0得到Σ x_i(y_i - wx_i - b) 0Σ (x_i y_i - w x_i^2 - b x_i) 0Σ x_i y_i - w Σ x_i^2 - b Σ x_i 0——公式(2)将公式(1)b ȳ - w x̄代入公式(2)Σ x_i y_i - w Σ x_i^2 - (ȳ - w x̄) Σ x_i 0Σ x_i y_i - w Σ x_i^2 - ȳ Σ x_i w x̄ Σ x_i 0我们知道Σ x_i n * x̄代入Σ x_i y_i - w Σ x_i^2 - ȳ * n * x̄ w x̄ * n * x̄ 0Σ x_i y_i - w Σ x_i^2 - n ȳ x̄ w n x̄^2 0整理含有w的项w (n x̄^2 - Σ x_i^2) n ȳ x̄ - Σ x_i y_i两边乘以-1调整形式w (Σ x_i^2 - n x̄^2) Σ x_i y_i - n x̄ ȳ注意到Σ x_i^2 - n x̄^2其实就是Σ (x_i - x̄)^2即x的方差乘以(n-1)样本方差的无偏估计分母是n-1但这里不影响。Σ x_i y_i - n x̄ ȳ是Σ (x_i - x̄)(y_i - ȳ)即x和y的协方差乘以(n-1)。因此我们得到w的经典公式w (Σ x_i y_i - n x̄ ȳ) / (Σ x_i^2 - n x̄^2) Σ (x_i - x̄)(y_i - ȳ) / Σ (x_i - x̄)^2——公式(3)最终最优解为w Σ (x_i - x̄)(y_i - ȳ) / Σ (x_i - x̄)^2b ȳ - w * x̄3.2 多变量线性回归矩阵形式的推导当特征不止一个时例如预测房价特征包括面积、房间数、楼层等我们就需要使用多元线性回归y w_1*x_1 w_2*x_2 ... w_p*x_p b。用矩阵表示更加简洁。令X是一个n x (p1)的矩阵每一行是一个样本第一列全为1对应截距b后面p列是特征。y是一个n x 1的列向量是所有样本的真实标签。θ是一个(p1) x 1的列向量包含我们需要求解的参数[b, w_1, w_2, ..., w_p]^T。则模型预测为ŷ Xθ目标函数损失函数为L(θ) ||y - Xθ||^2 (y - Xθ)^T (y - Xθ)这里||·||表示向量的L2范数。我们对向量θ求梯度导数∇L(θ) -2X^T (y - Xθ)令梯度为零向量-2X^T (y - Xθ) 0X^T (y - Xθ) 0X^T y X^T X θ如果X^T X这个矩阵是可逆的即满秩特征之间不共线我们就可以得到著名的正规方程θ (X^T X)^{-1} X^T y——公式(4)这就是多元线性回归最小二乘解的矩阵形式。它漂亮、简洁但实际应用中直接使用它有两个主要问题计算复杂度高矩阵求逆的复杂度大约是O(p^3)当特征数量p很大例如上万时计算会非常缓慢且消耗内存。数值稳定性问题如果X^T X接近奇异矩阵即特征之间存在多重共线性求逆运算会变得非常不稳定导致结果误差极大。因此在实际的机器学习库如Scikit-learn中对于多元线性回归通常会采用更数值稳定的算法如奇异值分解SVD来求解而不是直接计算(X^T X)^{-1}。4. 从零实现与Scikit-learn实战理解了原理和推导最好的巩固方式就是动手实现。我们将完成两个版本纯Python的“轮子”版本以及使用工业标准库Scikit-learn的“实战”版本。4.1 纯Python实现造自己的“轮子”我们根据公式(3)来实现单变量线性回归。这个过程能让你对每一步计算都了如指掌。import numpy as np class SimpleLinearRegression: 手动实现的最小二乘法单变量线性回归 def __init__(self): self.w_ None # 斜率 self.b_ None # 截距 def fit(self, x_train, y_train): 根据训练数据拟合模型参数 # 将输入转换为numpy数组便于向量化计算 x np.asarray(x_train).reshape(-1) y np.asarray(y_train).reshape(-1) # 计算x和y的均值 x_mean np.mean(x) y_mean np.mean(y) # 计算公式(3)中的分子和分母 # 分子: sum((x_i - x_mean) * (y_i - y_mean)) numerator np.sum((x - x_mean) * (y - y_mean)) # 分母: sum((x_i - x_mean)^2) denominator np.sum((x - x_mean) ** 2) # 计算斜率w和截距b self.w_ numerator / denominator self.b_ y_mean - self.w_ * x_mean return self # 支持链式调用 def predict(self, x_test): 对新的数据点进行预测 if self.w_ is None or self.b_ is None: raise ValueError(Model is not fitted yet. Call fit first.) x np.asarray(x_test).reshape(-1) return self.w_ * x self.b_ # 示例使用我们自己造的“轮子” if __name__ __main__: # 生成一些模拟数据 y 2*x 3 噪声 np.random.seed(42) x np.random.rand(100) * 10 y 2 * x 3 np.random.randn(100) * 2 # 加入高斯噪声 # 创建模型并拟合 reg SimpleLinearRegression() reg.fit(x, y) print(f拟合的斜率 w: {reg.w_:.4f}) print(f拟合的截距 b: {reg.b_:.4f}) # 理论值是 w2, b3由于噪声拟合值会接近但略有不同 # 预测一个新值 x_new 7.5 y_pred reg.predict(x_new) print(f当 x {x_new} 时预测 y {y_pred:.4f})运行这段代码你会看到模型成功地学习到了数据背后近似y2x3的规律。这个实现虽然简单但它完整地揭示了最小二乘法的计算内核。4.2 Scikit-learn实战工业级应用与评估在实际项目中我们几乎不会自己从头写线性回归而是使用成熟的库。Scikit-learn提供了高效、稳定且功能丰富的实现。import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, r2_score from sklearn.datasets import make_regression # 1. 生成更复杂的模拟数据多特征 # 生成100个样本5个特征其中只有3个是有效特征 X, y make_regression(n_samples100, n_features5, n_informative3, noise10, random_state42) # 2. 划分训练集和测试集非常重要 X_train, X_test, y_train, y_test train_test_split(X, y, test_size0.2, random_state42) # 3. 创建并训练模型 # Scikit-learn的LinearRegression默认使用最小二乘法基于SVD求解 model LinearRegression() model.fit(X_train, y_train) # 4. 查看模型参数 print(模型截距 (b):, model.intercept_) print(模型系数 (w):, model.coef_) # 系数对应每个特征的权重绝对值越大通常表示该特征对目标影响越大 # 5. 在测试集上进行预测并评估 y_pred model.predict(X_test) # 计算均方误差 (MSE) - 这就是我们最小化的目标函数的平均值 mse mean_squared_error(y_test, y_pred) print(f测试集均方误差 (MSE): {mse:.2f}) # 计算R平方 (R²) - 模型解释的方差比例越接近1越好 r2 r2_score(y_test, y_pred) print(f测试集R平方 (R²): {r2:.4f}) # 6. 可视化对于单特征或前两个主成分 # 由于我们有5个特征无法直接可视化全部。可以绘制预测值与真实值的散点图。 plt.figure(figsize(8, 6)) plt.scatter(y_test, y_pred, alpha0.7, edgecolorsk) # 绘制理想对角线 y_pred y_true min_val min(y_test.min(), y_pred.min()) max_val max(y_test.max(), y_pred.max()) plt.plot([min_val, max_val], [min_val, max_val], r--, lw2, labelIdeal Fit) plt.xlabel(True Values) plt.ylabel(Predictions) plt.title(Linear Regression: True vs. Predicted) plt.legend() plt.grid(True, alpha0.3) plt.show()关键点解析LinearRegression()这是Scikit-learn对普通最小二乘法的封装。它内部使用数值稳定的方法如SVD求解正规方程比直接求逆更鲁棒。train_test_split永远不要用全部数据来评估你的模型必须将数据分为训练集用于拟合模型和测试集用于评估模型在新数据上的泛化能力这是机器学习的基本准则。mean_squared_error均方误差就是最小二乘目标函数L(θ)除以样本数n。它是我们模型好坏的直接度量。r2_score决定系数。它表示模型能够解释的目标变量方差的比例。R² 1表示完美拟合R² 0表示模型不比直接用均值预测好R²为负表示模型比均值预测还差。5. 最小二乘法的“阿喀琉斯之踵”假设与局限没有哪个算法是万能的最小二乘法也不例外。它的优异性能建立在几个核心假设之上。一旦这些假设被严重违反最小二乘法的结果就可能不可靠甚至产生误导。5.1 核心假设与违反后果线性关系假设自变量X和因变量y之间存在线性关系。违反后果如果真实关系是非线性的如指数、对数强行用直线拟合会导致系统性的预测偏差模型效果很差。诊断与解决绘制y与X的散点图。如果发现非线性模式可以考虑对特征进行变换如多项式特征x^2,x^3 对数变换log(x)或使用非线性模型如决策树、神经网络。误差项独立同分布假设误差项ε_i之间相互独立且服从均值为0、方差为σ^2的正态分布。独立性违反自相关常见于时间序列数据。今天的误差可能影响明天的误差。这会导致参数估计的方差被低估使得某些特征看起来“显著”而实际不显著。诊断绘制残差e_i与序号i或时间t的图。如果看到明显的模式如连续为正或为负则可能存在自相关。可使用Durbin-Watson检验。同方差性违反异方差误差的方差不是常数而是随着X的变化而变化例如预测房价时高价房的预测误差波动可能更大。这不会影响参数估计的无偏性但会影响其标准误的估计进而影响假设检验如t检验、F检验的可靠性。诊断绘制残差e_i与预测值ŷ_i的散点图。如果散点图呈现漏斗形、扇形等有规律的形状则存在异方差。解决对于异方差可以考虑对因变量y进行变换如对数变换或使用加权最小二乘法。自变量无多重共线性假设特征之间没有高度相关性。违反后果这是多元线性回归中最常见也最棘手的问题之一。它不会影响模型的整体预测能力但会导致单个特征系数的估计值变得极不稳定标准误会非常大。系数的解释变得困难因为一个特征的变化可能被其他相关特征“抵消”。在极端情况下完全共线X^T X矩阵不可逆无法求解。诊断计算特征之间的相关系数矩阵。方差膨胀因子是更专业的诊断工具。VIF 10 通常被认为存在严重共线性。解决删除冗余特征如果两个特征高度相关保留一个即可。主成分分析将原始特征转换为一组不相关的主成分。使用正则化方法如岭回归或Lasso回归这是处理共线性最有效、最常用的方法。5.2 正则化当最小二乘法“过拟合”时最小二乘法的目标是完美拟合训练数据最小化训练误差。但当特征很多或特征间存在共线性时它可能会找到一组系数值很大且正负抵消的“复杂”模型来完美拟合训练数据中的噪声导致过拟合。这样的模型在训练集上表现很好但在未知的测试集上表现糟糕。正则化的思想是在损失函数中加入一个对模型复杂度的惩罚项防止系数变得过大。最常用的两种是L2正则化岭回归在损失函数中加入系数向量的L2范数平方λ * Σ w_j^2。它会让所有系数都向零收缩但不会完全为零。适用于处理共线性。L1正则化Lasso回归在损失函数中加入系数向量的L1范数λ * Σ |w_j|。它倾向于产生稀疏解即把一些不重要的特征的系数直接压缩为零从而实现特征选择。在Scikit-learn中使用它们非常简单from sklearn.linear_model import Ridge, Lasso # 岭回归 ridge_model Ridge(alpha1.0) # alpha是正则化强度λ ridge_model.fit(X_train, y_train) # Lasso回归 lasso_model Lasso(alpha0.1) lasso_model.fit(X_train, y_train) print(Lasso选择的特征非零系数:, np.where(lasso_model.coef_ ! 0)[0])选择alpha参数通常通过交叉验证来完成。正则化是机器学习中防止过拟合、提升模型泛化能力的核心手段也是最小二乘法在复杂现实问题中的重要进化。6. 从线性回归到更广阔的世界最小二乘法的延伸最小二乘法的思想远不止于拟合一条直线。它是许多现代机器学习算法的基石。6.1 非线性关系的处理多项式回归如果y和x的关系是二次的、三次的怎么办我们依然可以使用最小二乘法的思想只是我们的“特征”变了。多项式回归本质上还是线性回归因为它是关于参数线性的。例如对于二次关系y w0 w1*x w2*x^2我们只需要构造新特征x1 x,x2 x^2然后使用多元线性回归求解w0, w1, w2即可。import numpy as np from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.pipeline import make_pipeline # 生成非线性数据 np.random.seed(0) x np.random.rand(50) * 10 y 0.5 * x**2 - 2 * x 3 np.random.randn(50) * 2 # 二次关系加噪声 # 使用管道组合多项式特征构造和线性回归 degree 2 # 多项式阶数 model make_pipeline(PolynomialFeatures(degree), LinearRegression()) model.fit(x.reshape(-1, 1), y) # 预测和绘图 x_plot np.linspace(0, 10, 100).reshape(-1, 1) y_plot model.predict(x_plot)这里的关键是PolynomialFeatures它自动将单特征x转换为[1, x, x^2]的特征矩阵然后交给LinearRegression用最小二乘法求解。多项式回归是线性模型拟合非线性数据的有力工具但阶数不宜过高否则极易过拟合。6.2 分类问题逻辑回归的“表亲”你可能会惊讶最小二乘法甚至能和分类问题扯上关系。逻辑回归虽然名字里有“回归”但它解决的是二分类问题。它的核心是使用Sigmoid函数将线性组合w^T x b映射到(0,1)区间解释为概率。逻辑回归的损失函数是交叉熵损失而不是平方损失。但是有一种历史更悠久的分类方法叫做最小二乘分类。它强行将分类标签如1 -1视为连续值然后用线性回归去拟合最后通过符号函数sign(w^T x b)来预测类别。这种方法虽然直观但在理论上不如逻辑回归的交叉熵损失合理对异常值也更敏感因此在实践中较少使用。不过它体现了最小二乘法思想的一种直接应用。6.3 基石地位神经网络中的均方误差损失在深度学习中当我们处理回归任务时如预测房价、预测股价最常用的损失函数之一就是均方误差。它正是最小二乘目标函数的平均值。在神经网络的训练过程中反向传播算法通过梯度下降来最小化这个MSE损失。可以说最小二乘法的思想最小化预测与真实的平方误差是深度学习回归任务的灵魂。7. 避坑指南与最佳实践结合我多次在项目和教学中使用最小二乘法的经验以下是一些容易踩坑的地方和对应的建议。7.1 数据预处理标准化/归一化不是必须但强烈推荐对于普通的线性回归无正则化理论上是否对特征进行标准化使均值为0方差为1或归一化缩放到[0,1]不会影响模型的预测性能。因为系数会自适应地缩放。但是这会影响系数的解释。更关键的是如果你使用了正则化如岭回归、Lasso标准化是必须的。因为正则化惩罚项对每个系数是公平的如果特征尺度差异巨大大尺度的特征对应的系数自然会小从而不公平地逃避了惩罚。最佳实践养成习惯在拟合任何线性模型尤其是带正则化的之前先使用StandardScaler或MinMaxScaler对特征进行标准化/归一化。from sklearn.preprocessing import StandardScaler scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test) # 注意使用训练集的均值和方差来转换测试集 model.fit(X_train_scaled, y_train)7.2 共线性诊断不要只看相关系数矩阵很多人只用相关系数矩阵来看特征是否相关但这只能检测两两之间的线性关系。多重共线性指的是一个特征可以被其他多个特征的线性组合很好地预测。更可靠的诊断工具是方差膨胀因子。from statsmodels.stats.outliers_influence import variance_inflation_factor import pandas as pd # 假设X是一个DataFrame或二维数组 # 为截距项添加一列1 X_with_const pd.DataFrame(X) # 假设X是特征矩阵 X_with_const[const] 1 # 计算每个特征的VIF vif_data pd.DataFrame() vif_data[feature] X_with_const.columns vif_data[VIF] [variance_inflation_factor(X_with_const.values, i) for i in range(X_with_const.shape[1])] print(vif_data)通常VIF大于10就表明存在严重的共线性问题需要考虑使用正则化或删除特征。7.3 模型评估R²的陷阱R²是一个直观的指标但它有局限性。当你在模型中增加无关特征时R²永远不会下降反而可能轻微上升这会导致你误以为模型变好了。因此在特征很多时更推荐使用调整后R²或直接在测试集上评估MSE。调整后R²考虑了特征数量p和样本数量n的惩罚Adjusted R² 1 - [(1-R²)(n-1)/(n-p-1)]当增加的特征不能提升模型真实解释力时调整后R²会下降。7.4 异常值处理最小二乘法的“软肋”由于最小二乘法对误差进行平方异常值会对模型产生巨大的影响因为它会贡献一个非常大的平方误差项模型会为了“讨好”这个异常点而严重偏离大多数正常数据点。诊断绘制残差图或使用库克距离等统计量。解决检查数据确认异常值是否是数据录入错误如果是则修正或删除。使用稳健回归如果异常值是真实的可以考虑使用对异常值不敏感的损失函数如Huber损失或Tukey的双权重损失。Scikit-learn中的HuberRegressor是一个选择。数据变换对因变量y进行对数变换等有时可以缓解异常值的影响。最小二乘法是机器学习领域最经典、最直观的算法之一。它像一把瑞士军刀简单但用途广泛。理解它的原理、推导、实现和局限不仅能让你在《机器学习》期末考试中游刃有余更能为你理解更复杂的模型从正则化线性模型到神经网络打下坚实的基础。记住所有模型都是错的但有些是有用的。最小二乘法提供的这条“最优”直线就是在我们当前认知和计算能力下对数据背后规律一个非常有用的近似。