优化算法:梯度下降算法

优化算法:梯度下降算法

  • 前言
  • 相关介绍
    • 拓展
      • 一阶泰勒展开
      • 梯度下降公式相关证明
  • 梯度下降算法
    • 单变量
    • 代码示例
      • Python实现
      • PyTorch实现
    • 多变量
    • 代码示例
      • Python实现
      • 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开源项目

相关介绍

梯度下降算法(Gradient Descent Algorithm)是一种一阶优化算法,主要用于机器学习和深度学习中的参数优化,尤其是在训练神经网络时找到损失函数的最小值。其核心思想是通过迭代的方式来逐步调整模型参数,使得每次迭代后模型的损失函数值朝着全局最小值或者局部最小值的方向靠近。

基本原理:

  1. 损失函数
    在机器学习中,我们有一个损失函数(Loss Function),它反映了模型预测结果与实际标签之间的差异程度。我们的目标是通过调整模型参数,使损失函数的值最小化。

  2. 梯度
    梯度是损失函数关于模型参数的偏导数,指向了函数增长最快的方向。在多变量情况下,梯度是一个向量,指向函数值增加最快的方向。对于损失函数来说,梯度的相反方向则是函数值减少最快的方向。

  3. 更新规则
    梯度下降算法的核心步骤就是在每次迭代中,根据梯度信息更新模型参数。参数更新的通用公式如下:
    w t = w t − 1 − η ∇ w J ( w t − 1 ) w_t = w_{t-1} - \eta \nabla_w J(w_{t-1}) wt=wt1ηwJ(wt1)
    其中, w t w_t wt 表示在第 t 次迭代时的参数, w t − 1 w_{t-1} wt1 是前一次迭代的参数, η \eta η 是学习率(Learning Rate),决定了每次更新的步伐大小, ∇ w J ( w t − 1 ) \nabla_w J(w_{t-1}) wJ(wt1) 是损失函数 J J J 关于参数 w w w w t − 1 w_{t-1} wt1 点处的梯度。

  4. 迭代过程
    从一个初始的参数估计开始,梯度下降算法反复执行以下步骤:

    • 计算当前参数下的损失函数梯度;
    • 根据梯度更新参数,向梯度的反方向移动;
    • 重复上述过程,直至满足停止条件(如达到足够小的损失值、达到设定的迭代次数或参数变化非常小等)。

变种和优化:

  • 批量梯度下降(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)TwJ(wt)

由于 ∇ w J ( w t ) T ∇ w J ( w t ) \nabla_w J(w_t)^T \nabla_w J(w_t) wJ(wt)TwJ(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)(xa)

这里的 f ( a ) f(a) f(a) 是函数在点 a a a 的函数值, f ′ ( a ) f'(a) f(a) 是函数在点 a a a 的导数值, x − a x-a xa 表示偏离 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:RnR,我们要寻找其输入参数 θ ∈ 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)(xa)
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 是偏置。

  1. 定义损失函数
    假设我们有一组训练样本 { ( 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=1n(wxi+byi)2

  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=1n(wxi+byi)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=1n(wxi+byi)

  3. 参数更新
    使用梯度下降算法,我们对 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 α 是学习率。

  4. 迭代过程
    从一组初始值 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[(01+02)1+(02+03)2+(03+04)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[(01+02)+(02+03)+(03+04)]

更新参数:

  • 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=1m(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)} θjJ(θ)=m1i=1m(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αθjJ(θ)

其中, α \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) θ1J(θ)=θ1(2m1i=1m(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)} θ1J(θ)=m1i=1m(hθ(x(i))y(i))x1(i)

同理可得其他参数的梯度,然后按照上述参数更新规则进行迭代优化,直至损失函数收敛或达到预设的最大迭代次数。

在多变量线性回归问题中,当我们有大量的特征和样本时,使用矩阵形式可以更方便地表示梯度下降算法。假设我们有 m 个样本和 n+1 个特征(包括偏置项),特征矩阵 Xm×(n+1) 的,目标变量向量 ym×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(y)T(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 θjJ(θ)=m1XT(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(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(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开源项目

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559100.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【代理模式】静态代理-简单例子

在Java中&#xff0c;静态代理是一种设计模式&#xff0c;它涉及到为一个对象提供一个代理以控制对这个对象的访问。静态代理在编译时就已经确定&#xff0c;代理类和被代理类会实现相同的接口或者是代理类继承被代理类。客户端通过代理类来访问&#xff08;调用&#xff09;被…

QT跨平台读写Excel

QT跨平台读写Excel 背景Excel工具CMakeLists.txt工程目录 背景 开发框架QT&#xff0c;makefile构建工具CMake&#xff0c;编译器MinGW Excel工具 考虑跨平台则不能使用针对微软COM组件的QAxObject来读写Excel&#xff0c;因此使用开源QtXlsx。 这里是将QXlsx当做源码嵌入使…

【Linux学习】Linux权限(二)

文章目录 &#x1f680;Linux权限管理&#x1f680;修改文件的所有者&#x1f680;修改文件或目录的所属组&#x1f680;同时修改为念的拥有者与所属组&#x1f680;文件类型&#x1f680;file指令&#x1f680;目录权限&#x1f680;umask指令&#x1f680;粘滞位 &#x1f68…

使用 Docker 部署 instantbox 轻量级 Linux 系统

1&#xff09;instantbox 介绍 GitHub&#xff1a;https://github.com/instantbox/instantbox instantbox 是一款非常实用的项目&#xff0c;它能够让你在几秒内启动一个主流的 Linux 系统&#xff0c;随起随用&#xff0c;支持 Ubuntu&#xff0c;CentOS&#xff0c; Arch Li…

c#+unity基础

序列化&#xff1a; [SerializeField]&#xff0c;点不出来&#xff0c;只能在面板上显示绑定游戏物体 //公有隐藏 特有函数 特有函数&#xff1a;不需要调用&#xff0c;自动执行 Awake最先执行->OnEable 面向对象思想 面向对象思想&#xff1a;分为具体对象和抽象对…

nas如何异地共享文件?

nas异地共享文件是一种通过网络实现不同地区电脑与电脑、设备与设备、电脑与设备之间的文件共享的技术。通过nas&#xff08;网络附加存储&#xff09;设备&#xff0c;用户可以在不同地点的电脑或设备之间快速、安全地共享文件和数据。本文将介绍nas异地共享文件的原理以及它在…

day4网络编程作业

#include <myhead.h> #define SER_IP "192.168.125.78" #define SER_PORT 69 #define CLI_IP "192.168.125.176" #define CLI_PORT 4399 //文件上传 void upload(int cfd,struct sockaddr_in sin)//服务器信息结构体传参 {//填充读写请求字符数组--&…

如何查看项目中使用的Qt版本

如何查看项目中使用的Qt版本 1.点击左下角电脑按钮查看Qt版本。 2.点击左侧栏项目按钮查看Qt版本。

代码编辑工具PilotEditPro18.4版本在Windows系统的下载与安装配置

目录 前言一、PilotEdit Pro安装二、使用配置总结 前言 “ PilotEdit Pro是一个功能强大且功能丰富的文本和代码编辑器&#xff0c;可满足程序员、开发人员和IT专业人员的不同需求。定位为一个多功能的编辑解决方案&#xff0c;PilotEdit Pro以其对广泛的文本和代码文件格式的…

【黑马头条】-day11热点文章实时计算-kafka-kafkaStream-Redis

文章目录 今日内容1 实时流式计算1.1 应用场景1.2 技术方案选型 2 Kafka Stream2.1 概述2.2 KafkaStream2.3 入门demo2.3.1 需求分析2.3.2 实现2.3.2.1 添加依赖2.3.2.2 创建快速启动&#xff0c;生成kafka流2.3.2.3 修改生产者2.3.2.4 修改消费者2.3.2.5 测试 2.4 SpringBoot集…

短视频批量采集软件|视频无水印下载提取工具

全新发布&#xff01;DY视频批量下载工具&#xff0c;实现轻松快捷的视频提取 为了更好地满足您的需求&#xff0c;我们自主研发了全新的DY视频批量下载工具。相较于市面上单个视频链接提取的工具&#xff0c;我们的产品更为便捷&#xff0c;不仅支持单个视频链接提取&#xf…

mysql 日环比 统计

接到一个任务&#xff0c;要计算日环比的情况。 16、查询销售额日环比情况 日环比&#xff1a; &#xff08;今日-昨日&#xff09;/ 昨日 的一个比率情况。 1&#xff0c;建表 DROP TABLE IF EXISTS sale; create table sale(id int not null AUTO_INCREMENT,record_date da…

Linux下SPI设备驱动实验:测试读取ICM20608设备中数据是否正常

一. 简介 前面文章实现了 SPI设备的读写功能&#xff0c;也对ICM20608设备中&#xff08;即SPI设备&#xff09;寄存器里的数据进行了读取。文章如下&#xff1a; Linux下SPI设备驱动实验&#xff1a;读取ICM20608设备的数据-CSDN博客 本文对驱动功能进行测试&#xff0c;即…

SpringSecurity源码分析3--UserDetail部分

前言&#xff1a;本章提及的类都是与用户名、密码相关的类 UserDetailsService.class 用于加载用户信息 DaoAuthenticationProvider.class 将数据库的信息拿出来进行认证 AbstractUserDetailsAuthenticationProvider.class DaoAuthenticationProvider的父类&#xff0c;通过模…

基于Web的宠物医院信息管理系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本宠物医院信息管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据…

【海思Hi3516CV610】是面向新一代视频编解码标准、网络安全和隐私保护、人工智能行业应用方面的IPC SoC

海思Hi3516CV610是面向新一代视频编解码标准、网络安全和隐私保护、人工智能行业应用方面的IPC SoC&#xff0c;除了开发普通摄像机&#xff0c;还可以打造极具竞争力的枪球一体机、双目长短焦摄像机产品&#xff1b; 处理器内核: 支持ARM Cortex-A7 MP2 时钟速率950MHz 支持…

java中的枚举概述

枚举指的是将一个事物的所有情况列举出来&#xff0c;这个概念在数学中的概率那一块经常有所体现。用一个简单的例子加以说明&#xff0c;我们对投掷两个骰子时出现的点数进行记录&#xff0c;当把所有可能出现的情况都列举出来时&#xff0c;所体现的就是枚举的概念。这里可能…

C++_类型转换

文章目录 学习目标&#xff1a;1.static_cast2. reinterpret_cast3.const_cast4. dynamic_cast 学习过程1.static_cast2. reinterpret_cast3.const_cast在这里插入图片描述4. dynamic_cast 学习目标&#xff1a; 标准C为了加强类型转换的可视性&#xff0c;引入了四种命名的强…

Web程序设计-实验03 JavaScript语言基础

题目 【实验主题】 素数问题求解。计算&#xff08;判断&#xff09; 1~100中哪些是素数、哪些是合数。 素数也称为质数&#xff0c;是只能被1及其自身整除的自然数。与素数相对应的是合数&#xff0c;合数可以被分解为若干个素数的乘积&#xff0c;这些素数称为这个合数的质…

IntelliJ-platform plugIn 插件开发专题内容介绍,学习指导(一)

这系列文章出炉对于笔者来说确实不容易&#xff0c;历时快两年了&#xff0c;先后迭代了3版本&#xff0c;暂时与官方最新版本API同步&#xff08;2024.03&#xff09;&#xff0c;文章内容覆盖2022~2024版内容 专题由来 最早接触插件开发是源于公司一个国际化项目&#xff0c…
最新文章