26. 深度学习进阶 - 深度学习的优化方法

news2024/12/26 17:22:32

在这里插入图片描述

Hi, 你好。我是茶桁。

上一节课中我们预告了,本节课是一个难点,同时也是一个重点,大家要理解清楚。

我们在做机器学习的时候,会用不同的优化方法。

SGD

Alt text

上图中左边就是Batch Gradient Descent,中间是Mini-Batch Gradient Descent, 最右边则是Stochastic Gradient Descent。

我们还是直接上代码写一下来看。首先我们先定义两个随机值,一个x,一个ytrue:

import numpy as np
x = np.random.random(size=(100, 8))
ytrue = torch.from_numpy(np.random.uniform(0, 5, size=(100, 1)))

x是一个1008的随机值,ytrue是1001的随机值,在0到5之间,这100个x对应着这100个ytrue的输入。

然后我们来定义一个Sequential, 在里面按顺序放一个线性函数,一个Sigmoid激活函数,然后再来一个线性函数,别忘了咱们上节课所讲的,要注意x的维度大小。

linear = torch.nn.Linear(in_features=8, out_features=1)
sigmoid = torch.nn.Sigmoid()
linear2 = torch.nn.Linear(in_features=1, out_features=1)

train_x = torch.from_numpy(x)

model = torch.nn.Sequential(linear, sigmoid, linear2).double()

我们先来看一下训练x和ytrue值的大小:

print(model(train_x).shape)
print(ytrue.shape)

---
torch.Size([100, 1])
torch.Size([100, 1])

然后我们就可以来求loss了,先拿到预测值,然后将预测值和真实值一起放进去求值。

loss_fn = torch.nn.MSELoss()
yhat = model(train_x)
loss = loss_fn(yhat, ytrue)
print(loss)

---
36.4703

我们现在可以定义一个optimer, 来尝试进行优化,我们来将之前的所做的循环个100次,在其中我们加上反向传播:

optimer = torch.optim.SGD(model.parameters(), lr=1e-3)

for e in range(100):
    yhat = model(train_x)
    loss = loss_fn(yhat, ytrue)
    loss.backward()
    print(loss)
    optimer.step()

---
tensor(194.9302, dtype=torch.float64, grad_fn=<MseLossBackward0>)
...
tensor(1.9384, dtype=torch.float64, grad_fn=<MseLossBackward0>)

可以看到,loss会一直降低。从194一直降低到了2左右。

在求解loss的时候,我们用到了所有的train_x,那这种方式就叫做Batch gradient Descent,批量梯度下降。

它会对整个数据集计算损失函数相对于模型参数的梯度。梯度是一个矢量,包含了每个参数相对与损失函数的变化率。

这个方法会使用计算得到的梯度来更新模型的参数。更新规则通常是按照一下方式进行:

w t + 1 = w t − η ▽ w t \begin{align*} w_{t+1} = w_t - \eta \triangledown w_t \end{align*} wt+1=wtηwt

w t + 1 w_{t+1} wt+1是模型参数, η \eta η是学习率, ▽ w t \triangledown w_t wt是损失函数相对于参数的梯度。

但是在实际的情况下这个方法可能会有一个问题,比如说,我们在随机x的时候参数不是100,而是10^8,维度还是8维。假如它的维度很大,那么会出现的情况就是把x给加载到模型里面运算的时候,消耗的内存就会非常非常大,所需要的运算空间就非常大。

这也就是这个方法的一个缺点,计算成本非常高,由于需要计算整个训练数据集的梯度,因此在大规模数据集上的计算成本较高。而且可能会卡在局部最小值,难以逃离。说实话,我上面演示的数据也是尝试了几次之后拿到一次满意的,也遇到了在底部震荡的情况。

在这里可以有一个很简单的方法,我们规定每次就取20个:

for e in range(100):
    for b in range(100 // 20):
        batch_index = np.random.choice(range(len(train_x)), size=20)

        yhat = model(train_x[batch_index])
        loss = loss_fn(yhat, ytrue[batch_index])
        loss.backward()
        print(loss)
        optimer.step()

这样做loss也是可以下降的,那这种方法就叫做Mini Batch。

还有一种方法很极端,就是Stochhastic Gradient Descent,就是每次只取一个个数字:

for e in range(100):
    for b in range(100 // 1):
        ...

这种方法很极端,但是可以每次都可以运行下去。那大家就知道,有这三种不同的优化方式。

Alt text

这样的话,我们来看一下,上图中的蓝色,绿色和紫色,分别对应哪种训练方式?

紫色的是Stochastic Gradient Descent,因为它每次只取一个点,所以它的loss变化会很大,随机性会很强。换句话说,这一次取得数据好,可能loss会下降,如果数据取得不好,它的这个抖动会很大。

绿色就是Mini-Batch, 我们刚才20个、20个的输入进去,是有的时候涨,有的时候下降。

最后蓝色的就是Batch Gradient Descent, 因为它x最多,所以下降的最稳定。

但是因为每次x特别多内存,那有可能就满了。内存如果满了,机器就没有时间去运行程序,就会变得特别的慢。

MOMENTUM

我们上面讲到的了这个式子:

w t + 1 = w t − η ▽ w t \begin{align*} w_{t+1} = w_t - \eta \triangledown w_t \end{align*} wt+1=wtηwt

这个是最原始的Grady descent, 我们会发现一个问题,就是本来在等高线上进行梯度下降的时候,它找到的不是最快的下降的那条线,在实际情况中,数据量会很多,数量会很大。比方说做图片什么的,动辄几兆几十兆,如果要再加载几百个这个进去,那就会很慢。这个梯度往往可能会变的抖动会很大。

那有人就想了一个办法去减少抖动。就是我们每一次在计算梯度下降方向的时候,连带着上一次的方向一起考虑,然后取一个比例改变了原本的方向。那这样的话,整个梯度下降的线就会平缓了,抖动也就没有那么大,这个就叫做Momentum, 动量。

v t = γ ⋅ v t − 1 + η ▽ w t w t + 1 = w t − v t \begin{align*} v_t & = \gamma \cdot v_{t-1} + \eta \triangledown w_t \\ w_{t+1} & = w_t - v_t \end{align*} vtwt+1=γvt1+ηwt=wtvt

动量在物理学中就是物体沿某个方向运动的能量。

之前我们每次的wt是直接去减去学习率乘以梯度,现在还考虑了v{t-1}的值,乘上一个gamma,这个值就是我们刚才说的取了一个比例。

Alt text

就像这个图一样,原来是红色,加了动量之后就变成蓝色,可以看到更平稳一些。

RMS-PROP

除了动量法之外呢,还有一个RMS-PROP方法,全称为Root mean square prop。

Alt text

S ∂ l o s s ∂ w = β S ∂ l o s s ∂ w + ( 1 − β ) ∣ ∣ ∂ l o s s ∂ w ∣ ∣ 2 S ∂ l o s s ∂ b = β S ∂ l o s s ∂ b + ( 1 − β ) ∣ ∣ ∂ l o s s ∂ b ∣ ∣ 2 w = w − α ∂ l o s s ∂ w S ∂ l o s s ∂ w b = b − α ∂ l o s s ∂ b S ∂ l o s s ∂ b \begin{align*} S_{\frac{\partial loss}{\partial w}} & = \beta S_{\frac{\partial loss}{\partial w}} + (1 - \beta)||\frac{\partial loss}{\partial w} ||^2 \\ S_{\frac{\partial loss}{\partial b}} & = \beta S_{\frac{\partial loss}{\partial b}} + (1 - \beta)||\frac{\partial loss}{\partial b} ||^2 \\ w & = w - \alpha \frac{\frac{\partial loss}{\partial w}}{\sqrt{S_{\frac{\partial loss}{\partial w}}}} \\ b & = b - \alpha \frac{\frac{\partial loss}{\partial b}}{\sqrt{S_{\frac{\partial loss}{\partial b}}}} \end{align*} SwlossSblosswb=βSwloss+(1β)∣∣wloss2=βSbloss+(1β)∣∣bloss2=wαSwloss wloss=bαSbloss bloss

这个方法看似复杂,其实也是非常简单。这些方法在PyTorch里其实都有包含,我们可以直接调用。我们在这里还是要理解一下它的原理,之后做事的时候也并不需要真的取从头写这些玩意。

在讲它之前,我们再回头来说一下刚刚求解的动量法,动量法其实已经做的比较好了,但是还是有一个问题,它每次的rate是人工定义的。也就是我们上述公式中的 γ \gamma γ, 这个比例是人工定义的,那在RMS-PROP中就写了一个动态的调整方法。

这个动态的调整方法就是我们每一次在进行调整w或者b的时候,都会除以一个根号下的 S ∂ l o s s ∂ w S_{\frac{\partial loss}{\partial w}} Swloss,我们往上看,如果 ∂ l o s s ∂ w \frac{\partial loss}{\partial w} wloss比较大的话,那么 S ∂ l o s s ∂ w S_{\frac{\partial loss}{\partial w}} Swloss也就将会比较大,那放在下面的式子中,根号下,也就是 S ∂ l o s s ∂ w \sqrt{S_{\frac{\partial loss}{\partial w}}} Swloss 在分母上,那么w就会更小,反之则会更大。

所以说,当这一次的梯度很大的时候,这样一个方法就让 ∂ l o s s ∂ w \frac{\partial loss}{\partial w} wloss其实变小了,对b来说也是一样的情况。

也就说,如果上一次的方向变化的很严重,那么这一次就会稍微的收敛一点,就会动态的有个缩放。那么如果上一次变化的很小,那为了加速它,这个值反而就会变大一些。

所以说他是实现了一个动态的学习率的变化,当然它前面还有一个初始值,这个 γ \gamma γ需要人为设置,但是在这个 γ \gamma γ基础上它实现了动态的学习速率的变化。

动态的学习速率考察两个值,一个是前一时刻的变化的快慢,另一个就是它此时此刻变化的快慢。这个就叫做RMS。

ADAM

那我们在这里,其实还有一个方法:ADAM。
V d w = β 1 V d w + ( 1 − β 1 ) d w V d b = β 1 V d b + ( 1 − β 1 ) d b S d w = β 2 S d w + ( 1 − β 2 ) ∣ ∣ d w ∣ ∣ 2 S d b = β 2 S d b + ( 1 − β 2 ) ∣ ∣ d b ∣ ∣ 2 V d w c o r r e c t e d = V d w 1 − β 1 t V d b c o r r e c t e d = V d b 1 − β 1 t S d w c o r r e c t e d = S d w 1 − β 2 t S d b c o r r e c t e d = S d b 1 − β 2 t w = w − α V d b c o r r e c t e d S d w c o r r e c t e d + ε b = b − α V d b c o r r e c t e d S d b c o r r e c t e d + ε \begin{align*} V_{dw} & = \beta_1V_{dw} + (1-\beta_1)dw \\ V_{db} & = \beta_1V_{db} + (1-\beta_1)db \\ S_{dw} & = \beta_2S_{dw} + (1-\beta_2)||dw||^2 \\ S_{db} & = \beta_2S_{db} + (1-\beta_2)||db||^2 \\ & V_{dw}^{corrected} = \frac{V_{dw}}{1-\beta_1^t} \\ & V_{db}^{corrected} = \frac{V_{db}}{1-\beta_1^t} \\ & S_{dw}^{corrected} = \frac{S_{dw}}{1-\beta_2^t} \\ & S_{db}^{corrected} = \frac{S_{db}}{1-\beta_2^t} \\ w & = w - \alpha\frac{V_{db}^{corrected}}{\sqrt{S_{dw}^{corrected}}+\varepsilon} \\ b & = b - \alpha\frac{V_{db}^{corrected}}{\sqrt{S_{db}^{corrected}}+\varepsilon} \\ \end{align*} VdwVdbSdwSdbwb=β1Vdw+(1β1)dw=β1Vdb+(1β1)db=β2Sdw+(1β2)∣∣dw2=β2Sdb+(1β2)∣∣db2Vdwcorrected=1β1tVdwVdbcorrected=1β1tVdbSdwcorrected=1β2tSdwSdbcorrected=1β2tSdb=wαSdwcorrected +εVdbcorrected=bαSdbcorrected +εVdbcorrected

刚刚讲过的RMS特点其实是动态的调整了我们的学习率,之前讲Momentum其实还保持了上一时刻的方向,RMS就没有解决这个问题,RMS把上一时刻的方向给弄没了。

RMS,它的定义其实就没有考虑上次的方向,它只考虑上次变化的大小。而现在提出来这个ADAM,这个ADAM的意思就是Adaptive Momentum, 还记不记得咱们讲随机森林和Adaboost那一节,我们讲过Adaboost就是Adaptive Boosting,这里的Adaptive其实就是一个意思,就是自适应动量,也叫动态变化动量。

ADAM就结合了RMS和动量的两个优点。第一个是他在分母上也加了一个根号下的数,也就做了RMS做的事,然后在分子上还有一个数,这个数就保留了上一时刻的数,比如 V d w c o r r e c t e d V_{dw}^{corrected} Vdwcorrected, 就保留了上一时刻的V,就保留了上一时刻的方向。

所以ADAM既是动态的调整了学习率,又保留了上一时刻的方向。

那除此之外,其实还有一个AdaGrad和L-BFGS方法,不过常用的方法也就是上面详细讲的这几种。

到此为止,我们进阶神经网络的基础知识就都差不多具备了,接下来我们就该来讲解下卷机和序列,比如说LSTM和RNN、CNN的东西。在这些结束之后,我们还会有Attention机制,Transformer机制,YOLO机制,Segmentation机制,还有强化深度学习其实都是基于这些东西。

那我们下节课,就先从RNN来说开去。

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

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

相关文章

实验 elk+filebeat+kafka

kafka 3.4.1 elkfilebeatkafka 实现日志收集 httpd1 mysql1 topic 2.7 3.0 关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 安装 JDK yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel java -version 安装 Zookeeper cd /…

Selenium+Python自动化测试之验证码处理

两种方式&#xff1a; 验证码识别技术 (很难达到100%) 添加Cookie &#xff08;*****五星推荐&#xff09; 方式一&#xff1a;验证码识别技术 逻辑方式&#xff1a; 1&#xff1a;打开验证码所在页面&#xff0c;截图。获取验证码元素坐标&#xff0c;剪切出验证码图片&…

xss漏洞后端进行html消毒

import org.jsoup.Jsoup;public static String sanitizeHtml(String input) {// 使用 Jsoup 消毒 HTMLreturn Jsoup.clean(input, Safelist.relaxed());}public static void main(String[] args) {String userInput "<p><script>alert(1)</script>Safe…

震坤行:数字驱动食品农副行业采购的新兴趋势与实践

震坤行&#xff1a;数字驱动食品农副行业采购的新兴趋势与实践 近年来消费者对于营养价值和健康的追求日益凸显&#xff0c;促使各类有机食品、低糖低脂食品、素食等健康食品受到热烈追捧。同时&#xff0c;以往单一的产品也被各家企业“卷”出了个性化&#xff0c;光是卖水&a…

机器学习笔记 - 基于百度飞桨PaddleSeg的人体分割模型以及TensorRT部署说明

一、简述 虽然Segment Anything用于图像分割的通用大模型看起来很酷(飞桨也提供分割一切的模型),但是个人感觉落地应用的时候心里还是更倾向于飞桨这种场景式的,因为需要用到一些人体分割的需求,所以这里主要是对飞桨高性能图像分割开发套件进行了解和使用,但是暂时不训练…

初识Linux(下).妈妈再也不用担心我Linux找不到门了

文章目录 前言1. date时间相关的指令1.1 date1.2 在设定时间方面示例如下&#xff1a; 1.3 时间戳示例如下&#xff1a; 2. Cal指令示例如下&#xff1a;类似windows 3. find指令&#xff1a;&#xff08;非常重要&#xff09; -name示例如下&#xff1a;类似windows 4. grep指…

【HuggingFace Transformer库学习笔记】基础组件学习:Tokenizer

基础组件——Tokenizer &#xff08;1&#xff09;模型加载 from transformers import AutoTokenizersen "弱小的我也有大梦想!" # 从HuggingFace加载&#xff0c;输入模型名称&#xff0c;即可加载对于的分词器 tokenizer AutoTokenizer.from_pretrained("m…

数据结构:图文详解单链表的各种操作(头插法,尾插法,任意位置插入,删除节点,查询节点,求链表的长度,清空链表)

目录 一.什么是链表 二.链表的实现 节点的插入 头插法 尾插法 指定位置插入 节点的删除 删除第一次出现的关键字节点 删除所有关键字节点 节点的查找 链表的清空 链表的长度 前言&#xff1a;在上一篇文章中&#xff0c;我们认识了线性数据结构中的顺序表&#xff0…

CKafka 一站式搭建数据流转链路,助力长城车联网平台降低运维成本

关于长城智能新能源 长城汽车是一家全球化智能科技公司&#xff0c;业务包括汽车及零部件设计、研发、生产、销售和服务&#xff0c;旗下拥有魏牌、哈弗、坦克、欧拉及长城皮卡。2022年&#xff0c;长城汽车全年销售1,067,523辆&#xff0c;连续7年销量超100万辆。长城汽车面向…

兼容jlink OB arm仿真器使用(杜邦线过长导致烧写总是失败)

一、兼容jlink OB的使用&#xff1a; 1、设置中要选择jlink&#xff1b; 2、模式选择SWD模式&#xff08;接三根线&#xff09;&#xff1b; 二、杜邦线过长导致stm32的stlink烧写总是失败 用ST-link烧写提示的错误信息有&#xff1a; Error while accessing a target reso…

【开源】基于Vue和SpringBoot的快递管理系统

项目编号&#xff1a; S 007 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S007&#xff0c;文末获取源码。} 项目编号&#xff1a;S007&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 快递类型模块2.3 快…

思维模型 逆向思维

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。弱者道之用反者道之动。 1 逆向思维的应用 1.1 历史典故 1 曹冲称象 这个故事讲述的是曹操的儿子曹冲如何利用逆向思维解决了称大象重量的难题。曹冲没有直接去称大象的重量&#xff0c;…

图解「差分」入门(“前缀和“ 到 “差分“ 丝滑过渡)

题目描述 这是 LeetCode 上的 「1094. 拼车」 &#xff0c;难度为 「中等」。 Tag : 「差分」、「前缀和」 车上最初有 capacity 个空座位&#xff0c;车只能向一个方向行驶&#xff08;不允许掉头或改变方向&#xff09;。 给定整数 capacity 和一个数组 trips, 表示第 i 次旅…

神经网络 表述(Neural Networks: Representation)

神经网络 表述(Neural Networks: Representation) 1 非线性假设 我们之前学的&#xff0c;无论是线性回归还是逻辑回归都有这样一个缺点&#xff0c;即&#xff1a;当特征太多时&#xff0c;计算的负荷会非常大。 下面是一个例子&#xff1a; 当我们使用 x 1 x_1 x1​, x 2…

Docker下安装Tomcat

目录 Tomcat简介 Tomcat安装 免修改版Tomcat安装 Tomcat简介 Tomcat是Apache软件基金会&#xff08;Apache Software Foundation&#xff09;的Jakarta 项目中的一个核心项目&#xff0c;由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持&#x…

C++算法入门练习——最短路径-多路径

现有一个共n个顶点&#xff08;代表城市&#xff09;、m条边&#xff08;代表道路&#xff09;的无向图&#xff08;假设顶点编号为从0到n-1&#xff09;&#xff0c;每条边有各自的边权&#xff0c;代表两个城市之间的距离。求从s号城市出发到达t号城市的最短路径条数和最短路…

almalinux centos8系统zlmediakit编译安装

脚本 # 安装依赖 gcc-c.x86_64 这个不加的话会有问题&#xff0c; cmake需要在线安装 sudo yum -y install gcc gcc-c libssl-dev libsdl-dev libavcodec-dev libavutil-dev ffmpeg git openssl-devel gcc-c.x86_64 cmake mkdir -p /home/zenglg cd /home/zenglg git clon…

linux 手动安装移植 haveged,解决随机数初始化慢的问题

文章目录 1、问题描述2、安装 haveged3、问题解决4、将安装好的文件跟库移植到开发板下 Haveged是一个软件工具&#xff0c;用于生成高质量的熵&#xff08;Entropy&#xff09;源&#xff0c;以供计算机系统使用。熵在计算机科学中指的是一种随机性或不可预测性的度量&#xf…

JavaScript 数据结构

JavaScript 数据结构 目录 JavaScript 数据结构 一、标识符 二、关键字 三、常量 四、变量 每一种计算机编程语言都有自己的数据结构&#xff0c;JavaScript脚本语言的数据结构包括&#xff1a;标识符、常量、变量、保留字等。 一、标识符 标识符&#xff0c;说白了&…

【数据结构】拆分详解 - 堆

文章目录 前言一、堆是什么&#xff1f;二、堆的接口实现&#xff08;以小堆为例&#xff09;  0.声明  1. 创建&#xff0c;初始化  2. 销毁  3. 插入   3.1  向上调整 4. 删除   4.1 向下调整 5. 获取堆顶元素值  6. 获取有效元素个数  7. 判断是否为空 …