【深度学习笔记】7_2 梯度下降和随机梯度下降

news2024/11/18 11:40:44

注:本文为《动手学深度学习》开源内容,部分标注了个人理解,仅为个人学习记录,无抄袭搬运意图

7.2 梯度下降和随机梯度下降

在本节中,我们将介绍梯度下降(gradient descent)的工作原理。虽然梯度下降在深度学习中很少被直接使用,但理解梯度的意义以及沿着梯度反方向更新自变量可能降低目标函数值的原因是学习后续优化算法的基础。随后,我们将引出随机梯度下降(stochastic gradient descent)。

7.2.1 一维梯度下降

我们先以简单的一维梯度下降为例,解释梯度下降算法可能降低目标函数值的原因。假设连续可导的函数 f : R → R f: \mathbb{R} \rightarrow \mathbb{R} f:RR的输入和输出都是标量。给定绝对值足够小的数 ϵ \epsilon ϵ,根据泰勒展开公式,我们得到以下的近似:

f ( x + ϵ ) ≈ f ( x ) + ϵ f ′ ( x ) . f(x + \epsilon) \approx f(x) + \epsilon f'(x) . f(x+ϵ)f(x)+ϵf(x).

这里 f ′ ( x ) f'(x) f(x)是函数 f f f x x x处的梯度。一维函数的梯度是一个标量,也称导数。

接下来,找到一个常数 η > 0 \eta > 0 η>0,使得 ∣ η f ′ ( x ) ∣ \left|\eta f'(x)\right| ηf(x)足够小,那么可以将 ϵ \epsilon ϵ替换为 − η f ′ ( x ) -\eta f'(x) ηf(x)并得到

f ( x − η f ′ ( x ) ) ≈ f ( x ) − η f ′ ( x ) 2 . f(x - \eta f'(x)) \approx f(x) - \eta f'(x)^2. f(xηf(x))f(x)ηf(x)2.

如果导数 f ′ ( x ) ≠ 0 f'(x) \neq 0 f(x)=0,那么 η f ′ ( x ) 2 > 0 \eta f'(x)^2>0 ηf(x)2>0,所以

f ( x − η f ′ ( x ) ) ≲ f ( x ) . f(x - \eta f'(x)) \lesssim f(x). f(xηf(x))f(x).

这意味着,如果通过

x ← x − η f ′ ( x ) x \leftarrow x - \eta f'(x) xxηf(x)

来迭代 x x x,函数 f ( x ) f(x) f(x)的值可能会降低。因此在梯度下降中,我们先选取一个初始值 x x x和常数 η > 0 \eta > 0 η>0,然后不断通过上式来迭代 x x x,直到达到停止条件,例如 f ′ ( x ) 2 f'(x)^2 f(x)2的值已足够小或迭代次数已达到某个值。

下面我们以目标函数 f ( x ) = x 2 f(x)=x^2 f(x)=x2为例来看一看梯度下降是如何工作的。虽然我们知道最小化 f ( x ) f(x) f(x)的解为 x = 0 x=0 x=0,这里依然使用这个简单函数来观察 x x x是如何被迭代的。首先,导入本节实验所需的包或模块。

%matplotlib inline
import numpy as np
import torch
import math
import sys
sys.path.append("..") 
import d2lzh_pytorch as d2l

接下来使用 x = 10 x=10 x=10作为初始值,并设 η = 0.2 \eta=0.2 η=0.2。使用梯度下降对 x x x迭代10次,可见最终 x x x的值较接近最优解。

def gd(eta):
    x = 10
    results = [x]
    for i in range(10):
        x -= eta * 2 * x  # f(x) = x * x的导数为f'(x) = 2 * x
        results.append(x)
    print('epoch 10, x:', x)
    return results

res = gd(0.2)

输出:

epoch 10, x: 0.06046617599999997

下面将绘制出自变量 x x x的迭代轨迹。

def show_trace(res):
    n = max(abs(min(res)), abs(max(res)), 10)
    f_line = np.arange(-n, n, 0.1)
    d2l.set_figsize()
    d2l.plt.plot(f_line, [x * x for x in f_line])
    d2l.plt.plot(res, [x * x for x in res], '-o')
    d2l.plt.xlabel('x')
    d2l.plt.ylabel('f(x)')

show_trace(res)

在这里插入图片描述

7.2.2 学习率

上述梯度下降算法中的正数 η \eta η通常叫作学习率。这是一个超参数,需要人工设定。如果使用过小的学习率,会导致 x x x更新缓慢从而需要更多的迭代才能得到较好的解。

下面展示使用学习率 η = 0.05 \eta=0.05 η=0.05时自变量 x x x的迭代轨迹。可见,同样迭代10次后,当学习率过小时,最终 x x x的值依然与最优解存在较大偏差。

show_trace(gd(0.05))

输出:

epoch 10, x: 3.4867844009999995

在这里插入图片描述

如果使用过大的学习率, ∣ η f ′ ( x ) ∣ \left|\eta f'(x)\right| ηf(x)可能会过大从而使前面提到的一阶泰勒展开公式不再成立:这时我们无法保证迭代 x x x会降低 f ( x ) f(x) f(x)的值。

举个例子,当设学习率 η = 1.1 \eta=1.1 η=1.1时,可以看到 x x x不断越过(overshoot)最优解 x = 0 x=0 x=0并逐渐发散。

show_trace(gd(1.1))

输出:

epoch 10, x: 61.917364224000096

在这里插入图片描述

7.2.3 多维梯度下降

在了解了一维梯度下降之后,我们再考虑一种更广义的情况:目标函数的输入为向量,输出为标量。假设目标函数 f : R d → R f: \mathbb{R}^d \rightarrow \mathbb{R} f:RdR的输入是一个 d d d维向量 x = [ x 1 , x 2 , … , x d ] ⊤ \boldsymbol{x} = [x_1, x_2, \ldots, x_d]^\top x=[x1,x2,,xd]。目标函数 f ( x ) f(\boldsymbol{x}) f(x)有关 x \boldsymbol{x} x的梯度是一个由 d d d个偏导数组成的向量:

∇ x f ( x ) = [ ∂ f ( x ) ∂ x 1 , ∂ f ( x ) ∂ x 2 , … , ∂ f ( x ) ∂ x d ] ⊤ . \nabla_{\boldsymbol{x}} f(\boldsymbol{x}) = \bigg[\frac{\partial f(\boldsymbol{x})}{\partial x_1}, \frac{\partial f(\boldsymbol{x})}{\partial x_2}, \ldots, \frac{\partial f(\boldsymbol{x})}{\partial x_d}\bigg]^\top. xf(x)=[x1f(x),x2f(x),,xdf(x)].

为表示简洁,我们用 ∇ f ( x ) \nabla f(\boldsymbol{x}) f(x)代替 ∇ x f ( x ) \nabla_{\boldsymbol{x}} f(\boldsymbol{x}) xf(x)。梯度中每个偏导数元素 ∂ f ( x ) / ∂ x i \partial f(\boldsymbol{x})/\partial x_i f(x)/xi代表着 f f f x \boldsymbol{x} x有关输入 x i x_i xi的变化率。为了测量 f f f沿着单位向量 u \boldsymbol{u} u(即 ∥ u ∥ = 1 \|\boldsymbol{u}\|=1 u=1)方向上的变化率,在多元微积分中,我们定义 f f f x \boldsymbol{x} x上沿着 u \boldsymbol{u} u方向的方向导数为

D u f ( x ) = lim ⁡ h → 0 f ( x + h u ) − f ( x ) h . \text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) = \lim_{h \rightarrow 0} \frac{f(\boldsymbol{x} + h \boldsymbol{u}) - f(\boldsymbol{x})}{h}. Duf(x)=h0limhf(x+hu)f(x).

依据方向导数性质[1,14.6节定理三],以上方向导数可以改写为

D u f ( x ) = ∇ f ( x ) ⋅ u . \text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) = \nabla f(\boldsymbol{x}) \cdot \boldsymbol{u}. Duf(x)=f(x)u.

方向导数 D u f ( x ) \text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) Duf(x)给出了 f f f x \boldsymbol{x} x上沿着所有可能方向的变化率。为了最小化 f f f,我们希望找到 f f f能被降低最快的方向。因此,我们可以通过单位向量 u \boldsymbol{u} u来最小化方向导数 D u f ( x ) \text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) Duf(x)

由于 D u f ( x ) = ∥ ∇ f ( x ) ∥ ⋅ ∥ u ∥ ⋅ cos ( θ ) = ∥ ∇ f ( x ) ∥ ⋅ cos ( θ ) \text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) = \|\nabla f(\boldsymbol{x})\| \cdot \|\boldsymbol{u}\| \cdot \text{cos} (\theta) = \|\nabla f(\boldsymbol{x})\| \cdot \text{cos} (\theta) Duf(x)=∥∇f(x)ucos(θ)=∥∇f(x)cos(θ)
其中 θ \theta θ为梯度 ∇ f ( x ) \nabla f(\boldsymbol{x}) f(x)和单位向量 u \boldsymbol{u} u之间的夹角,当 θ = π \theta = \pi θ=π时, cos ( θ ) \text{cos}(\theta) cos(θ)取得最小值 − 1 -1 1。因此,当 u \boldsymbol{u} u在梯度方向 ∇ f ( x ) \nabla f(\boldsymbol{x}) f(x)的相反方向时,方向导数 D u f ( x ) \text{D}_{\boldsymbol{u}} f(\boldsymbol{x}) Duf(x)被最小化。因此,我们可能通过梯度下降算法来不断降低目标函数 f f f的值:

x ← x − η ∇ f ( x ) . \boldsymbol{x} \leftarrow \boldsymbol{x} - \eta \nabla f(\boldsymbol{x}). xxηf(x).

同样,其中 η \eta η(取正数)称作学习率。

下面我们构造一个输入为二维向量 x = [ x 1 , x 2 ] ⊤ \boldsymbol{x} = [x_1, x_2]^\top x=[x1,x2]和输出为标量的目标函数 f ( x ) = x 1 2 + 2 x 2 2 f(\boldsymbol{x})=x_1^2+2x_2^2 f(x)=x12+2x22。那么,梯度 ∇ f ( x ) = [ 2 x 1 , 4 x 2 ] ⊤ \nabla f(\boldsymbol{x}) = [2x_1, 4x_2]^\top f(x)=[2x1,4x2]。我们将观察梯度下降从初始位置 [ − 5 , − 2 ] [-5,-2] [5,2]开始对自变量 x \boldsymbol{x} x的迭代轨迹。我们先定义两个辅助函数,第一个函数使用给定的自变量更新函数,从初始位置 [ − 5 , − 2 ] [-5,-2] [5,2]开始迭代自变量 x \boldsymbol{x} x共20次,第二个函数对自变量 x \boldsymbol{x} x的迭代轨迹进行可视化。

def train_2d(trainer):  # 本函数将保存在d2lzh_pytorch包中方便以后使用
    x1, x2, s1, s2 = -5, -2, 0, 0  # s1和s2是自变量状态,本章后续几节会使用
    results = [(x1, x2)]
    for i in range(20):
        x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
        results.append((x1, x2))
    print('epoch %d, x1 %f, x2 %f' % (i + 1, x1, x2))
    return results

def show_trace_2d(f, results):  # 本函数将保存在d2lzh_pytorch包中方便以后使用
    d2l.plt.plot(*zip(*results), '-o', color='#ff7f0e')
    x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))
    d2l.plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
    d2l.plt.xlabel('x1')
    d2l.plt.ylabel('x2')

然后,观察学习率为 0.1 0.1 0.1时自变量的迭代轨迹。使用梯度下降对自变量 x \boldsymbol{x} x迭代20次后,可见最终 x \boldsymbol{x} x的值较接近最优解 [ 0 , 0 ] [0,0] [0,0]

eta = 0.1

def f_2d(x1, x2):  # 目标函数
    return x1 ** 2 + 2 * x2 ** 2

def gd_2d(x1, x2, s1, s2):
    return (x1 - eta * 2 * x1, x2 - eta * 4 * x2, 0, 0)

show_trace_2d(f_2d, train_2d(gd_2d))

输出:

epoch 20, x1 -0.057646, x2 -0.000073

在这里插入图片描述

7.2.4 随机梯度下降

在深度学习里,目标函数通常是训练数据集中有关各个样本的损失函数的平均。设 f i ( x ) f_i(\boldsymbol{x}) fi(x)是有关索引为 i i i的训练数据样本的损失函数, n n n是训练数据样本数, x \boldsymbol{x} x是模型的参数向量,那么目标函数定义为

f ( x ) = 1 n ∑ i = 1 n f i ( x ) . f(\boldsymbol{x}) = \frac{1}{n} \sum_{i = 1}^n f_i(\boldsymbol{x}). f(x)=n1i=1nfi(x).

目标函数在 x \boldsymbol{x} x处的梯度计算为

∇ f ( x ) = 1 n ∑ i = 1 n ∇ f i ( x ) . \nabla f(\boldsymbol{x}) = \frac{1}{n} \sum_{i = 1}^n \nabla f_i(\boldsymbol{x}). f(x)=n1i=1nfi(x).

如果使用梯度下降,每次自变量迭代的计算开销为 O ( n ) \mathcal{O}(n) O(n),它随着 n n n线性增长。因此,当训练数据样本数很大时,梯度下降每次迭代的计算开销很高。

随机梯度下降(stochastic gradient descent,SGD)减少了每次迭代的计算开销。在随机梯度下降的每次迭代中,我们随机均匀采样的一个样本索引 i ∈ { 1 , … , n } i\in\{1,\ldots,n\} i{1,,n},并计算梯度 ∇ f i ( x ) \nabla f_i(\boldsymbol{x}) fi(x)来迭代 x \boldsymbol{x} x

x ← x − η ∇ f i ( x ) . \boldsymbol{x} \leftarrow \boldsymbol{x} - \eta \nabla f_i(\boldsymbol{x}). xxηfi(x).

这里 η \eta η同样是学习率。可以看到每次迭代的计算开销从梯度下降的 O ( n ) \mathcal{O}(n) O(n)降到了常数 O ( 1 ) \mathcal{O}(1) O(1)。值得强调的是,随机梯度 ∇ f i ( x ) \nabla f_i(\boldsymbol{x}) fi(x)是对梯度 ∇ f ( x ) \nabla f(\boldsymbol{x}) f(x)的无偏估计:

E i ∇ f i ( x ) = 1 n ∑ i = 1 n ∇ f i ( x ) = ∇ f ( x ) . E_i \nabla f_i(\boldsymbol{x}) = \frac{1}{n} \sum_{i = 1}^n \nabla f_i(\boldsymbol{x}) = \nabla f(\boldsymbol{x}). Eifi(x)=n1i=1nfi(x)=f(x).

这意味着,平均来说,随机梯度是对梯度的一个良好的估计。

下面我们通过在梯度中添加均值为0的随机噪声来模拟随机梯度下降,以此来比较它与梯度下降的区别。

def sgd_2d(x1, x2, s1, s2):
    return (x1 - eta * (2 * x1 + np.random.normal(0.1)),
            x2 - eta * (4 * x2 + np.random.normal(0.1)), 0, 0)

show_trace_2d(f_2d, train_2d(sgd_2d))

输出:

epoch 20, x1 -0.047150, x2 -0.075628

在这里插入图片描述

可以看到,随机梯度下降中自变量的迭代轨迹相对于梯度下降中的来说更为曲折。这是由于实验所添加的噪声使模拟的随机梯度的准确度下降。在实际中,这些噪声通常指训练数据集中的无意义的干扰。

小结

  • 使用适当的学习率,沿着梯度反方向更新自变量可能降低目标函数值。梯度下降重复这一更新过程直到得到满足要求的解。
  • 学习率过大或过小都有问题。一个合适的学习率通常是需要通过多次实验找到的。
  • 当训练数据集的样本较多时,梯度下降每次迭代的计算开销较大,因而随机梯度下降通常更受青睐。

参考文献

[1] Stewart, J. (2010). Calculus: early transcendentals. 7th ed. Cengage Learning.


注:本节与原书基本相同,原书传送门

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue 监听器:让你的应用实时响应变化

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

Kibana二次开发环境搭建

1 kibana环境搭建 1.1 搭建后端服务 (1)java环境安装 ElasticSearch运行需要java jdk支持。所以要先安装JAVA环境。由于ElasticSearch 5.x 往后依赖于JDK 1.8的,所以现在我们下载JDK 1.8或者更高版本。下载JDK1.8,下载完成后安装&#xff…

计算机设计大赛 疲劳驾驶检测系统 python

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.2 打哈欠检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 🔥 优质竞赛项目系列&#x…

ai+模型选择+过拟合和欠拟合

ai模型选择过拟合和欠拟合 1模型选择1训练误差和泛化误差2验证数据集和测试数据集3k-折交叉验证4总结 2过拟合和欠拟合1模型容量2估计模型容量3VC维4数据复杂度5总结 3代码 1模型选择 1训练误差和泛化误差 训练误差(Training Error)和泛化误差&#xff…

STM32(19)I2C模块 主机发数据

发送数据: 等待空闲: 发送起始位: 发送地址: 发送数据:

day16_购物车(添加购物车,购物车列表查询,删除购物车商品,更新选中商品状态,完成购物车商品的全选,清空购物车)

文章目录 购物车模块1 需求说明2 环境搭建3 添加购物车3.1 需求说明3.2 远程调用接口开发3.2.1 ProductController3.2.2 ProductService 3.3 openFeign接口定义3.3.1 环境搭建3.3.2 接口定义3.3.3 降级类定义 3.4 业务后端接口开发3.4.1 添加依赖3.4.2 修改启动类3.4.3 CartInf…

Docker:部署微服务集群

1. 部署微服务集群 实现思路: ① 查看课前资料提供的cloud-demo文件夹,里面已经编写好了docker-compose文件 ② 修改自己的cloud-demo项目,将数据库、nacos地址都命名为docker-compose中的服务名 ③ 使用maven打包工具,将项目…

linux信号的概念

目录 1.预备 2.信号如何产生 1.引入 2.原理 3.总结 3.接口 1.singal函数 2.kill函数 3.raise函数(给自己发信号) 4.abort函数(给自己发送6号信号) 4.异常 1.现象 2.原理 5.core和term区别 6.由软件条件产生信号 3.…

P8661 [蓝桥杯 2018 省 B] 日志统计

[P8661 蓝桥杯 2018 省 B] 日志统计 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路:双指针,对日志按时间从小到大进行排序。快指针指向的 i d id id点赞数加一,快慢指针做差得到之间的时间间隔,大于等于 k k k将慢指针指向…

preg_replace漏洞e模式函数执行

关于PHP函数preg_replace \e 模式函数执行漏洞 preg_replace: ​ 该函数执行一个正则表达式的搜索和替换。 ​ 语法: mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit -1 [, int &$count ]] )​ 搜索…

摘花生c++

题目 Hello Kitty想摘点花生送给她喜欢的米老鼠。 她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南角出来。 地里每个道路的交叉点上都有种着一株花生苗,上面有若干颗花生,经过一株花生苗就能摘走该它上面所有的…

【海贼王的数据航海:利用数据结构成为数据海洋的霸主】探究二叉树的奥秘

目录 1 -> 树的概念及结构 1.1 -> 树的概念 1.2 -> 树的相关概念 1.3 -> 树的表示 1.4 -> 树在实际中的运用(表示文件系统的目录树结构) 2 -> 二叉树概念及结构 2.1 -> 二叉树的概念 2.2 -> 现实中的二叉树 2.3 -> 特殊的二叉树 2.4 ->…

【软考】单元测试

目录 1. 概念2. 测试内容2.1 说明2.2 模块接口2.3 局部数据结构2.4 重要的执行路径 3. 测试过程2.1 说明2.2 单元测试环境图2.3 驱动模块2.4 桩模块 4. 模块接口测试与局部数据结构测试的区别 1. 概念 1.单元测试也称为模块测试,在模块编写完成且无编译错误后就可以…

数据库(mysql)-新手笔记(主外键,视图)

数据库基本知识点- http://t.csdnimg.cn/CVa9e 主外键 主键(唯一性,非空性) 主键是数据库表中的一个或多个字段,其值唯一标识表中的每一行/记录。 唯一性: 主键字段中的每个值都必须是唯一的,不能有两个或更多的记录具有相同的主键值 非空性&#x…

Ajax (1)

什么是Ajax&#xff1a; 浏览器与服务器进行数据通讯的技术&#xff0c;动态数据交互 axios库地址&#xff1a; <script src"https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> 如何使用呢&#xff1f; 我们现有个感性的认识 <scr…

【Python-Docx库】Word与Python的完美结合

今天给大家分享Python处理Word的第三方库&#xff1a;Python-Docx。 什么是Python-Docx&#xff1f; Python-Docx是用于创建和更新Microsoft Word&#xff08;.docx&#xff09;文件的Python库。 日常需要经常处理Word文档&#xff0c;用Python的免费第三方包&#xff1a;Pyt…

【自制操作系统】系统启动流程,工具使用和启动区的制作

&#x1f4dd;本文介绍 本文主要从系统系统的启动流程开始&#xff0c;中间介绍一些所用工具的使用方法&#xff0c;最后将完成一个启动区的制作。此次的启动区只涉及到汇编代码。 &#x1f44b;作者简介&#xff1a;一个正在积极探索的本科生 &#x1f4f1;联系方式&#xff1…

《MySQL实战45讲》课程大纲

1MySQL实战45讲-01基础架构&#xff1a;一条SQL查询语句是如何执行的&#xff1f;2MySQL实战45讲-02日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f;3MySQL实战45讲-03事务隔离&#xff1a;为什么你改了我还看不见&#xff1f;4MySQL实战45讲-04深入浅出索引&…

vue3 ref获取子组件显示 __v_skip : true 获取不到组件的方法 怎么回事怎么解决

看代码 问题出现了 当我想要获取这个组件上的方法时 为什么获取不到这个组件上的方法呢 原來&#xff1a; __v_skip: true 是 Vue 3 中的一个特殊属性&#xff0c;用于跳过某些组件的渲染。当一个组件被标记为 __v_skip: true 时&#xff0c;Vue 将不会对该组件进行渲染&am…

Springboot——JSR303校验

1. 请求参数的合法性校验 使用基于JSR303的校验框架实现&#xff0c;Springboot提供了JSR-303的支持&#xff0c;它就是spring-boot-starter-validation&#xff0c;他包括了很多的校验规则&#xff0c;只需要在模型中通过注解指定校验规则&#xff0c;在Controller方法上开启校…