📈 线性回归 — 手写梯度下降

从数学原理到代码实现,理解回归的每一个细节

📖 线性回归基础

线性回归是最基本也最重要的统计学习方法。它假设因变量与自变量之间存在线性关系,用最小二乘法找到最佳拟合直线。

线性回归模型: 简单线性回归: 多元线性回归: y = wx + b y = w₁x₁ + w₂x₂ + ... + wₙxₙ + b 损失函数 (MSE): L = (1/n) Σᵢ (ŷᵢ - yᵢ)² 优化目标: min L = min (1/n) Σᵢ (w·xᵢ + b - yᵢ)² 求解方法: ├── 解析解: w = (XᵀX)⁻¹Xᵀy (小数据,精确) ├── 梯度下降: w ← w - α·∂L/∂w (大数据,迭代) ├── 随机梯度下降(SGD): 单样本梯度 (在线学习) └── Adam等自适应优化器 (深度学习常用)

五种回归评估指标

指标公式含义理想值
MSE(1/n)Σ(ŷ-y)²均方误差0
RMSE√MSE均方根误差(与y同量纲)0
MAE(1/n)Σ|ŷ-y|平均绝对误差0
1 - SS_res/SS_tot决定系数(解释方差比)1
Adjusted R²1-(1-R²)(n-1)/(n-p-1)调整决定系数(惩罚特征数)1

🔢 梯度下降详解

梯度下降是机器学习最核心的优化算法。沿着损失函数梯度的反方向更新参数,逐步逼近最优解。

梯度下降过程: 损失函数 L(w,b) = (1/n) Σ (wxᵢ+b - yᵢ)² 梯度计算: ∂L/∂w = (2/n) Σ (wxᵢ+b - yᵢ) · xᵢ ∂L/∂b = (2/n) Σ (wxᵢ+b - yᵢ) 参数更新: w ← w - α · ∂L/∂w b ← b - α · ∂L/∂b 学习率α的影响: α太小 → 收敛极慢 🐢 ████████→→→→ α适中 → 稳定收敛 🎯 ████→→→ ✓ α太大 → 震荡发散 💥 →←→←↗↘✗ 收敛判据: |L(t) - L(t-1)| < ε 或 梯度接近0
import numpy as np

np.random.seed(42)

# 生成数据: y = 2.5x + 3 + noise
n = 100
X = np.random.uniform(0, 10, n)
y = 2.5 * X + 3.0 + np.random.normal(0, 2, n)

# 标准化(梯度下降前必须做!)
X_mean, X_std = X.mean(), X.std()
y_mean, y_std = y.mean(), y.std()
X_norm = (X - X_mean) / X_std
y_norm = (y - y_mean) / y_std

# 梯度下降
w, b = 0.0, 0.0
lr = 0.01
losses = []

for epoch in range(1000):
    y_pred = w * X_norm + b
    error = y_pred - y_norm
    dw = (2/n) * np.dot(error, X_norm)
    db = (2/n) * np.sum(error)
    w -= lr * dw
    b -= lr * db
    losses.append(np.mean(error**2))

# 反标准化
w_orig = w * (y_std / X_std)
b_orig = y_mean - w_orig * X_mean

print(f"学习参数: w={w_orig:.4f} (真实2.5), b={b_orig:.4f} (真实3.0)")

# R²计算
y_pred_orig = w_orig * X + b_orig
ss_res = np.sum((y - y_pred_orig)**2)
ss_tot = np.sum((y - y_mean)**2)
r_squared = 1 - ss_res / ss_tot
print(f"R²: {r_squared:.4f}")  # 0.9403
print(f"MSE下降: {losses[0]:.4f} → {losses[-1]:.4f} (↓{(1-losses[-1]/losses[0])*100:.1f}%)")

🔬 解析解 vs 梯度下降

# 解析解 (Normal Equation): w = (XᵀX)⁻¹Xᵀy
X_with_bias = np.c_[np.ones(n), X]  # 添加偏置列
w_analytic = np.linalg.inv(X_with_bias.T @ X_with_bias) @ X_with_bias.T @ y
print(f"解析解: w={w_analytic[1]:.4f}, b={w_analytic[0]:.4f}")
print(f"梯度下降: w={w_orig:.4f}, b={b_orig:.4f}")
print(f"差异: Δw={abs(w_analytic[1]-w_orig):.6f}")

# 性质对比
print("\n--- 解析解 vs 梯度下降 ---")
print("解析解: O(n³) 一次计算,小数据精确")
print("梯度下降: O(n·k·iter) 迭代,大数据可扩展")
print("当n>10万时,解析解矩阵求逆极慢,梯度下降更优")

📊 多元线性回归

# 多元线性回归: y = w₁x₁ + w₂x₂ + b
np.random.seed(42)
n = 200
X_multi = np.random.randn(n, 3)
y_multi = 3 * X_multi[:, 0] - 2 * X_multi[:, 1] + 1.5 * X_multi[:, 2] + 5 + np.random.randn(n) * 0.5

# 解析解
X_bias = np.c_[np.ones(n), X_multi]
w_multi = np.linalg.inv(X_bias.T @ X_bias) @ X_bias.T @ y_multi
print(f"截距: {w_multi[0]:.4f}")
print(f"系数: {w_multi[1:]}")

# R²
y_pred_multi = X_bias @ w_multi
r2_multi = 1 - np.sum((y_multi - y_pred_multi)**2) / np.sum((y_multi - y_multi.mean())**2)
print(f"R²: {r2_multi:.4f}")

# Adjusted R²
p = X_multi.shape[1]
adj_r2 = 1 - (1 - r2_multi) * (n - 1) / (n - p - 1)
print(f"Adjusted R²: {adj_r2:.4f}")

🛡️ 回归诊断

假设检验方法违反时的处理
线性关系残差图(应随机分布)多项式回归/非线性变换
误差独立Durbin-Watson检验时间序列模型
同方差性Breusch-Pagan检验加权最小二乘/对数变换
正态性Shapiro-Wilk/Q-Q图大样本下可放宽
无多重共线性VIF < 10岭回归/Lasso/PCA
# 多重共线性检测 (VIF)
from numpy.linalg import inv

def calc_vif(X, feature_idx):
    """计算第feature_idx个特征的VIF"""
    y_vif = X[:, feature_idx]
    X_others = np.delete(X, feature_idx, axis=1)
    X_bias = np.c_[np.ones(len(y_vif)), X_others]
    beta = inv(X_bias.T @ X_bias) @ X_bias.T @ y_vif
    y_pred = X_bias @ beta
    ss_res = np.sum((y_vif - y_pred)**2)
    ss_tot = np.sum((y_vif - y_vif.mean())**2)
    r2 = 1 - ss_res / ss_tot
    return 1 / (1 - r2) if r2 < 1 else float('inf')

for i in range(X_multi.shape[1]):
    vif = calc_vif(X_multi, i)
    flag = '⚠️ 高共线性' if vif > 10 else '✅ OK'
    print(f"特征{i} VIF: {vif:.2f} {flag}")

📐 2024-2025 线性回归新视角

🧮 正则化回归对比

方法损失函数特点
OLSMSE无正则化,可能过拟合
Ridge (L2)MSE + αΣwᵢ²收缩系数,不稀疏
Lasso (L1)MSE + αΣ|wᵢ|特征选择,稀疏解
ElasticNetMSE + α₁Σ|wᵢ| + α₂Σwᵢ²兼顾稀疏和分组效应
# Ridge回归手动实现
def ridge_regression(X, y, alpha=1.0):
    """w = (XᵀX + αI)⁻¹Xᵀy"""
    X_bias = np.c_[np.ones(len(y)), X]
    n_features = X_bias.shape[1]
    I = np.eye(n_features)
    I[0, 0] = 0  # 不正则化截距
    w = inv(X_bias.T @ X_bias + alpha * I) @ X_bias.T @ y
    return w

for alpha in [0, 0.1, 1, 10, 100]:
    w_ridge = ridge_regression(X_multi, y_multi, alpha)
    print(f"α={alpha:6.1f}: 截距={w_ridge[0]:.3f}, 系数={w_ridge[1:].round(3)}")
🏆 成就解锁:手写梯度下降R²>0.9
从零实现梯度下降线性回归,学习参数w=2.4080(真实2.5)、b=3.4302(真实3.0),R²=0.9403,远超0.9目标!
Python验证通过 — 梯度下降1000轮后MSE下降94%;学习参数w=2.4080 (真实2.5), b=3.4302 (真实3.0);R²=0.9403 > 0.9目标;解析解与梯度下降结果一致。
思考题:
1. 为什么要标准化后再做梯度下降?不标准化会怎样?
2. R²=0.95就一定比R²=0.85的模型好吗?
3. L1正则化为什么能产生稀疏解?从几何角度解释。
4. 如果残差图呈现"漏斗形",说明什么问题?

📝 课后练习

  1. 实现SGD和Mini-batch SGD,与全批量梯度下降对比收敛速度
  2. 实现Lasso回归的坐标下降算法
  3. 用多项式回归拟合非线性数据,分析过拟合
  4. 实现交叉验证选择最优正则化系数α
  5. 用线性回归预测波士顿房价,完整Pipeline
📚 参考资料:
• The Elements of Statistical Learning (Hastie et al., 2009)
• Pattern Recognition and Machine Learning (Bishop, 2006)
• sklearn.linear_model 官方文档
• Causal Inference: The Mixtape (Cunningham, 2021)