用Python手把手实现最小二乘法:从数学推导到代码实战(附完整数据集)

# 用Python手把手实现最小二乘法:从数学推导到代码实战(附完整数据集) 如果你刚开始接触数据科学或者机器学习,线性回归模型很可能是你遇到的第一个“拦路虎”。它看起来简单,公式也清晰,但真要自己动手从零实现,很多人会卡在数学推导和代码转换的环节。网上教程要么过于理论,满篇矩阵求导让人望而生畏;要么直接甩给你几行`sklearn`代码,知其然不知其所以然。这篇文章,我想带你真正“手把手”走一遍最小二乘法的完整流程。我们不只满足于调用现成的库,而是要深入理解背后的数学原理,并用Python从零实现两种核心解法:**解析解**和**梯度下降**。我会用一个完整的房价预测案例贯穿始终,过程中你会看到如何处理真实数据、如何评估模型,以及两种方法在实际应用中的微妙差异。准备好了吗?我们开始吧。 ## 1. 最小二乘法的数学直觉:从“最佳直线”说起 最小二乘法(Least Squares, LS)的核心思想非常直观:**找一条直线(或超平面),使得所有数据点到这条直线的垂直距离(误差)的平方和最小**。为什么是平方和?而不是绝对值之和或者四次方之和?这里有几个关键原因。 首先,平方运算保证了所有误差都是正数,避免了正负误差相互抵消,从而真实反映总误差大小。其次,平方函数处处可导,性质良好,这为我们后续使用求导等数学工具寻找最优解提供了便利。最后,从概率论的角度看,当误差服从正态分布时,最小二乘估计等价于**极大似然估计**,这赋予了它坚实的统计理论基础。 我们从一个最简单的单变量线性模型开始:`y = w * x + b`。其中,`w`是斜率(权重),`b`是截距(偏置)。假设我们有`m`个样本点 `(x_i, y_i)`,模型预测值为 `ŷ_i = w * x_i + b`,那么单个样本的误差就是 `e_i = y_i - ŷ_i`。我们的目标函数,即**均方误差(MSE)**为: ``` J(w, b) = (1/m) * Σ (y_i - ŷ_i)^2 = (1/m) * Σ (y_i - (w*x_i + b))^2 ``` 我们的任务就是找到一对 `(w, b)`,使得 `J(w, b)` 这个“代价函数”的值最小。这个寻找最优解的过程,就是最小二乘法的精髓。 > **提示**:这里你可能听到另一个紧密相关的术语——**线性最小均方误差(LMSE)**。LS和LMSE目标都是最小化均方误差,但哲学起点不同。LS将 `x` 和 `y` 视为确定的观测值,通过纯代数方法求解;而LMSE(或MMSE)则将参数和观测值都视为随机变量,在已知其统计特性(如均值、协方差)的前提下,寻求估计误差的统计期望最小。当样本量趋于无穷时,LS估计会收敛到LMSE估计。对于初学者,先从确定性的LS入手理解概念更为直接。 为了更清晰地展示不同误差度量方式的目标,我们看下面这个对比: | 误差度量方式 | 目标函数形式 | 主要特点 | | :--- | :--- | :--- | | **最小二乘(LS)** | `min Σ(y_i - ŷ_i)²` | 代数方法,计算简便,最优解有闭式解。 | | **最小绝对值(LA)** | `min Σ\|y_i - ŷ_i\|` | 对异常值更鲁棒,但目标函数不可导,求解复杂。 | | **最小均方误差(LMSE)** | `min E[(y - ŷ)²]` | 统计方法,需要已知随机变量的二阶统计量。 | 我们的旅程就从最小化这个平方和损失函数 `J(w, b)` 开始。 ## 2. 单变量线性回归:解析解推导与NumPy实现 我们先攻克单变量的情况,把思路理清。要让 `J(w, b)` 最小,一个经典的方法是分别对 `w` 和 `b` 求偏导数,并令其等于零。这个过程就是寻找函数的驻点,对于凸函数而言,驻点就是全局最优点。 **步骤1:构建目标函数** ``` J(w, b) = (1/m) * Σ_{i=1}^{m} (y_i - w*x_i - b)^2 ``` **步骤2:对 `b` 求偏导并令其为零** ``` ∂J/∂b = (2/m) * Σ (y_i - w*x_i - b) * (-1) = 0 => Σ (y_i - w*x_i - b) = 0 => Σ y_i - w * Σ x_i - m * b = 0 => b = (Σ y_i - w * Σ x_i) / m = ȳ - w * x̄ ``` 看,我们得到了 `b` 的表达式,它依赖于 `w` 以及 `x` 和 `y` 的均值 `x̄` 和 `ȳ`。 **步骤3:对 `w` 求偏导并令其为零** 将 `b = ȳ - w * x̄` 代入原目标函数,或者直接对 `w` 求导(计算稍复杂): ``` ∂J/∂w = (2/m) * Σ (y_i - w*x_i - b) * (-x_i) = 0 ``` 将 `b` 的表达式代入并整理(推导过程略),最终可以得到: ``` w = Σ((x_i - x̄) * (y_i - ȳ)) / Σ((x_i - x̄)^2) ``` 这个公式非常直观:**斜率 `w` 等于 `x` 和 `y` 的协方差除以 `x` 的方差**。 **步骤4:Python代码实现** 理论有了,我们立刻用代码来验证。首先,我们生成一组模拟数据。 ```python import numpy as np import matplotlib.pyplot as plt # 设置随机种子,确保结果可复现 np.random.seed(42) # 生成模拟数据:真实模型为 y = 3*x + 5 + 噪声 m = 50 # 样本数量 x = 2 * np.random.rand(m, 1) # 生成[0, 2)区间内的特征 true_w, true_b = 3, 5 y = true_w * x + true_b + np.random.randn(m, 1) # 加入高斯噪声 # 计算均值 x_mean, y_mean = np.mean(x), np.mean(y) # 根据推导公式计算 w 和 b # 计算协方差和方差 numerator = np.sum((x - x_mean) * (y - y_mean)) denominator = np.sum((x - x_mean) ** 2) w_manual = numerator / denominator b_manual = y_mean - w_manual * x_mean print(f"手动计算解析解:斜率 w = {w_manual:.4f}, 截距 b = {b_manual:.4f}") print(f"真实模型参数:斜率 w = {true_w}, 截距 b = {true_b}") ``` 运行这段代码,你会看到计算出的 `w` 和 `b` 非常接近真实值 3 和 5。这就是最小二乘解析解的威力——直接、精确。让我们把拟合的直线画出来看看效果。 ```python # 绘制数据点和拟合直线 plt.figure(figsize=(8, 5)) plt.scatter(x, y, alpha=0.7, label='观测数据') x_plot = np.array([[0], [2]]) # 用于画线的两个端点 y_plot = w_manual * x_plot + b_manual plt.plot(x_plot, y_plot, 'r-', linewidth=3, label=f'拟合直线: y={w_manual:.2f}x+{b_manual:.2f}') plt.xlabel('特征 x') plt.ylabel('目标值 y') plt.title('单变量线性回归:解析解拟合结果') plt.legend() plt.grid(True, alpha=0.3) plt.show() ``` 图像会清晰地显示,红色直线确实穿过了数据点的“中心”,直观地证明了我们公式的正确性。然而,现实世界的问题往往是多变量的。要处理多个特征,我们需要更强大的工具——矩阵。 ## 3. 多元线性回归的矩阵形式与解析解 当特征不止一个时,例如预测房价,我们需要考虑面积、房间数、地理位置等多个因素。模型变为: `y = w1*x1 + w2*x2 + ... + wn*xn + b` 用矩阵表示可以极大地简化书写和计算。我们将所有参数 `w1, w2, ..., wn, b` 打包成一个列向量 `θ`(通常 `θ[0]` 是 `b`,`θ[1:]` 是 `w`)。同时,为每个样本的特征向量前面添加一个恒为1的项,以便与 `b` 相乘。这样,模型可以优雅地写成: `ŷ = Xθ` 其中,`X` 是 `m × (n+1)` 的矩阵(`m` 个样本,`n+1` 个特征,包含常数项),`θ` 是 `(n+1) × 1` 的参数向量。 此时,均方误差目标函数为: `J(θ) = (1/m) * (y - Xθ)^T (y - Xθ)` 我们的目标是最小化 `J(θ)`。通过对向量 `θ` 求导(涉及矩阵微积分),并令导数为零向量,我们可以得到著名的**正规方程(Normal Equation)**: `X^T X θ = X^T y` 如果 `X^T X` 是可逆矩阵,那么解析解为: `θ = (X^T X)^{-1} X^T y` **这就是多元线性回归的“闭式解”或“解析解”**。它的计算复杂度主要在于求矩阵的逆,当特征维度 `n` 很高(例如上万维)时,求逆的计算成本会变得非常高昂(时间复杂度约为 `O(n^3)`)。但对于特征维度不是特别高的问题,这仍然是首选方法,因为它能一次性给出精确解。 让我们用NumPy实现这个矩阵解法,并应用于一个更真实的场景。 ```python # 生成一个多元线性回归的模拟数据集(3个特征) np.random.seed(123) m = 100 n_features = 3 # 随机生成特征矩阵 X (不包含常数项) X_raw = np.random.randn(m, n_features) * 2 + 1 # 让数据有一定分布 # 设定真实参数 theta_true = [bias, w1, w2, w3] theta_true = np.array([4, 2.5, -1.5, 0.8]) # 生成目标值 y = X * w + b + 噪声 # 注意:这里需要为X_raw添加一列1,以便与theta_true相乘 X_with_bias = np.c_[np.ones((m, 1)), X_raw] # 添加常数项列 y = X_with_bias.dot(theta_true) + np.random.randn(m) * 1.5 # 加入噪声 # 使用正规方程求解 theta # θ = (X^T X)^{-1} X^T y X = X_with_bias # 我们的设计矩阵 XT_X = X.T.dot(X) # 使用np.linalg.inv求逆,更稳健的做法是使用np.linalg.pinv(伪逆) theta_hat = np.linalg.inv(XT_X).dot(X.T).dot(y) # 或者使用NumPy的求解线性方程组函数,数值上更稳定 # theta_hat = np.linalg.solve(XT_X, X.T.dot(y)) print("真实参数 theta_true:", theta_true) print("解析解估计 theta_hat:", theta_hat.round(4)) # 计算估计值与真实值的均方误差 y_pred = X.dot(theta_hat) mse = np.mean((y - y_pred) ** 2) print(f"在训练集上的均方误差(MSE): {mse:.4f}") ``` 你会看到,估计出的 `theta_hat` 非常接近我们预设的 `theta_true`。这个矩阵公式虽然简洁,但背后隐藏着一个重要的数值稳定性问题:**当特征之间存在高度相关性(多重共线性)时,`X^T X` 可能接近奇异矩阵,求逆会不稳定,导致结果对数据微小变动极其敏感**。在实际应用中,我们常使用 `np.linalg.lstsq`(基于SVD分解)或 `np.linalg.pinv`(计算伪逆)来替代直接求逆,它们能更好地处理病态问题。 ```python # 更稳健的求解方式:使用NumPy的lstsq(最小二乘求解) theta_hat_lstsq, residuals, rank, s = np.linalg.lstsq(X, y, rcond=None) print("使用np.linalg.lstsq求解的 theta:", theta_hat_lstsq.round(4)) ``` ## 4. 梯度下降法:另一种优化视角 当特征维度 `n` 非常大(例如在文本处理中特征可能上万),或者数据集巨大无法全部装入内存时,计算 `(X^T X)^{-1}` 变得不切实际。这时,我们需要一种迭代的、逼近最优解的方法——**梯度下降法**。 梯度下降法的思想源于爬山:如果你想最快下到山谷,就沿着当前最陡峭的方向向下走。在数学上,“最陡峭的方向”就是损失函数 `J(θ)` 在当前 `θ` 点的**梯度**(由偏导数组成的向量)。梯度方向是函数值上升最快的方向,因此,我们沿着梯度的反方向更新参数,就能逐步减小损失函数值。 参数更新公式如下: `θ := θ - α * ∇J(θ)` 其中 `α` 是**学习率**,控制每一步更新的幅度。对于我们的线性回归和MSE损失,梯度 `∇J(θ)` 有一个非常简洁的表达式: `∇J(θ) = (2/m) * X^T (Xθ - y)` 梯度下降有三种主要变体: - **批量梯度下降(BGD)**:每次更新使用全部 `m` 个样本计算梯度。收敛稳定,但每次迭代计算开销大。 - **随机梯度下降(SGD)**:每次更新随机使用一个样本计算梯度。计算快,可以跳出局部极小,但收敛路径震荡剧烈。 - **小批量梯度下降(MBGD)**:折中方案,每次使用一个小批量(如32、64个)样本计算梯度。这是深度学习中的标配。 下面,我们实现一个简单的批量梯度下降来求解线性回归。 ```python def batch_gradient_descent(X, y, theta_init, learning_rate=0.01, n_iters=1000, tol=1e-6): """ 批量梯度下降实现线性回归 参数: X: 设计矩阵 (m x n_features) y: 目标向量 (m, ) theta_init: 参数初始值 learning_rate: 学习率 n_iters: 最大迭代次数 tol: 收敛容忍度(两次迭代损失变化小于此值则停止) 返回: theta: 学习到的参数 cost_history: 每次迭代的损失值记录 """ m = len(y) theta = theta_init.copy() cost_history = [] for i in range(n_iters): # 计算预测值和误差 y_pred = X.dot(theta) error = y_pred - y # 计算梯度 (X^T * error) / m gradient = (X.T.dot(error)) / m # 更新参数 theta = theta - learning_rate * gradient # 计算当前损失并记录 cost = np.mean(error ** 2) cost_history.append(cost) # 简单收敛判断:如果损失下降非常小,则提前停止 if i > 0 and abs(cost_history[-2] - cost_history[-1]) < tol: print(f"迭代 {i+1} 次后提前收敛。") break return theta, cost_history # 使用梯度下降求解之前的多元回归问题 np.random.seed(456) theta_init_gd = np.random.randn(X.shape[1]) # 随机初始化参数 learning_rate = 0.1 # 学习率需要仔细调节 theta_gd, cost_hist = batch_gradient_descent(X, y, theta_init_gd, learning_rate=learning_rate, n_iters=2000) print("\n梯度下降法结果:") print("估计参数 theta_gd:", theta_gd.round(4)) print("解析解参数 theta_hat:", theta_hat.round(4)) print(f"参数差异的L2范数: {np.linalg.norm(theta_gd - theta_hat):.6f}") # 绘制损失函数下降曲线 plt.figure(figsize=(10, 4)) plt.subplot(1, 2, 1) plt.plot(cost_hist, linewidth=2) plt.xlabel('迭代次数') plt.ylabel('损失 (MSE)') plt.title('梯度下降:损失下降曲线') plt.grid(True, alpha=0.3) # 对比梯度下降与解析解在训练集上的预测效果 y_pred_gd = X.dot(theta_gd) y_pred_analytic = X.dot(theta_hat) plt.subplot(1, 2, 2) plt.scatter(y, y_pred_analytic, alpha=0.6, label='解析解预测', s=50) plt.scatter(y, y_pred_gd, alpha=0.6, marker='^', label='梯度下降预测', s=50) # 画一条y=x的参考线,完美预测点将落在这条线上 min_val, max_val = min(y.min(), y_pred_gd.min()), max(y.max(), y_pred_gd.max()) plt.plot([min_val, max_val], [min_val, max_val], 'k--', label='完美预测线') plt.xlabel('真实值 y') plt.ylabel('预测值 ŷ') plt.title('预测值 vs 真实值') plt.legend() plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() ``` 运行代码,你会发现梯度下降得到的参数 `theta_gd` 与解析解 `theta_hat` 几乎一模一样,并且损失函数曲线平滑下降至稳定值。右侧的散点图也显示,两种方法的预测点都紧密分布在完美预测线附近,说明它们都成功拟合了数据。 > **注意**:学习率 `α` 的选择至关重要。太大可能导致损失震荡甚至发散;太小则收敛缓慢。一个实用的技巧是观察损失曲线:如果曲线上下剧烈波动,说明学习率太大;如果曲线下降极其缓慢,说明学习率太小。可以尝试按0.1, 0.03, 0.01, 0.003等序列进行调试。 ## 5. 实战:波士顿房价预测与模型评估 现在,我们将学到的知识应用于一个经典数据集(虽然波士顿房价数据集因伦理问题已从`sklearn`最新版本中移除,但其变体或类似数据集仍具教学意义)。我们将使用`sklearn`中的加利福尼亚房价数据集,并完成从数据加载、预处理、模型训练到评估的全流程。 **步骤1:加载和探索数据** ```python from sklearn.datasets import fetch_california_housing from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 加载加州房价数据集 data = fetch_california_housing() X_full = data.data # 特征矩阵 y_full = data.target # 目标值(房价中位数,单位:十万美元) feature_names = data.feature_names print("数据集形状:", X_full.shape) print("特征名称:", feature_names) print("\n前5个样本的特征值:") print(X_full[:5]) print("\n前5个样本的目标值:", y_full[:5]) # 数据拆分:训练集和测试集 X_train, X_test, y_train, y_test = train_test_split( X_full, y_full, test_size=0.2, random_state=42 ) print(f"\n训练集大小: {X_train.shape}, 测试集大小: {X_test.shape}") ``` **步骤2:数据预处理** 在应用线性模型前,对特征进行标准化是一个好习惯。这能使得梯度下降更快收敛,并且让模型对不同尺度的特征赋予公平的权重。 ```python scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) # 注意:使用训练集的均值和方差来转换测试集 # 为线性回归添加常数项(截距项) X_train_b = np.c_[np.ones((X_train_scaled.shape[0], 1)), X_train_scaled] X_test_b = np.c_[np.ones((X_test_scaled.shape[0], 1)), X_test_scaled] ``` **步骤3:使用解析解(正规方程)训练模型** ```python # 使用正规方程求解 theta_normal_eq = np.linalg.inv(X_train_b.T.dot(X_train_b)).dot(X_train_b.T).dot(y_train) print("正规方程求解的参数(含截距):", theta_normal_eq.round(4)) ``` **步骤4:使用梯度下降训练模型** ```python # 定义梯度下降函数(带小批量) def mini_batch_gradient_descent(X, y, theta_init, learning_rate=0.01, batch_size=32, n_epochs=100): m = len(y) theta = theta_init.copy() cost_history = [] for epoch in range(n_epochs): # 每个epoch前打乱数据 indices = np.random.permutation(m) X_shuffled = X[indices] y_shuffled = y[indices] for i in range(0, m, batch_size): X_batch = X_shuffled[i:i+batch_size] y_batch = y_shuffled[i:i+batch_size] y_pred_batch = X_batch.dot(theta) error_batch = y_pred_batch - y_batch gradient = (X_batch.T.dot(error_batch)) / len(y_batch) theta = theta - learning_rate * gradient # 计算整个训练集的损失 y_pred_all = X.dot(theta) cost = np.mean((y_pred_all - y) ** 2) cost_history.append(cost) if epoch % 20 == 0: print(f"Epoch {epoch}: MSE = {cost:.4f}") return theta, cost_history # 初始化参数并运行小批量梯度下降 np.random.seed(42) theta_init_mbgd = np.random.randn(X_train_b.shape[1]) theta_mbgd, cost_hist_mbgd = mini_batch_gradient_descent( X_train_b, y_train, theta_init_mbgd, learning_rate=0.05, batch_size=64, n_epochs=200 ) ``` **步骤5:模型评估与比较** 训练好模型后,我们需要在未见过的测试集上评估其泛化能力。常用的回归评估指标有: - **均方误差(MSE)**:我们一直最小化的目标,数值越小越好。 - **均方根误差(RMSE)**:MSE的平方根,与目标值 `y` 单位一致,更易解释。 - **平均绝对误差(MAE)**:对异常值不如MSE敏感。 - **决定系数(R²)**:表示模型可解释的方差比例,越接近1越好。 ```python def evaluate_model(theta, X_test, y_test, model_name): y_pred = X_test.dot(theta) mse = np.mean((y_test - y_pred) ** 2) rmse = np.sqrt(mse) mae = np.mean(np.abs(y_test - y_pred)) # 计算R² ss_res = np.sum((y_test - y_pred) ** 2) ss_tot = np.sum((y_test - np.mean(y_test)) ** 2) r2 = 1 - (ss_res / ss_tot) print(f"\n{model_name} 在测试集上的表现:") print(f" 均方误差 (MSE): {mse:.4f}") print(f" 均方根误差 (RMSE): {rmse:.4f}") print(f" 平均绝对误差 (MAE): {mae:.4f}") print(f" 决定系数 (R²): {r2:.4f}") return {'MSE': mse, 'RMSE': rmse, 'MAE': mae, 'R²': r2} # 评估两个模型 metrics_normal_eq = evaluate_model(theta_normal_eq, X_test_b, y_test, "正规方程(解析解)") metrics_mbgd = evaluate_model(theta_mbgd, X_test_b, y_test, "小批量梯度下降") # 对比结果 print("\n" + "="*50) print("模型性能对比摘要:") print("="*50) print(f"{'指标':<10} {'正规方程':<12} {'梯度下降':<12} {'差异'}") print("-"*50) for key in ['MSE', 'RMSE', 'MAE', 'R²']: diff = metrics_normal_eq[key] - metrics_mbgd[key] print(f"{key:<10} {metrics_normal_eq[key]:<12.4f} {metrics_mbgd[key]:<12.4f} {diff:+.4f}") ``` **步骤6:特征重要性分析** 线性模型的一个巨大优势是可解释性。我们可以通过观察参数 `θ` 的大小和符号,来理解每个特征对预测结果的影响。 ```python # 提取特征名称(包含常数项) all_feature_names = ['Intercept'] + list(feature_names) # 创建特征重要性表格 importance_df = pd.DataFrame({ 'Feature': all_feature_names, 'Coefficient (Normal Eq)': theta_normal_eq, 'Coefficient (MBGD)': theta_mbgd, 'Abs_Coeff_NormalEq': np.abs(theta_normal_eq[1:]), # 忽略截距 }) # 按绝对值大小排序(仅对特征,不包括截距) sorted_idx = np.argsort(importance_df.loc[1:, 'Abs_Coeff_NormalEq'].values)[::-1] sorted_features = importance_df.loc[1:, 'Feature'].iloc[sorted_idx].values sorted_coeff_normal = importance_df.loc[1:, 'Coefficient (Normal Eq)'].iloc[sorted_idx].values sorted_coeff_mbgd = importance_df.loc[1:, 'Coefficient (MBGD)'].iloc[sorted_idx].values print("\n特征重要性排序(基于正规方程系数的绝对值):") print("-"*60) print(f"{'特征':<15} {'系数(解析解)':<15} {'系数(梯度下降)':<15} {'解释'}") print("-"*60) for feat, coeff_n, coeff_g in zip(sorted_features, sorted_coeff_normal, sorted_coeff_mbgd): # 简单解释:正系数表示特征与房价正相关,负系数表示负相关 direction = "正向影响" if coeff_n > 0 else "负向影响" print(f"{feat:<15} {coeff_n:<15.4f} {coeff_g:<15.4f} {direction}") ``` 通过这个完整的案例,你不仅实现了最小二乘法的两种核心算法,还走完了机器学习项目的一个标准 pipeline:数据准备、模型训练、评估和解释。你会发现,在这个数据集上,解析解和梯度下降的结果高度一致,验证了算法的正确性。同时,特征重要性分析能告诉你,例如“平均房间数”对房价有最强的正向影响,这与常识是吻合的。 ## 6. 进阶讨论:解析解与梯度下降的深层对比与选择 走到这里,你可能会问:既然解析解一步到位、精确无误,为什么我们还需要梯度下降这种迭代的、近似的方法呢?让我们深入对比一下,这在实际项目中是至关重要的技术选型决策。 **计算复杂度与可扩展性** - **解析解**:需要计算 `(X^T X)^{-1}`,其时间复杂度约为 `O(n^3)`,其中 `n` 是特征数量。此外,需要将整个矩阵 `X` 存储在内存中,空间复杂度为 `O(m*n)`。当特征数 `n` 很大(例如 > 10,000)时,求逆操作会变得极其缓慢甚至不可行。 - **梯度下降**:每次迭代的计算复杂度约为 `O(m*n)`(批量梯度下降)或 `O(batch_size * n)`(小批量)。它不需要存储巨大的 `X^T X` 矩阵,也不需要求逆。通过调节批量大小,它可以处理无法完全装入内存的超大规模数据集(外存学习)。 **对非凸问题的适用性** - **解析解**:依赖于损失函数是凸函数且有闭式解。对于线性回归的MSE损失,这成立。但对于逻辑回归、神经网络等非凸模型,不存在通用的解析解。 - **梯度下降**:是一种通用的优化框架,只要损失函数可导(或次可导),就可以应用。它是训练深度学习模型的基石。 **数值稳定性与正则化** - **解析解**:当 `X^T X` 不可逆或条件数很大时(特征共线或特征数多于样本数),直接求逆会失败或产生数值不稳定解。虽然可以通过伪逆(SVD)缓解,但本质问题仍在。 - **梯度下降**:本身对病态问题有一定容忍度,尤其是结合了动量(Momentum)或自适应学习率(如Adam)的变种。更重要的是,梯度下降可以非常自然地与**正则化**(如L1/L2)结合,通过修改损失函数或更新规则来防止过拟合,而解析解公式需要相应修改。 为了更直观地展示在不同数据规模下的选择策略,可以参考以下决策逻辑: ```python # 伪代码:选择最小二乘法实现方式的决策逻辑 def choose_least_squares_method(X, y): m, n = X.shape # 样本数,特征数 if n < 1000 and m < 100000: # 特征和样本数都不太大,优先使用解析解,快速精确 print("建议:使用正规方程(解析解)或 sklearn 的 LinearRegression。") print("理由:计算速度快,解精确,无需调学习率等超参数。") method = "analytical" elif n >= 1000 or m >= 100000: # 特征多或样本量大,解析解计算成本高或内存不足 print("建议:使用(小批量)梯度下降或 SGDRegressor。") print("理由:避免高维矩阵求逆,内存效率高,可扩展到海量数据。") method = "gradient_descent" # 如果怀疑特征存在严重共线性,即使n小,也建议使用梯度下降+Ridge正则化 if is_ill_conditioned(X): # 需要判断条件数等 print("补充:检测到设计矩阵可能病态,建议使用带L2正则化的梯度下降(Ridge回归)。") method = "gradient_descent_with_regularization" return method ``` 在实际工作中,我个人的经验法则是:对于特征数在几千以内、数据能完全放入内存的“干净”建模问题,直接使用解析解或`sklearn`的`LinearRegression`,省心省力。当特征维度进入万级,或者数据是流式生成、需要在线学习时,梯度下降及其变种是唯一的选择。此外,在研究和原型开发阶段,梯度下降能让你更深入地理解优化过程,方便后续迁移到更复杂的模型上。 最后,别忘了我们最初提到的LMSE(最小均方误差)。虽然本文聚焦于确定性的最小二乘法,但了解其统计兄弟LMSE能拓宽视野。当你拥有关于参数或噪声的先验分布信息时(例如在信号处理、贝叶斯推断中),LMSE会提供更优的估计。不过,对于大多数机器学习应用场景,尤其是大数据背景下,样本统计量足以替代先验知识,经典的最小二乘法依然是那个可靠、直观且强大的起点。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

Python内容推荐

Python中实现最小二乘法思路及实现代码

Python中实现最小二乘法思路及实现代码

主要介绍了Python中实现最小二乘法思路及实现代码,具有一定借鉴价值,需要的朋友可以参考下

线性回归 最小二乘法的求解推导与基于Python的底层代码实现
附逐句代码讲解(在博客中的机器学习算法专栏)

线性回归 最小二乘法的求解推导与基于Python的底层代码实现 附逐句代码讲解(在博客中的机器学习算法专栏)

线性回归 最小二乘法的求解推导与基于Python的底层代码实现(即不调包) 附逐句代码讲解(在博客中的机器学习算法专栏)

最小二乘法详细推导过程以及python代码

最小二乘法详细推导过程以及python代码

我已经设置了CSDN免费0积分下载,并且设置了不允许动态调分。 如果需要注册登录,请使用百度网盘链接。 链接:https://pan.baidu.com/s/177Sqkbc4ClTQ0emp1xhluQ?pwd=1024 提取码:1024 --来自百度网盘超级会员V4的分享

知识图谱实战案例完全剖析(附完整源码和数据集)Python与Neo4j的集成 -- 独家.rar

知识图谱实战案例完全剖析(附完整源码和数据集)Python与Neo4j的集成 -- 独家.rar

知识图谱实战案例完全剖析(附完整源码和数据集)Python与Neo4j的集成

python最小二乘法代码

python最小二乘法代码

使用python实现最小二乘法,将多项式曲线拟合。当然可以修改一下用在其他需要最小二乘法的地方

Python基于最小二乘法实现曲线拟合示例

Python基于最小二乘法实现曲线拟合示例

主要介绍了Python基于最小二乘法实现曲线拟合,涉及Python基于numpy及scipy库进行曲线拟合操作相关运算技巧,需要的朋友可以参考下

不使用函数库实现最小二乘法python代码

不使用函数库实现最小二乘法python代码

不使用python函数库实现最小二乘法,唯一使用一个读取csv文件的库,如果自己输入数据,是可以删除的

项目实战 Python Django 个人网站 电影推荐网站 完整代码

项目实战 Python Django 个人网站 电影推荐网站 完整代码

完整版Python-Django项目,调试通过,直接下载即可运行包括:登陆、注册、浏览、搜索、发布资源、评论等多个功能。可作为新手练习,课程设计,毕业设计,代码注释详细,便于理解。

逻辑回归的Python实现,附源代码,数据集,结果图片

逻辑回归的Python实现,附源代码,数据集,结果图片

逻辑回归 python的简单实现,附加数据集,源代码,迭代结果图片 适合机器学习初学者,标注清晰,明确

最小二乘法及其python实现详解

最小二乘法及其python实现详解

最小二乘法Least Square Method,做为分类回归算法的基础,有着悠久的历史(由马里·勒让德于1806年提出)。它通过最小化误差的平方和寻找数据的最佳函数匹配。利用最小二乘法可以简便地求得未知的数据,并使得这些求得的数据与实际数据之间误差的平方和为最小。最小二乘法还可用于曲线拟合。其他一些优化问题也可通过最小化能量或最大化熵用最小二乘法来表达。 那什么是最小二乘法呢?别着急,我们先从几个简单的概念说起。 假设我们现在有一系列的数据点 ,那么由我们给出的拟合函数h(x)得到的估计量就是 ,那么怎么评估我们给出的拟合函数与实际待求解的函数的拟合程度比较高呢?这里我们先定义一个概念:

手把手教你用Python实现“坦克大战”,附详细代码!

手把手教你用Python实现“坦克大战”,附详细代码!

小时候玩的“坦克大战”,你还记得吗? 满满的回忆 今天,我们使用Python以及强大的第三方库来实现一个简单的坦克大战游戏。【文中代码点击阅读原文下载,可直接运行】 整体效果 环境依赖 pygame介绍Pygame被设计用来写游戏的python模块集合,基于SDL库开发。使用python可以导入pygame来开发具有全部特性的游戏和多媒体软件,Pygame是极度轻便的并且可以运行在几乎所有的平台和操作系统上。 1. 导入依赖 & 通用配置 1import random 2import sys 3import time 4from urllib.request import urlretri

项目实战 Python Django    电影推荐网站 完整代码

项目实战 Python Django 电影推荐网站 完整代码

完整版Python-Django项目,调试通过,直接下载即可运行包括:登陆、注册、浏览、搜索、发布资源、评论等多个功能。可作为新手练习,课程设计,毕业设计,代码注释详细,便于理解。

从数学小白到大神:最小二乘法+Python实战.pdf

从数学小白到大神:最小二乘法+Python实战.pdf

从数学小白到大神:最小二乘法+Python实战.pdf

Python最小二乘法矩阵

Python最小二乘法矩阵

今天小编就为大家分享一篇关于Python最小二乘法矩阵,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

Python推导式简单示例【列表推导式、字典推导式与集合推导式】

Python推导式简单示例【列表推导式、字典推导式与集合推导式】

主要介绍了Python推导式,结合简单实例形式分析了Python列表推导式、字典推导式与集合推导式基本使用方法,需要的朋友可以参考下

Python列表推导式实现代码实例

Python列表推导式实现代码实例

主要介绍了Python列表推导式实现代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

Python深度学习实战-源代码和数据集.rar

Python深度学习实战-源代码和数据集.rar

Python深度学习实战-源代码和数据集

python最小二乘法三维坐标拟合平面-最小二乘法拟合平面

python最小二乘法三维坐标拟合平面-最小二乘法拟合平面

python最小二乘法三维坐标拟合平面-最小二乘法拟合平面

machine-learning-code-writing:机器学习算法的数学推导和纯Python代码实现

machine-learning-code-writing:机器学习算法的数学推导和纯Python代码实现

机器学习代码编写 机器学习算法的数学推导和纯Python代码实现。

79套Python数据分析可视化预测项目例子实例源码代码实战案例带数据集.zip

79套Python数据分析可视化预测项目例子实例源码代码实战案例带数据集.zip

79套Python数据分析可视化预测项目例子实例源码代码实战案例带数据集,包含(简略): 案例-50个Pyecharts可视化例子 电商-超市销售数据分析与报表-约200行(数据运视分析) 电商-广告投放效果分析-约250行(KMeans聚类、数据分析) 电商-京东评论数据情感分析-约150行(分词、关键词提取、情感分析) 电商-母婴市场消费数据分析-约350行(pyecharts可视化、数据分析) 电商-天猫双十一美妆销售数据分析-约400行(matplotlib可视化、数据分析) 电商-优衣库门店可视化与顾客分组-约500行(pyecharts地图可视化、KMeans聚类、大屏可视化) 电商-预测天猫用户使用优惠券概率较高的客群-约350行(逻辑回归模型、模型评估) 电商-预测小红书用户消费金额-约500行(线性回归模型、模型评估优化) 房地产-二手房房价分析和预测-约300行(多元线性回归) 房地产-二手房数据分析-约400行(数据探索分析、matplotlib可视化) 房地产-二手房信息抓取+可视化-约300行(爬虫+pyecharts可视化)

最新推荐最新推荐

recommend-type

Python中实现最小二乘法思路及实现代码

最小二乘法是一种广泛应用的数学优化技术,它的主要目的是找到一条直线或曲线,使得这条直线或曲线与一组数据点的拟合误差平方和最小。在Python中,我们可以借助科学计算库如NumPy和SciPy来轻松实现最小二乘法。 在...
recommend-type

python实现将两个文件夹合并至另一个文件夹(制作数据集)

在Python编程中,合并两个文件夹到另一个文件夹是一个常见的任务,特别是在数据预处理阶段,如制作深度学习数据集。本教程将详细讲解如何利用Python完成这个任务。 首先,我们需要导入必要的库,如`numpy`、`os`和`...
recommend-type

超完整 Python基础入门知识教程 Python从入门到进阶知识大全 Python开发基础知识能力集全部课件 1885页

Python的发展历史可以追溯到1982年,Guido von Rossum在阿姆斯特丹大学获得数学和计算机硕士学位时。Guido von Rossum是一位数学家,但他更加热衷于计算机相关的工作,并热衷于做任何和编程相关的工作。在那个时候,...
recommend-type

《python数据分析与挖掘实战》第一章总结.docx

《Python数据分析与挖掘实战》第一章主要探讨了数据挖掘在餐饮行业的应用,以及数据挖掘的基本流程和技术工具。在第一章中,作者以T餐饮企业为例,强调了由于多系统管理导致的数据冗余和处理难题,提出了利用计算机...
recommend-type

Python爬取数据保存为Json格式的代码示例

在Python编程中,经常需要从网络上爬取数据,然后将其存储为便于处理和分析...总的来说,这个代码示例提供了一个完整的Python爬虫流程,从请求网页、解析HTML到存储数据为JSON格式,对于初学者来说是非常有价值的参考。
recommend-type

学生成绩管理系统C++课程设计与实践

资源摘要信息:"学生成绩信息管理系统-C++(1).doc" 1. 系统需求分析与设计 在进行学生成绩信息管理系统开发前,首先需要进行系统需求分析,这是确定系统开发目标与范围的过程。需求分析应包括数据需求和功能需求两个方面。 - 数据需求分析: - 学生成绩信息:需要收集学生的姓名、学号、课程成绩等数据。 - 数据类型和长度:明确每个数据项的数据类型(如字符串、整型等)和长度,例如学号可能是字符串类型且长度为一定值。 - 描述:详细描述每个数据项的意义,以确保系统能够准确处理。 - 功能需求分析: - 列出功能列表:用户界面应提供清晰的操作指引,列出所有可用功能。 - 查询学生成绩:系统应能通过学号或姓名查询学生的成绩信息。 - 增加学生成绩信息:允许用户添加未保存的学生成绩信息。 - 删除学生成绩信息:能够通过学号或姓名删除已经保存的成绩信息。 - 修改学生成绩信息:通过学号或姓名修改已有的成绩记录。 - 退出程序:提供安全退出程序的选项,并确保所有修改都已保存。 2. 系统设计 系统设计阶段主要完成内存数据结构设计、数据文件设计、代码设计、输入输出设计、用户界面设计和处理过程设计。 - 内存数据结构设计: - 使用链表结构组织内存中的数据,便于动态增删查改操作。 - 数据文件设计: - 选择文本文件存储数据,便于查看和编辑。 - 代码设计: - 根据功能需求,编写相应的函数和模块。 - 输入输出设计: - 设计简洁明了的输入输出提示信息和操作流程。 - 用户界面设计: - 用户界面应为字符界面,方便在命令行环境下使用。 - 处理过程设计: - 设计数据处理流程,确保每个操作都有明确的处理逻辑。 3. 系统实现与测试 实现阶段需要根据设计阶段的成果编写程序代码,并进行系统测试。 - 程序编写: - 完成系统设计中所有功能的程序代码编写。 - 系统测试: - 设计测试用例,通过测试用例上机测试系统。 - 记录测试方法和测试结果,确保系统稳定可靠。 4. 设计报告撰写 最后,根据系统开发的各个阶段,撰写详细的设计报告。 - 系统描述:包括问题说明、数据需求和功能需求。 - 系统设计:详细记录内存数据结构设计、数据文件设计、代码设计、输入/输出设计、用户界面设计、处理过程设计。 - 系统测试:包括测试用例描述、测试方法和测试结果。 - 设计特点、不足、收获和体会:反思整个开发过程,总结经验和教训。 时间安排: - 第19周(7月12日至7月16日)完成项目。 - 7月9日8:00到计算机学院实验中心(三楼)提交程序和课程设计报告。 指导教师和系主任(或责任教师)需要在文档上签名确认。 系统需求分析: - 使用表格记录系统需求分析的结果,包括数据项、数据类型、数据长度和描述。 - 分析数据项如学生成绩信息、状态器、链表节点等,确定其属性和行为。 以上就是文档中提到的学生成绩信息管理系统开发的关键知识点。开发此类系统需要熟练掌握C++编程基础,了解面向对象的程序设计思想,以及熟悉文件操作和链表等数据结构的应用。此外,良好的软件开发流程意识、测试意识和文档撰写能力也是必不可少的。
recommend-type

别再手动拖拽了!用Lumerical脚本批量创建FDTD仿真结构(附完整代码)

# 告别低效建模:Lumerical脚本自动化实战指南 在光子学仿真领域,时间就是科研生命线。当同行还在GUI界面里反复点击菜单时,你已经用脚本批量生成了20组参数化结构——这不是未来场景,而是每位FDTD用户都应该掌握的基础生产力革命。本文将彻底改变你与Lumerical的交互方式,从手动拖拽的农耕时代,跃迁到自动化建模的工业文明。 ## 1. 为何脚本建模是必然选择 2019年Nature Photonics的一项研究显示,科研工作者在仿真工具上平均浪费37%的时间在重复性操作上。对于需要参数扫描的纳米光学结构设计,这个数字可能更高。手动创建10个不同尺寸的纳米柱阵列意味着: -
recommend-type

Java邮件解析任务中,如何安全高效地提取HTML邮件内容并避免硬编码、资源泄漏和类型转换异常?

<think>我们被要求优化一段Java代码,该代码用于处理邮件(特别是来自特定发件人的构建通知邮件)。代码的主要问题包括: 1. 重复获取邮件内容:在检查MIME类型后,多次调用`msg[i].getContent()`,这可能导致性能问题或流关闭异常。 2. 类型转换问题:直接将邮件内容转换为`Multipart`而不进行类型检查,可能引发`ClassCastException`。 3. 代码结构问题:逻辑嵌套过深,可读性差,且存在重复代码(如插入邮件详情的操作在两个地方都有)。 4. 硬编码和魔法值:例如在解析HTML表格时使用了硬编码的索引(如list3.get(10)),这容易因邮件
recommend-type

RH公司应收账款管理优化策略研究

资源摘要信息:"本文针对RH公司的应收账款管理问题进行了深入研究,并提出了改进策略。文章首先分析了应收账款在企业管理中的重要性,指出其对于提高企业竞争力、扩大销售和充分利用生产能力的作用。然后,以RH公司为例,探讨了公司应收账款管理的现状,并识别出合同管理、客户信用调查等方面的不足。在此基础上,文章提出了一系列改善措施,包括完善信用政策、改进业务流程、加强信用调查和提高账款回收力度。特别强调了建立专门的应收账款回收部门和流程的重要性,并建议在实际应用过程中进行持续优化。同时,文章也意识到企业面临复杂多变的内外部环境,因此提出的策略需要根据具体情况调整和优化。 针对财务管理领域的专业学生和从业者,本文提供了一个关于应收账款管理问题的案例研究,具有实际指导意义。文章还探讨了信用管理和征信体系在应收账款管理中的作用,强调了它们对于提升企业信用风险控制和市场竞争能力的重要性。通过对比国内外企业在应收账款管理上的差异,文章总结了适合中国企业实际环境的应收账款管理方法和策略。" 根据提供的文件内容,以下是详细的知识点: 1. 应收账款管理的重要性:应收账款作为企业的一项重要资产,其有效管理关系到企业的现金流、财务健康以及市场竞争力。不良的应收账款管理会导致资金链断裂、坏账损失增加等问题,严重影响企业的正常运营和长远发展。 2. 应收账款的信用风险:在信用交易日益频繁的商业环境中,企业必须对客户信用进行评估,以便采取合理的信用政策,降低信用风险。 3. 合同管理的薄弱环节:合同是应收账款管理的法律基础,严格的合同管理能够保障企业权益,减少因合同问题导致的应收账款风险。 4. 客户信用调查:了解客户的信用状况对于预测和控制应收账款风险至关重要。企业需要建立有效的客户信用调查机制,识别和筛选信用良好的客户。 5. 应收账款回收策略:企业应建立有效的账款回收机制,包括定期的账款跟进、逾期账款的催收等。同时,建立专门的应收账款回收部门可以提升回收效率。 6. 应收账款管理流程优化:通过改进企业内部管理流程,如简化审批流程、提高工作效率等措施,能够提升应收账款的管理效率。 7. 应收账款管理策略的调整和优化:由于企业的内外部环境复杂多变,因此制定的管理策略需要根据实际情况进行动态调整和持续优化。 8. 信用管理和征信体系的作用:建立和完善企业内部信用管理体系和征信体系,有助于企业更好地控制信用风险,并在市场竞争中占据有利地位。 9. 对比国内外应收账款管理实践:通过研究国内外企业在应收账款管理上的不同做法和经验,可以借鉴先进的管理理念和方法,提升国内企业的应收账款管理水平。 综上所述,本文深入探讨了应收账款管理的多个方面,为RH公司乃至其他同类型企业提供了应收账款管理的改进方向和策略,对于财务管理专业的教育和实践都具有重要的参考价值。
recommend-type

新手别慌!用BingPi-M2开发板带你5分钟搞懂Tina Linux SDK目录结构

# 新手别慌!用BingPi-M2开发板带你5分钟搞懂Tina Linux SDK目录结构 第一次拿到BingPi-M2开发板时,面对Tina Linux SDK里密密麻麻的文件夹,我完全不知道从哪下手。就像走进一个陌生的大仓库,每个货架上都堆满了工具和零件,却找不到操作手册。这种困惑持续了整整两天,直到我意识到——理解目录结构比死记硬背每个文件更重要。 ## 1. 为什么SDK目录结构如此重要 想象你正在组装一台复杂的模型飞机。如果所有零件都混在一个箱子里,你需要花大量时间寻找每个螺丝和面板。但如果有分门别类的隔层,标注着"机身部件"、"电子设备"、"紧固件",组装效率会成倍提升。Ti