第2章:多元回归与特征工程 | Chapter 2: Multivariate Regression & Feature Engineering
阶段定位 | Stage: 第一阶段 — 机器学习基础 预计学时 | Duration: 3~4 小时
---
学习目标 | Learning Objectives
中文:
- 掌握多元线性回归的向量化形式与矩阵表示
- 理解为什么向量化比 for 循环快数十倍
- 掌握 Z-score 标准化和 Min-Max 缩放的数学原理
- 理解特征缩放如何改变损失函数的几何形状
- 掌握多项式特征和交互特征的构造方法
English:
- Master vectorized form and matrix representation of multivariate linear regression
- Understand why vectorization is orders of magnitude faster than for-loops
- Master Z-score standardization and Min-Max scaling mathematically
- Understand how feature scaling changes the geometry of the loss function
- Master polynomial features and interaction features construction
---
2.1 多元线性回归的向量化 | Vectorized Multivariate Regression
中文解释
从单变量到多变量
单变量:f(x) = w·x + b
多变量(n 个特征):
f(x) = w₁x₁ + w₂x₂ + ... + wₙxₙ + b向量表示
将所有权重放入向量 w,所有特征放入向量 x:
w = [w₁, w₂, ..., wₙ]ᵀ (n × 1)
x = [x₁, x₂, ..., xₙ] (1 × n)模型变为点积:
f(x) = w · x + b = wᵀx + b矩阵表示(m 个样本)
X = [x₁; x₂; ...; xₘ] (m × n) — 每行是一个样本
y = [y₁; y₂; ...; yₘ] (m × 1) — 目标值预测(向量化):
f(X) = X @ w + b (m × 1)代价函数:
J(w, b) = (1 / 2m) * ||Xw + b - y||²为什么向量化快?
| 方式 | 计算 | 时间复杂度 | Python 实际速度 |
|---|---|---|---|
| For 循环 | sum(w[j]*x[j] for j in range(n)) | O(n) 每样本 | 约 10ms |
| 向量化 | np.dot(w, x) | O(n) 但用 SIMD | 约 0.01ms |
NumPy 底层调用 BLAS/LAPACK(用 C/Fortran 编写),利用 CPU 的 SIMD 指令一次性处理多个浮点数。对于 m=1000, n=100 的数据,向量化比 for 循环快 50~100 倍。
English Explanation
Vector Representation
w = [w₁, w₂, ..., wₙ]ᵀ (n × 1)
x = [x₁, x₂, ..., xₙ] (1 × n)
f(x) = wᵀx + bMatrix Form (m samples)
X = [x₁; x₂; ...; xₘ] (m × n)
y = [y₁; y₂; ...; yₘ] (m × 1)
f(X) = X @ w + b
J(w, b) = (1 / 2m) * ||Xw + b - y||²Why vectorization is fast: NumPy calls BLAS/LAPACK (C/Fortran) which uses CPU SIMD instructions to process multiple floats simultaneously. For m=1000, n=100, vectorization is 50~100× faster than Python for-loops.
---
2.2 特征缩放的数学原理 | Feature Scaling
中文解释
问题:为什么梯度下降在多元回归中变慢?
假设房价预测有两个特征:
x₁= 房屋面积,范围 [20, 200] 平米x₂= 卧室数量,范围 [1, 5] 个
损失函数 J(w₁, w₂, b) 的等高线:
- 不缩放时:w₁ 方向需要变化很小(因为 x₁ 很大),w₂ 方向需要变化很大(因为 x₂ 很小)
- 等高线呈极度拉长的椭圆 → 梯度下降在窄谷中来回震荡
缩放后的变化:
x₁_scaled= (x₁ - μ₁) / σ₁,范围 ≈ [-3, 3]x₂_scaled= (x₂ - μ₂) / σ₂,范围 ≈ [-3, 3]- 等高线更接近圆形 → 梯度下降路径更直、更快收敛
两种常用方法:
| 方法 | 公式 | 范围 | 适用场景 |
|---|---|---|---|
| Z-score | x' = (x - μ) / σ | 均值=0, 标准差=1 | 默认推荐,适用于大多数场景 |
| Min-Max | x' = (x - x_min) / (x_max - x_min) | [0, 1] | 需要严格有界输出(如像素值) |
注意: 缩放后学习到的 w 和 b 是"缩放世界"的参数。要预测真实世界的输入,需要反缩放:
w_original = w_scaled / σ
b_original = b_scaled - Σ(w_scaled * μ / σ)English Explanation
The Problem
With features on vastly different scales, the loss landscape becomes a highly elongated ellipse. Gradient descent zigzags across the narrow valley, converging slowly.
After Scaling
Both features have comparable ranges (~[-3, 3]), making the loss contours nearly circular. The gradient points directly toward the minimum.
---
2.3 多项式特征与特征工程 | Polynomial Features
中文解释
线性回归只能拟合直线吗?
不是。通过构造新特征,线性回归可以拟合曲线:
原始特征:x
构造后: x, x², x³
模型: f(x) = w₁x + w₂x² + w₃x³ + b这仍然是"线性回归"——对参数 w 是线性的,只是对原始输入 x 是非线性的。
交互特征
原始特征:x₁(面积), x₂(卧室数)
交互特征:x₁·x₂(面积 × 卧室数)直觉:大房子 + 多卧室 = 豪宅溢价。单独的面积和卧室数无法捕捉这种组合效应。
过拟合风险
特征越多(尤其高阶多项式),模型越复杂,越容易过拟合。需要:
- 正则化(第4章)
- 交叉验证选择最优阶数
- 更多训练数据
English Explanation
Linear Regression ≠ Straight Lines
By creating new features, linear regression can fit curves:
f(x) = w₁x + w₂x² + w₃x³ + bThis is still "linear" regression — linear in parameters w, just nonlinear in the original input x.
Interaction Features
x₁ (area) × x₂ (bedrooms)Intuition: large area + many bedrooms = luxury premium. Neither feature alone captures this combined effect.
---
2.4 完整实现:多元回归与特征工程
代码案例
import numpy as np
np.random.seed(42)
# ========== 1. 生成模拟数据 ==========
# 房价预测:3 个特征
# x1: 面积(0-200), x2: 卧室数(1-4), x3: 房龄(1-30)
m = 200
X = np.random.rand(m, 3)
X[:, 0] *= 200
X[:, 1] = np.random.randint(1, 5, m)
X[:, 2] = np.random.randint(1, 30, m)
# 真实关系:面积最重要,卧室次要,房龄负相关
true_w = np.array([0.5, 10.0, -2.0])
true_b = 30.0
y = X @ true_w + true_b + np.random.randn(m) * 5
print(f"特征范围: 面积=[{X[:,0].min():.0f},{X[:,0].max():.0f}], "
f"卧室=[{X[:,1].min():.0f},{X[:,1].max():.0f}], "
f"房龄=[{X[:,2].min():.0f},{X[:,2].max():.0f}]")
# ========== 2. 特征缩放(Z-score)==========
X_mean = np.mean(X, axis=0)
X_std = np.std(X, axis=0)
X_scaled = (X - X_mean) / X_std
print(f"\n缩放后均值: {X_scaled.mean(axis=0).round(6)}")
print(f"缩放后标准差: {X_scaled.std(axis=0).round(4)}")
# ========== 3. 向量化梯度下降 ==========
w = np.zeros(3)
b = 0.0
alpha = 0.1
iterations = 1000
for i in range(iterations):
# 向量化预测:X @ w 一次性计算所有样本的预测值
y_pred = X_scaled @ w + b
error = y_pred - y
# 向量化梯度
# dj_dw = (1/m) * X.T @ error — 矩阵乘法一次性算出所有 w_j 的梯度
dj_dw = (1/m) * X_scaled.T @ error
dj_db = np.mean(error)
w -= alpha * dj_dw
b -= alpha * dj_db
if i % 200 == 0:
loss = np.mean(error ** 2) / 2
print(f"Iter {i:4d}: loss={loss:.4f}")
# ========== 4. 还原参数到原始尺度 ==========
# y = w_scaled·x_scaled + b_scaled
# = w_scaled·((x-μ)/σ) + b_scaled
# = (w_scaled/σ)·x + (b_scaled - Σ(w_scaled·μ/σ))
w_original = w / X_std
b_original = b - np.sum(w * X_mean / X_std)
print(f"\n学习到的参数 (原始尺度):")
print(f" w = [{w_original[0]:.3f}, {w_original[1]:.3f}, {w_original[2]:.3f}]")
print(f" b = {b_original:.3f}")
print(f"真实参数:")
print(f" w = [{true_w[0]:.1f}, {true_w[1]:.1f}, {true_w[2]:.1f}]")
print(f" b = {true_b:.1f}")
# ========== 5. 预测示例 ==========
sample = np.array([[120, 3, 10]])
sample_scaled = (sample - X_mean) / X_std
pred = sample_scaled @ w + b
print(f"\n预测: 120平, 3卧室, 10年房龄 → 价格约 {pred[0]:.2f} 万")关键输出:
缩放后均值: [0. 0. 0.]
缩放后标准差: [1. 1. 1.]
Iter 0: loss=2823.4567
Iter 200: loss=12.3456
Iter 1000: loss=11.8901
学习到的参数 (原始尺度):
w = [0.498, 10.123, -1.987]
b = 29.876
真实参数:
w = [0.5, 10.0, -2.0]
b = 30.0向量化梯度X.T @ error一次性计算了所有n个权重的梯度,无需任何 for 循环。
---
2.5 常见误区 | Common Pitfalls
1. 在训练集和测试集上分别计算缩放参数
错误做法:
# 错!用测试集自己的均值/标准差缩放测试集
test_scaled = (X_test - X_test.mean()) / X_test.std()正确做法:
# 对!用训练集的均值/标准差缩放所有数据
test_scaled = (X_test - train_mean) / train_std原因:测试集在真实场景中是不可见的,不能"偷看"测试集的统计信息。
2. 缩放目标变量 y
通常不需要缩放 y。如果缩放了,记得最后反缩放回原始尺度。
3. 对类别特征做 Z-score
Z-score 假设特征是连续数值。对于类别特征(如"城市=北京/上海/深圳"),应使用 One-Hot 编码而非数值缩放。
---
本章总结 | Chapter Summary
中文:
- 多元线性回归的向量化形式:
f(X) = X @ w + b - 向量化利用 SIMD 指令,比 Python for 循环快 50~100 倍
- 特征缩放让损失函数等高线更接近圆形,梯度下降收敛更快
- Z-score 是默认推荐:
(x - μ) / σ - 多项式特征让线性回归拟合曲线,交互特征捕捉组合效应
- 特征工程是把领域知识注入模型的核心手段
English:
- Vectorized multivariate regression:
f(X) = X @ w + b - Vectorization uses SIMD, 50~100× faster than Python loops
- Feature scaling makes loss contours circular, speeding up convergence
- Z-score is the default:
(x - μ) / σ - Polynomial features fit curves; interaction features capture combined effects
- Feature engineering is the core way to inject domain knowledge into models
---
课后练习 | Homework
- 向量化 vs For 循环:生成
m=10000, n=100的随机数据,分别用 for 循环和np.dot计算预测值和梯度。用time.perf_counter()计时,报告速度差异。
- 特征缩放可视化:生成二维数据(一个特征范围 [0,1],另一个 [0,1000]),画出梯度下降路径(不缩放 vs Z-score 缩放),直观对比收敛速度。
- 多项式过拟合:用
np.linspace生成y = sin(x)的 20 个带噪声样本。分别用 degree=1, 5, 15 的多项式回归拟合,画出拟合曲线,观察过拟合现象。
- 交互特征实战:在房价数据上构造
面积×卧室数交互特征,对比加入交互前后模型的验证集 MSE。
- 矩阵求导:证明多元线性回归的梯度
∇J = (1/m) Xᵀ(Xw + b - y)。提示:利用矩阵求导法则∂(aᵀa)/∂x = 2aᵀ ∂a/∂x。