梯度下降优化方法概述

Updated on 2023-06-24: add AdamW 梯度下降是优化神经网络和机器机器学习算法的首选优化方法。本文重度参考SEBASTIAN RUDER的文章。对于英文比较好的同学请直接阅读原文。本文只为个人的学习总结,难免有所欠缺和不足。 一、梯度下降变种 根据训练数据集的大小,梯度下降有三种变体,但是本质是一样的,不一样的是每次使用多少条样本。如果内存一次可以计算所有样本的梯度,称为:批梯度下降(Batch gradient descent);如果内存一次只允许一个样本,称为:随机梯度下降(Stochastic gradient descent);大部分时候,内存一次是可以计算部分样本的,称为:最小批梯度下降(Mini-batch gradient descent)。三种变体的数据表达如下: 1.1批梯度下降(Vanilla gradient descent,又称Batch gradient descent) $\theta = \theta - \eta \cdot \nabla_\theta J( \theta)$ 1.2随机梯度下降(Stochastic gradient descent) $\theta = \theta - \eta \cdot \nabla_\theta J( \theta; x^{(i)}; y^{(i)})$ 1.3最小批梯度下降(Mini-batch gradient descent) $\theta = \theta - \eta \cdot \nabla_\theta J( \theta; x^{(i:i+n)}; y^{(i:i+n)})$ 注意,在其他地方并没对上述三种变体做严格区别,统称为SGD(随机梯度下降),下文其余部分,我们也不加区分,统称为SGD 二、梯度下降的几种优化方法 传统的梯度下降法不能保证一个很好的收敛,而且有一些挑战需要被解决。 选择这个合适的学习率是比较困难的。特别是对一个新的模型和新数据集时候,我们是不知道选择什么样的学习率是合适的。只能不断的去尝试。 学习率调度算法可以在训练的过程中去调整模型的学习率。模型一开始的时候可以使用大一点的学习率,后面再使用小一点的学习率去微调模型。更好的方法是一开始也用一个小的学习率去warm-up训练,让参数先适应数据集。但是无论哪种学习率调度算法都需要预先定义调度算法,这种方法也是没有办法很好的适应模型的特征的、 对每一个参数都使用同样的学习率是不合适的。对于稀疏的数据或者特征非常不均衡的数据。最好是使用不同学习率学习不同频率的特征。 另外的挑战是对于高阶非凸的损失函数,往往会陷于局部极值点。还有一种鞍点的情况,模型也是很难学习的。此时损失函数在各个方向的梯度接近于0。SGD是很难逃脱与鞍点或者局部极值点的。 针对上面的一些问题,慢慢出现了一些针对梯度下降的优化方法。 在介绍SGD变种之前。先给出各个变种的一般范式。后天的各个变种优化方法都离不开这个范式。 (1)计算目标函数关于参数的梯度 $g_t = \nabla_\theta J( \theta)$...

十一月 28, 2021 · 1 分钟 · Pan

pytorch ddp

(1) 关键概念 world_size: 集群中所有GPU的数量 rank: 范围[0, world_size-1], 表示GPU的编号 local_rank: GPU在每台机器上的编号 比如,两台机器,每台机器4块卡,那么world_size= 2*4, rank 取值范围 [0,1,2,3,4,5,6,7], local_rank 取值范围[0,1,2,3] (2) torch.distributed.launch 启动集群参数 –nnodes: 一共多少台机器 –node_rank: 当前机器编号 –nproc_per_node: 每台机器多少个进程 –master_adderss: master节点ip地址 –master_port: master节点端口 master节点的node_rank必须为0 command example: python -m torch.distributed.launch --nnodes=2 --node_rank=0 --nproc_per_node 8 \ --master_adderss $my_address --master_port $my_port main.py (3) mp.spwan 启动 PyTorch引入了torch.multiprocessing.spawn,可以使得单卡、DDP下的外部调用一致,即不用使用torch.distributed.launch。 python xxxx.py一句话搞定DDP模式。 def demo_fn(rank, world_size): dist.init_process_group("nccl", rank=rank, world_size=world_size) # lots of code. ... def run_demo(demo_fn, world_size): mp.spawn(demo_fn, args=(world_size,), nprocs=world_size, join=True) (4) 集群训练步骤 import torch....

九月 15, 2021 · 2 分钟 · Pan

Transformer研究综述

一、基础部分 2017年google发表了一篇All Attention Is All You Need论文, 在机器翻译任务中取得了SOTA 的成绩。论文中提出的Transformer结构取消了传统的Seg2Seg模型中RNN和CNN传统神经网络单元,取而代之代之的Self-Attention(自注意力机制)的计算单元。该计算单元并行化程度高,训练时间短。所以Transformer引起了学术界和工业界的广泛注意。目前已经是NLP领域的标配。随后在2019年google提出基本Transformer的bert模型, 该模型在大量的数据集中通过自监督的训练,然后在特定任务上只需要做少量改动和训练就可以得到非常好的效果,开源的bert模型在11个NLP任务取得SOTA结果。 Transformer除了在NLP任务上表现优异,在CV领域也取得了很多突破。2020年google又发表了一篇 VIT Transformer的论文,实验证明Transformer在imagenet分类任务上取得了SOTA结果。后来CV领域中的各种基本问题,比如目标检测、语义分割、物体追踪、视频等各种任务都用Transformer方法又搞了一遍,基本上也取得了一些不错的结果。 鉴于Transformer在NLP和CV上的巨大成功,本文竟可能详细描述Transformer的基本原理;特定的一些应用,主要是一些经典论文的方法;以及目前Transformer在效率问题上的一些改进的方案。 1.1 Attention 在学习Transformer之前,了解一些基础问题是很有必要。毕竟在没有Transformer之前,学术上在NLP领域也做了大量的研究和成果。我们先从Encoder Decoder和Seq2Seq开始说起。我想大家肯定都听过这两个名称,简单来说就是如下图。 Encoder输入(可以是文字也可以是图像)编程成一个固定长度的向量(content),那么这个content向量肯定是包含输入的信息的(至于包含多少那就看这个编码了),Decoder根据content解码出我们需要的结果。Encoder Decoder可以是机器翻译问题、语义分割问题等等。那么Seq2Seq(Sequence-to-sequence )是什么?输入一个序列,输出另一个序列。这种结构最重要的地方在于输入序列和输出序列的长度是可变的。如下图所示。 从本质上看,Encoder-Decoder和seq2seq好像差不多,但是又有一点区别。 Seq2Seq 属于 Encoder-Decoder 的大范畴 Seq2Seq 更强调目的,Encoder-Decoder 更强调方法 那么这个Encoder-Decoder有什么缺陷呢? 从上面的示意图我们看到,无论输入的信息又多少,Encoder后就剩下一个content向量了,那么这里面有一个缺陷就是这个content向量会丢掉一些信息,特别是输入很大(文本很长图像分辨率很高)的情况下。尽管后面出现的LSTM、GRU等通过门设计的循环神经网络单元,可以一定程度上缓解长距离问题,但是效果有限。 从这里开始,我们要进入文章的正题了,Transformer的核心是Self-Attention,那么在这之前,我们最起码要了解什么是Attention,然后再看是这么在 Attention的基础上加上self的。 1.1.1 NLP中的Attention 由于传统的Encoder-Decoder模型将所有的输入信息编码成一个固定长度的content向量存在长距离问题。那么随之而然的一个做法就是我们在decoder阶段解码$h_t$不仅依赖前一个节点的隐藏状态$h_{t-1}$, 同时依赖Encoder阶段所有的状态,就和我们自已翻译的时候一样。这里有两个经典注意力机制,Bahdanau Attention (2014年提出)和 Luong Attention(2015年)。 1.1.1.1 Bahdanau Attention 注意力机制 示意图如下: 假设现在我们Decoder t时刻。 那么$h_t$隐状态计算过程如下: 计算对齐向量$a_t$ $a_t$的长度与Encoder输出向量的个数相同。$a_t(s)$表示Decoder阶段的转态$h_{t-1}$与Encoder阶段第s个隐状态,通过align对齐函数计算出的一个权重。$a_t$就是$h_{t-1}与每一个Encoder隐状态计算权重后组成的一个向量。 计算$c_t$即content vector 将上一步计算出的$a_t$向量乘以Encoder所有的隐向量。即Encoder所有的隐向量的加权和。 计算Decoder阶段t时刻的输出,$h_t$ 将$h^{l-1}{t-1}$与concat($c_t$, $h{t-1}$)送入多层RNN(最后一层)。其中$h^{l-1}{t-1}$为上一阶段的预测输出。concat($c_t$, $h{t-1}$)相当于RNN的隐状态。最终将$h_t$过一个softmax就可以预测最终的输出($y^t$)了。 1.1.1.2 Luong Attention 注意力机制 Luong Attention是在Bahdanau Attention之后提出的。结构更加简洁,效果也更加好一点。 假设现在我们Decoder t时刻。 那么$h_t$隐状态计算过程如下: 计算对齐向量$a_t$ $a_t$的长度与Encoder输出向量的个数相同。$a_t(s)$表示Decoder阶段的状态$h_t$与Encoder阶段第s个隐状态,通过align对齐函数计算出的一个权重。$a_t$就是$h_t$与每一个Encoder隐状态计算权重后组成的一个向量。...

4 分钟 · Pan

VAE

updates-20241020: 添加变分相关概念 Vanilla VAE( Autoencoder) 一、AutoEncoder 回顾 生成模型 最理想的生成就是知道输入样本的分布$P(X)$, 然后我们并不知道该分布。那么可以近似求解。 $P(X) = \Sigma P(X|Z)*P(Z)$。但$P(X|Z), P(Z)$我们同样不知道。但是我们可以用神经网络去学习这两个分布。上图中的latent vector可以看成是$P(Z)$的一个采样,decoder可以看成条件概率$P(X|Z)$。但是我们真的可以采样一个z,然后用加一个decoder来作为我们的生成模型吗? z是Encoder对应着样本X的输出,如果我们直接用Decoder对z还原,那么最终得到的$\hat{X}$是和X是差不多的,我们需要生成模型是生成一个和X类似的,而不是一模一样的 如果对z做一些扰动,必然加一些噪声,那是不是就可以生成类似但是不一样的东西呢?理论上是可以,但是到目前为止,我们的模型并没有保证这一点(模型还没有学习) 加噪声是一个好的思路,如何加噪声? 让z从一个分布采样(注意不是直接使用encoder的输出),就是噪声。 那不放让z从一个$N(u, \sigma^2)$中采样。那需要知道$u, \sigma^2$, 既然不知道那就用神经网络生成吧。 如果我们按照上述去训练我们的模型,生成的方差$\sigma^2$会倾向于变成0(因为容易学)。那如何加以限制?使z倾向于一个标准正态分布,即$\sigma^2$倾向于1。 如下图 如何监督模型达到该目的,KL loss作为监督信号,KL loss如下 reparameterization trick 让z从$N(u, \sigma^2)$中采样,由于这个操作是不可导的,所以需要使用重采样技巧去解决不可导的问题。 $$z \sim N(u, \sigma^2) \iff z \sim u+\sigma^2 \times \epsilon$$ ,其中$\epsilon \sim N(0,1)$ 思考? 为什么要正态分布、其它分布可否? Variational Bayes 什么是变分贝叶斯推断?将贝叶斯后验概率计算问题转化为一个优化问题(计算->优化)。本章节汇总记录一些概念,上面讲解的部分已经可以支撑去训练一个VAE模型了,但是如果还不知道VAE中Variational的含义就有点说不过去了,我本来以为Variational是一个简单的概念,但是了解后发现并不简单,涉及一个数学分支。所以下面就记录一些涉及的相关概念,有助于大家的理解。后续我有新的认知会进一步完善。 泛函(functional) 定义1:泛函(functional)通常是指定义域为函数集,而值域为实数或者复数的映射。换而言之,泛函是从由函数组成的一个向量空间到标量域的映射。 定义2:设C是函数的集合,B是实数集合;如果对C中的任一个元素y(x),在B中都有一个元素J与之对应,则称J为y(x)的泛函,记为J[y(x)]。 通俗的讲泛函就是函数的函数,对于函数的定义我们再熟悉不过了,y = f(x),其中x是一个数值(定义域),y是一个数值(值域),f为映射关系。如果将x变成一个函数集合,那么就称之为泛函,即f[g(x)]。 变分(variational) 变分与函数的微分类似,变分为定义在泛函上的微分。g(x)和新函数g(x)+m$\eta(x)$的差导致泛函的变化就叫变分。即 $$\delta J = J[g(x)+m\eta(x)]-J(g(x))$$ ,其中$\delta J$就是变分。 变分法(Calculus of Variations or variational method) 使用变分来找到泛函的最大值和最小值的方法...

1 分钟 · Pan

机器学习基础之交叉熵和MSE

机器学习基础之交叉熵与均方误差 我们都知道,对于分类任务,可以选用交叉熵做为模型的损失函数;对于回归任务,可以选用MSE来作为模型的损失函数。那么分类任务能否选用MSE来做为损失函数;回归任务能否选用交叉熵来作为损失函数呢? 本文只能尽可能尝试回答这个问题,帮助大家有个大概的认识,目前尚无法对其做严格的数学证明。如果大家看到对这个问题有很好的数学证明,欢迎讨论 符号定义: $N$: 类别数量 $y_i$: 样本onehot编码后label $p_i$: 模型预测第i个类别的输出 那么可以用交叉熵和MSE来衡量真值和模型预测结果的偏差。公式如下: 交叉熵:$loss_{cross_entropy}=-\sum_i^N y_ilog(p_i)$ MSE: $\frac{1}{N}\sum_i^N (y_i-p_i)^2$ CE是多项式分布的最大似然; 一、为什么分类任务用交叉熵,不能用MSE 1.1 直观感受 假设真实标签为(1,0,0),预测结果一是(0.8,0.1,0.1), 二是(0.8,0.15,0.05)。那么这两个预测哪个更好一点呢? 两个预测结果的交叉熵都是$-log0.8=0.223$, 预测一的MSE=0.02, 预测二MSE=0.025。 即MSE任务预测一的结果要好于预测二。MSE潜在的会让预测结果除真实标签以后的标签趋于平均分布。但是实际上我们不能主观的认为预测结果一好于二。 1.2 凹凸性角度 1.2.1 使用sigmod激活、或者softmax,MSE是非凸的 我们知道,如果一个优化问题是凸优化,那么我们是可以找到全局最优解的。但是如果问题是非凸的,那么很有可能找的解是sub-optimal的。 我们用desmos(一个非常好的画图工具)画一个图来说明,对于分类问题,如果用MSE来作为损失函数,它的函数图像是非凸的。 这个例子使用了7个样本,每个样本只具有单个特征。我们可以看到函数图像是非凸的。 在参考文献3中,作者也给出了简单的数学证明,过程如下: 但是以上证明只是证明了最简单的情况(逻辑回归),且只有一个参数$\theta$的情况,如果要证明多元函数是凸的,需要证明黑塞矩阵的正定的,这个很难证明 1.2.2 交叉熵是凸的 还以逻辑回归为例。 $z = wx+b\ a=\sigma(z)\ P(Y=1;w)=a, P(Y=0;w)=1-a$ $\sigma=\frac{1}{1+e^(-x)}$是激活函数 交叉熵为$J(w)=-[y_ilog(a)+(1-y_i)log(1-a)]$ $\frac{\partial J(w)}{\partial w}=-x(y_i-a)$ $\frac{\partial^2 J(w)}{\partial w^2}=-x[-a*(1-a)x]=x^2a*(1-a)$, 其中$a \in (0,1)$, 所以交叉熵的二阶导是大于等于0的。所以交叉熵是凸的。 注意上述证明是特例证明,非严格证明 1.3 参数估计角度 交叉熵多项式分布的极大似然估计 对于样本${(x_1,y_1), (x_2, y_2), …,(x_N, y_N)}$,使用逻辑回归来分类,那么这批样本的极大似然估计可以用如下式子表达,其中a(x)是sigmod激活 $L(w)=\prod_{i=1}^N(a_w(x_i))^{y_i}(1-a_w(x_i))^{1-y_i}$$ 对数似然如下: $ln(L(w))=\sum_{i=1}^N[y_iln(a(x_i))+(1-y_i)ln(1-a(x_i))]$ 上述式子是不是很眼熟,其实就是交叉熵。 其实,对于分类任务不能用MSE的原因是分类需要用sigmod或者softmax来作为激活函数,导致了MSE变成了非凸的函数...

1 分钟 · Pan