Deep Learning基础知识 整理

Basic Component

Loss常见损失

机器学习中监督学习的本质是给定一系列样本$(x_i,y_i)$,尝试学习$x\rightarrow y$的映射关系。损失函数是用来估量模型的输出$\hat{y}$和真实值$y$之间的差距,从而给模型的优化指引方向。

其中,前面的均值函数为经验风险,$\mathcal{L}(y_i,f(x_i;\theta))$为损失函数,后面的项$\lambda \Phi(\theta)$为结构风险,$\Phi(\theta)$衡量模型复杂度。

A loss function is a part of a cost function which is a type of an objective function.

  • loss function:对单个训练样本而言
  • cost fucntion:对整个训练集而言
  • objective function:任意希望被优化的函数

MSE(Mean Squared Error) Loss

就是L2 Loss

  • 推导:通过假设模型预测与真实值之间的误差服从高斯分布$(\mu=0,\sigma=1)$,那么对于任意$x_i$,模型生成他对应真实值$y_i$的概率为:假设N个样本点相互独立,则这组$x$得到对于$y$的概率为他们的累乘:两边取对数:去掉第一项常数,转换成最小化负对数似然(Negative Log-Likelihood)就得到了:
  • 所以在假设成立的前提下使用是比较合适的,比如回归问题。而类似分类问题就不是一个好选择了。

MAE(Mean Absolute Error) Loss

就是L1 Loss

  • 推导:假设模型预测和真实值之间的误差服从Laplace分布$(\mu=0,\beta=1)$,则对于一个$x_i$其输出值为$y_i$的概率为:同上推导:
  • MSE和MAE的区别
    • 梯度不同,MSE为$-\hat{y_i}$,MAE为$\pm1$,对于梯度下降而言,MSE更好
    • MAE对异常点更鲁棒:平方项会拉大噪点的影响

BCE(Binary Cross Entropy) Loss

先通过sigmoid将输出$\hat{y_i}$限制在$(0,1)$之间,那么对于$x_i$其被判断为正例的概率为$p(y_i=1|x_i)=\hat{y_i}$,那么对应为负例的概率为$p(y_i=0|x_i)=1-\hat{y_i}$

同上

二元的cross entropy就是

CE(Cross Entropy) Loss

类似于二分类的BCE,此处的$y_i$变成了one-hot的向量,同时模型输出的压缩从Sigmoid函数变成了Softmax函数,其压缩输出在(0,1)之间,且所有输出之和为1

其中k属于class的种类数,

又由于$y_i$是one-hot向量,上式可以写成:

其中$c_i$是$x_i$的目标类。

  • 为什么分类中不使用MSE Loss?
    • 因为输出的$\hat{y_i}$和真实值$y_i$需要在分类的类别中,其差值不是高斯分布所以效果很差。
  • 为什么分类使用交叉熵损失?
    1. 最大似然来解释,即上面的推导
    2. KL散度,让输出分布和真实分布贴近可以用KL散度表示。$KL(y_i,\hat{y_i})=\sum_{k=1}^K y_i\log y_i-\sum_{k=1}^K y_i\log \hat{y_i}$,又第一项与优化无关,就变成了Cross Entropy $-\sum_{k=1}^K y_i\log{\hat{y_i}}$

Hinge Loss

当y_i=1的时候,hinge loss会对判断为负的结果有较大惩罚,同时还会在[0,1]区间对判断为正,但是不确定的结果有较小惩罚。所以hinge loss会给出一个清晰的决策边界

Exponential Loss

Adaboost中使用

Log-Likelihood Loss

非常好的表征概率分布,在很多场景尤其是多分类,如果需要知道结果属于每个类别的置信度,那它非常适合;健壮性不强,相比于hinge loss对噪声更敏感

Focal Loss

由于在one-stage的object detection中windows的前景类别和背景的可能达到1:1000,使用CE Loss,主要的class的Loss会overwhelm数量少的class(也就是背景的判断影响了对前景的学习),所以在CE Loss的基础上改进:

Activation 激活函数

通过激活函数给网络添加增加非线性,使得NN可以学习到非线性的复杂函数

  1. 连续并可导(允许少数点不可导)的分线性函数。可导的激活函数可以直接利用数值优化的方式来学习网络参数
  2. 激活函数及其导函数要尽可能简单,有利于提高计算效率
  3. 激活函数的导函数的值域要在一个合适的区间内,不能太大也不能太小,否则会影响训练的效率和稳定性(?ReLU)

常见激活函数

  • tanh: $\tanh(x)=\frac{\exp(x)-\exp(-x)}{\exp(x)+\exp(-x)}$
    • zero-centered,[0,1]区分
  • Sigmoid: $\sigma (x)=\frac{1}{1+e^{-x}}$
    • 梯度消失,[0,1]区分
  • Softplus: $ln(1+e^{x}$
    • [0,inf]区分
  • Softmax: $\frac{e^{x_i}}{\sum_{j=1}^J e^{x_j} }\text{for} i=1,…,J$
  • ReLU: $max(0, x)$
    • 解决梯度消失的问题。[0,inf]区分
  • LeakyReLU: $\begin{cases} 0.01x & \text{ if } x<0 \\ x & \text{ if }>0 \end{cases}$
  • ELU: $f(x)=\begin{cases} x &\text{if }x >0 \\ \alpha(e^x-1) &\text{if} x\leq 0 \end{cases}$
    • relu没有负值,导致激活值的均值不在零,在多层的激活函数累加下会导致bias shift。elu既利用了负值的信息,又让均值为0.

Batch Normalization

在深层网络的训练过程中,由于网络中参数变化而引起内部节点数据分布发生变化的过程被称为Internal Covariate Shift。以MLP的一层线性变换为例:$Z^{[l]}=W^{[l]}\times input + b^{[l]}$,其中l代表层数;非线性变换为$A^{[l]}=g^{[l]}(Z^{[l]})$,g为激活函数。随着梯度下降,每一层的$W^{[l]},b^{[l]},Z^{[l]}$都会被更新,进而$A^{[l]}$也同样出现分布的改变。但是$A^{[l]}$作为第l+1层的输入,第l+1蹭的参数需要不断去适应新的数据分布的变化。

  • 上层网络需要不停调整来适应输入数据分布的变化,导致网络学习速度降低
  • 网络的训练过程容易陷入梯度饱和取,减缓网络收敛速度
    • 解释:当我们采用sigmoiod这类饱和激活函数(saturated activation function)时,随着模型训练的进行,我们的参数$W^{[l]}$会逐渐更新并变大,$Z^{[l]}=W^{[l]}A^{[l-1]}+b^{[l]}$也会随之变大,随着网络层数的加深,$z^{[l]}$越来越大,而饱和激活函数在数值较大的区域的梯度逐渐变小直至接近于0,于是更新速度就会变慢。ReLU是解决这个问题的一个方法。

要解决ICS的问题,就要解决由于梯度更新带来的数据分布变化的问题。 机器学习中白化whitening 对输入数据分布进行变化

  • 使得输入特征分布具有相同的均值与方差。其中PCA白化保证了所有特征分布均值为0,方差为1;而ZCA白化则保证了所有特征分布均值为0,方差相同;
  • 去除特征之间的相关性。
  • 但是 1. 白化计算成本太高,2. 白化过程改变了网络每一层的分布,从而影响了网络层中本身数据的表达能力。

BN具体步骤:

  1. 对每个特征进行独立的normalization,假设输入为$Z$,对于这个输入的第j个维度,
  2. Normalization操作虽然解决了ICS问题,让每一层网络的输入数据分布都变得稳定,但却导致数据表达能力的确实,另外因为所有输入均值为零,方差为1,集中在sigmoid和tanh的中间的线性区域,缺失非线性的表达能力。

    所以BN引入了两个可学习的参数$\gamma,\beta$,目的是恢复数据本身的表达能力,$\tilde{Z_j}=\gamma_j\hat{Z_j}+\beta_j$
    3.在training的时候,每次过minibatch时,记录并更新每一层的$\mu_{batch}$和$\sigma^2_{batch}$

    所以pytorch的model.train()和model.eval()会影响bn层。

  • 好处:
    • 使得网络中每层输入数据的分布相对稳定,加速模型学习速度
    • 使得模型对网络中的参数不那么敏感,简化调参过程,学习稳定
    • 允许使用saturated activation function,缓解梯度消失
    • 有一定正则化效果,相当于对每个mini-batch加随机噪音。

Convolution卷积

Input $\begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \\ \end{bmatrix}$, kernel $\begin{bmatrix} 1 & 2 \\ 3 & 4 \\ \end{bmatrix}$

Output的尺寸:$output = (input-kernel+2*padding)/stride+1$

  • 为什么用卷积代替mlp
    • 全链接层参数太多,导致训练效率低下
    • Convolution优点:
      • 局部连接local connection:局部感知野:对于一个部分,左右移动一个pixel应该不影响结果(所以mlp中连接所有点的操作过于冗余),局部内的像素连接比较紧密,对较远的像素连接稀疏,通过多层全链接,深处的一个点就相当于原图的一块区域。
      • 权值共享weight sharing:减少了weights的数量,降低了网络复杂度。kernel中每一个channel可以理解为不同的特征提取方法,比如边缘检测的算子,这些channel可以通用在整个图片上。
      • 池化Pooling:降尺寸,减少计算量,特征压缩。通过pooling层,提取特征(max pooling最大的,avg pooling平均的)来减缓卷积层对位置的敏感性。(注:conv2d配合stride同样可以达到downsample的效果,且可能可以学到不一样的特征,而pooling的操作时固定的,但是其占用的计算量和存储量更小。有文章证明conv2d with stride更好)(注2:BP的时候,avg pooling将gradient平均到上一层的每个位置,max pooling将gradient传到最大的那个位置)
  • 1x1卷积和mlp层的关系
    • 对于1x1的输入而言,两者没有区别,而对于[b,c,h,w]的输入,1x1的卷积的输出尺寸不变,[b,c’,h,w],c’可以进行升维或者降维。而对于MLP层来说,其输出维[b,c,1,1]。另外参数量在h x w的情况下会更多。
  • 空洞卷积
    • 对于每次stride,kernel对应的每一个点中间都要隔开dilation个点。以此希望在不改变参数量的情况下增加感受野。(卷积核填充0,或者输入等间隔采样)
    • 网格效应:局部信息丢失,远距离获取的信息没有相关性

Transpose Convolution转置卷积/反卷积

Input $\begin{bmatrix} a & b &c \\ d & e & f \\ g & h & i \end{bmatrix}$, kernel $\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix}$, 反卷积操作:

$H_{out}=(H_{in}−1)×stride[0]−2×padding[0]+dilation[0]×(kernel_size[0]−1)+output_padding[0]+1$

先将input两边补零(kernel_size-1):

如果stride > 1:

把kernel 180度旋转:

然后进行正常卷积。

  • ConvTranspose2d会导致什么?
    • Checkerboard Artifact 转置卷积因为stride=2和kernelsize的倍数关系,总会有些下层的点upsampling到上层的时候被多次计算,但是我们又不希望去专门设计不会overlap的filter这样会限制模型的自由度,所以提出了Nearest Neighbor Interpolation-Resize Deconvolution,Bilinear-Resize Deconvolution,虽然一定程度上能减轻棋盘格现象,但是不一定是最好的。

Optimizer优化器

待优化参数$\theta$,目标函数$f(\theta )$,学习率$\eta$

每一次epoch t迭代优化:

  1. 计算目标函数关于当前参数的梯度:$g_t=\triangledown_{\theta} f(\theta_{t})$
  2. 根据历史梯度计算一阶动量和二阶动量:$m_t=\phi(g_1,g_2,…,g_t); V_t = \psi(g_1,g_2,…,g_t)$
  3. 计算当前时刻的下降梯度: $\triangle\theta_t=-\eta\times m_t/\sqrt{V_t}$
  4. 根据下降梯度进行更新:$\theta_{t+1}=\theta_t+\triangle\theta_t$

Stochastic Gradient Descendent随机梯度下降

对于一个数据量大,复杂度高的模型,不可能一口气看完所有数据然后计算其梯度。所以才用了minibatch的方法,以一个minibatch的数据为单位,计算一个批次的梯度,然后再反向传播,并更新参数

没有动量$m_t = g_t; V_t = I^2$,所以:

其中,$g_t$参数梯度,$\eta$学习率。

  • 好处
    • 分担训练压力
    • 加速收敛,因为一个epoch中有多次更新梯度的机会。
  • 坏处
    • 开始的学习率难以确定
    • 容易陷入局部最优解:为了解决这个问题,通常我们会加入动量(momentum)保留一些历史的更新方向,希望增加优化的稳定性,降低陷入局部最优无法跳出的风险
      • 理论上,如果$m_t,m_{t-1}$的方向相同的话,梯度会变大,加快收敛。如果方向不同,则梯度会变小,抑制梯度更新的震荡增加稳定性。当训练结束的中后期,$g_t$可能趋近于0,动量可以使得梯度不为零从而跳出局部最优。

AdaGrad

自适应学习率:对于经常更新的参数,已经积累了大量关于他的只是,不希望被单个样本影响太大,希望学习率慢一点;对于偶尔更新的参数,了解的信息太少,希望从每个偶然出现的样本上多学一点,希望学习率快一些。那么应该如何度量历史更新频率呢?二阶动量,即迄今为止所有梯度值的平方和

那么我们得到:

于是学习率变成了$\frac{\eta}{\sqrt{V_t}}$,更新的越多$\sqrt{V_t}$越大,学习率就越小。

  • 但是由于$\sqrt{V_t}$是单调递增的,最后会导致学习率递减至0,提前结束学习。

AdaDelta/RMSProp

既然基于所有历史二阶动量的AdaGrad太过激进,那么何不只选择关注一段时期内,关注过去一段时间窗口的下降梯度。

指数移动平局值:

Adam(Adaptive + Momentum)

SGD的一阶动量:

加上AdaDelta的二阶动量:

得到

Some Problem

  • AdaGrad和SGD都会收敛,只是有会提前收敛的问题。但是AdaDelta和Adam由于用的是一段时间内的累积,$V_t$可能时大时小导致训练不稳定,模型无法收敛。可以通过$max(V_{t-1},Adam)$的方式保证收敛。
  • 自适应学习率算法可能会对前期出现的特征过拟合,后期才出现的特征很难纠正前期的拟合效果。