正则化

early stopping

随着训练,training loss会一直减小,但是valid loss不一定会一直减小,利用validation数据集来进行early stopping。当valid loss开始增大时,及时停止训练。

L2

L(θ)=L(θ)+λ12∣∣θ∣∣2 L(\theta)=L(\theta)+\lambda\frac{1}{2}||\theta||_2 L(θ)=L(θ)+λ21θ2

新的loss函数的导数是:
∂L(θ)∂θ+λθ \frac{\partial L(\theta)}{\partial \theta}+\lambda\theta θL(θ)+λθ
那么梯度更新的公式变为:
θ=θ−η∗(∂L(θ)∂θ+λθ)θ=(1−ηλ)θ−η∗∂L(θ)∂θ \theta=\theta-\eta*(\frac{\partial L(\theta)}{\partial \theta}+\lambda\theta) \\ \theta=(1-\eta\lambda)\theta-\eta*\frac{\partial L(\theta)}{\partial\theta} θ=θη(θL(θ)+λθ)θ=(1ηλ)θηθL(θ)
我们会发现在更新参数时,比原来的更新公式多减少了ηλθ\eta\lambda\thetaηλθ
从而减低参数的数值,缓解过拟合。也叫weight decay.

L1

L(θ)=L(θ)+λ12∣∣θ∣∣1 L(\theta)=L(\theta)+\lambda\frac{1}{2}||\theta||_1 L(θ)=L(θ)+λ21θ1
新的loss函数的导数是:
∂L(θ)∂θ+λsgn(θ) \frac{\partial L(\theta)}{\partial \theta}+\lambda sgn(\theta) θL(θ)+λsgn(θ)

sgn(θ)sgn(\theta)sgn(θ)代表θ>0\theta>0θ>0则等于1,否则-1

那么梯度更新的公式变为:
θ=θ−η∗(∂L(θ)∂θ+λsgn(θ)θ=θ−ηλsgn(θ)−η∗∂L(θ)∂θ \theta=\theta-\eta*(\frac{\partial L(\theta)}{\partial \theta}+\lambda sgn(\theta) \\ \theta=\theta-\eta\lambda sgn(\theta)-\eta*\frac{\partial L(\theta)}{\partial\theta} θ=θη(θL(θ)+λsgn(θ)θ=θηλsgn(θ)ηθL(θ)

L1与L2不同的是L1每一次减小的是固定的ηλ\eta\lambdaηλ,比L2更加的稀疏。

Dropout

Dropout原理

Dropout是指在训练神经网络的时候,每当传入一批数据时,对于某一层来说,会以一定的概率p随机丢弃一些神经元。
从而可以缓解过拟合的问题。那么为什么丢弃神经元可以缓解过拟合呢?可以从多个角度解释,但是本质都是正则化。

  1. 通过丢弃神经元,网络的参数会变少,通过减少网络参数可以缓解过拟合问题;
  2. 由于丢弃神经元的随机性,每当传入一批数据,整个网络的结构就会有少许变化,这个过程会减少全体神经元之间的联合适应性,而且测试阶段通过乘以对应的(1-p)可以达到集成所有模型的目的,从而降低过拟合的风险(不同的模型不会在相同的测试集上产生同样的误差,所以集成模型是有用的);
  3. 另一方面,由于神经元可能会被随机的丢弃,所以一定程度上减少了神经元依赖单一特征的现象,并且具有收缩权重平方范数的作用。具体的说,假如第1层有4个节点,那么对于第二层的某一个节点iii来说,它和这四个节点之间的权重分别是w1i,w2i,w3i,w4iw_{1i},w_{2i},w_{3i},w_{4i}w1i,w2i,w3i,w4i,依赖单一特征是指节点iii可能十分依赖第2个和第4个节点,那么w2i,w4iw_{2i},w_{4i}w2i,w4i会很大,其它权重则比较小。比如四个权重数值分别是1,4,1,41,4,1,41,4,1,4。如果加入Dropout,由于神经元可能会被丢弃,所以第二层的节点iii不会过分的依赖某些节点,而是会均匀的分配每一个权重数值,例如2,1,3,42,1,3,42,1,3,4。注意到: 4+1+9+16=30<1+16+1+16=344+1+9+16=30<1+16+1+16=344+1+9+16=30<1+16+1+16=34。所以达到了收缩权重平方范数的作用,这也正是L2正则化的作用。

Dropout在训练阶段和测试阶段的具体细节

在训练阶段,每当取一批数据时,将Dropout作用于某一层。假如这一层有5个神经元,使用二项伯努力分布生成5个随机数,这五个随机数取值在(0,1)之间,我们以概率p丢弃神经元是指,五个随机数中比p小的数字对应的神经元会被丢弃,比p大的随机数对应的神经元保留。然后更重要的是除以概率p,这是因为我们直接将这一层的五个神经元砍掉几个,会改变这一层原本的输出分布,因此处以p之后,这一层整体输出的期望值还是不变的

测试阶段是不能用dropout的,因为dropout操作使得网络输出具有不确定性,从而给预测结果添加噪声。但是测试阶段我们需要将做了dropout操作的那一层的权重乘以1-p。这是因为在训练阶段,就某一个神经元来讲,它以p的概率被丢弃,1-p的概率会参与计算。那么测试阶段这个神经元的数值当然也要乘以1-p,这是为了和训练阶段保持数量上大小一致,不然训练阶段的权重偏高。
另一方面,假如训练了N批数据,那么这一层的网络结构会有N种,测试阶段通过对这一层的所有神经元乘以1-p,可以达到平均N个模型的目的。

Dropout代码实现

class Dropout:
    def __init__(self,keep_prob=1.0):
        '''
        keep_prob代表的是以多大概率保留神经元,凡是小于keep_prob的都保留
        默认是1.0,也就是说默认不丢弃神经元
        '''
        self.drop_prob=1.0-keep_prob#drop_prob是以多大概率丢弃神经元,凡是小于drop_prob的都丢弃
        self.mask=None#mask是一个矩阵,根据概率drop_prob从二项伯努力分布中生成随机数
    def forward(self,inputs,is_train=True):
        if is_train:
            self.mask=np.random.binomial(inputs,p=self.drop_prob)>self.drop_prob#比drop_prob小的随机数对应的神经元会被丢弃
            return inputs*self.mask/self.drop_prob
        else:
            return inputs*(1.0-self.drop_prob)
            #训练阶段以drop_prob的概率被丢弃,自然以1-drop_prob的概率保留
            #测试阶段需要乘以1-drop_prob使得训练和测试阶段的权重数值一致
    
    def backward(self,dout):
        return dout*self.mask#反向传播的时候,已经被丢弃的神经元的梯度置为0

BatchNormalization

协变量偏移(covariate shift)

在机器学习的模型中,假设输入数据的分布是稳定不变的,但是显然不现实。训练集和测试集数据之间的差异以及训练过程中输入数据的变化,这都使得输入数据的分布发生了变化,这种现象叫协变量偏移

内部协变量偏移(internal covariate shift)

我们不仅假设输入数据的分布是稳定的,还假设各个层之间的输入数据的分布也是稳定的。然而实际中,第lll层输入的数据分布会随着前面l−1l-1l1个层网络参数的变化而变化,这个现象是内部协变量偏移。

层标准化

层标准化主要解决内部协变量偏移现象。内部协变量偏移的问题:

  • 随着前面网络层参数的更新,导致后面的网络层的输入发生了变化,所以后面的层又要不断调整参数。使得学习过程不稳定。
  • 由于前面层的更新,很可能使得后面层的输入数据变得太大或者太小,掉进了激活函数的饱和区,学习难以进行下去。
  • 由于协变量偏移问题,学习率必须要小,这导致收敛慢。

层标准化的公式:
x^=γx−μσ+β \hat{x}=\gamma\frac{x-\mu}{\sigma}+\beta x^=γσxμ+β

μ\muμ是这批数据的均值,σ\sigmaσ是这批数据的标准差。

为什么要引入额外的两个参数

  • 如果不引入额外的参数,仅仅强行的将输入数据标准化成均值是0,方差是1的分布。这破坏了原始数据已经学习的分布。
  • 另外,由于每一层数据的分布都变成了均值是0,方差是1,这些数据都落在了激活函数(sigmoid,tanh)的线性区域,这使得整个网络退化成线性的,削弱了模型的非线性特征提取能力。假如γ,β\gamma,\betaγ,β后,就可以进入非线性区域而且不会使得数据值特别大进入饱和区域

常用的激活函数

激活函数的目的是为了使得神经网络可以学习数据中比较复杂的模式

sigmoid

σ(x)=11+e−x \sigma(x)=\frac{1}{1+e^{-x}} σ(x)=1+ex1
导数
σ′(x)=σ(x)(1−σ(x)) \sigma'(x)=\sigma(x)(1-\sigma(x)) σ(x)=σ(x)(1σ(x))

缺点:

  • 存在很大的梯度饱和区域,这些区域的梯度接近0,由于在反向传播过程中,涉及到链式求导,所以前面层的参数很难更新,因为传回来的梯度太小.具体一点:当第lll层用了sigmoid函数时,那么这一层反向传给l−1l-1l1层的值是dxl=dxl+1∗σ(x)∗(1−σ(x))d_{x}^{l}=d_{x}^{l+1}*\sigma(x)*(1-\sigma(x))dxl=dxl+1σ(x)(1σ(x)),显然当正向传播给第lll层的输入xxx比较小或者比较大的时候,σ(x)(1−σ(x))\sigma(x)(1-\sigma(x))σ(x)(1σ(x))都非常小,所以第l−1l-1l1层的参数很难更新。
  • sigmoid函数的输出的分布不是以0为中心的,因为输出值全部都大于0,那么后一层参数的梯度全是正的,eg:f(x)=wx+b,df(w)=xf(x)=wx+b,df(w)=xf(x)=wx+b,df(w)=x,这就导致参数只会沿着一个方向更新,不利于学习
  • sigmoid存在指数运算,计算较慢

tanh

tanh⁡(x)=1−e−2x1+e−2x \tanh(x)=\frac{1-e^{-2x}}{1+e^{-2x}} tanh(x)=1+e2x1e2x
导数
tanh⁡′(x)=1−tanh⁡2(x) \tanh'(x)=1-\tanh^2(x) tanh(x)=1tanh2(x)
特点

  • 函数的输出区间是-1到1,比sigmoid更广一些
  • 输出值的分布以0为中心
  • 同样存在较大的梯度饱和区域,所以会导致严重的梯度消失问题
  • 存在指数运算,计算速度慢
  • 相比sigmoid,更适合作为隐藏层的激活函数

ReLU家族

ReLU

f(x)=max(0,x) f(x)=max(0,x) f(x)=max(0,x)
特点:

  • 不存在指数运算,计算速度快
  • 当输入xxx大于0时,不存在梯度饱和区域,有效缓解梯度消失问题
  • 具有死亡区域,当输入xxx小于0时,反向传回来的梯度被置为0。导致该神经元不会被更新

Leaky ReLU

f(x)={x,(x>0)αx,(x≤0) f(x)=\left\{\begin{matrix} x,(x>0)\\ \alpha x,(x\leq 0) \end{matrix}\right. f(x)={x,(x>0)αx,(x0)

α\alphaα是超参数,默认是0.01

Parametric ReLU

f(x)={x,(x>0)αx,(x≤0) f(x)=\left\{\begin{matrix} x,(x>0)\\ \alpha x,(x\leq 0) \end{matrix}\right. f(x)={x,(x>0)αx,(x0)
α\alphaα是需要学习的参数

ELU

f(x)={x,(x>0)γ(ex−1),(x≤0) f(x)=\left\{\begin{matrix} x,(x>0)\\ \gamma(e^x-1),(x\leq 0) \end{matrix}\right. f(x)={x,(x>0)γ(ex1),(x0)
γ\gammaγ是需要学习的参数

  • 将原来的线性变换改为非线性

Maxout

Maxout严格来说并不是一个函数,它可以视为是ReLU的扩展,它将输入分成K个组,每一组都有自己的参数,然后取每一组里面的最大值。例如输入是xxx,那么整个maxout就是
max⁡{W1x+b1,W2x+b2,⋯ ,Wkx+bk} \max\{W_1x+b_1,W_2x+b_2,\cdots,W_kx+b_k\} max{W1x+b1,W2x+b2,,Wkx+bk}

我们可以看到当W1=0,b1=0,W2=1,b2=1W_1=0,b_1=0,W_2=1,b_2=1W1=0,b1=0,W2=1,b2=1时,就变成了ReLU
其中的所有的W,bW,bW,b都是学习的参数,所以说maxout是学习出来的激活函数。

特点:

  • 理论上k足够大的情况下,可以学习任意的凸函数
  • 参数量是k倍的增加

常用的优化算法

目前的所有优化算法都是基于梯度的。

SGD or GD

θt+1=θt−η∗gt \theta_{t+1}=\theta_{t}-\eta *g_t θt+1=θtηgt
gtg_tgt指的是当前的梯度

GD的问题:

  • 由于要遍历所有的样本,计算量大
  • 陷入平原区域后便不会再更新参数

并不是batch越大越好,虽然越大的batch,梯度估计很准确,但是一方面计算量线性增长,然而得到的梯度估计的回报却小于线性(严格来说是估计的方差降低的幅度不是线性的)。举例:当你将batch由100改为10000时,计算量增加100倍。此时你是用这10000个样本估计总体梯度,但是我们知道,样本均值的标准差是σ/n\sigma/\sqrt nσ/n,所以我们看到,此时梯度估计的标准差只是下降了10倍。另一方面,当数据重复或者相似时,那么这些数据梯度估计参数更新都是没什么贡献的,然而还需要计算。

SGD的问题:

  • SGD对学习率很敏感,学习率太大,会导致loss逐渐变大
  • SGD收敛速度慢,这是因为梯度的方向指向的是函数值变化最快的方向,但不一定是函数最小值的方向。所以SGD训练的过程中很容易出现“之”字形曲线,这是因为loss在不断震荡,特别是当输入的特征取值空间较大而且没有归一化时,“之”字形曲线越明显
  • SGD对数据质量要求高,如果数据中有噪声的时候,拿单个样本估计梯度会引入噪声,导致不收敛

GD的问题很多,其中陷入平原问题可以采用momentum,通过施加力的作用使其冲出平原。另外一个就是固定学习率,解决办法采用自适应学习率算法

SGD with Momentum

θt+1=θt−η∗mtmt=β1mt−1+(1−β1)gt \theta_{t+1}=\theta_{t}-\eta*m_t \\ m_t=\beta_1m_{t-1}+(1-\beta_1)g_t θt+1=θtηmtmt=β1mt1+(1β1)gt

这个mtm_tmt称为动量,我们可以看到,相比与SGD,多减去一个β1mt−1\beta_1m_{t-1}β1mt1,所以如果说之前的mt−1m_{t-1}mt1和当前的梯度gtg_tgt是同方向的,那么参数更新幅度会变大,有望冲出平原区域或者加快收敛。相当于给梯度一个力。

SGD with Momentum仍然是固定学习率。即使是利用学习率衰减,也是在全局的角度上来做的,我们希望不同的参数更新幅度应该不一样,也就是说不同的参数应该有不同的学习率。

AdaGrad

引入“历史梯度平方和”的概念,即对于某一个参数iii,记录这个参数过去到第ttt轮所有梯度的平方和。即:g02+g12+⋯+gt2g_0^2+g_1^2+\cdots+g_{t}^2g02+g12++gt2
然后采用
θt+1=θt−η∑k=0tgk2gt \displaystyle\theta_{t+1}=\displaystyle\theta_{t}-\displaystyle\frac{\eta}{\sqrt{\displaystyle\sum_{k=0}^{t}g_k^2}}\displaystyle g_t θt+1=θtk=0tgk2ηgt
从而不同的参数有不同的学习率。
缺点是随着训练,分母越来越大,最终可能还未真正收敛,但是已经不能更新参数了。

RMRPros

θt+1=θt−ηvtgtvt=β2vt−1+(1−β2)gt2 \displaystyle\theta_{t+1}=\displaystyle\theta_{t}-\displaystyle\frac{\eta}{\sqrt{v_t}}\displaystyle g_t \\ v_t=\beta_2v_{t-1}+(1-\beta_2)g_t^2 θt+1=θtvtηgtvt=β2vt1+(1β2)gt2

RMRPros是为了解决AdaGrad算法无限记忆过去的梯度,导致过早停止的问题

Adam

AdaGrad和RMRPros没有考虑动量,所以也是有可能陷入平原或者局部极小值处。

Adam综合了RMRPros和Momentum的思想:

由:
θt+1=θt−η∗mtmt=β1∗mt−1+(1−β1)∗gt \theta_{t+1}=\theta_t-\eta *m_t \\ m_t=\beta_1*m_{t-1}+(1-\beta_1)*g_t θt+1=θtηmtmt=β1mt1+(1β1)gt

θt+1=θt−ηvtgtvt=β2vt−1+(1−β2)gt2 \theta_{t+1}=\theta_t-\frac{\eta}{\sqrt{v_t}}g_t \\ v_t=\beta_{2}v_{t-1}+(1-\beta_2)g_t^2 θt+1=θtvtηgtvt=β2vt1+(1β2)gt2

得到:
θt+1=θt−ηv^t∗m^tm^t=mt1−β1tv^t=vt1−β2tmt=β1∗mt−1+(1−β1)∗gtvt=β2vt−1+(1−β2)gt2 \theta_{t+1}=\theta_{t}-\displaystyle\frac{\eta}{\hat{v}_t}*\hat{m}_t \\ \hat{m}_t= \frac{m_t}{1-\beta_{1}^t}\\ \hat{v}_t=\frac{v_t}{1-\beta_2^t} \\ m_t=\beta_1*m_{t-1}+(1-\beta_1)*g_t \\ v_t=\beta_{2}v_{t-1}+(1-\beta_2)g_t^2 θt+1=θtv^tηm^tm^t=1β1tmtv^t=1β2tvtmt=β1mt1+(1β1)gtvt=β2vt1+(1β2)gt2

什么是鞍点

鞍点不是局部极小值,不要混淆。鞍点是这样的点:某一维度在这个点上是极小值,而另一个维度在这个点上是极大值。鞍点处梯度是0,陷入鞍点处很难优化。

RNN LSTM GRU

RNN

ht=tanh⁡(Wxxt+Whht−1+b) h_t=\tanh(W_xx_t+W_hh_{t-1}+b) ht=tanh(Wxxt+Whht1+b)

在RNN中,梯度消失和梯度爆炸的原因在于由于参数共享导致同一个矩阵的连乘。即如果时间步是T的话,那么传给第一个时间步的梯度包含有WTW^TWT这个项。

LSTM

LSTM引入了门控的思想和一个记忆单元,门控单元用来控制参数更新的程度,记忆单元专门用来记忆过去的信息,它是隐藏的,不对外开放。

  • 首先输出门用来控制当前时间步的隐藏状态多大程度输出到外面:ht=ot⊙tanh⁡(ct)h_t=o_t\odot\tanh(c_t)ht=ottanh(ct)
  • 遗忘门用来控制上一时间步的隐藏单元的信息多大程度的被遗忘:ft⊙ct−1f_t\odot c_{t-1}ftct1
  • 然而现在只有遗忘,还需要根据当前时间步的输入更新当前时间步的记忆单元,所以定义gt=tanh⁡(Wxgxt+Whght−1+b)g_t=\tanh(W_x^gx_t+W_h^gh_{t-1}+b)gt=tanh(Wxgxt+Whght1+b)
  • 但是不能无限制的更新,所以引入输入门控制当前时间步新的信息多大程度更新到记忆单元中:it⊙gti_t\odot g_titgt
  • 最终ct=ft⊙ct−1+it⊙gtc_t=f_t\odot c_{t-1}+i_t\odot g_tct=ftct1+itgt

LSTM当前时间步的更新公式:
ct=ft⊙ct−1+it⊙gt c_t=f_t\odot c_{t-1}+i_t\odot g_{t} ct=ftct1+itgt

LSTM可以缓解梯度消失的原因在于:
对前一时刻记忆单元求导对应的导数值正是当前时刻遗忘门的值。而不同时间步的遗忘门的数值是不同的,所以没有同一个矩阵连乘的情况。另一方面,对于重要的信息,对应的遗忘门ftf_tft的数值会比较大,极端的情况是ft=1f_t=1ft=1,也就是全部保留上一时刻的信息,那么对上一时刻记忆单元的导数是1,梯度流更加稳定。进一步缓解梯度消失问题。

GRU

GRU相比于LSTM,没有了记忆单元,GRU有两个门,重置门rrr和更新门zzz
ht=(1−zt)⊙ht−1+zt⊙h^h^=tanh⁡(Wxxt+Wh(rt⊙ht−1)+b) h_t=(1-z_t)\odot h_{t-1}+z_t\odot \hat{h}\\ \hat{h}=\tanh(W_xx_t+W_h(r_t\odot h_{t-1})+b) ht=(1zt)ht1+zth^h^=tanh(Wxxt+Wh(rtht1)+b)

我们发现更新门zzz既充当了遗忘门又充当了输入门的作用。
rt⊙ht−1r_t\odot h_{t-1}rtht1指的是上一时刻的隐藏状态多大程度上更新到当前时刻的隐藏状态。
(1−zt)⊙ht−1(1-z_t)\odot h_{t-1}(1zt)ht1指的是上一时刻的隐藏状态多大程度上被遗忘。
输入门和遗忘门本就应该是此消彼长。

CNN

CNN比全连接层好在哪里

对于图像来说,全连接层丢弃了图像的空间结构信息。比如图像中相邻的两个元素经过全连接展开之后可能是两个完全不相关的元素。卷积层作用于图像之后仍然保持图像的空间结构信息。

CNN的权值共享与局部连接

局部连接是指输出层的每一个节点仅与输入层的部分区域内的节点相连接,而不是像全连接层那样输出层的节点与输入层所有节点都连接。(对于图像数据可以更好的提取局部特征)

权值共享是指输出层所有节点虽然处于不同的位置,但是与输入层的连接权值是共享的。而不像全连接层那样,输入层的每一个节点和输出层的每一个节点都有不同的权值。(权值共享大大减少了参数量,此外权值共享的物理意义要从平移不变性角度考虑)

CNN的参数量和计算量

定义几个变量:
input_width,input_height,input_channels,
filter_width,filter_height,filter_nums.
那么这一层的参数量是:
(filter_width*filter_height*input_channels)*filter_nums

如果是全连接层,那么参数量是:
(input_width*input_height*input_channels*filter_nums)

计算量上:
h=(input_height+2padding_height-filter_height)/stride_height+1
w=(input_width+2
padding_width-filter_width)/stride_width+1

则有:(filter_width*filter_height*input_channels)*filter_nums*h*w

1*1卷积核的作用

  • 跨通道信息融合(这是因为不同通道的同一个位置的元素进行了交互而不涉及其它位置,所以是跨通道信息融合)
  • 减少通道数从而达到减少计算量的目的(如果用其它尺寸的卷积核,虽然可以减少通道数目但是不会减少计算量)

为什么要在CNN和输出层之间接入几个全连接层

这是因为CNN对位置其实是不敏感的,一张猫出现在各个位置都能识别出来,这显然是位置不敏感(原因在于不同位置都是使用一个filter)。那么导致CNN对整张图片全局信息的建模能力较弱。而全连接层对于输入层的每一个元素都用不同的权值,所以可以更好的建模全局信息。

深度可分离卷积

Depthwise Seperable convolution

深度可分离卷积比传统的卷积运算会减少很多的计算量。
整体分两步
第一步,也叫Depthwise卷积,做法是将输出数据按照通道这一维度分割开来,每一个通道有一个单独的卷积核。
例如输入数据的形状是(3,7,7),3代表通道数目,两个7分别代表宽和高。
那么depthwise卷积就是:

  1. 将数据分成3个7*7的矩阵
  2. 有3个3*3的卷积核分别单独的作用于每一个矩阵,得到3个5*5的特征图,合并得到(3,5,5)形状的输出

第二步也叫pointwise卷积,我们发现第一步的卷积并没有考虑通道之间的结构信息。因此这一步采用传统的卷积方式,卷积核的形状是(3,1,1),即卷积核的通道数和输入数据的通道数相同,达到融合信息的目的。
一共有128个卷积核的话,那么输出就是(128,5,5)。
经过depthwise+pointwise后得到的数据和利用传统的卷积方式即利用128个(3,3,3)的卷积核是一样的输出形状,只不过此时极大的减少了计算量。

因为传统的卷积计算量是

(3*3*3)*(5*5)*128=86400

而depthwise卷积的计算量是:

(3*3*1)*(5*5)*3=675

pointwise卷积的计算量是:

(1*1*3)*(5*5)*128=9600

加起来才10275,是普通卷积计算量的1/8.

密集连接卷积

持续更新

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐