用Python和NumPy手把手实现最小二乘法:从拟合直线到理解投影矩阵

在数据分析和机器学习领域,最小二乘法是一个基础但极其重要的概念。它不仅是线性回归的核心算法,更是理解许多高级机器学习模型的基础。本文将通过Python和NumPy库,从零开始实现最小二乘法,并通过可视化手段直观展示其背后的线性代数原理。

1. 最小二乘法基础与问题设定

最小二乘法(Least Squares, LS)的核心思想是通过最小化误差的平方和来寻找数据的最佳函数匹配。假设我们有一组二维数据点,希望找到一条直线y = kx + b,使得所有数据点到这条直线的垂直距离的平方和最小。

为什么需要最小二乘法?

  • 在实际测量中,数据往往存在噪声和误差
  • 当方程数量多于未知数时,通常无法找到精确解
  • 最小二乘解提供了在统计意义上最优的近似解

让我们先创建一个简单的数据集作为示例:

import numpy as np
import matplotlib.pyplot as plt

# 创建示例数据
x = np.array([1, 2, 3, 4, 5])
y = np.array([2.1, 3.9, 6.2, 8.1, 9.8])

plt.scatter(x, y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('原始数据点')
plt.show()

2. 构建矩阵方程与投影矩阵

最小二乘问题可以转化为线性代数中的矩阵方程。对于直线拟合问题,我们需要解以下形式的方程:

Aθ = b

其中:

  • A是设计矩阵,包含x值和常数项
  • θ是参数向量[k, b]ᵀ
  • b是观测值向量

具体构建方法如下:

# 构建矩阵A和向量b
A = np.column_stack([x, np.ones(len(x))])  # 添加一列1用于截距项
b = y.reshape(-1, 1)  # 转换为列向量

print("设计矩阵A:\n", A)
print("\n观测向量b:\n", b)

投影矩阵在最小二乘法中扮演着关键角色。它可以将向量b投影到矩阵A的列空间上:

P = A(AᵀA)⁻¹Aᵀ

这个矩阵的性质非常有趣:

  • 对称性:P = Pᵀ
  • 幂等性:P² = P
  • 秩等于A的秩

3. 计算最小二乘解

有了投影矩阵的概念,我们可以通过两种等价的方式计算最小二乘解:

方法一:直接求解正规方程

# 计算最小二乘解
theta = np.linalg.inv(A.T @ A) @ A.T @ b
k, b = theta.flatten()

print(f"拟合直线方程: y = {k:.3f}x + {b:.3f}")

方法二:使用投影矩阵

# 计算投影矩阵
P = A @ np.linalg.inv(A.T @ A) @ A.T

# 计算投影后的b值
b_proj = P @ b

# 解方程Aθ = b_proj
theta_proj = np.linalg.pinv(A) @ b_proj

这两种方法得到的结果应该完全相同,这验证了最小二乘法的数学一致性。

4. 结果可视化与误差分析

理解最小二乘法的几何意义至关重要。让我们将原始数据、拟合直线和投影点可视化:

# 生成拟合直线上的点
x_fit = np.linspace(0, 6, 100)
y_fit = k * x_fit + b

# 计算投影点
y_proj = k * x + b

plt.figure(figsize=(10, 6))
plt.scatter(x, y, label='原始数据点', c='blue')
plt.plot(x_fit, y_fit, label=f'拟合直线: y = {k:.2f}x + {b:.2f}', c='red')
plt.scatter(x, y_proj, label='投影点', c='green', marker='x')

# 绘制误差线
for xi, yi, ypi in zip(x, y, y_proj):
    plt.plot([xi, xi], [yi, ypi], 'k--', alpha=0.3)

plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.title('最小二乘法拟合结果')
plt.grid(True)
plt.show()

误差分析是评估拟合质量的重要环节。我们可以计算几个关键指标:

# 计算预测值
y_pred = k * x + b

# 计算残差
residuals = y - y_pred

# 计算R平方
ss_res = np.sum(residuals**2)
ss_tot = np.sum((y - np.mean(y))**2)
r_squared = 1 - (ss_res / ss_tot)

print(f"残差平方和: {ss_res:.3f}")
print(f"R平方值: {r_squared:.3f}")

5. 扩展到多元线性回归

最小二乘法不仅适用于直线拟合,还可以扩展到多元线性回归。假设我们有多个自变量,只需扩展设计矩阵A即可:

# 假设我们有第二个特征x2
x2 = np.array([0.5, 1.5, 2.5, 3.5, 4.5])

# 构建设计矩阵
A_multi = np.column_stack([x, x2, np.ones(len(x))])

# 计算多元回归系数
theta_multi = np.linalg.inv(A_multi.T @ A_multi) @ A_multi.T @ b.reshape(-1, 1)

print("多元回归系数:", theta_multi.flatten())

投影矩阵在多元回归中的应用同样重要。通过投影矩阵,我们可以:

  1. 理解模型如何将高维响应变量投影到设计矩阵的列空间
  2. 计算帽子矩阵(Hat Matrix)用于诊断回归分析
  3. 进行变量选择和模型比较

6. 数值稳定性与实用技巧

在实际应用中,直接计算(AᵀA)⁻¹可能会遇到数值不稳定的问题。以下是几种改进方法:

使用QR分解

Q, R = np.linalg.qr(A)
theta_qr = np.linalg.inv(R) @ Q.T @ b

print("QR分解得到的解:", theta_qr.flatten())

使用奇异值分解(SVD)

U, S, Vt = np.linalg.svd(A, full_matrices=False)
theta_svd = Vt.T @ np.linalg.inv(np.diag(S)) @ U.T @ b

print("SVD得到的解:", theta_svd.flatten())

正则化方法(如岭回归)可以处理病态矩阵问题:

lambda_ = 0.1  # 正则化参数
theta_ridge = np.linalg.inv(A.T @ A + lambda_ * np.eye(2)) @ A.T @ b

print("岭回归解:", theta_ridge.flatten())

7. 从线性代数视角理解最小二乘

最小二乘法的美妙之处在于它完美结合了几何直观和代数严谨性。从线性代数角度看:

  1. 列空间与投影:最小二乘法寻找的是b在设计矩阵A列空间上的正交投影
  2. 正交性原理:残差向量与A的列空间正交
  3. 四个基本子空间:理解值域、零空间、行空间和左零空间的关系

这些概念不仅帮助我们理解算法本质,还能指导我们解决更复杂的问题。

8. 实际应用中的注意事项

在实际项目中使用最小二乘法时,需要注意以下几点:

数据预处理

  • 特征缩放:特别是使用正则化时
  • 处理异常值:最小二乘对异常值敏感
  • 检查多重共线性:避免(AᵀA)接近奇异矩阵

模型诊断

  • 残差分析:检查是否满足线性假设
  • 影响点检测:识别对模型影响过大的样本
  • 交叉验证:评估模型泛化能力

替代方法

  • 当数据存在异方差性时,考虑加权最小二乘
  • 对于高维数据,考虑正则化方法(岭回归、Lasso)
  • 对于非线性关系,考虑多项式回归或核方法

9. 性能优化与大规模实现

当数据量很大时,直接矩阵求逆可能效率低下。可以考虑以下优化:

迭代方法

  • 共轭梯度法
  • 随机梯度下降

分块计算

# 假设数据太大,无法一次性加载
def block_solver(A_blocks, b_blocks):
    ATA = np.zeros((A_blocks[0].shape[1], A_blocks[0].shape[1]))
    ATb = np.zeros(A_blocks[0].shape[1])
    
    for A, b in zip(A_blocks, b_blocks):
        ATA += A.T @ A
        ATb += A.T @ b
    
    return np.linalg.solve(ATA, ATb)

使用专用库

  • scikit-learn的LinearRegression
  • statsmodels提供的更全面的统计工具
  • 分布式计算框架如Spark MLlib

10. 从最小二乘到现代机器学习

最小二乘法是许多现代机器学习算法的基础。理解它有助于掌握:

  1. 线性模型:如何扩展到广义线性模型
  2. 正则化路径:从岭回归到弹性网络
  3. 核方法:通过特征映射处理非线性问题
  4. 贝叶斯视角:最大后验估计与高斯过程

在深度学习时代,最小二乘的思想仍然重要。例如,神经网络的训练通常使用梯度下降来最小化平方误差损失函数。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐