前言
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入人工智能知识点专栏、Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页查看
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目
相关介绍
梯度下降算法(Gradient Descent Algorithm)是一种一阶优化算法,主要用于机器学习和深度学习中的参数优化,尤其是在训练神经网络时找到损失函数的最小值。其核心思想是通过迭代的方式来逐步调整模型参数,使得每次迭代后模型的损失函数值朝着全局最小值或者局部最小值的方向靠近。
基本原理:
-
损失函数:
在机器学习中,我们有一个损失函数(Loss Function),它反映了模型预测结果与实际标签之间的差异程度。我们的目标是通过调整模型参数,使损失函数的值最小化。 -
梯度:
梯度是损失函数关于模型参数的偏导数,指向了函数增长最快的方向。在多变量情况下,梯度是一个向量,指向函数值增加最快的方向。对于损失函数来说,梯度的相反方向则是函数值减少最快的方向。 -
更新规则:
梯度下降算法的核心步骤就是在每次迭代中,根据梯度信息更新模型参数。参数更新的通用公式如下:
w t = w t − 1 − η ∇ w J ( w t − 1 ) w_t = w_{t-1} - \eta \nabla_w J(w_{t-1}) wt=wt−1−η∇wJ(wt−1)
其中, w t w_t wt 表示在第 t 次迭代时的参数, w t − 1 w_{t-1} wt−1 是前一次迭代的参数, η \eta η 是学习率(Learning Rate),决定了每次更新的步伐大小, ∇ w J ( w t − 1 ) \nabla_w J(w_{t-1}) ∇wJ(wt−1) 是损失函数 J J J 关于参数 w w w在 w t − 1 w_{t-1} wt−1 点处的梯度。 -
迭代过程:
从一个初始的参数估计开始,梯度下降算法反复执行以下步骤:- 计算当前参数下的损失函数梯度;
- 根据梯度更新参数,向梯度的反方向移动;
- 重复上述过程,直至满足停止条件(如达到足够小的损失值、达到设定的迭代次数或参数变化非常小等)。
变种和优化:
- 批量梯度下降(Batch Gradient Descent):每次计算梯度时使用全部训练样本。
- 随机梯度下降(Stochastic Gradient Descent, SGD):每次计算梯度时只使用一个训练样本,具有较快的计算速度,但稳定性可能较差。
- 小批量梯度下降(Mini-batch Gradient Descent):折衷方案,每次计算梯度时使用一小部分训练样本。
- 动量(Momentum):在SGD的基础上引入动量项,帮助更快穿越平坦区域和避免局部极小点。
- 自适应学习率方法(如Adam, Adagrad, RMSprop):动态调整学习率,使得每个参数有不同的学习速率,从而更好地适应训练过程。
注意事项:
- 学习率的选择对梯度下降的效果至关重要,过大可能导致振荡或发散,过小则会导致收敛速度慢。
- 对于非凸优化问题,梯度下降可能陷入局部最优,而非全局最优。因此,在某些情况下需要使用更高级的优化算法,或者采用随机化策略来尽量跳出局部最优。
梯度下降算法参数更新的通用公式通常表示为:
w t + 1 = w t − α ∇ w J ( w t ) w_{t+1} = w_t - \alpha \nabla_w J(w_t) wt+1=wt−α∇wJ(wt)
其中:
- w t w_t wt 表示在第 t 次迭代时的参数向量。
- w t + 1 w_{t+1} wt+1是在第 t+1 次迭代时更新后的参数向量。
- α \alpha α是学习率(learning rate),它决定了参数更新的步伐大小,是个超参数,需要人为设定。
- ∇ w J ( w t ) \nabla_w J(w_t) ∇wJ(wt) 表示损失函数 J J J 关于参数向量 w w w 在当前参数值 w t w_t wt 处的梯度向量,梯度给出了损失函数增大的最快方向。
推导过程:
梯度下降算法的初衷是通过调整参数 w w w 来最小化损失函数 J ( w ) J(w) J(w)。直观上,如果损失函数在某一点 w t w_t wt 的梯度 ∇ w J ( w t ) \nabla_w J(w_t) ∇wJ(wt) 是非零向量,那么沿着梯度的相反方向调整参数会使损失函数值下降最快。
假设 Δ w \Delta w Δw是参数向量的一次更新,我们希望损失函数 J J J 在这次更新后有尽可能大的下降,即 J ( w t − Δ w ) J(w_t - \Delta w) J(wt−Δw) 尽可能小。为了简化问题,我们考虑沿梯度的相反方向更新,即 Δ w = − α ∇ w J ( w t ) \Delta w = - \alpha \nabla_w J(w_t) Δw=−α∇wJ(wt),其中 α \alpha α是一个正标量,用于控制更新幅度。
对于非常小的 α \alpha α,泰勒展开可以近似损失函数在 w t w_t wt附近的局部行为:
J ( w t − α ∇ w J ( w t ) ) ≈ J ( w t ) − α ∇ w J ( w t ) T ∇ w J ( w t ) J(w_t - \alpha \nabla_w J(w_t)) \approx J(w_t) - \alpha \nabla_w J(w_t)^T \nabla_w J(w_t) J(wt−α∇wJ(wt))≈J(wt)−α∇wJ(wt)T∇wJ(wt)
由于 ∇ w J ( w t ) T ∇ w J ( w t ) \nabla_w J(w_t)^T \nabla_w J(w_t) ∇wJ(wt)T∇wJ(wt)是梯度的平方范数,总是非负的,所以在 α > 0 \alpha > 0 α>0的条件下,损失函数 J J J在更新后会减小。因此,我们按照这个方向更新参数,就得到了梯度下降算法的更新公式:
w t + 1 = w t − α ∇ w J ( w t ) w_{t+1} = w_t - \alpha \nabla_w J(w_t) wt+1=wt−α∇wJ(wt)
这个更新步骤不断地重复,直到损失函数不再显著下降或达到预先设定的停止条件为止。
拓展
一阶泰勒展开
一阶泰勒展开是泰勒级数的一种特殊情况,它只考虑了函数在某一点处的函数值和一阶导数值,忽略更高阶的导数信息。泰勒公式的一般形式如下:
对于定义在某区间内的可导函数 f ( x ) f(x) f(x),在点 a a a 处的一阶泰勒展开式为:
f ( x ) ≈ f ( a ) + f ′ ( a ) ( x − a ) f(x) \approx f(a) + f'(a)(x-a) f(x)≈f(a)+f′(a)(x−a)
这里的 f ( a ) f(a) f(a) 是函数在点 a a a 的函数值, f ′ ( a ) f'(a) f′(a) 是函数在点 a a a 的导数值, x − a x-a x−a 表示偏离 a a a 的位移量。此公式表明函数在 a a a 点附近可以被线性函数很好地近似。
例如,对于函数 f ( x ) = ln ( 1 + x ) f(x) = \ln(1+x) f(x)=ln(1+x),在一阶泰勒展开时,我们将其在 x = 0 x=0 x=0 处展开,得到:
ln ( 1 + x ) ≈ x \ln(1+x) \approx x ln(1+x)≈x
这是因为 ln ( 1 + 0 ) = 0 \ln(1+0) = 0 ln(1+0)=0,并且 d d x ln ( 1 + x ) ∣ x = 0 = 1 \left.\frac{d}{dx}\ln(1+x)\right|_{x=0}=1 dxdln(1+x) x=0=1,所以忽略二阶及更高阶导数的影响后,一阶展开仅剩下 x x x 这一项。
一阶泰勒展开在很多实际应用中很有价值,特别是在优化算法如梯度下降中,每一迭代步只考虑函数的一阶导数(梯度),用于确定参数更新的方向和步长。然而,对于更加精确的近似,通常需要考虑二阶甚至更高阶的泰勒展开。
梯度下降公式相关证明
梯度下降算法的核心思想是利用函数的梯度信息指导参数更新的方向,以期望逐步降低目标函数的值。下面简要介绍梯度下降算法更新参数公式的一般推导过程,并给出直观解释。
假设我们有一个实值函数 f : R n → R f: \mathbb{R}^n \rightarrow \mathbb{R} f:Rn→R,我们要寻找其输入参数 θ ∈ R n \theta \in \mathbb{R}^n θ∈Rn 的一组值,使函数值 f ( θ ) f(\theta) f(θ) 最小化。梯度下降法的更新规则基于梯度向量 ∇ f ( θ ) \nabla f(\theta) ∇f(θ),它是函数在点 θ \theta θ 处的全微分,表示函数增长最快的方向及其增长率。
梯度的反方向则代表了函数值下降最快的方向。在每一步迭代中,我们希望朝着函数值下降最快的方向调整参数。给定学习率(步长) η > 0 \eta > 0 η>0,参数更新的公式为:
θ t + 1 = θ t − η ⋅ ∇ f ( θ t ) \theta_{t+1} = \theta_t - \eta \cdot \nabla f(\theta_t) θt+1=θt−η⋅∇f(θt)
这里, θ t \theta_t θt 表示当前时刻 t t t 的参数向量, θ t + 1 \theta_{t+1} θt+1 是经过更新后的参数向量。
直观解释:在一点 θ t \theta_t θt 上,若函数 f f f 的梯度为 ∇ f ( θ t ) \nabla f(\theta_t) ∇f(θt),那么在其邻域内,我们可以近似地用一阶泰勒展开来描述函数的变化:
由定义在某区间内的可导函数
f
(
x
)
f(x)
f(x),在点
a
a
a 处的一阶泰勒展开式为:
f
(
x
)
≈
f
(
a
)
+
f
′
(
a
)
(
x
−
a
)
f(x) \approx f(a) + f'(a)(x-a)
f(x)≈f(a)+f′(a)(x−a)令
a
=
θ
t
a=\theta_t
a=θt
x
=
θ
t
+
Δ
θ
x=\theta_t + \Delta \theta
x=θt+Δθ得
f
(
θ
t
+
Δ
θ
)
≈
f
(
θ
t
)
+
∇
f
(
θ
t
)
T
Δ
θ
+
高阶项
f(\theta_t + \Delta \theta) \approx f(\theta_t) + \nabla f(\theta_t)^T \Delta \theta + \text{高阶项}
f(θt+Δθ)≈f(θt)+∇f(θt)TΔθ+高阶项
为了使 f ( θ t + Δ θ ) f(\theta_t + \Delta \theta) f(θt+Δθ) 减少最多,我们需要 Δ θ \Delta \theta Δθ 和 − ∇ f ( θ t ) -\nabla f(\theta_t) −∇f(θt) 方向相同且幅度适当(由学习率控制)。因此选取:
Δ θ = − η ⋅ ∇ f ( θ t ) \Delta \theta = -\eta \cdot \nabla f(\theta_t) Δθ=−η⋅∇f(θt)
从而得到了梯度下降的更新公式。
严谨的数学证明通常会涉及到分析函数 f f f 在点 θ t \theta_t θt 附近的局部性质,并利用一阶泰勒展开的线性近似部分来决定下一步的迭代方向。对于非凸函数,尽管梯度下降不一定能找到全局最小值,但它至少确保在每一步都能局部减小函数值。
梯度下降算法
单变量
为了更详细地展示梯度下降算法的参数更新过程,我们考虑一个简单的单变量线性回归问题。假设我们有一个数据集,并希望通过最小化均方误差(MSE)损失函数来训练一个线性模型 y = w x + b y = wx + b y=wx+b,其中 w w w 是权重, b b b 是偏置。
-
定义损失函数:
假设我们有一组训练样本 { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x n , y n ) } \{(x_1, y_1), (x_2, y_2), ..., (x_n, y_n)\} {(x1,y1),(x2,y2),...,(xn,yn)},损失函数(MSE)定义为:
J ( w , b ) = 1 2 n ∑ i = 1 n ( w x i + b − y i ) 2 J(w, b) = \frac{1}{2n}\sum_{i=1}^{n} (wx_i + b - y_i)^2 J(w,b)=2n1i=1∑n(wxi+b−yi)2 -
计算梯度:
我们需要分别计算损失函数关于 w w w 和 b b b 的梯度:
∇ w J ( w , b ) = 1 n ∑ i = 1 n ( w x i + b − y i ) x i \nabla_w J(w, b) = \frac{1}{n}\sum_{i=1}^{n} (wx_i + b - y_i)x_i ∇wJ(w,b)=n1i=1∑n(wxi+b−yi)xi
∇ b J ( w , b ) = 1 n ∑ i = 1 n ( w x i + b − y i ) \nabla_b J(w, b) = \frac{1}{n}\sum_{i=1}^{n} (wx_i + b - y_i) ∇bJ(w,b)=n1i=1∑n(wxi+b−yi) -
参数更新:
使用梯度下降算法,我们对 w w w 和 b b b 分别进行更新:
w t + 1 = w t − α ∇ w J ( w t , b t ) w_{t+1} = w_t - \alpha \nabla_w J(w_t, b_t) wt+1=wt−α∇wJ(wt,bt)
b t + 1 = b t − α ∇ b J ( w t , b t ) b_{t+1} = b_t - \alpha \nabla_b J(w_t, b_t) bt+1=bt−α∇bJ(wt,bt)其中, w t w_t wt 和 b t b_t bt 分别是当前迭代时刻的权重和偏置, α \alpha α 是学习率。
-
迭代过程:
从一组初始值 w 0 w_0 w0 和 b 0 b_0 b0 开始,重复执行以下步骤:- 计算当前参数下损失函数关于 w w w 和 b b b 的梯度。
- 根据梯度更新 w w w 和 b b b。
- 直到满足停止条件(如损失函数值的变化小于某个阈值,或达到预定的最大迭代次数)。
例如,如果我们有一组数据:
- 数据点:(1, 2), (2, 3), (3, 4)
初始设置 w 0 = 0 w_0 = 0 w0=0 和 b 0 = 0 b_0 = 0 b0=0,学习率 α = 0.01 \alpha = 0.01 α=0.01。
第一次迭代时,计算梯度:
- ∇ w J ( w 0 , b 0 ) = 1 3 [ ( 0 ∗ 1 + 0 − 2 ) ∗ 1 + ( 0 ∗ 2 + 0 − 3 ) ∗ 2 + ( 0 ∗ 3 + 0 − 4 ) ∗ 3 ] \nabla_w J(w_0, b_0) = \frac{1}{3}[(0*1+0-2)*1 + (0*2+0-3)*2 + (0*3+0-4)*3] ∇wJ(w0,b0)=31[(0∗1+0−2)∗1+(0∗2+0−3)∗2+(0∗3+0−4)∗3]
- ∇ b J ( w 0 , b 0 ) = 1 3 [ ( 0 ∗ 1 + 0 − 2 ) + ( 0 ∗ 2 + 0 − 3 ) + ( 0 ∗ 3 + 0 − 4 ) ] \nabla_b J(w_0, b_0) = \frac{1}{3}[(0*1+0-2) + (0*2+0-3) + (0*3+0-4)] ∇bJ(w0,b0)=31[(0∗1+0−2)+(0∗2+0−3)+(0∗3+0−4)]
更新参数:
- w 1 = w 0 − α ∇ w J ( w 0 , b 0 ) w_1 = w_0 - \alpha \nabla_w J(w_0, b_0) w1=w0−α∇wJ(w0,b0)
- b 1 = b 0 − α ∇ b J ( w 0 , b 0 ) b_1 = b_0 - \alpha \nabla_b J(w_0, b_0) b1=b0−α∇bJ(w0,b0)
接下来,继续对剩余迭代次数执行上述步骤,直至收敛。在实际编程实现中,我们会使用循环和自动微分库(如PyTorch或TensorFlow)来自动计算梯度和执行更新操作。
代码示例
Python实现
以下是一个使用纯Python实现的梯度下降算法示例,针对简单的单变量线性回归问题:
import numpy as np
# 假设数据点
X = np.array([1, 2, 3, 4, 5])
y = np.array([3, 5, 7, 9, 11])
# 初始化模型参数和学习率
w = np.random.rand() # 初始权重
b = np.random.rand() # 初始偏置
learning_rate = 0.01
n_iters = 1000 # 迭代次数
# 损失函数(均方误差)
def loss_function(X, y, w, b):
y_pred = w * X + b
return np.mean((y - y_pred)**2)
# 梯度计算
def gradient(X, y, w, b):
grad_w = -2 * np.mean(X * (y - (w * X + b)))
grad_b = -2 * np.mean(y - (w * X + b))
return grad_w, grad_b
# 梯度下降算法实现
for _ in range(n_iters):
# 计算梯度
dw, db = gradient(X, y, w, b)
# 更新参数
w -= learning_rate * dw
b -= learning_rate * db
# 可选:每100次迭代打印损失函数和参数
if (_ + 1) % 100 == 0:
print(f"Iteration {_ + 1}: Loss={loss_function(X, y, w, b):.4f}, w={w:.4f}, b={b:.4f}")
# 最终输出训练得到的参数
print(f"Final parameters after {n_iters} iterations: w={w:.4f}, b={b:.4f}")
'''
Iteration 100: Loss=0.0156, w=2.0810, b=0.7077
Iteration 200: Loss=0.0079, w=2.0577, b=0.7916
Iteration 300: Loss=0.0040, w=2.0411, b=0.8515
Iteration 400: Loss=0.0020, w=2.0293, b=0.8942
Iteration 500: Loss=0.0010, w=2.0209, b=0.9246
Iteration 600: Loss=0.0005, w=2.0149, b=0.9462
Iteration 700: Loss=0.0003, w=2.0106, b=0.9617
Iteration 800: Loss=0.0001, w=2.0076, b=0.9727
Iteration 900: Loss=0.0001, w=2.0054, b=0.9805
Iteration 1000: Loss=0.0000, w=2.0038, b=0.9861
Final parameters after 1000 iterations: w=2.0038, b=0.9861
'''
注意:这个示例中的梯度计算和损失函数都针对的是单变量线性回归问题。对于多变量线性回归或多层神经网络,梯度计算会更复杂,通常推荐使用自动求导库如PyTorch或TensorFlow来自动计算和应用梯度,以简化代码并提高计算效率。
PyTorch实现
在PyTorch中,我们可以利用自动求导和优化器来简洁地实现梯度下降算法。下面是一个使用PyTorch实现多变量线性回归问题的梯度下降法示例:
import torch
import torch.optim as optim
# 假设我们有以下数据
X = torch.tensor([[1.0], [2.0], [3.0], [4.0], [5.0]], requires_grad=False)
y = torch.tensor([[3.0], [5.0], [7.0], [9.0], [11.0]], requires_grad=False)
# 初始化模型参数
w = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
# 设置损失函数和优化器
criterion = torch.nn.MSELoss(reduction='mean') # 平均平方误差损失函数
optimizer = optim.SGD([w, b], lr=0.01) # 使用随机梯度下降优化器
# 迭代次数
n_iters = 1000
for epoch in range(n_iters):
# 前向传播
y_pred = w * X + b
loss = criterion(y_pred, y)
# 反向传播和参数更新
optimizer.zero_grad() # 清空梯度缓存
loss.backward() # 计算梯度
optimizer.step() # 更新参数
# 可选:每100次迭代打印损失函数和参数
if (epoch + 1) % 100 == 0:
print(f'Epoch [{epoch + 1}/{n_iters}], Loss: {loss.item():.4f}, w: {w.item():.4f}, b: {b.item():.4f}')
# 输出最终训练得到的参数
print(f"Final parameters after {n_iters} iterations: w={w.item():.4f}, b={b.item():.4f}")
'''
Epoch [100/1000], Loss: 0.0838, w: 2.1873, b: 0.3239
Epoch [200/1000], Loss: 0.0426, w: 2.1335, b: 0.5181
Epoch [300/1000], Loss: 0.0216, w: 2.0951, b: 0.6566
Epoch [400/1000], Loss: 0.0110, w: 2.0678, b: 0.7552
Epoch [500/1000], Loss: 0.0056, w: 2.0483, b: 0.8255
Epoch [600/1000], Loss: 0.0028, w: 2.0344, b: 0.8757
Epoch [700/1000], Loss: 0.0014, w: 2.0245, b: 0.9114
Epoch [800/1000], Loss: 0.0007, w: 2.0175, b: 0.9368
Epoch [900/1000], Loss: 0.0004, w: 2.0125, b: 0.9550
Epoch [1000/1000], Loss: 0.0002, w: 2.0089, b: 0.9679
Final parameters after 1000 iterations: w=2.0089, b=0.9679
'''
在这个示例中,PyTorch的optim.SGD
优化器封装了梯度计算和参数更新的过程,只需要通过.backward()
方法触发反向传播,然后调用.step()
方法就能自动完成参数更新。同时,criterion
对象负责计算损失值。
多变量
多变量线性回归问题的目标是找到一组最佳的参数 θ = ( θ 0 , θ 1 , . . . , θ n ) \theta = (\theta_0, \theta_1, ..., \theta_n) θ=(θ0,θ1,...,θn),使得模型能够较好地拟合数据集。模型的形式如下:
h θ ( X ) = θ 0 + θ 1 x 1 + θ 2 x 2 + . . . + θ n x n h_\theta(X) = \theta_0 + \theta_1 x_1 + \theta_2 x_2 + ... + \theta_n x_n hθ(X)=θ0+θ1x1+θ2x2+...+θnxn
其中,X
是输入特征矩阵,包含 n 个特征和一个额外的全为1的列(偏置项),y
是目标变量。
损失函数(Cost Function):
我们使用均方误差(Mean Squared Error, MSE)作为损失函数,对于给定的训练集 (X, y)
,损失函数定义为:
J ( θ ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 J(\theta) = \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)})^2 J(θ)=2m1i=1∑m(hθ(x(i))−y(i))2
其中,m
是样本数量,
x
(
i
)
x^{(i)}
x(i) 是第 i
个样本的特征向量,
y
(
i
)
y^{(i)}
y(i) 是第 i
个样本的目标值。
梯度下降法(Gradient Descent):
梯度下降法是用来最小化损失函数的一个迭代优化算法。对于多变量线性回归问题,我们需要计算损失函数关于每个参数
θ
j
\theta_j
θj 的梯度:
∂ J ( θ ) ∂ θ j = 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \frac{\partial J(\theta)}{\partial \theta_j} = \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)}) x_j^{(i)} ∂θj∂J(θ)=m1i=1∑m(hθ(x(i))−y(i))xj(i)
其中,
x
j
(
i
)
x_j^{(i)}
xj(i) 是第 i
个样本在第 j
个特征上的值。
参数更新规则:
在每一次梯度下降迭代中,我们按照梯度的反方向更新参数:
θ j : = θ j − α ∂ J ( θ ) ∂ θ j \theta_j := \theta_j - \alpha \frac{\partial J(\theta)}{\partial \theta_j} θj:=θj−α∂θj∂J(θ)
其中, α \alpha α 是学习率,它决定了每次更新的步长。
详细推导:
以参数
θ
1
\theta_1
θ1 为例,其梯度推导如下:
∂ J ( θ ) ∂ θ 1 = ∂ ∂ θ 1 ( 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 ) \frac{\partial J(\theta)}{\partial \theta_1} = \frac{\partial}{\partial \theta_1} \left( \frac{1}{2m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)})^2 \right) ∂θ1∂J(θ)=∂θ1∂(2m1i=1∑m(hθ(x(i))−y(i))2)
由于 h θ ( x ( i ) ) = θ 0 + θ 1 x 1 ( i ) + . . . + θ n x n ( i ) h_\theta(x^{(i)}) = \theta_0 + \theta_1 x_1^{(i)} + ... + \theta_n x_n^{(i)} hθ(x(i))=θ0+θ1x1(i)+...+θnxn(i),我们对 θ 1 \theta_1 θ1 求偏导:
∂ J ( θ ) ∂ θ 1 = 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) ⋅ x 1 ( i ) \frac{\partial J(\theta)}{\partial \theta_1} = \frac{1}{m} \sum_{i=1}^{m} (h_\theta(x^{(i)}) - y^{(i)}) \cdot x_1^{(i)} ∂θ1∂J(θ)=m1i=1∑m(hθ(x(i))−y(i))⋅x1(i)
同理可得其他参数的梯度,然后按照上述参数更新规则进行迭代优化,直至损失函数收敛或达到预设的最大迭代次数。
在多变量线性回归问题中,当我们有大量的特征和样本时,使用矩阵形式可以更方便地表示梯度下降算法。假设我们有 m
个样本和 n+1
个特征(包括偏置项),特征矩阵 X
是 m×(n+1)
的,目标变量向量 y
是 m×1
的,模型参数向量
θ
\theta
θ 是 (n+1)×1
的。损失函数的矩阵形式可以写作:
J ( θ ) = 1 2 m ( X θ − y ) T ( X θ − y ) J(\theta) = \frac{1}{2m} (X\theta - y)^T (X\theta - y) J(θ)=2m1(Xθ−y)T(Xθ−y)
为了求解梯度,我们对 θ j \theta_j θj 求偏导:
∂ J ( θ ) ∂ θ j = 1 m X T ( X θ − y ) e j \frac{\partial J(\theta)}{\partial \theta_j} = \frac{1}{m} X^T (X\theta - y) e_j ∂θj∂J(θ)=m1XT(Xθ−y)ej
其中,
e
j
e_j
ej 是单位矩阵的第 j
列,全为0,只有第 j
个元素为1。
将所有参数的偏导数合并成一个向量,我们得到梯度向量:
∇ θ J ( θ ) = 1 m X T ( X θ − y ) \nabla_\theta J(\theta) = \frac{1}{m} X^T (X\theta - y) ∇θJ(θ)=m1XT(Xθ−y)
参数更新规则用矩阵形式表示为:
θ : = θ − α ∇ θ J ( θ ) \theta := \theta - \alpha \nabla_\theta J(\theta) θ:=θ−α∇θJ(θ)
θ : = θ − α m X T ( X θ − y ) \theta := \theta - \frac{\alpha}{m} X^T (X\theta - y) θ:=θ−mαXT(Xθ−y)
这就是多变量线性回归问题中梯度下降算法的矩阵形式计算。在实际应用中,尤其在大数据集和多维特征的情况下,使用矩阵运算可以极大地提高计算效率。同时,借助numpy或PyTorch等库提供的向量化操作,可以轻松实现上述矩阵计算。
代码示例
Python实现
多变量线性回归问题中,假设我们想要通过以下公式预测目标变量 y
:
y = θ 0 + θ 1 x 1 + θ 2 x 2 + . . . + θ n x n y = \theta_0 + \theta_1 x_1 + \theta_2 x_2 + ... + \theta_n x_n y=θ0+θ1x1+θ2x2+...+θnxn
其中, x 1 , x 2 , . . . , x n x_1, x_2, ..., x_n x1,x2,...,xn 是 n 个输入变量, θ 0 \theta_0 θ0 是偏置项, θ 1 \theta_1 θ1 到 θ n \theta_n θn 是对应的权重系数。
以下是一个使用纯Python实现多变量线性回归的梯度下降算法详细示例:
import numpy as np
# 假设我们有以下多变量数据
X = np.array([[1.0, 2.0], [2.0, 3.0], [3.0, 4.0], [4.0, 5.0], [5.0, 6.0]])
y = np.array([3.0, 5.0, 7.0, 9.0, 11.0])
# 添加一列全为1的向量,用于实现偏置项
X_with_bias = np.column_stack((np.ones(len(X)), X))
# 初始化模型参数和学习率
theta = np.random.randn(X_with_bias.shape[1]) # 包括偏置在内的所有参数
learning_rate = 0.01
n_iters = 1000
epsilon = 1e-6 # 判断是否收敛的阈值
# 损失函数(均方误差)
def loss_function(theta, X, y):
m = len(y)
hypothesis = np.dot(X, theta)
squared_errors = (hypothesis - y) ** 2
return 1 / (2 * m) * np.sum(squared_errors)
# 梯度计算
def gradient(theta, X, y):
m = len(y)
gradients = np.zeros_like(theta)
hypothesis = np.dot(X, theta)
errors = hypothesis - y
gradients = 1 / m * np.dot(X.T, errors)
return gradients
# 梯度下降算法实现
prev_loss = float('inf')
for iter in range(n_iters):
gradients = gradient(theta, X_with_bias, y)
# 更新参数
theta -= learning_rate * gradients
curr_loss = loss_function(theta, X_with_bias, y)
# 每100次迭代打印损失函数和参数
if (iter + 1) % 100 == 0:
print(f"Iteration {iter + 1}: Loss={curr_loss:.4f}, Theta={theta.tolist()}")
# 检查是否收敛
if abs(curr_loss - prev_loss) < epsilon:
print(f"Converged at iteration {iter + 1}.")
break
prev_loss = curr_loss
# 输出最终训练得到的参数
print(f"Final parameters after {iter + 1} iterations: Theta={theta.tolist()}")
'''
Iteration 100: Loss=0.0338, Theta=[-0.7715060312736924, 1.0077590737746966, 1.1637845602392827]
Iteration 200: Loss=0.0226, Theta=[-0.6871121092852314, 0.9499052488465561, 1.190324657299603]
Iteration 300: Loss=0.0151, Theta=[-0.6181235290457362, 0.9026121126920053, 1.2120201013845477]
Iteration 400: Loss=0.0101, Theta=[-0.5617281857399464, 0.8639519077684599, 1.2297552397667924]
Iteration 500: Loss=0.0067, Theta=[-0.5156272986723052, 0.8323487711135716, 1.2442529901795447]
Iteration 600: Loss=0.0045, Theta=[-0.47794170862456764, 0.8065144991948711, 1.2561043083085812]
Iteration 700: Loss=0.0030, Theta=[-0.4471352797772801, 0.7853960383010185, 1.2657922762620148]
Iteration 800: Loss=0.0020, Theta=[-0.42195228391279116, 0.768132560083838, 1.2737117939093228]
Iteration 900: Loss=0.0013, Theta=[-0.40136621456166305, 0.7540203725691784, 1.2801856757457906]
Iteration 1000: Loss=0.0009, Theta=[-0.38453794466042174, 0.742484236343124, 1.285477809420977]
Final parameters after 1000 iterations: Theta=[-0.38453794466042174, 0.742484236343124, 1.285477809420977]
'''
在这个示例中,我们首先在输入数据 X
中添加了一列全为1的向量以实现偏置项。然后,我们定义了损失函数和梯度计算函数,并通过梯度下降算法更新模型参数。当损失函数值变化低于预设阈值时,我们认为模型已经收敛。最后,输出训练得到的模型参数。
PyTorch实现
在多变量线性回归问题中,我们处理的是多个输入变量的情况。以下是一个使用Python和PyTorch实现多变量线性回归问题的梯度下降法示例:
import torch
import torch.nn as nn
import torch.optim as optim
# 假设我们有以下多变量数据
# 注意:这里为了简化,假设我们有两个输入变量
X = torch.tensor([[1.0, 2.0], [2.0, 3.0], [3.0, 4.0], [4.0, 5.0], [5.0, 6.0]], requires_grad=False)
y = torch.tensor([[3.0], [5.0], [7.0], [9.0], [11.0]], requires_grad=False)
# 初始化模型参数
# 这里我们有两个权重(一个对应每个输入变量)和一个偏置
w1 = torch.randn(1, requires_grad=True)
w2 = torch.randn(1, requires_grad=True)
b = torch.randn(1, requires_grad=True)
# 定义模型
def model(X):
return w1 * X[:, 0] + w2 * X[:, 1] + b
# 设置损失函数和优化器
criterion = nn.MSELoss(reduction='mean') # 平均平方误差损失函数
optimizer = optim.SGD([w1, w2, b], lr=0.01) # 使用随机梯度下降优化器
# 迭代次数
n_iters = 1000
for epoch in range(n_iters):
# 前向传播
y_pred = model(X)
loss = criterion(y_pred, y)
# 反向传播和参数更新
optimizer.zero_grad() # 清空梯度缓存
loss.backward() # 计算梯度
optimizer.step() # 更新参数
# 可选:每100次迭代打印损失函数和参数
if (epoch + 1) % 100 == 0:
print(f'Epoch [{epoch + 1}/{n_iters}], Loss: {loss.item():.4f}, w1: {w1.item():.4f}, w2: {w2.item():.4f}, b: {b.item():.4f}')
# 输出最终训练得到的参数
print(f"Final parameters after {n_iters} iterations: w1={w1.item():.4f}, w2={w2.item():.4f}, b={b.item():.4f}")
'''
Epoch [100/1000], Loss: 11.5290, w1: -1.4123, w2: 2.6475, b: -0.0233
Epoch [200/1000], Loss: 9.5746, w1: -2.1700, w2: 2.9951, b: 1.0820
Epoch [300/1000], Loss: 8.7025, w1: -2.6761, w2: 3.2273, b: 1.8203
Epoch [400/1000], Loss: 8.3135, w1: -3.0142, w2: 3.3823, b: 2.3135
Epoch [500/1000], Loss: 8.1399, w1: -3.2400, w2: 3.4859, b: 2.6429
Epoch [600/1000], Loss: 8.0624, w1: -3.3909, w2: 3.5551, b: 2.8630
Epoch [700/1000], Loss: 8.0278, w1: -3.4916, w2: 3.6014, b: 3.0100
Epoch [800/1000], Loss: 8.0124, w1: -3.5589, w2: 3.6322, b: 3.1081
Epoch [900/1000], Loss: 8.0055, w1: -3.6039, w2: 3.6529, b: 3.1737
Epoch [1000/1000], Loss: 8.0025, w1: -3.6339, w2: 3.6666, b: 3.2175
Final parameters after 1000 iterations: w1=-3.6339, w2=3.6666, b=3.2175
'''
在这个示例中,我们定义了一个简单的多变量线性模型,并使用PyTorch的优化器和损失函数自动完成了梯度计算和参数更新。
- 由于本人水平有限,难免出现错漏,敬请批评改正。
- 更多精彩内容,可点击进入人工智能知识点专栏、Python日常小操作专栏、OpenCV-Python小应用专栏、YOLO系列专栏、自然语言处理专栏或我的个人主页查看
- 基于DETR的人脸伪装检测
- YOLOv7训练自己的数据集(口罩检测)
- YOLOv8训练自己的数据集(足球检测)
- YOLOv5:TensorRT加速YOLOv5模型推理
- YOLOv5:IoU、GIoU、DIoU、CIoU、EIoU
- 玩转Jetson Nano(五):TensorRT加速YOLOv5目标检测
- YOLOv5:添加SE、CBAM、CoordAtt、ECA注意力机制
- YOLOv5:yolov5s.yaml配置文件解读、增加小目标检测层
- Python将COCO格式实例分割数据集转换为YOLO格式实例分割数据集
- YOLOv5:使用7.0版本训练自己的实例分割模型(车辆、行人、路标、车道线等实例分割)
- 使用Kaggle GPU资源免费体验Stable Diffusion开源项目