Jiahong的个人博客

凡事预则立不预则废


  • Home

  • Tags

  • Archives

  • Search

DL——NNLM

Posted on 2018-02-04

神经网络语言模型(Nerual Network Language Model, NNLM)

参考论文: A Neural Probabilistic Language Model
参考博客: 神经网路语言模型(NNLM)的理解


概率模型

  • 传统的n元语言模型: $$(p(w_{t}|w_{t-(n-1)},…,w_{t-1}))$$
  • 一般来说可以通过前n-1个词将预测第n个词的概率分布

NNLM模型原理

  • NNLM模型直接通过一个神经网络结构对n元条件概率进行评估

模型结构

  • NNLM网络结构图如下:

数据集预处理

  • 对于给定的预料库,我们需要生成训练数据集(大量训练样本的集合),单个样本如下是长度为n的序列\((w_{1},…,w_{n})\),其中\((w_{1},…,w_{n-1})\)对应训练样本特征值,训练样本标记为\(w_{n}\),通常可以用One-Hot编码(一个维度为|V|的向量)

模型分析

模型分析主要介绍前向传播过程

输入与输出
  • 将构造的数据集作为训练样本集
  • 其中每个样本输入为\((w_{1},…,w_{n-1})\),输出为一个向量(维度为|V|),向量代表词的分布,该分布应该与词\(w_{n}\)的One-Hot编码(也是一个|V|维向量)尽量匹配,输出误差就是这两个向量的差异大小(不同损失函数均通过将上述两个向量作为输入,输出一个标量等(也可能n为向量,此时按照不同维度分别计算,或者是其他的值)从而实现当前样本损失的计算
模型结构分析
  • \(x=(C_{j|w_{j}=w_{1}};…;C_{j|w_{j}=w_{n-1}})\)

    • x为输入向量,\(x\in R^{(n-1)m}\),x是词序列\((w_{1},…,w_{n-1})\)对应的拼接向量,其中每个词都会先被矩阵C映射成一个m维的向量,将(n-1)维的向量拼接起来就得到了x
  • \(y=b+Wx+Utanh(d+Hx)\)

    • y为输出向量,\(y\in R^{|V|}\),\(y_{i}\)表示\(w_{i}\)是第n个单词的概率
模型参数分析
  • C:映射矩阵\(C\in R^{|V|\times m}\),其中矩阵的第j行\(C_{j}\)是词\(w_{j}\)对应的特征向量,m为特征向量的维度

  • H:输入层到隐含层的权重矩阵\(H\in R^{(n-1)m\times h}\),其中h为隐含层神经元的数量

  • W:输入层到输出层的权重矩阵\(W\in R^{(n-1)m\times |V|}\),W是可选参数,对应模型结构图中的绿色虚线,如果输入层输出层不直接相连,则直接令\(W=0\)即可

  • U:隐含层到输出层的权重矩阵\(U\in R^{h\times |V|}\)

  • b:输出层的偏执参数

  • d:隐含层的偏执参数

模型训练

损失函数
  • 似然函数:\(L(\theta)=\prod_{t=1}^{T} p(w_{t-(n-1)},…,w_{t-1},w_{t}|\theta)+R(\theta)\)
    • T为训练集D中的样本总数,即\(T=|D|\)
    • \(R(\theta)\)是正则项
  • 对数似然函数:\(L(\theta)=\sum_{t=1}^{T} log(p(w_{t-(n-1)},…,w_{t-1},w_{t})|\theta) + R(\theta)\),其中T为训练集D中的样本总数,即\(T=|D|\)
  • 我们选择优化对数似然函数,原因如下:
    • 似然函数连乘操作造成浮点数溢出,乘积越来越小(概率值都在[0,1]之间)
    • 似然函数连乘操作耗时大于对数似然函数的连加操作
    • 取对数的操作可以同时把函数中其他的指数项(比如出现在正则项\(R(\theta)\)中)中的处理成连加,减少运算量
    • 对数似然函数的单调性与似然函数相同,最大化对数似然函数等价于最大化似然函数(最重要的一点)
训练目标
  • 最大化对数似然函数(等价于最大化似然函数)
参数迭代
  • 使用梯度上升法,每轮迭代时朝着正梯度方向移动
    • \(\theta=\theta+\lambda\frac{\partial L(\theta)}{\partial\theta}\)
    • \(\lambda\)为步长

总结

  • NNLM模型使用了低维连续的词向量对上文进行表示,这解决了词袋模型带来的数据稀疏、语义鸿沟等问题
  • 相比传统模型,NNLM是一种更好的n元语言模型(NNLM的n元不是由神经元决定,而是在根据语料库生成训练数据时单个训练样本中包含的词数,也就是窗口大小)
    • n元模型指的是跟军前n-1个词预测第n个词的语言模型,而不是根据前n个词生成第n+1个词的模型
  • 根据相似的上下文语境,NNLM模型可以预测出相似的目标词,而传统模型无法做到这一点

DL——softmax遇上交叉熵损失时的梯度计算

Posted on 2018-02-04

softmax

基本定义

展开定义


softmax求导


交叉熵定义

Cross Entropy Loss


梯度计算

偏置量的梯度

权重参数的梯度

同偏置量的梯度计算相似步骤可得

DL——为什么Dropout能防止过拟合

Posted on 2018-02-04
  • 参考博客: https://blog.csdn.net/dQCFKyQDXYm3F8rB0/article/details/81976571

关于Dropout

  • 用途是防止过拟合,关于过拟合的讲解可参考
    • DL——深度学习中降低过拟合的方法
    • ML——模型的方差与偏差(机器学习中的正则化与过拟合)

定义

  • dropout是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而每一个mini-batch都在训练不同的网络。(因为每一轮被丢弃的神经元不同)

应用

  • 在CNN中防止过拟合的效果明显
  • 一般选择0.5比较好,因为0.5的时候Dropout随机生成的网络结果最多,但是实际使用中一般需要调节甚至变化
    • 亲测: 在使用VGG16模型迁移学习来分类Dogs2Cats数据集时,先使用0.5,然后再使用0.2略优于一直使用0.5的情况

Why能防止过拟合?

  • 虽然Dropout在实际应用中的确能防止过拟合,但是关于Dropout防止过拟合的原理,大家众说纷纭
  • 下面介绍两个主流的观点

组合派观点

集成学习方法论
  • 传统神经网络的缺点: 费时, 容易过拟合
  • 过拟合是很多机器学习的通病
  • 一种修改模型的过拟合解决思路是: 采用Ensemble方法的Bagging方法(平均多个模型的结果,从而能够减少模型的方差,同时减轻过拟合)或者Boosting方法(减小模型的偏差,同时能减轻过拟合?[待更新]),即训练多个模型做组合
  • 但是解决了过拟合后, 费时就成为一个大问题,不仅训练起来费时,测试起来多个模型也很费时
  • Dropout能同时解决以上问题:
    • Dropout的示意图如下: 左图是原图结构,右图是加入Dropout层的
    • 从图上可以看出,有了Dropout,训练的模型就可以看成是多个模型的组合,最终预测时丢弃Dropout即可的到所有模型的组合,从而实现类似于Ensemble方法的Bagging方法,实现了多个模型的组合
动机论
  • 虽然直观上看dropout是ensemble在分类性能上的一个近似,然而实际中,dropout毕竟还是在一个神经网络上进行的,只训练出了一套模型参数。那么他到底是因何而有效呢?

  • 首先分析一个小故事

在自然界中,在中大型动物中,一般是有性繁殖,有性繁殖是指后代的基因从父母两方各继承一半。但是从直观上看,似乎无性繁殖更加合理,因为无性繁殖可以保留大段大段的优秀基因。而有性繁殖则将基因随机拆了又拆,破坏了大段基因的联合适应性。但是自然选择中毕竟没有选择无性繁殖,而选择了有性繁殖,须知物竞天择,适者生存。我们先做一个假设,那就是基因的力量在于混合的能力而非单个基因的能力。不管是有性繁殖还是无性繁殖都得遵循这个假设。为了证明有性繁殖的强大,我们先看一个概率学小知识。

  • 基本思想: 有性繁殖的方式不仅仅可以将优秀的基因传下来,还可以降低基因之间的联合适应性,使得复杂的大段大段基因联合适应性变成比较小的一个一个小段基因的联合适应性。

  • dropout也能达到同样的效果,它强迫一个神经单元,和随机挑选出来的其他神经单元共同工作,达到好的效果。消除减弱了神经元节点间的联合适应性,增强了泛化能力。

噪声派观点

  • 对于每一个dropout后的网络,进行训练时,相当于做了Data Augmentation,因为,总可以找到一个样本,使得在原始的网络上也能达到dropout单元后的效果。 比如,对于某一层,dropout一些单元后,形成的结果是(1.5,0,2.5,0,1,2,0),其中0是被drop的单元,那么总能找到一个样本(新样本),使得结果也是如此。这样,每一次dropout其实都相当于增加了样本。
噪声派观点总结
  • 将dropout映射回得样本训练一个完整的网络,可以达到dropout的效果。
  • dropout由固定值变为一个区间,可以提高效果
  • 将dropout后的表示映射回输入空间时,并不能找到一个样本 x 使得所有层都能满足dropout的结果,但可以为每一层都找到一个样本,这样,对于每一个dropout,都可以找到一组样本可以模拟结果。

其他需要注意的点

  • 数据量小的时候,dropout效果不好,数据量大了,dropout效果好。
  • dropout的缺点就在于训练时间是没有dropout网络的2-3倍。

DL——关于参数的初始化

Posted on 2018-02-04

为什么参数不能初始化为全0?

  • 因为此时会导致同一隐藏层的神经元互相对称,可以通过递推法证明,不管迭代多少次,此时所有的神经元都将计算完全相同的函数
  • 并不会因为参数都为0就导致所有神经元死亡!

为什么参数不能初始化为太大的数值?

  • 因为参数太大会导致sigmoid(z)或tanh(z)中的z太大,从而导致梯度太小而更新太慢

  • 如果网络中完全没有sigmoid和tanh等激活函数,那就还好,但是要注意,二分类中使用sigmoid函数于输出层时也不应该将参数初始化太大

  • 单层隐藏层的神经网络一般这样初始化:

    1
    W = np.random.randn((n1, n2)) * 0.01
    • 适用于单层隐藏层神经网络的参数
    • 如果是深层网络则要考虑使用其他常数而不是0.01

神经网络的层数也可当做参数

  • 不是越深越好
  • 一个问题的开始一般从单层网络开始,即Logistic回归开始
  • 逐步加深网络层数,不断测试效果,寻找合适的网络层数即可

DL——各种梯度下降相关的优化算法

Posted on 2018-02-04

本文从梯度下降(Gradient Descent, GD)开始,讲述深度学习中的各种优化算法

参考文章:【干货】深度学习必备:随机梯度下降(SGD)优化算法及可视化


三种梯度下降框架

随机梯度下降

核心思想
  • 每次从随机从训练集中选择一个训练样本来计算误差,进而更新模型参数
  • 单次迭代时参数移动方向可能不太精确甚至相反,但是最终会收敛
  • 单次迭代的波动也带来了一个好处,可以到达一个更好的局部最优点,甚至到达全局最优点
参数更新公式

Stochastic Gradient Descent, SGD

  • 公式: \(\theta=\theta-\lambda\frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta}\)
  • 其中:\(L(\theta;x_{i};y_{i})=L(f(\theta;x_{i}),y_{i})\)

批量梯度下降

Batch Gradient Descent, BGD

核心思想
  • 每次使用全量的训练集样本(假设共m个)来计算误差,进而更新模型参数
  • 每次参数能够朝着正确的方向移动
  • 每次遍历所有数据,耗费时间较长
参数更新公式
  • 公式: \(\theta=\theta-\lambda\frac{\partial L(\theta;x_{1:m};y_{1:m})}{\partial \theta}\)
  • 一般来说: \(L(\theta;x_{1:m};y_{1:m}) = \frac{1}{m}\sum_{i=1}^{m} L(\theta;x_{i};y_{i})\)

小批量梯度下降

核心思想
  • 每次从随机从训练集中选择k(k < m)个训练样本来计算误差,进而更新模型参数
  • 介于SGD和BGD之间
    • 波动小
    • 内存占用也相对较小
参数更新公式

Mini-Batch Gradient Descent, MBGD

  • 公式: \(\theta=\theta-\lambda\frac{\partial L(\theta;x_{i:i+k};y_{i:i+k})}{\partial \theta}\)
  • 一般来说: \(L(\theta;x_{1:k};y_{1:k}) = \frac{1}{k}\sum_{i=1}^{k} L(\theta;x_{i};y_{i})\)

总结

优点
  • 梯度下降算法应用广泛,算法效果很好
缺点
学习速率
  • 大小很难确定,太大容易震荡,太小则收敛太慢
  • 学习速率一般为定值,有时候会实现为逐步衰减
  • 但是无论如何,都需要事前固定一个值,因此无法自适应不同的数据集特点
局部最优
  • 对于非凸的目标函数,容易陷入局部极值点中
  • 比局部极值点更严重的问题:有时候会嵌入鞍点?

SD算法的优化

Momentum法(动量法)

核心思想
  • 考虑一种情况,在峡谷地区(某些方向比另一些方向陡峭很多)
    • SGD(或者MBGD,实际上,SGD是特殊的MBGD,平时可以认为这两者是相同的东西)会在这些放附近振荡,从而导致收敛速度变慢
    • 这里最好的例子是鞍点,鞍点出的形状像一个马鞍,一个方向两头上翘,一个方向两头下垂,当上翘的方向比下垂的方向陡峭很多时,SDG和MDG等方法容易在上翘方向上震荡
  • 此时动量可以使得
    • 当前梯度方向与上一次梯度方向相同的地方进行加强,从而加快收敛速度
    • 当前梯度方向与上一次梯度方向不同的地方进行削减,从而减少振荡
  • 动量可以理解为一个从山顶滚下的小球,遇到新的力(当前梯度)时,会结合之前的梯度方向决定接下来的运动方向
参数更新公式
  • 公式: \(\theta=\theta-m_{t}\)
    • \(m_{t}\)表示当前下降方向, \(m_{t-1}\)表示上一次的下降方向
    • \(m_{t}=\gamma m_{t-1}+\lambda\frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta}\)
    • \(\gamma<1\),值一般取0.9
    • \(\gamma m_{t-1}\)是动量项
    • \(\gamma\)是衰减量
    • \(\lambda\)是学习率
图示
小结
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本\({x_{1},…,x_{m}}\),以及相关的输出\({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新v和参数\(\theta\)

NAG,涅斯捷罗夫梯度加速法

Nesterov Accelerated Gradient,NAG

核心思想
  • 继续考虑普通的SDG算法,添加了Momentum,此时从山顶滚下的球会盲目的选择斜坡
  • 更好的方式是在遇到向上的斜坡时减慢速度
  • NAG在计算梯度时首先获取(近似获得)未来的参数而不是当前参数,然后计算未来参数对应的损失函数的梯度
  • NAG在预测了未来的梯度后,根据未来(\(\theta - \gamma m_{t-1}\))梯度方向和之前梯度的方向决定当前的方向, 这样可以保证在遇到下一点为上升斜坡时适当减慢当前点的速度(否则可能由于惯性走上斜坡, 提前知道\(\theta - \gamma m_{t-1}\)处的梯度, 从而保证不要走上去), 从而找到了比Momentum超前的更新方向
  • 对比: Momentum是根据当前梯度方向和之前梯度方向决定当前的方向
参数更新公式
  • 公式: \(\theta=\theta-m_{t}\)
    • \(m_{t}=\gamma m_{t-1}+\lambda\frac{\partial L(\theta - \gamma v_{t-1};x_{i};y_{i})}{\partial \theta}\)
    • NAG使用的是未来的梯度方向(Momentum使用的是当前梯度方向)和之前的梯度方向
图示
  • Momentum(动量)法首先计算当前的梯度值(小蓝色向量),然后在更新的积累向量(大蓝色向量)方向前进一大步
  • NAG 法则首先(试探性地)在之前积累的梯度方向(棕色向量)前进一大步,再根据当前地情况修正,以得到最终的前进方向(绿色向量)
  • 这种基于预测的更新方法,使我们避免过快地前进,并提高了算法地响应能力(responsiveness),大大改进了 RNN 在一些任务上的表现
    • 公式中\(-\gamma m_{t-1}\)对应BC向量
    • \(\theta-\gamma m_{t-1}\)就对应C点(参数)
小结
  • Momentum和NAG法可以使得参数更新过程中根据随时函数的斜率自适应的学习,从而加速SGD的收敛
  • 实际应用中,NAG将比Momentum收敛快很多
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本\({x_{1},…,x_{m}}\),以及相关的输出\({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新v和参数\(\theta\)

Adagrad

核心思想
  • 对于较少出现的特征,使用较大的学习率更新,即对低频的参数给予更大的更新
  • 对于较多出现的特征,使用较小的学习率更新,即对高频的参数给予更小的更新
  • 很适合处理稀疏数据
参数更新公式
  • 计算梯度
    • 分量形式: \(g_{t,k} = \frac{\partial L(\theta;x_{i};y_{i})}{\theta}|_{\theta = \theta_{t-1,k}}\)
      • \(g_{t,k}\)是指第t次迭代时第k个参数\(\theta_{t-1, k}\)的梯度
      • 有些地方会这样表达: \(g_{t,k} = \frac{\partial L(\theta_{t-1,k};x_{i};y_{i})}{\theta_{t-1,k}}\)
        • 式子中使用\(\theta_{t-1, k}\)在梯度中,事实上不够严谨, 容易让人误解分子分母都不是函数,而是一个确定的值, 事实上我们是先求了导数然后再带入 \(\theta = \theta_{t-1}\) 的
    • 向量形式: \(g_{t} = \frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta}|_{\theta=\theta_{t-1}}\)
  • 此时普通的SGD如下更新参数
    • 分量形式:\(\theta_{t,k} = \theta_{t-1,k} - \lambda g_{t,k}\)
    • 向量形式:\(\theta_{t} = \theta_{t-1} - \lambda g_{t}\)
  • 而Adagrad对学习率\(\lambda\)根据不同参数进行了修正
    • 分量形式:\(\theta_{t,k} = \theta_{t-1,k} - \frac{\lambda}{\sqrt{G_{t,kk}+\epsilon}} g_{t,k}\)
      • \(G_{t,kk}=\sum_{r=1}^{t}(g_{r,k})^{2}\)
    • 向量形式:\(\theta_{t} = \theta_{t-1} - \frac{\lambda}{\sqrt{G_{t}+\epsilon}}\bigodot g_{t}\)
      • \(G_{t}=\sum_{r=1}^{t}g_{r}\bigodot g_{r}\)
      • \(\bigodot\)表示按照对角线上的值与对应梯度相乘
      • 进一步可以简化写为: \(G_t = G_{t-1} + g_t^2\)
        • 注意: 这里\(g_t^2\)是指向量按照维度分别相乘, 计算后还是原始向量维度
    • G是一个对角矩阵,对角线上的元素(\(G_{k,k}\))是从一开始到k次迭代目标函数对于参数(\(\theta_{k}\))的梯度的平方和
      • G的累计效果保证了出现次数多的参数(\(\theta_{k}\))对应的对角线上的元素(\(G_{k,k}\))大,从而得到更小的更新
    • \(\epsilon\)是一个平滑项,用于防止分母为0
  • 总结参数更新公式:
    • \(\theta_{t} = \theta_{t-1} - \frac{\lambda}{\sqrt{G_{t}+\epsilon}} g_{t}\)
    • \(g_{t} = \frac{\partial L(\theta;x_{i};y_{i})}{\partial \theta }|_{\theta = \theta_{t-1}}\)
    • \(G_t = G_{t-1} + g_t^2\)
小结
  • 在分母上累计了平方梯度和,造成训练过程中G的对角线元素越来越大,最终导致学习率非常小,甚至是无限小的值,从而学不到东西
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本\({x_{1},…,x_{m}}\),以及相关的输出\({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新G的每个元素,再根据G以及梯度计算参数更新量

Adadelta

核心思想
  • 是Adagrad的一个扩展,目标是解决Adagrad学习率单调下降的问题
  • 解决方案:只累计一段时间内的平方梯度值?
  • 实际上实现是累加时给前面的平方梯度和一个衰减值
  • 方法名delta的来源是选取部分
参数更新公式
  • 将矩阵G的每一项变成当前梯度平方加上过去梯度平方的衰减值(指数衰减)即可
    • 指数衰减:前n-1项的系数是衰减率的n-1次方
    • 实现指数衰减
    • 在Adagrad的基础上修改为: \(G_t = \gamma G_{t-1} + (1-\gamma)g_t^2\)
      • 注意: 这里\(g_t^2\)是指向量按照维度分别相乘, 计算后还是原始向量维度
    • 我们通常也把 \(G_t\) 表达为 \(E[g^2]_t\)
      • 因为修改后的 \(G_t\)可以视为于对 \(g_t^2\) 求期望(不同的\(t\)概率权重不一样的分布的期望)
      • 进一步表达为: \(E[g^2]_t = \gamma E[g^2]_{t-1} + (1-\gamma)g_t^2\)
小结
  • 经过衰减后,G的每一项(忽略掉平滑项\(\epsilon\))相当于有权重的梯度均方差(Root Mean Square, RMS),后面RMSprop算法就用了这个RMS来命名
    • 均方根的定义是:对所有数求平方和,取平均值(每一项的权重根据概率分布可以不同),再开方
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本\({x_{1},…,x_{m}}\),以及相关的输出\({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新G的每个元素,再根据G以及梯度计算参数更新量

RMSprop

Root Mean Square prop

核心思想
  • 一种适应性学习率方法,至今未公开发表
  • 是Adagrad的一个扩展,目标也是解决Adagrad学习率单调下降的问题
  • RMS的来源是由于分母相当于(忽略掉平滑项\(\epsilon\))是梯度的均方根(Root Mean Squared, RMS)
参数更新公式
  • 参见Adadelta
  • RMSprop的本质是对Adadelta简单的取之前值和当前值的权重为0.9和0.1实现指数加权平均, 即 \(\gamma = 0.9\)
  • 有些地方也说RMSprop权重取的是0.5和0.5实现指数加权平均即 \(\gamma = 0.5\)
  • 学习率\(\lambda\)一般取值为0.001
小结
  • RMSprop是Adadelta的一种特殊形式
  • Adagrad的分母不能算是均方差(即使忽略平滑项\(\epsilon\)),因为这里没有取平均值的操作
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本\({x_{1},…,x_{m}}\),以及相关的输出\({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新G的每个元素,再根据G以及梯度计算参数更新量

Adam

Adaptive Moment Estimation

核心思想
  • 一种适应性学习率方法,相当于 RMSprop + Momentum + Bias Correction
  • 像Adadelta和RMSprop一样存储了梯度的平方的指数衰减平均值
  • 像Momentum一样保持了过去梯度的指数衰减平均值
  • Bias Correction是为了得到期望的无偏估计
参数更新公式
  • \(\theta_{t} = \theta_{t-1} - \frac{\lambda}{\sqrt{\tilde{v}_t+\epsilon}} \tilde{m}_t\)
  • \(\tilde{v}_t=\frac{v_{t}}{1-\beta_{1}^{t}}\)
  • \(\tilde{m}_t=\frac{m_{t}}{1-\beta_{2}^{t}}\)
  • 梯度平方的指数衰减:\(v_{t} = \beta_{1}v_{t-1}+(1-\beta_{1})g_{t}^{2}\)
  • 梯度向量的指数衰减:\(m_{t} = \beta_{2}m_{t-1}+(1-\beta_{2})g_{t}\)
    • \(m_t\) 和 \(v_t\) 是对梯度一阶矩估计和二阶矩估计
    • \(m_t\) 和 \(v_t\) 可以看做是对期望 \(E[g]_t\) 和 \(E[g^2]_t\) 的估计
    • \(\tilde{m}_t\) 和 \(\tilde{v}_t\) 是对 \(m_t\) 和 \(v_t\) 的 Bias Correction, 这样可以近似为对对期望 \(E[g]_t\) 和 \(E[g^2]_t\) 的无偏估计
小结
  • 超参数设定推荐
    • 梯度平方衰减率:\(\beta_{1}=0.999\)
    • 梯度动量衰减率:\(\beta_{2}=0.9\)
    • 平滑项:\(\epsilon=10e^-8=1*10^{-8}\)
    • 一阶动量v,初始化为0
    • 二街动量m,初始化为0
  • 学习过程
    • 从训练集中的随机抽取一批容量为m的样本\({x_{1},…,x_{m}}\),以及相关的输出\({y_{1},…,y_{m}}\)
    • 计算梯度和误差,更新v和m,再根据v和m以及梯度计算参数更新量

各种优化方法的比较

鞍点

  • SGD optimization on saddle point

等高线表面

  • SGD optimization on loss surface contours

  • 上面两种情况都可以看出,Adagrad, Adadelta, RMSprop 几乎很快就找到了正确的方向并前进,收敛速度也相当快,而其它方法要么很慢,要么走了很多弯路才找到

  • 由图可知自适应学习率方法即 Adagrad, Adadelta, RMSprop, Adam 在这种情景下会更合适而且收敛性更好

如何选择

  • 如果数据是稀疏的,就用自适用方法,即 Adagrad, Adadelta, RMSprop, Adam
    • 因为他们能够为出现更新次数少(确切的说是梯度累计结果小)的特征分配更高的权重
  • RMSprop, Adadelta, Adam 在很多情况下的效果是相似的
  • Adam 可解释为 RMSprop + Momentum + Bias Correction
  • 随着梯度变的稀疏,Adam 比 RMSprop 效果会好
  • 整体来讲,Adam 是最好的选择
  • 很多论文里都会用 SGD,没有 momentum 等, SGD 虽然能达到极小值,但是比其它算法用的时间长,而且可能会被困在鞍点, 在不正确的方向上来回震荡
  • 如果需要更快的收敛,或者是训练更深更复杂的神经网络,需要用一种自适应的算法

DL——深度学习中降低过拟合的方法

Posted on 2018-02-04

添加Dropout

  • 详情可参考: DL——为什么Dropout能防止过拟合

参数范书惩罚

相关参数: Weight decay(权重衰减)
添加L2或L1正则化, 详情可参考: ML——模型的方差与偏差

  • L1正则化:

    • L1又称为: Lasso Regularization(稀疏规则算子)
    • 计算公式为: 参数绝对值求和
    • 意义: 趋向于让一些参数为0, 可以起到特征选择的作用
  • L2正则化:

    • L2又称为: Ridge Regression(岭回归)
    • Weight decay 是放在正则项(Regularization)前面的一个系数,正则项一般指模型的复杂度
    • Weight decay 控制模型复杂度对损失函数的影响, 若Weight Decay很大,则模型的损失函数值也就大
    • pytorch中实现了L2正则化,也叫做权重衰减,具体实现是在优化器中,参数是 weight_decay, 默认为0
  • PyTorch中的weight_decay参数说明

weight_decay (float, optional): weight decay (L2 penalty) (default: 0)

  • 我之前的实现代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # zero the parameter gradients
    optimizer.zero_grad()
    # forward
    outputs = model(inputs)
    # _, preds = torch.max(outputs.data, 1)
    loss = loss_criterion(outputs, labels)

    # L1 regularization
    l1_loss = 0
    for w in model.parameters():
    l1_loss += torch.sum(torch.abs(w))
    loss += l1_rate * l1_loss

    # backward + optimize only if in training phase
    if phase == 'train':
    loss.backward()
    optimizer.step()
    • 其中 # L1 regularization后面是添加的L1 正则化
  • 就整体而言,对比加入正则化和未加入正则化的模型,训练输出的loss和Accuracy信息,我们可以发现,加入正则化后,loss下降的速度会变慢,准确率Accuracy的上升速度会变慢,并且未加入正则化模型的loss和Accuracy的浮动比较大(或者方差比较大),而加入正则化的模型训练loss和Accuracy,表现的比较平滑。并且随着正则化的权重lambda越大,表现的更加平滑。这其实就是正则化的对模型的惩罚作用,通过正则化可以使得模型表现的更加平滑,即通过正则化可以有效解决模型过拟合的问题。

数据增强

  • 提高模型的泛化能力最好的办法是, 使用更多的训练数据进行训练
  • 创造一些假数据添加到训练集中
  • 实例:
    • AlexNet中使用对图片旋转等方式生成新的图片作为样本加入训练, 误差能降低1%

提前终止训练

  • 当发现数据在验证集上的损失趋于收敛甚至开始增加时,停止训练
  • 即使模型在验证集上的损失还在减小

参数绑定与参数共享

Soft Weight Sharing

  • 类似于CNN中卷积层的权重共享方法
  • RNN中也有权重共享, 整条时间链上的参数共享

Bagging

  • 其实bagging的方法是可以起到正则化的作用,因为正则化就是要减少泛化误差,而bagging的方法可以组合多个模型起到减少泛化误差的作用
  • 在深度学习中同样可以使用此方法,但是其会增加计算和存储的成本
    • 这一点在Kaggle比赛中有用过,的确有很大提高

Batch Normalization

  • 在Google Inception V2中所采用,是一种非常有用的正则化方法,可以让大型的卷积网络训练速度加快很多倍,同事收敛后分类的准确率也可以大幅度的提高.
  • N在训练某层时,会对每一个mini-batch数据进行标准化(normalization)处理,使输出规范到N(0,1)的正太分布,减少了Internal convariate shift(内部神经元分布的改变),传统的深度神经网络在训练是,每一层的输入的分布都在改变,因此训练困难,只能选择用一个很小的学习速率,但是每一层用了BN后,可以有效的解决这个问题,学习速率可以增大很多倍
  • 更多信息参考: DL——BN-LN-IN-GN-LRN-WN

辅助分类节点

(auxiliary classifiers)

  • 在Google Inception V1中,采用了辅助分类节点的策略,即将中间某一层的输出用作分类,并按一个较小的权重加到最终的分类结果中,这样相当于做了模型的融合,同时给网络增加了反向传播的梯度信号,提供了额外的正则化的思想.

尝试不同神经网络架构

  • 尝试替换以下方面:
    • 激活函数
    • 层数
    • 权重?
    • 层的参数?

DL——迁移学习

Posted on 2018-02-04
  • 参考博客: https://blog.csdn.net/dakenz/article/details/85954548
    [待更新]

迁移学习的描述

PyTorch——CrossEntopyLoss和NLLLoss的区别

Posted on 2018-02-04

NLLLoss

  • 使用

    1
    torch.nn.NLLLoss()
  • 具体操作

    • 返回负对数似然损失(The negative log likelihood loss)
    • 虽然命名是负对数自然损失, 但是实际上本函数不含有log操作,本函数假设log操作在输入前已经完成了
  • 常用于分类问题的损失函数

  • 一般适用于网络最后一层为log_softmax的时候

计算公式

  • 单个样本的计算公式:
    • 普通样本计算公式:
      $$loss(x, class) = -x[class]$$
    • 带有权重的单个样本计算公式:
      $$loss(x, class) = -weights[class] * x[class]$$
    • 因为多类别分类中,类别中只有一个维度是1, 其他维度都是0, 所以在计算时只考虑为1的维度就行, 为0的维度与当前类别值相乘为0
      • (在这里我们存储的不是向量,而是该为1的维度的索引,所以使用-x[class]即可直接取出该样本对应的对数似然损失,其中,取对数的操作在输入前已经完成了)
  • 批量样本的计算公式:
    • size_average=True(default):
      $$all\_loss = \frac{1}{mini\_batch\_size}\sum_i loss(x_i, class_i)$$
    • size_average=False:
      $$all\_loss = \sum_i loss(x_i, class_i)$$

CrossEntropyLoss

  • 使用

    1
    torch.nn.CrossEntropyLoss()
  • 具体操作

    • 等价于 log_softmax + torch.nn.NLLLoss()
    • 先对网络输出做对数似然, 然后再
  • softmax的定义
    $$softmax(x_{i}) = \frac{e^{x_{i}}}{\sum_{j=1}x_{j}}$$

  • log_softmax的定义
    $$log(softmax(x_{i}))$$

    • 注意: 这里的log是以e为底的对数

为什么是这种实现方式?

  • 为什么是log_softmax + torch.nn.NLLLoss()的方式而不是普通的计算方式
    • 普通的计算方式是直接对每个概率求出log值, 然后相加, 本质上是一样的
    • CrossEntropyLoss中多了个softmax是为了保证输入都是概率值
  • log(softmax(x))的优化
    • 实际上使用的是log_softmax(x)
    • log_softmax(x)的运算速度比单独计算softmax + log的速度快
    • 同时二者的运算结果相同
    • 文档中没有提到, 但是一种可能的优化方法是
      $$
      \begin{align}
      log_sofmax(x) &= log \frac{e^{x_{i}}}{\sum_{j=1}x_{j}} \\
      &= log e^{x_i} - log \sum_{j=1}x_{j} \\
      &= x_i - log \sum_{j=1}x_{j}
      \end{align}
      $$
    • 上面的式子中,只需要计算一次 \(log \sum_{j=1}x_{j}\)即可(且不同维度可重用该值), 其他的都是加减法运算

相关损失函数 BCELoss

  • 使用

    1
    torch.nn.BCELoss()
  • 具体操作

    • 就是实现了书上定义的二分类交叉熵定义

计算公式:

  • 单个样本的计算公式:
    • 普通样本计算公式:
      $$ loss(o,t)=-\frac{1}{n}\sum_i(t[i] log(o[i])+(1-t[i]) log(1-o[i])) $$
    • 带有权重的单个样本计算公式:
      $$ loss(o,t)=-\frac{1}{n}\sum_iweights[i]\ (t[i]log(o[i])+(1-t[i])* log(1-o[i])) $$
    • 因为多类别分类中,类别中只有一个维度是1, 其他维度都是0, 所以在计算时只考虑为1的维度就行, 为0的维度与当前类别值相乘为0
      • (在这里我们存储的不是向量,而是该为1的维度的索引,所以使用-x[class]即可直接取出该样本对应的对数似然损失,其中,取对数的操作在输入前已经完成了)
  • 批量样本的计算公式:
    • size_average=True(default):
      $$all\_loss = \frac{1}{mini\_batch\_size}\sum_i loss(o_i, t_i)$$
    • size_average=False:
      $$all\_loss = \sum_i loss(o_i, t_i)$$

BCELoss vs CrossEntropyLoss

  • BCELoss对应的网络只有一个输出值
  • CrossEntropyLoss对应的网络有两个输出值
  • 可以证明, 二分类时BCELoss 与 CrossEntropyLoss等价
    • 证明时, 将每个CrossEntropyLoss的计算公式中的 softmax 函数分子分母同时除以shift, 即可得到为下面的定义, 进一步可得到BCELoss的计算公式
      $$f_i(x) = \frac{e^{(x_i - shift)}} { \sum^j e^{(x_j - shift)}},shift = max (x_i)$$

相关损失函数 MultiLabelMarginLoss

  • 使用

    1
    torch.nn.MultiLabelMarginLoss()
  • 用于多标签分类的损失函数


总结

  • 一般来说直接使用CrossEntropyLoss即可
    • 二分类时还可以使用nn.BCELoss
    • 二分类时使用nn.BCELoss的话,输入的input和target维度都为n * 1的维度
    • 二分类时使用CrossEntropyLoss则输入的input为n * 2的维度
  • 如果使用NLLLoss则一定记得在输出层最后加一层log_softmax层
  • 注意,log指的是以e为底的对数函数,而不是以10为底的
    • Mac自带的计算器中log是以10为底的,ln才是以e为底的

PyTorch——backward函数详细解析

Posted on 2018-02-04

本文主要介绍PyTorch中backward函数和grad的各种用法


梯度的定义

  • \(y\)对\(x\)的梯度可以理解为: 当 \(x\) 增加1的时候, \(y\) 值的增加量
  • 如果\(x\)是矢量(矩阵或者向量等),那么计算时也需要看成是多个标量的组合来计算,算出来的值表示的也是 \(x\) 当前维度的值增加1的时候, \(y\) 值的增加量

backward基础用法

  • tensorflow是先建立好图,在前向过程中可以选择执行图的某个部分(每次前向可以执行图的不同部分,前提是,图里必须包含了所有可能情况)
  • pytorch是每次前向过程都会重新建立一个图,反向(backward)的时候会释放,每次的图可以不一样, 所以在Pytorch中可以随时使用if, while等语句
    • tensorflow中使用if, while就得在传入数据前(构建图时)告诉图需要构建哪些逻辑,然后才能传入数据运行
    • PyTorch中由于不用在传入数据前先定义图(图和数据一起到达,图构建的同时开始计算数据?)

计算标量对标量的梯度

  • 结构图如下所示

  • 上面图的代码构建如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import torch
    from torch.autograd import Variable

    w1 = Variable(torch.Tensor([2]),requires_grad=True)
    w2 = Variable(torch.Tensor([3]),requires_grad=True)
    w3 = Variable(torch.Tensor([5]),requires_grad=True)
    x = w1 + w2
    y = w2*w3
    z = x+y
    z.backward()
    print(w1.grad)
    print(w2.grad)
    print(w3.grad)
    print(x.grad)
    print(y.grad)

    # output:
    tensor([1.])
    tensor([6.])
    tensor([3.])
    None
    None
    • 从图中的推导可知,梯度符合预期
    • \(x, y\)不是叶节点,没有梯度存储下来,注意可以理解为梯度计算了,只是没有存储下来,PyTorch中梯度是一层层计算的

计算标量对矢量的梯度

  • 修改上面的构建为

    • 增加变量 \(s = z.mean\),然后直接求取\(s\)的梯度
  • 结构图如下:

  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import torch
    from torch.autograd import Variable

    w1 = Variable(torch.ones(2,2)*2,requires_grad=True)
    w2 = Variable(torch.ones(2,2)*3,requires_grad=True)
    w3 = Variable(torch.ones(2,2)*5,requires_grad=True)
    x = w1 + w2
    y = w2*w3
    z = x+y
    # z.backward()
    s = z.mean()
    s.backward()
    print(w1.grad)
    print(w2.grad)
    print(w3.grad)
    print(x.grad)
    print(y.grad)
    # output:
    tensor([[0.2500, 0.2500],
    [0.2500, 0.2500]])
    tensor([[1.5000, 1.5000],
    [1.5000, 1.5000]])
    tensor([[0.7500, 0.7500],
    [0.7500, 0.7500]])
    None
    None
    • 显然推导结果符合代码输出预期
    • 梯度的维度与原始自变量的维度相同,每个元素都有自己对应的梯度,表示当当前元素增加1的时候, 因变量值的增加量

计算矢量对矢量的梯度

  • 还以上面的结构图为例

  • 直接求中间节点 \(z\) 关于自变量的梯度

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import torch
    from torch.autograd import Variable

    w1 = Variable(torch.ones(2,2)*2, requires_grad=True)
    w2 = Variable(torch.ones(2,2)*3, requires_grad=True)
    w3 = Variable(torch.ones(2,2)*5, requires_grad=True)
    x = w1 + w2
    y = w2*w3
    z = x+y
    z_w1_grad = torch.autograd.grad(outputs=z, inputs=w1, grad_outputs=torch.ones_like(z))
    print(z_w1_grad)
    • 在因变量是矢量时,grad_outputs参数不能为空,标量时可以为空(grad_outputs为空时和grad_outputs维度为1时等价)
    • grad_outputs的维度必须和outputs参数的维度兼容

关于autograd.grad函数

  • 参考博客: https://blog.csdn.net/qq_36556893/article/details/91982925
grad_outputs参数详解
  • 在因变量是矢量时,grad_outputs参数不能为空,标量时可以为空(grad_outputs为空时和grad_outputs维度为1时等价)
  • grad_outputs的维度必须和outputs参数的维度兼容
    [待更新]

PyTorch——各种常用函数总结

Posted on 2018-02-04

PyTorch封装了很多有用的函数,本文主要介绍介绍其中常用的函数


torch.max

torch.min与torch.max完全类似

单参数

  • 用法

    1
    torch.max(input) -> Tensor
    • input: 一个Tensor的对象
    • return: 返回input变量中的最大值

多参数

  • 用法

    1
    torch.max(input, dim, keepdim=False, out=None) -> tuple[Tensor, Tensor]
    • input: 一个Tensor的对象
    • dim: 指明维度
      • dim=0: 生成的结果是第一维的数据为1, 对每个元素, 当前数据是遍历第一维的数据后的最大值
        • 如果数据为2维, 则搜索每一列中最大的那个元素, 且返回最大元素的行索引(实际上相当于对每个列我们要求出来一个数,这个数是遍历第一维(行)得到的), 每列返回一个行索引(该索引就是当前列中数字最大的行)
        • input 为 (2,3), 则返回 (1,3)
      • dim=1:
        • 如果数据为2维, 则搜索每一行中最大的那个元素, 且返回最大元素的列索引(实际上相当于对每个行我们要求出来一个数,这个数是遍历第2维(列)得到的), 每列返回一个列索引(该索引就是当前行中数字最大的列)
        • input 为 (2,3), 则返回 (2,1)
    • keepdim: 指明是否
1…161718…20
Joe Zhou

Joe Zhou

世界上只有一种真正的英雄主义,那就是在认清生活真相之后依然热爱生活。 ——罗曼·罗兰

195 posts
38 tags
GitHub E-Mail
© 2024 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4