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

张开发
2026/4/7 8:36:16 15 分钟阅读

分享文章

用Python和NumPy手把手实现投影矩阵:从二维投影到最小二乘法拟合
用Python和NumPy手把手实现投影矩阵从二维投影到最小二乘法拟合线性代数中的投影概念看似抽象但在数据科学和机器学习中无处不在。想象一下当你用线性回归拟合数据点时本质上是在寻找一个子空间使得所有数据点到该子空间的垂直距离最小——这正是投影的核心思想。本文将用Python和NumPy带你从零构建各种投影矩阵并通过可视化验证其数学性质最终实现一个完整的最小二乘法线性回归案例。1. 投影矩阵的基础构建投影矩阵的核心特性是幂等性——无论投影多少次结果都不会改变。我们先从最简单的二维投影开始逐步扩展到更复杂的情况。1.1 投影到坐标轴的实现让我们先用NumPy实现将向量投影到x轴的投影矩阵import numpy as np # 定义投影到x轴的矩阵 P_x np.array([[1, 0], [0, 0]]) # 测试向量 v np.array([3, 4]) # 计算投影 proj_v P_x v # 结果应为 [3, 0]验证幂等性# 验证P^2 P assert np.allclose(P_x P_x, P_x), 幂等性验证失败1.2 投影到任意直线的通用方法更一般地要将向量投影到任意方向的单位向量u上可以使用公式P uuᵀdef projection_matrix(u): 构建投影到单位向量u方向的矩阵 u np.array(u).reshape(-1, 1) # 转换为列向量 return u u.T # 示例投影到45度方向的直线 u np.array([1, 1]) / np.sqrt(2) # 单位向量 P_u projection_matrix(u) # 测试投影 v np.array([3, 4]) proj_v P_u v # 结果应为 [3.5, 3.5]关键性质验证表性质数学表达代码验证方法幂等性P² Pnp.allclose(P P, P)对称性Pᵀ Pnp.allclose(P.T, P)迹等于秩tr(P) rank(P)np.trace(P) np.linalg.matrix_rank(P)2. 高维空间中的投影从二维扩展到三维甚至更高维度投影矩阵的构建原理相同但计算复杂度会增加。2.1 三维空间中的平面投影# 投影到xy平面 P_xy np.array([[1, 0, 0], [0, 1, 0], [0, 0, 0]]) # 测试向量 v np.array([1, 2, 3]) proj_v P_xy v # 结果应为 [1, 2, 0]2.2 投影到任意子空间对于由一组基向量{u₁, u₂, ..., uₖ}张成的子空间投影矩阵公式为P U(UᵀU)⁻¹Uᵀ其中U是将基向量作为列向量组成的矩阵。def subspace_projection(basis): 构建投影到由basis列向量张成的子空间的矩阵 U np.array(basis).T # 确保每列是一个基向量 return U np.linalg.inv(U.T U) U.T # 示例投影到由[1,0,0]和[1,1,0]张成的平面 basis [[1, 0, 0], [1, 1, 0]] P subspace_projection(basis) # 验证投影矩阵性质 assert np.allclose(P P, P), 幂等性验证失败 assert np.allclose(P.T, P), 对称性验证失败3. 投影的几何可视化理解投影的几何意义至关重要。我们可以用Matplotlib将投影过程可视化import matplotlib.pyplot as plt def plot_projection_2d(v, P, title): proj_v P v origin np.zeros(2) plt.figure(figsize(8, 6)) plt.quiver(*origin, *v, scale1, scale_unitsxy, anglesxy, colorr, label原向量) plt.quiver(*origin, *proj_v, scale1, scale_unitsxy, anglesxy, colorb, label投影向量) # 绘制投影线 plt.plot([v[0], proj_v[0]], [v[1], proj_v[1]], k--, lw0.8) plt.xlim(-1, max(5, v[0]1)) plt.ylim(-1, max(5, v[1]1)) plt.axhline(0, colorblack, lw0.5) plt.axvline(0, colorblack, lw0.5) plt.grid(True) plt.title(title) plt.legend() plt.show() # 可视化x轴投影 v np.array([3, 4]) P_x np.array([[1, 0], [0, 0]]) plot_projection_2d(v, P_x, 投影到x轴)4. 最小二乘法实战应用投影矩阵最重要的应用之一就是解决最小二乘问题。我们将用投影的思想实现线性回归并与scikit-learn的结果对比。4.1 问题设定假设有以下数据点求最佳拟合直线y mx bxy1223354.2 投影矩阵解法# 构建矩阵A和向量b A np.array([[1, 1], [2, 1], [3, 1]]) b np.array([2, 3, 5]) # 计算投影矩阵 P_A A np.linalg.inv(A.T A) A.T # 计算b在A列空间上的投影 b_proj P_A b # 求解参数m和b params np.linalg.inv(A.T A) A.T b m, b_fit params print(f拟合直线方程: y {m:.2f}x {b_fit:.2f})4.3 与scikit-learn对比验证from sklearn.linear_model import LinearRegression # 准备数据 X np.array([1, 2, 3]).reshape(-1, 1) y np.array([2, 3, 5]) # 使用scikit-learn model LinearRegression(fit_interceptTrue) model.fit(X, y) # 对比结果 print(f投影矩阵结果: m{m:.4f}, b{b_fit:.4f}) print(fscikit-learn结果: m{model.coef_[0]:.4f}, b{model.intercept_:.4f})4.4 结果可视化# 生成拟合线数据点 x_line np.linspace(0, 4, 100) y_line m * x_line b_fit plt.figure(figsize(8, 6)) plt.scatter([1, 2, 3], [2, 3, 5], cr, label数据点) plt.plot(x_line, y_line, b-, labelf拟合直线: y{m:.2f}x{b_fit:.2f}) # 绘制投影线 for xi, yi in zip([1, 2, 3], [2, 3, 5]): y_proj m * xi b_fit plt.plot([xi, xi], [yi, y_proj], k--, lw0.8) plt.xlabel(x) plt.ylabel(y) plt.title(最小二乘法线性回归) plt.legend() plt.grid(True) plt.show()5. 高级应用与性能优化在实际应用中我们还需要考虑数值稳定性和计算效率问题。5.1 使用SVD提高稳定性当AᵀA接近奇异时直接求逆可能不稳定可以使用SVD分解def least_squares_svd(A, b): U, s, Vh np.linalg.svd(A, full_matricesFalse) s_inv np.diag(1/s) return Vh.T s_inv U.T b params_svd least_squares_svd(A, b)5.2 稀疏矩阵优化对于大规模稀疏问题可以使用稀疏矩阵运算from scipy.sparse import csr_matrix from scipy.sparse.linalg import lsqr # 假设A是稀疏矩阵 A_sparse csr_matrix(A) result lsqr(A_sparse, b) params_sparse result[0]5.3 正则化处理岭回归为防止过拟合可以加入L2正则化def ridge_regression(A, b, alpha0.1): n_features A.shape[1] return np.linalg.inv(A.T A alpha * np.eye(n_features)) A.T b params_ridge ridge_regression(A, b, alpha0.1)6. 常见问题与调试技巧在实际实现过程中可能会遇到各种问题。以下是一些常见问题及其解决方法矩阵不可逆检查矩阵A是否列满秩使用伪逆np.linalg.pinv代替逆添加小的正则化项数值不稳定对数据进行标准化处理使用SVD分解代替直接求逆增加浮点精度dtypenp.float64投影结果不符合预期验证投影矩阵的幂等性检查基向量是否线性独立可视化投影过程辅助调试# 调试示例检查矩阵条件数 print(f矩阵A的条件数: {np.linalg.cond(A):.2f}) # 条件数越大矩阵越接近奇异通过本文的代码实践我们不仅实现了各种投影矩阵还将其应用于实际的线性回归问题。这种从数学理论到代码实现的过程正是数据科学和机器学习工作中最核心的技能之一。

更多文章