机器学习数学推导(五)线性回归
Chen Kai BOSS

1886 年, Francis Galton 研究父母身高与子女身高的关系时,发现了一个奇特现象:极端身高的父母,其子女身高往往更接近平均值。他创造了"regression toward the mean"(向均值回归)这个术语,这就是"回归"一词的由来。但线性回归的真正力量不在于统计描述,而在于它是几乎所有机器学习算法的数学基础——从神经网络到支持向量机,都可以看作线性回归的推广。

线性回归的本质是在数据空间中寻找最优的超平面。这个看似简单的问题,背后隐藏着线性代数、概率论、优化理论的深刻联系。本章从多个角度完整推导线性回归的数学理论。

线性回归的基本形式

问题定义

预测房价的例子

场景:你是房产中介,要预测房价

历史数据: | 面积(㎡) | 距地铁(km) | 楼层 | 房价(万) | |---------|-----------|------|---------| | 100 | 0.5 | 10 | 300 | | 80 | 2.0 | 5 | 200 | | 120 | 0.2 | 15 | 450 |

直觉发现: - 面积越大 → 房价越高(正相关) - 距地铁越远 → 房价越低(负相关) - 楼层影响较小

线性回归的假设:房价和这些因素是"线性关系"(直线关系)

关键问题:如何找到最好的系数(3, -50, 2, 50)?

数学形式化

给定训练数据集,其中:

-维输入向量(特征) - 例如:(面积、距离、楼层) - :标量输出(目标值) - 例如:(房价300万)

目标:找到参数向量和偏置,使得线性模型:

能够最好地拟合训练数据。

具体例子

记号简化:为了统一表示,我们将偏置吸收到权重向量中。定义增广特征向量:

则模型简化为:

后续为简洁起见,我们省略波浪号,直接写为,理解为已包含常数项 1 。

矩阵形式

将所有训练样本组织成矩阵形式:

设计矩阵(Design Matrix):

输出向量

预测向量

我们的目标是找到最优的使得预测向量尽可能接近真实值

最小二乘法:代数推导

损失函数

使用平方损失( L2 损失)衡量预测误差:

这里系数是为了求导时消去常数。

目标

梯度推导

计算损失函数关于的梯度。使用链式法则:

展开:

求导(使用矩阵求导公式):

因此:

正规方程

令梯度为零:

这就是著名的正规方程( Normal Equation)。

定理 1(最小二乘解):如果可逆,则最小二乘问题的唯一解为:

证明

  1. 一阶必要条件给出
  2. 二阶充分条件:计算 Hessian 矩阵:

对于任意非零向量

如果列满秩(即),则当且仅当,因此正定。

  1. 正定 Hessian + 零梯度 = 全局最小值。证毕。

矩阵的可逆条件

  • 充要条件列满秩,即 - 等价条件正定
  • 实际意义
    • 样本数(样本数至少要多于特征数)
    • 特征之间线性无关(无完全共线性)

Moore-Penrose 伪逆

不可逆时(例如或特征共线),使用伪逆( Pseudoinverse):

其中是 Moore-Penrose 伪逆。

性质

  • 可逆时,(退化为普通逆) -是所有满足的解中范数最小的解:

计算方法:通过奇异值分解( SVD)。设,则:

其中 ^+的伪逆(非零奇异值取倒数,零保持为零)。

几何解释:投影视角

列空间与投影

线性回归的几何本质是正交投影

定义

-的列空间(Column Space),即的列向量张成的子空间 -中的向量 - 目标是找到中距离最近的点

定理 2(正交投影定理)上的正交投影,当且仅当残差向量正交:

这正是正规方程!

证明

对于任意,考虑预测误差的平方范数:

使用毕达哥拉斯定理(当两向量正交时):

。如果,则:

因此:

等号成立当且仅当。证毕。

投影矩阵

定义:投影矩阵将任意向量投影到

性质

  1. 幂等性(投影两次等于投影一次)

  2. 对称性

  3. 作用在列空间的投影

残差投影矩阵

满足:

性质

-(幂等) -(对称) -(残差与列空间正交)

几何直觉

维空间中:

-是真实输出向量 - 维子空间(如果列满秩) - 的"影子" - 是垂直于的残差

类比( 2 维平面投影):

想象在 3 维空间中,一个点到 2 维平面的垂直距离。投影点使得距离最小。

下图直观展示了最小二乘法的几何本质——通过正交投影将真实向量投影到列空间中,残差向量与列空间正交:

Least Squares Geometric Interpretation

下图展示了 OLS 作为正交投影的三维视角——观测向量被投影到设计矩阵的列空间上,投影点就是最优预测,残差向量与列空间正交:

Projection Geometry

概率视角:最大似然估计

线性高斯模型

假设数据生成过程为:

其中是独立同分布的高斯噪声。

等价形式

即给定服从均值为、方差为的高斯分布。

似然函数

给定训练数据,参数的似然函数为:

对数似然

取对数(单调变换不改变最大值点):

最大似然估计

关于的优化

最大化等价于最小化:

这正是最小二乘目标!

定理 3:在线性高斯模型下,最大似然估计等价于最小二乘估计:

关于的优化

固定,对求偏导:

解得:

即噪声方差的最大似然估计是残差平方和的均值。

贝叶斯视角

引入参数的先验分布,通过贝叶斯公式计算后验分布:

高斯先验:假设,则:

最大化后验概率(MAP)等价于最小化:

这正是岭回归( Ridge Regression)的目标函数!正则化项 = ^2/反映了先验信念的强度。

正则化: Ridge 回归与 Lasso

Ridge 回归( L2 正则化)

目标函数

其中是正则化参数。

梯度

令梯度为零:

解析解

关键观察

  • 添加保证了的可逆性(即使不可逆)
  • ,退化为普通最小二乘
  • (极端正则化)

矩阵视角:Ridge 回归通过添加对角项"稳定"了矩阵,避免了病态问题。

Lasso 回归( L1 正则化)

目标函数

其中是 L1 范数。

特点

  • 无解析解( L1 范数不可微)
  • 需要迭代算法求解(如坐标下降、近端梯度)
  • 稀疏性:部分参数会被精确压缩到 0,实现特征选择

几何解释

在约束形式下:

L1 约束球是菱形(在高维中是超菱形),其尖角更容易与等高线相交于坐标轴上,导致稀疏解。

Elastic Net

结合 L1 和 L2:

优点

  • 保留 L1 的稀疏性
  • 保留 L2 的稳定性(对共线特征友好)

正则化的效果

偏差-方差权衡

  • 无正则化():低偏差、高方差(过拟合)
  • 强正则化(大):高偏差、低方差(欠拟合)
  • 最优:通过交叉验证选择

岭迹图(Ridge Trace):

绘制变化的曲线,观察参数的收缩行为。

下图对比了 Ridge 和 Lasso 正则化的效果—— Ridge 使系数逐渐收缩但不为零,而 Lasso 可以将系数精确压缩到 0,实现特征选择的稀疏性:

Regularization Path Comparison

下图详细对比了 Ridge(L2)和 Lasso(L1)正则化的效果——左图展示 Ridge 系数随增大趋向零但不变为零;中图展示 L1 和 L2 约束区域的几何差异(L1 的菱形角点导致稀疏解);右图展示偏差-方差权衡,最优在两者之间取得平衡:

Regularization Comparison

梯度下降算法

批量梯度下降( BGD)

当数据量大时,直接计算的代价过高()。使用迭代算法。

算法

  1. 初始化
  2. 重复直到收敛:

其中是学习率,梯度为:

完整形式

计算复杂度:每次迭代(矩阵-向量乘法)。

随机梯度下降( SGD)

每次只使用一个样本更新参数。

算法

  1. 初始化
  2. 对于
    • 随机选择样本
    • 更新:

优点

  • 每次迭代快(
  • 适合大规模数据
  • 有逃离局部最小值的能力(对于非凸问题)

缺点

  • 收敛不稳定(高方差)
  • 需要精心调整学习率

小批量梯度下降( Mini-batch GD)

折中方案:每次使用个样本( batch size)。

其中是第次迭代的批次(大小为)。

典型选择

收敛性分析

定理 4(BGD 收敛性):对于学习率,批量梯度下降线性收敛到最优解:

其中的最小和最大特征值。

证明思路

  1. 损失函数是强凸的( Hessian 正定)
  2. 梯度 Lipschitz 连续
  3. 应用强凸函数的收敛定理

实践建议

  • 学习率:(保守选择)
  • 自适应学习率:Adam、RMSprop 等
  • 学习率衰减:

下图展示了不同学习率下梯度下降的收敛轨迹:学习率太小收敛慢,太大可能震荡,适中的学习率能快速稳定收敛到最优点:

Gradient Descent Convergence

下面的动画展示了梯度下降拟合线性回归模型的完整过程——左图展示数据点与当前拟合直线,右图展示损失函数随迭代下降的曲线:

Gradient Descent Animation

模型评估与选择

评价指标

均方误差(MSE)

均方根误差(RMSE)

平均绝对误差(MAE)

决定系数(R²)

其中是均值。

解释

-:完美拟合 -:模型等价于预测均值 -:模型比均值还差(罕见,说明模型有问题)

调整 R²

考虑模型复杂度的惩罚:

优点:添加无用特征时,会下降(而总是增加)。

下图展示了线性回归模型的四种经典残差诊断图——通过残差 vs 拟合值、Q-Q 图、Scale-Location 图和残差分布直方图来检验模型假设是否成立:

Residual Diagnostics

交叉验证

k 折交叉验证

  1. 将数据分为
  2. 对于
    • 使用除第份外的数据训练
    • 在第份上测试
  3. 平均个测试误差

Python 实现

1
2
3
4
5
6
7
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LinearRegression

scores = cross_val_score(LinearRegression(), X, y, cv=5,
scoring='neg_mean_squared_error')
rmse_scores = np.sqrt(-scores)
print(f"RMSE: {rmse_scores.mean():.4f} ± {rmse_scores.std():.4f}")

完整代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

class LinearRegression:
"""
线性回归的完整实现

支持:
- 解析解(正规方程)
- 批量梯度下降
- 随机梯度下降
- Ridge 正则化
"""

def __init__(self, method='normal', alpha=0.01, n_iterations=1000,
lambda_reg=0.0, batch_size=None, random_state=42):
"""
参数:
method: str, 求解方法
'normal': 正规方程
'bgd': 批量梯度下降
'sgd': 随机梯度下降
'mini_batch': 小批量梯度下降
alpha: float, 学习率(仅用于梯度下降)
n_iterations: int, 迭代次数
lambda_reg: float, Ridge 正则化参数
batch_size: int, 批次大小(仅用于 mini_batch)
random_state: int, 随机种子
"""
self.method = method
self.alpha = alpha
self.n_iterations = n_iterations
self.lambda_reg = lambda_reg
self.batch_size = batch_size
self.random_state = random_state
self.w = None
self.loss_history = []

def fit(self, X, y):
"""
训练线性回归模型

参数:
X: np.array, shape=(m, d), 输入特征
y: np.array, shape=(m,), 输出标签
"""
# 添加偏置项
X_bias = self._add_bias(X)
m, d = X_bias.shape

# 初始化权重
np.random.seed(self.random_state)
self.w = np.random.randn(d) * 0.01

if self.method == 'normal':
# 正规方程
self.w = self._normal_equation(X_bias, y)
elif self.method == 'bgd':
# 批量梯度下降
self._batch_gradient_descent(X_bias, y)
elif self.method == 'sgd':
# 随机梯度下降
self._stochastic_gradient_descent(X_bias, y)
elif self.method == 'mini_batch':
# 小批量梯度下降
if self.batch_size is None:
self.batch_size = min(32, m)
self._mini_batch_gradient_descent(X_bias, y)
else:
raise ValueError(f"Unknown method: {self.method}")

return self

def predict(self, X):
"""
预测

参数:
X: np.array, shape=(m, d)

返回:
y_pred: np.array, shape=(m,)
"""
X_bias = self._add_bias(X)
return X_bias @ self.w

def score(self, X, y):
"""
计算 R ²分数
"""
y_pred = self.predict(X)
ss_res = np.sum((y - y_pred) ** 2)
ss_tot = np.sum((y - np.mean(y)) ** 2)
return 1 - ss_res / ss_tot

def _add_bias(self, X):
"""添加偏置列"""
return np.c_[X, np.ones(X.shape[0])]

def _compute_loss(self, X, y):
"""计算损失(包括正则化)"""
m = len(y)
predictions = X @ self.w
mse = np.mean((predictions - y) ** 2)
reg = self.lambda_reg * np.sum(self.w[:-1] ** 2) # 不正则化偏置
return mse + reg

def _compute_gradient(self, X, y):
"""计算梯度"""
m = len(y)
predictions = X @ self.w
gradient = X.T @ (predictions - y) / m
# 添加正则化梯度(不包括偏置)
if self.lambda_reg > 0:
reg_gradient = np.zeros_like(self.w)
reg_gradient[:-1] = 2 * self.lambda_reg * self.w[:-1]
gradient += reg_gradient
return gradient

def _normal_equation(self, X, y):
"""正规方程求解"""
# w = (X^T X + lambda*I)^{-1} X^T y
d = X.shape[1]
reg_matrix = self.lambda_reg * np.eye(d)
reg_matrix[-1, -1] = 0 # 不正则化偏置

try:
w = np.linalg.solve(X.T @ X + reg_matrix, X.T @ y)
except np.linalg.LinAlgError:
# 如果矩阵奇异,使用伪逆
w = np.linalg.pinv(X.T @ X + reg_matrix) @ X.T @ y

return w

def _batch_gradient_descent(self, X, y):
"""批量梯度下降"""
for i in range(self.n_iterations):
gradient = self._compute_gradient(X, y)
self.w -= self.alpha * gradient

# 记录损失
if i % 10 == 0:
loss = self._compute_loss(X, y)
self.loss_history.append(loss)

def _stochastic_gradient_descent(self, X, y):
"""随机梯度下降"""
m = len(y)
np.random.seed(self.random_state)

for i in range(self.n_iterations):
# 随机选择一个样本
idx = np.random.randint(m)
X_i = X[idx:idx+1]
y_i = y[idx:idx+1]

gradient = self._compute_gradient(X_i, y_i)
self.w -= self.alpha * gradient

# 记录损失(每 10 次迭代)
if i % 10 == 0:
loss = self._compute_loss(X, y)
self.loss_history.append(loss)

def _mini_batch_gradient_descent(self, X, y):
"""小批量梯度下降"""
m = len(y)
np.random.seed(self.random_state)

for i in range(self.n_iterations):
# 随机选择批次
indices = np.random.choice(m, self.batch_size, replace=False)
X_batch = X[indices]
y_batch = y[indices]

gradient = self._compute_gradient(X_batch, y_batch)
self.w -= self.alpha * gradient

# 记录损失
if i % 10 == 0:
loss = self._compute_loss(X, y)
self.loss_history.append(loss)


# 示例:波士顿房价预测
def demo_linear_regression():
"""
完整示例:线性回归在波士顿房价数据上的应用
"""
# 生成合成数据(模拟房价预测)
np.random.seed(42)
m = 500
d = 5

# 真实权重
w_true = np.array([50, -20, 30, 15, -10, 200]) # 最后一个是偏置

# 生成特征(标准化)
X = np.random.randn(m, d)

# 添加偏置并计算真实值
X_bias = np.c_[X, np.ones(m)]
y_true = X_bias @ w_true

# 添加噪声
noise = np.random.randn(m) * 20
y = y_true + noise

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)

# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 比较不同方法
methods = {
'Normal Equation': LinearRegression(method='normal'),
'Batch GD': LinearRegression(method='bgd', alpha=0.1, n_iterations=1000),
'SGD': LinearRegression(method='sgd', alpha=0.01, n_iterations=2000),
'Mini-batch GD': LinearRegression(method='mini_batch', alpha=0.05,
n_iterations=1000, batch_size=32)
}

results = {}

print("=" * 70)
print("线性回归方法比较")
print("=" * 70)

for name, model in methods.items():
# 训练
model.fit(X_train_scaled, y_train)

# 评估
train_score = model.score(X_train_scaled, y_train)
test_score = model.score(X_test_scaled, y_test)
y_pred = model.predict(X_test_scaled)
rmse = np.sqrt(np.mean((y_test - y_pred) ** 2))

results[name] = {
'train_r2': train_score,
'test_r2': test_score,
'rmse': rmse,
'weights': model.w
}

print(f"\n{name}:")
print(f" 训练 R ²: {train_score:.4f}")
print(f" 测试 R ²: {test_score:.4f}")
print(f" 测试 RMSE: {rmse:.2f}")

# 可视化:损失曲线
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 损失曲线
ax = axes[0]
for name in ['Batch GD', 'SGD', 'Mini-batch GD']:
model = methods[name]
if len(model.loss_history) > 0:
ax.plot(model.loss_history, label=name, alpha=0.7)
ax.set_xlabel('Iteration (× 10)')
ax.set_ylabel('Loss')
ax.set_title('Convergence of Gradient Descent Methods')
ax.legend()
ax.grid(True, alpha=0.3)

# 预测 vs 真实值
ax = axes[1]
model = methods['Normal Equation']
y_pred = model.predict(X_test_scaled)
ax.scatter(y_test, y_pred, alpha=0.5)
ax.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()],
'r--', lw=2, label='Perfect Prediction')
ax.set_xlabel('True Values')
ax.set_ylabel('Predictions')
ax.set_title('Predictions vs True Values')
ax.legend()
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('linear_regression_demo.png', dpi=150)
plt.show()

# Ridge 回归:正则化效果
print("\n" + "=" * 70)
print("Ridge 回归:正则化参数选择")
print("=" * 70)

lambdas = [0, 0.01, 0.1, 1, 10, 100]
train_scores = []
test_scores = []

for lam in lambdas:
model = LinearRegression(method='normal', lambda_reg=lam)
model.fit(X_train_scaled, y_train)
train_scores.append(model.score(X_train_scaled, y_train))
test_scores.append(model.score(X_test_scaled, y_test))
print(f"λ={lam:6.2f}: 训练 R ²={train_scores[-1]:.4f}, "
f"测试 R ²={test_scores[-1]:.4f}")

# 可视化正则化效果
plt.figure(figsize=(10, 6))
plt.plot(lambdas, train_scores, 'o-', label='Training R ²', linewidth=2)
plt.plot(lambdas, test_scores, 's-', label='Testing R ²', linewidth=2)
plt.xscale('log')
plt.xlabel('Regularization Parameter λ')
plt.ylabel('R ² Score')
plt.title('Effect of Ridge Regularization')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('ridge_regularization.png', dpi=150)
plt.show()


if __name__ == "__main__":
demo_linear_regression()

代码输出示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
======================================================================
线性回归方法比较
======================================================================

Normal Equation:
训练 R ²: 0.9452
测试 R ²: 0.9387
测试 RMSE: 19.87

Batch GD:
训练 R ²: 0.9452
测试 R ²: 0.9387
测试 RMSE: 19.87

SGD:
训练 R ²: 0.9449
测试 R ²: 0.9385
测试 RMSE: 19.91

Mini-batch GD:
训练 R ²: 0.9451
测试 R ²: 0.9386
测试 RMSE: 19.88

======================================================================
Ridge 回归:正则化参数选择
======================================================================
λ= 0.00: 训练 R ²=0.9452, 测试 R ²=0.9387
λ= 0.01: 训练 R ²=0.9452, 测试 R ²=0.9387
λ= 0.10: 训练 R ²=0.9451, 测试 R ²=0.9388
λ= 1.00: 训练 R ²=0.9442, 测试 R ²=0.9391
λ= 10.00: 训练 R ²=0.9361, 测试 R ²=0.9372
λ=100.00: 训练 R ²=0.8647, 测试 R ²=0.8723

❓ Q&A:线性回归核心问题

Q1:为什么使用平方损失而不是绝对值损失?

数学原因

  1. 可微性处处可微,便于优化
  2. 解析解:平方损失导致二次优化问题,有闭式解
  3. 统计意义:在高斯噪声假设下,平方损失对应最大似然估计

绝对值损失( L1 损失):

  • 优点:对异常值鲁棒( outliers 影响小)
  • 缺点:在 0 点不可微,无解析解,需要线性规划求解

对比

损失函数 可微性 解析解 异常值鲁棒性 对应噪声分布
平方( L2) 处处可微 高斯分布
绝对值( L1) 0 点不可微 拉普拉斯分布
Huber 处处可微 中等 混合分布

Huber 损失(折中方案):

时是二次的,时是线性的,结合了两者优点。


Q2:正规方程 vs 梯度下降,什么时候用哪个?

决策树

1
2
3
4
5
6
7
8
9
10
数据规模?
├─ 小规模 (m < 10000, d < 1000)
│ └─ 使用正规方程(速度快,精度高)
└─ 大规模 (m > 10000 或 d > 1000)
├─ X^T X 可逆?
│ ├─ 是 → 梯度下降
│ └─ 否 → Ridge 回归或梯度下降
└─ 内存够吗?
├─ 够 → 批量梯度下降
└─ 不够 → SGD 或 Mini-batch GD

详细对比

维度 正规方程 梯度下降
时间复杂度 是迭代次数)
空间复杂度
收敛性 一步到位 需要多次迭代
超参数 学习率、迭代次数
特征数量 可行 任意大
可逆性要求 可逆
正则化 容易加入 容易加入
在线学习 不支持 支持( SGD)

实践建议

  • 默认选择用正规方程,否则用梯度下降
  • 大数据:必须用 SGD 或 Mini-batch GD
  • 实时更新:必须用 SGD(支持在线学习)

Q3:为什么 Ridge 回归的解总是存在?

核心原因:添加使矩阵正定。

定理:对于任意,矩阵是正定的,因此可逆。

证明

对于任意非零向量

因为,所以 |v|^2 > 0使X v = 0vX

因此正定,可逆。

直觉

  • 可能奇异(例如特征共线)
  • 添加相当于在对角线上加上一个"安全垫"
  • 即使某些特征完全相关,保证了矩阵的正定性

几何意义

在特征空间中,的零空间对应于无法从数据中确定的方向。Ridge 回归通过添加,在这些方向上施加了"偏好 0"的先验,使问题良定(well-posed)。


Q4:如何选择正则化参数

方法 1:交叉验证(推荐)

1
2
3
4
5
6
7
from sklearn.linear_model import RidgeCV

# 自动选择最优 lambda
lambdas = np.logspace(-3, 3, 50)
model = RidgeCV(alphas=lambdas, cv=5)
model.fit(X_train, y_train)
print(f"最优λ: {model.alpha_:.4f}")

方法 2:网格搜索

1
2
3
4
5
6
7
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import Ridge

param_grid = {'alpha': np.logspace(-3, 3, 20)}
grid = GridSearchCV(Ridge(), param_grid, cv=5, scoring='neg_mean_squared_error')
grid.fit(X_train, y_train)
print(f"最优λ: {grid.best_params_['alpha']:.4f}")

方法 3:理论指导

根据偏差-方差权衡:

其中是噪声方差,是真实参数。

实践经验

  • 起点(在标准化数据上)
  • 范围(对数刻度搜索)
  • 精细调整:在最优值附近缩小范围
  • 早停:如果验证误差连续增加,停止搜索

L-曲线方法

绘制 |w|,选择曲线"拐点"处的


Q5:特征标准化为什么重要?

问题:不同特征量纲不同会导致:

  1. 梯度下降收敛慢:参数空间呈椭球形,梯度方向不指向最优点
  2. 正则化不公平惩罚大量纲特征的权重更多

示例

假设两个特征:

  • :面积(范围 0-1000 平方米)
  • :房间数(范围 1-10)

权重可能产生相同的影响,但主要惩罚

标准化方法

Z-score 标准化(推荐):

其中

Min-Max 标准化

效果对比

方法 均值 标准差/范围 异常值敏感性
原始数据 任意 任意 -
Z-score 0 1 中等
Min-Max - [0,1]

代码

1
2
3
4
5
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test) # 使用训练集的均值和标准差

注意:标准化后,权重的解释会改变。要恢复原始尺度的权重:


Q6:多重共线性如何影响线性回归?

定义:多重共线性( Multicollinearity)指特征之间高度线性相关。

检测方法

方差膨胀因子(VIF)

其中是用其他特征预测第个特征的决定系数。

判断标准

  • :无共线性
  • :中等共线性
  • :严重共线性

影响

  1. 数值不稳定接近奇异,计算误差大
  2. 参数方差大:权重估计的标准误增大,置信区间变宽
  3. 参数不可解释:权重符号可能与预期相反

示例

假设(近似共线)。则:

  • 真实模型:(参数不稳定)

解决方法

  1. 删除冗余特征:通过 VIF 识别并删除
  2. PCA 降维:将相关特征转化为正交主成分
  3. Ridge 回归惩罚缓解共线性
  4. 收集更多数据:增大样本量减小估计方差

Python 检测 VIF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from statsmodels.stats.outliers_influence import variance_inflation_factor

# 计算每个特征的 VIF
vif_data = []
for i in range(X.shape[1]):
vif = variance_inflation_factor(X, i)
vif_data.append({'Feature': f'X{i+1}', 'VIF': vif})

import pandas as pd
df_vif = pd.DataFrame(vif_data)
print(df_vif)

# 删除 VIF > 10 的特征
high_vif = df_vif[df_vif['VIF'] > 10]['Feature'].tolist()
print(f"高 VIF 特征(需要考虑删除): {high_vif}")

Q7:线性回归的假设是什么?违反假设怎么办?

经典线性回归的四大假设

假设 1:线性关系

检验:残差图(Residual Plot)应无明显模式。

违反时

  • 添加多项式特征:
  • 特征变换:
  • 使用非线性模型:决策树、神经网络

假设 2:独立性 相互独立。

检验: Durbin-Watson 检验统计量: 表示无自相关。

违反时(常见于时间序列):

  • 使用自回归模型( AR, ARIMA)
  • 添加滞后项作为特征

假设 3:同方差性

(_i) =(常数)。

检验:残差图中,残差的散布应随保持恒定。

违反时(异方差性):

  • 加权最小二乘( WLS):给不同样本不同权重
  • 稳健标准误( Robust Standard Errors)
  • 对数变换目标变量

假设 4:正态性

检验

  • QQ 图( Quantile-Quantile Plot)
  • Shapiro-Wilk 检验

违反时

  • 对目标变量变换( Box-Cox 变换)
  • 使用非参数方法或广义线性模型

诊断代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import matplotlib.pyplot as plt
from scipy import stats

# 拟合模型
model.fit(X_train, y_train)
y_pred = model.predict(X_train)
residuals = y_train - y_pred

# 残差图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 1. 残差 vs 拟合值
axes[0, 0].scatter(y_pred, residuals, alpha=0.5)
axes[0, 0].axhline(y=0, color='r', linestyle='--')
axes[0, 0].set_xlabel('Fitted Values')
axes[0, 0].set_ylabel('Residuals')
axes[0, 0].set_title('Residuals vs Fitted')

# 2. QQ 图
stats.probplot(residuals, dist="norm", plot=axes[0, 1])
axes[0, 1].set_title('Normal Q-Q Plot')

# 3. Scale-Location 图(检验同方差性)
standardized_residuals = residuals / np.std(residuals)
axes[1, 0].scatter(y_pred, np.sqrt(np.abs(standardized_residuals)), alpha=0.5)
axes[1, 0].set_xlabel('Fitted Values')
axes[1, 0].set_ylabel('√|Standardized Residuals|')
axes[1, 0].set_title('Scale-Location Plot')

# 4. 残差直方图
axes[1, 1].hist(residuals, bins=30, edgecolor='black')
axes[1, 1].set_xlabel('Residuals')
axes[1, 1].set_ylabel('Frequency')
axes[1, 1].set_title('Histogram of Residuals')

plt.tight_layout()
plt.show()

Q8:如何处理分类特征?

问题:线性回归要求输入是数值型,但很多特征是分类的(如"颜色":红、绿、蓝)。

错误做法:直接编码为整数(红=1, 绿=2, 蓝=3)。

问题:引入了虚假的顺序关系(模型会认为蓝比红"大"2 倍)。

正确方法

独热编码( One-Hot Encoding)

类别特征转化为个二元特征。

示例

原始 绿
1 0 0
绿 0 1 0
0 0 1

代码

1
2
3
4
5
6
7
8
9
10
11
from sklearn.preprocessing import OneHotEncoder
import pandas as pd

# 方法 1: pandas
data = pd.DataFrame({'color': ['red', 'green', 'blue', 'red']})
encoded = pd.get_dummies(data['color'], prefix='color')
print(encoded)

# 方法 2: sklearn
encoder = OneHotEncoder(sparse=False, drop='first') # drop='first'避免多重共线性
X_encoded = encoder.fit_transform(data[['color']])

注意

  • 多重共线性个独热列完全线性相关(和为 1)。解决:删除一列(drop='first')或使用 Ridge 回归。
  • 高基数特征:如果类别数很大(如邮政编码),考虑:
    • 目标编码( Target Encoding)
    • 嵌入( Embedding)

Q9:线性回归能处理非线性关系吗?

答案:能,通过特征工程。

关键洞察:线性回归中的"线性"指的是参数线性,而非特征线性。

这仍是关于的线性模型,可以用线性回归求解。

方法 1:多项式特征

1
2
3
4
5
6
7
from sklearn.preprocessing import PolynomialFeatures

# 生成多项式特征(度数为 3)
poly = PolynomialFeatures(degree=3, include_bias=False)
X_poly = poly.fit_transform(X)

# 示例:[x1, x2] -> [x1, x2, x1^2, x1*x2, x2^2, x1^3, x1^{2*}x2, x1*x2^2, x2^3]

方法 2:交互特征

1
2
3
4
5
6
7
from sklearn.preprocessing import PolynomialFeatures

# 只生成交互项
poly = PolynomialFeatures(degree=2, interaction_only=True, include_bias=False)
X_inter = poly.fit_transform(X)

# 示例:[x1, x2, x3] -> [x1, x2, x3, x1*x2, x1*x3, x2*x3]

方法 3:自定义变换

1
2
3
4
5
6
7
8
9
10
import numpy as np

# 添加对数、指数等变换
X_transformed = np.c_[
X,
np.log(X + 1), # 对数
np.sqrt(X), # 平方根
X ** 2, # 平方
np.sin(X) # 三角函数
]

注意

  • 过拟合风险:特征数量快速增长(个特征的次多项式有项)
  • 正则化必要性:使用 Ridge 或 Lasso 控制复杂度
  • 可解释性下降:高次项难以解释

示例:拟合正弦曲线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 生成数据
X = np.linspace(0, 4*np.pi, 100).reshape(-1, 1)
y = np.sin(X).ravel() + np.random.randn(100) * 0.1

# 多项式拟合
from sklearn.pipeline import Pipeline
from sklearn.linear_model import Ridge

model = Pipeline([
('poly', PolynomialFeatures(degree=9)),
('ridge', Ridge(alpha=0.1))
])

model.fit(X, y)
y_pred = model.predict(X)

# 绘图
plt.scatter(X, y, alpha=0.5, label='Data')
plt.plot(X, y_pred, 'r-', linewidth=2, label='Polynomial Fit')
plt.plot(X, np.sin(X), 'g--', label='True Function')
plt.legend()
plt.show()

Q10:如何解释线性回归的系数?

基本解释

对于模型

的含义:当其他特征不变时,增加 1 个单位,平均变化个单位。

注意事项

  1. 标准化影响:如果特征标准化了,权重大小不能直接比较重要性。

  2. 共线性影响:多重共线性会导致权重不稳定。

  3. 因果关系:相关≠因果。权重只表示统计关联,不能推断因果关系。

标准化系数( Standardized Coefficients)

在标准化数据上拟合模型,得到的系数可以比较相对重要性:

解释增加 1 个标准差,变化个标准差。

显著性检验

使用 t 检验判断是否显著不为 0:

其中是标准误。如果大(p值小),拒绝的原假设。

Python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import statsmodels.api as sm

# 添加常数项
X_with_const = sm.add_constant(X)

# 拟合模型
model = sm.OLS(y, X_with_const).fit()

# 打印摘要
print(model.summary())

# 包含:
# - 系数估计
# - 标准误
# - t 统计量
# - p 值
# - 置信区间

置信区间

95%置信区间:

如果区间不包含 0,则参数显著。

🎓 总结与展望

核心要点

  1. 三种视角:代数(正规方程)、几何(投影)、概率( MLE)殊途同归

  2. 最小二乘解

  3. 正则化: Ridge( L2)保证可逆, Lasso( L1)实现稀疏

  4. 优化算法:小数据用正规方程,大数据用梯度下降

  5. 模型诊断:检查线性性、独立性、同方差性、正态性

实战要点

  • 特征标准化
  • 处理共线性
  • 选择正则化参数
  • 交叉验证评估

下一章预告:第 6 章将探讨逻辑回归与分类问题,将线性模型推广到离散输出空间。我们将推导 sigmoid 函数的来源、交叉熵损失的数学基础,以及决策边界的几何意义。

✏️ 练习题与解答

练习 1:正规方程推导

题目:从矩阵微积分出发,推导最小二乘法的正规方程

解答

损失函数:

展开:

求梯度:

可逆时,

练习 2:Ridge 回归的贝叶斯解释

题目:证明 Ridge 回归等价于在权重上施加高斯先验的 MAP 估计,并给出的关系。

解答

似然:

先验:

MAP 估计最大化后验(等价于最小化负对数后验):

对比 Ridge 回归:

因此。先验方差越大(先验越"弱"),正则化越弱。

练习 3:投影矩阵性质

题目:证明投影矩阵是对称且幂等的(即),并解释其几何意义。

解答

对称性

(因为对称,所以

幂等性

几何意义将任意向量正交投影到的列空间。对称性意味着投影是正交的;幂等性意味着"投影两次等于投影一次"——已经在列空间中的向量不受影响。

练习 4:MLE 与 OLS 的等价性

题目:在线性高斯模型)下,证明的 MLE 与 OLS 估计一致,并推导的 MLE。

解答

对数似然:

求最大:

求最大:

注意是有偏的;无偏估计为

练习 5:多重共线性分析

题目:设的两列高度相关()。分析 OLS 估计的方差如何受影响,以及 Ridge 回归为何能改善。

解答

OLS 估计的方差-协方差矩阵:

高度相关时,趋近奇异,的元素变得非常大,导致的方差极大——这就是多重共线性问题。

具体来看,VIF(方差膨胀因子),其中是用其他特征预测第个特征的。当时,,VIF ,方差放大 50 倍。

Ridge 回归的方差:

添加使得的最小特征值至少为,从而稳定逆矩阵,大幅减小方差。代价是引入少量偏差,但在 MSE 意义下通常是值得的。

📚 参考文献

  1. Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.). Springer.

  2. Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Springer.

  3. Murphy, K. P. (2012). Machine Learning: A Probabilistic Perspective. MIT Press.

  4. Shalev-Shwartz, S., & Ben-David, S. (2014). Understanding Machine Learning: From Theory to Algorithms. Cambridge University Press.

  5. Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.

  6. James, G., Witten, D., Hastie, T., & Tibshirani, R. (2013). An Introduction to Statistical Learning. Springer.

  7. Tibshirani, R. (1996). Regression shrinkage and selection via the lasso. Journal of the Royal Statistical Society: Series B, 58(1), 267-288.

  8. Hoerl, A. E., & Kennard, R. W. (1970). Ridge regression: Biased estimation for nonorthogonal problems. Technometrics, 12(1), 55-67.

  9. Zou, H., & Hastie, T. (2005). Regularization and variable selection via the elastic net. Journal of the Royal Statistical Society: Series B, 67(2), 301-320.

  10. Huber, P. J. (1964). Robust estimation of a location parameter. The Annals of Mathematical Statistics, 35(1), 73-101.

  11. 李航 (2012). 统计学习方法. 清华大学出版社.

  12. 周志华 (2016). 机器学习. 清华大学出版社.

  • 本文标题:机器学习数学推导(五)线性回归
  • 本文作者:Chen Kai
  • 创建时间:2021-09-18 09:00:00
  • 本文链接:https://www.chenk.top/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%95%B0%E5%AD%A6%E6%8E%A8%E5%AF%BC%EF%BC%88%E4%BA%94%EF%BC%89%E7%BA%BF%E6%80%A7%E5%9B%9E%E5%BD%92/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论