随机梯度下降(Stochastic Gradient Descent)实现线性回归
摘要:本文通过Python代码实现随机梯度下降(SGD)算法,并应用于线性回归问题。代码通过动态调整学习率策略,逐个样本更新参数,最终得到接近真实值的模型参数。
1. 随机梯度下降简介
随机梯度下降(Stochastic Gradient Descent, SGD)是一种优化算法,其核心思想是在每一步迭代中仅使用一个训练样本计算梯度并更新参数。与全量梯度下降相比,SGD的计算开销更小,适合大规模数据集,但梯度方向波动较大,收敛过程可能震荡。
2. 代码实现
2.1 导入库与数据生成
生成模拟数据 y = 4 + 3x + 高斯噪声
,并添加偏置项 x_b
:
import numpy as np
# 生成模拟数据
x = 2 * np.random.rand(100, 1)
y = 4 + 3 * x + np.random.randn(100, 1)
x_b = np.c_[np.ones((100, 1)), x] # 添加偏置项
2.2 超参数设置与模型初始化
设置训练轮次和批次数量(实际每个批次仅含一个样本),并初始化模型参数 theta
:
n_epochs = 10000 # 训练轮次
n_batchs = 100 # 每轮训练中使用的样本数(此处实际为逐个样本更新)
theta = np.random.randn(2, 1) # 初始化参数
2.3 动态学习率调整
定义学习率调整函数,学习率随迭代次数增加逐步衰减:
t0, t1 = 1, 200
def learning_rate_adjust(t):
return t0 / (t + t1) # 学习率逐步减小
2.4 梯度下降迭代
随机梯度下降核心逻辑:每个epoch中随机打乱数据,逐个样本计算梯度并更新参数:
for t in range(n_epochs):
# 随机打乱数据索引
s_index = np.random.permutation(100)
s_x_b = x_b[s_index] # 打乱后的特征矩阵
s_y = y[s_index] # 打乱后的标签
# 逐个样本更新参数
for i in range(n_batchs):
xi = s_x_b[i:i+1] # 取第i个样本(保留二维结构)
yi = s_y[i:i+1]
gradients = xi.T.dot(xi.dot(theta) - yi) # 计算梯度
learning_rate = learning_rate_adjust(t + i) # 调整学习率
theta = theta - learning_rate * gradients # 更新参数
2.5 输出结果
训练完成后打印模型参数:
print("训练后的参数 theta:")
print(theta)
输出结果:
[[3.83569192]
[3.11419393]]
3. 结果分析
真实参数为 [4, 3]
,训练后得到 [3.8357, 3.1142]
,接近真实值。由于SGD的随机性,参数更新过程存在波动,但最终仍能收敛到较优解。
4. 关键公式说明
-
梯度计算:
单个样本的梯度公式为:
∇ θ J ( θ ) = x i T ( x i θ − y i ) \nabla_\theta J(\theta) = x_i^T (x_i \theta - y_i) ∇θJ(θ)=xiT(xiθ−yi)
代码中通过xi.T.dot(xi.dot(theta) - yi)
实现。 -
参数更新:
θ t + 1 = θ t − η ∇ θ J ( θ ) \theta_{t+1} = \theta_t - \eta \nabla_\theta J(\theta) θt+1=θt−η∇θJ(θ)
其中 η \eta η 为动态调整的学习率。
5. 随机梯度下降的优缺点
- 优点:计算速度快,适合大规模数据;能够跳出局部最优。
- 缺点:梯度方向波动大,收敛路径震荡;需精细调整学习率。
6. 与全量梯度下降的对比
特性 | 全量梯度下降 | 随机梯度下降 |
---|---|---|
梯度计算 | 使用全部样本 | 使用单个样本 |
计算开销 | 大(适合小数据) | 小(适合大数据) |
收敛稳定性 | 高(方向准确) | 低(方向波动) |
参数更新频率 | 每轮一次 | 每样本一次 |
7. 完整代码
相关标签:#机器学习
#梯度下降
#线性回归
#Python