本专栏将系统性地讲解计算机视觉基础知识、包含第1篇机器学习基础、第2篇深度学习基础、第3篇卷积神经网络、第4篇经典热门网络结构、第5篇目标检测基础、第6篇网络搭建及训练、第7篇模型优化方法及思路、第8篇模型超参数调整策略、第9篇模型改进技巧、第10篇模型部署基础等,全栏文章字数10万+,篇篇精品,让你轻松入门计算机视觉,欢迎大家订阅~《计算机视觉基础知识蓝皮书目录》
机器学习
机器学习就是利用计算机、概率论、统计学等知识,输入数据,让计算机学会新知识。机器学习的过程,就是训练数据去优化目标函数的过程。
深度学习
深度学习是机器学习的一个研究方向,深度学习是学习样本数据的内在规律和表示层次,这些学习过程中获得的信息对诸如文字,图像和声音等数据的解释有很大的帮助。它的最终目标是让机器能够像人一样具有分析学习能力,能够识别文字、图像和声音等数据。 深度学习是一个复杂的机器学习算法,在语音和图像识别方面取得的效果,远远超过先前相关技术。
神经网络的计算主要有两种:前向传播(foward propagation, FP)作用于每一层的输入,通过逐层计算得到输出结果;反向传播(backward propagation, BP)作用于网络的输出,通过计算梯度由深到浅更新网络参数。
假设上一层结点 i , j , k , . . . i,j,k,... i,j,k,... 等一些结点与本层的结点 w w w 有连接,那么结点 w w w 的值怎么算呢?就是通过上一层的 i , j , k , . . . i,j,k,... i,j,k,... 等结点以及对应的连接权值进行加权和运算,最终结果再加上一个偏置项(图中为了简单省略了),最后在通过一个非线性函数(即激活函数),如 R e L u ReLu ReLu, s i g m o i d sigmoid sigmoid 等函数,最后得到的结果就是本层结点 w w w 的输出。
最终不断的通过这种方法一层层的运算,得到输出层结果。
由于我们前向传播最终得到的结果,以分类为例,最终总是有误差的,那么怎么减少误差呢,当前应用广泛的一个算法就是梯度下降算法,但是求梯度就要求偏导数,下面以图中字母为例讲解一下:
设最终误差为 E E E且输出层的激活函数为线性激活函数,对于输出那么 E E E 对于输出节点 y l y_l yl 的偏导数是 y l − t l y_l - t_l yl−tl,其中 t l t_l tl 是真实值, ∂ y l ∂ z l \frac{\partial y_l}{\partial z_l} ∂zl∂yl 是指上面提到的激活函数, z l z_l zl 是上面提到的加权和,那么这一层的 E E E 对于 z l z_l zl 的偏导数为 ∂ E ∂ z l = ∂ E ∂ y l ∂ y l ∂ z l \frac{\partial E}{\partial z_l} = \frac{\partial E}{\partial y_l} \frac{\partial y_l}{\partial z_l} ∂zl∂E=∂yl∂E∂zl∂yl。同理,下一层也是这么计算,只不过 ∂ E ∂ y k \frac{\partial E}{\partial y_k} ∂yk∂E 计算方法变了,一直反向传播到输入层,最后有 ∂ E ∂ x i = ∂ E ∂ y j ∂ y j ∂ z j \frac{\partial E}{\partial x_i} = \frac{\partial E}{\partial y_j} \frac{\partial y_j}{\partial z_j} ∂xi∂E=∂yj∂E∂zj∂yj,且 ∂ z j ∂ x i = w i j \frac{\partial z_j}{\partial x_i} = w_i j ∂xi∂zj=wij。然后调整这些过程中的权值,再不断进行前向传播和反向传播的过程,最终得到一个比较好的结果。
近几年随着深度学习算法的发展,出现了许多深度学习框架。每种框架都有着自己的特色与优点,大家可以根据自己的任务需求选择优先掌握哪种框架。
谷歌的TensorFlow可以说是当今工业界最受欢迎的开源深度学习框架,可用于各类深度学习相关的任务中。
Keras是一个对小白用户非常友好且简单的深度学习框架。如果想快速入门深度学习, Keras将是不错的选择。
Keras是TensorFlow高级集成API,可以非常方便地和TensorFlow进行融合。Keras在高层可以调用TensorFlow、CNTK、Theano,还有更多优秀的库也在被陆续支持中。Keras的特点是能够快速搭建模型,是高效地进行科学研究的关键。
Keras的基本特性如下:
Caffe是由AI科学家贾扬清在加州大学伯克利分校读博期间主导开发的,是以C++/CUDA代码为主的早期深度学习框架之一,比TensorFlow、MXNet、PyTorch等都要早。Caffe需要进行编译安装,支持命令行、Python和Matlab接口,单机多卡、多机多卡等都可以很方便使用。
Caffe的基本特性如下:
Caffe的缺点也比较明显,主要包括如下几点:
Pytorch是Facebook团队于2017年1月发布的一个深度学习框架,虽然晚于TensorFlow、Keras等框架,但自发布之日起,其受到的关注度就在不断上升,目前在GitHub上的热度已经超过Theano、Caffe、MXNet等框架,目前Pytorch是学术界应用最广的框架。
PyTroch主要提供以下两种核心功能:
PyTorch的主要优点如下:
Theano诞生于2008年,由蒙特利尔大学的LISA实验室开发并维护,是一个高性能的符号计算及深度学习框架。它完全基于Python,专门用于对数学表达式的定义、求值与优化。得益于对GU的透明使用,Theano尤其适用于包含高维度数组的数学表达式,并且计算效率比较高。
因Theano出现的时间较早,后来涌现出一批基于Theano的深度学习库,并完成了对Theano的上层封装以及功能扩展。在这些派生库中,比较著名的就是Keras。Keras将一些基本的组件封装成模块,使得用户在编写、调试以及阅读网络代码时更加清晰。
CNTK(Microsoft Cognitive Toolkit)是微软开源的深度学习工具包,它通过有向图将神经网络描述为一系列计算步骤。在有向图中,叶节点表示输入值或网络参数,其他节点表示其输入上的矩阵运算。
CNTK允许用户非常轻松地实现和组合流行的模型,包括前馈神经网络(DNN)、卷积神经网络(CNN)和循环神经网络(RNN、LSTM)。与目前大部分框架一样,CNTK实现了自动求导,利用随机梯度下降方法进行优化。
CNTK的基本特性如下:
MXNet框架允许混合符号和命令式编程,以最大限度地提高效率和生产力。MXNet的核心是一个动态依赖调度程序,可以动态地自动并行化符号和命令操作。其图形优化层使符号执行更快,内存效率更高。
MXNet的基本特性如下:
ONNX(Open Neural Network eXchange,开放神经网络交换)项目由微软、亚马逊、Facebook和IBM等公司共同开发,旨在寻找呈现开放格式的深度学习模型。ONNX简化了在人工智能不同工作方式之间传递模型的过程,具有各种深度学习框架的优点。
ONNX的基本特性如下:
机器学习模型中有两类参数:
超参数 | 如何影响模型容量 | 影响原因 |
---|---|---|
学习率 | 调至最优,提升有效容量 | 过高或者过低的学习率,都会由于优化失败而导致降低模型有效容限。 |
损失函数部分超参数 | 调至最优,提升有效容量 | 损失函数超参数大部分情况都会可能影响优化,不合适的超参数会使即便是对目标优化非常合适的损失函数同样难以优化模型,降低模型有效容限。 |
批样本数量 | 过大过小,容易降低有效容量 | 大部分情况下,选择适合自身硬件容量的批样本数量,并不会对模型容限造成。 |
丢弃法 | 比率降低会提升模型的容量 | 较少的丢弃参数意味着模型参数量的提升,参数间适应性提升,模型容量提升,但不一定能提升模型有效容限 |
权重衰减系数 | 调至最优,提升有效容量 | 权重衰减可以有效的起到限制参数变化的幅度,起到一定的正则作用 |
优化器动量 | 调至最优,可能提升有效容量 | 动量参数通常用来加快训练,同时更容易跳出极值点,避免陷入局部最优解。 |
模型深度 | 同条件下,深度增加,模型容量提升 | 同条件,下增加深度意味着模型具有更多的参数,更强的拟合能力。 |
卷积核尺寸 | 尺寸增加,模型容量提升 | 增加卷积核尺寸意味着参数量的增加,同条件下,模型参数也相应的增加。 |
本质上,这是模型优化寻找最优解和正则项之间的关系。网络模型优化调整的目的是为了寻找到全局最优解,而正则项又希望模型尽量拟合到最优。两者通常情况下存在一定的对立,但两者的目标是一致的,即最小化期望风险。模型优化希望最小化经验风险,而容易陷入过拟合,正则项用来约束模型复杂度。所以如何平衡两者之间的关系,得到最优或者较优的解就是超参数调整优化的目的。
调整超参数的案例:
学习率可以说是模型训练最为重要的超参数。通常情况下,一个或者一组优秀的学习率既能加速模型的训练,又能得到一个较优甚至最优的精度。过大或者过小的学习率会直接影响到模型的收敛。我们知道,当模型训练到一定程度的时候,损失将不再减少,这时候模型的一阶梯度接近零,对应Hessian 矩阵通常是两种情况,一、正定,即所有特征值均为正,此时通常可以得到一个局部极小值,若这个局部极小值接近全局最小则模型已经能得到不错的性能了,但若差距很大,则模型性能还有待于提升,通常情况下后者在训练初最常见。二,特征值有正有负,此时模型很可能陷入了鞍点,若陷入鞍点,模型性能表现就很差。以上两种情况在训练初期以及中期,此时若仍然以固定的学习率,会使模型陷入左右来回的震荡或者鞍点,无法继续优化。所以,学习率衰减或者增大能帮助模型有效的减少震荡或者逃离鞍点。
在使用机器学习算法时,总有一些难调的超参数。例如权重衰减大小,高斯核宽度等等。这些参数需要人为设置,设置的值对结果产生较大影响。常见设置超参数的方法有:
超参数搜索一般过程:
其中,搜索过程需要搜索算法,一般有:网格搜索、随机搜过、启发式智能搜索、贝叶斯搜索。
激活函数 (Activation functions) 对于人工神经网络模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用。它们将非线性特性引入到神经网络中。在下图中,输入的 inputs 通过加权,求和后,还被作用了一个函数 f f f,这个函数 f f f就是激活函数。引入激活函数是为了增加神经网络模型的非线性。
如果不用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合,这种情况就是最原始的感知机(Perceptron)。
如果使用激活函数,激活函数给神经元引入了非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以应用到众多的非线性模型中。
如果使用线性激活函数,那么输入跟输出之间的关系为线性的,无论神经网络有多少层都是线性组合,这样就做不到用非线性来逼近任意函数。
使用非线性激活函数是为了增加神经网络模型的非线性因素,以便使网络更加强大,增加它的能力,使它可以学习复杂的事物,复杂的表单数据,以及表示输入输出之间非线性的复杂的任意函数映射。
4.4.1 Sigmoid
Sigmoid ( x ) = σ ( x ) = 1 1 + exp ( − x ) \operatorname{Sigmoid}(x)=\sigma(x)=\frac{1}{1+\exp (-x)} Sigmoid(x)=σ(x)=1+exp(−x)1
m = nn.Sigmoid()
input = torch.randn(2)
output = m(input)
优点:
Sigmoid函数的输出在(0,1)之间,输出范围有限,优化稳定,可以用作输出层。
连续函数,便于求导。
缺点:
sigmoid函数在变量取绝对值非常大的正值或负值时会出现饱和现象,意味着函数会变得很平,并且对输入的微小改变会变得不敏感。在反向传播时,当梯度接近于0,权重基本不会更新,很容易就会出现梯度消失的情况,从而无法完成深层网络的训练。
sigmoid函数的输出不是0均值的,会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响。
计算复杂度高,因为sigmoid函数是指数形式。
4.4.2 Tanh
Tanh ( x ) = tanh ( x ) = exp ( x ) − exp ( − x ) exp ( x ) + exp ( − x ) \operatorname{Tanh}(x)=\tanh (x)=\frac{\exp (x)-\exp (-x)}{\exp (x)+\exp (-x)} Tanh(x)=tanh(x)=exp(x)+exp(−x)exp(x)−exp(−x)
m = nn.Tanh()
input = torch.randn(2)
output = m(input)
4.4.3 ReLU
ReLU ( x ) = ( x ) + = max ( 0 , x ) \operatorname{ReLU}(x)=(x)^{+}=\max (0, x) ReLU(x)=(x)+=max(0,x)
m = nn.ReLU()
input = torch.randn(2)
output = m(input)
An implementation of CReLU - https://arxiv.org/abs/1603.05201
m = nn.ReLU()
input = torch.randn(2).unsqueeze(0)
output = torch.cat((m(input),m(-input)))
4.4.4 LeakyReLU
LeakyReLU ( x ) = max ( 0 , x ) + negative-slope ∗ min ( 0 , x ) \text { LeakyReLU }(x)=\max (0, x)+\text { negative-slope } * \min (0, x) LeakyReLU (x)=max(0,x)+ negative-slope ∗min(0,x)
m = nn.LeakyReLU(0.1)
input = torch.randn(2)
output = m(input)
4.4.5 Softmax
Softmax ( x i ) = exp ( x i ) ∑ j exp ( x j ) \operatorname{Softmax}\left(x_{i}\right)=\frac{\exp \left(x_{i}\right)}{\sum_{j} \exp \left(x_{j}\right)} Softmax(xi)=∑jexp(xj)exp(xi)
m = nn.Softmax(dim=1)
input = torch.randn(2, 3)
output = m(input)
4.4.6 SiLU
silu ( x ) = x ∗ σ ( x ) , where σ ( x ) is the logistic sigmoid. \operatorname{silu}(x)=x * \sigma(x) \text {, where } \sigma(x) \text { is the logistic sigmoid. } silu(x)=x∗σ(x), where σ(x) is the logistic sigmoid.
m = nn.SiLU()
input = torch.randn(2)
output = m(input)
4.4.7 ReLU6
ReLU 6 ( x ) = min ( max ( 0 , x ) , 6 ) \operatorname{ReLU} 6(x)=\min (\max (0, x), 6) ReLU6(x)=min(max(0,x),6)
m = nn.ReLU6()
input = torch.randn(2)
output = m(input)
4.4.8 Mish
Mish ( x ) = x ∗ Tanh ( Softplus ( x ) ) \operatorname{Mish}(x)=x * \operatorname{Tanh}(\operatorname{Softplus}(x)) Mish(x)=x∗Tanh(Softplus(x))
m = nn.Mish()
input = torch.randn(2)
output = m(input)
目前激活函数非常的多,选择一个最适合的激活函数并不容易,需要考虑很多因素,通常的做法是,如果不确定哪一个激活函数效果更好,可以把它们都试试,然后在验证集或者测试集上进行评价。然后看哪一种表现的更好,就去使用它。在实际应用过程中通常可以考虑目前使用最广泛的,比如ReLU,SiLU等。
以下是常见的选择情况:
深度学习中经常看到epoch、 iteration和batchsize,下面按自己的理解说说这三个的区别:
batchsize:批大小。在深度学习中,一般采用SGD训练,即每次训练在训练集中取batchsize个样本训练;
iteration:1个iteration等于使用batchsize个样本训练一次;
epoch:1个epoch等于使用训练集中的全部样本训练一次;举个例子,训练集有1000个样本,batchsize=10,那么:
训练完整个样本集需要:
100次iteration,1次epoch。
Batch 的选择,首先决定的是下降的方向。那么越准确的数据量,决定的梯度下降的方向就越准确,对于小的数据集来说,BatchSize可以选择全部数据集大小,但是对于大的数据集来说,如果BatchSize选择的过大,将导致运行内存不足,无法训练下去等问题。对于在线学习的数据集,我们把BatchSize设置为1。
BatchSize不宜选的太小,太小了容易修正方向导致不收敛,或者需要经过很大的Epoch才能收敛;也没必要选的太大,太大的话会导致显存爆炸,其次可能会因为迭代次数的减少而造成参数修正变的缓慢。如果数据集足够充分,那么用一半(甚至少得多)的数据训练算出来的梯度与用全部数据训练出来的梯度是几乎一样的。
归一化: x i − x min x max − x min 归一化:\frac{\mathrm{x}_{\mathrm{i}}-\mathrm{x}_{\min }}{\mathrm{x}_{\max }-\mathrm{x}_{\min }} 归一化:xmax−xminxi−xmin
标准化: x i − u σ 标准化:\frac{\mathrm{x}_{\mathrm{i}}-\mathrm{u}}{\sigma} 标准化:σxi−u
归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。首先归一化是为了后面数据处理的方便,其次是保证程序运行时收敛加快。归一化的具体作用是归纳统一样本的统计分布性。归一化在0-1之间是统计的概率分布,归一化在某个区间上是统计的坐标分布。
标准化和归一化本质上都是不改变数据顺序的情况下对数据的线性变化,而它们最大的不同是归一化(Normalization)会将原始数据规定在一个范围区间中,而标准化(Standardization)则是将数据调整为标准差为1,均值为0对分布。
另外归一化只与数据的最大值和最小值有关,缩放比例为 α = X m a x − X m i n α=X_{max}-X_{min} α=Xmax−Xmin,而标准化的缩放比例等于标准差 α = σ α=σ α=σ,平移量等于均值 β = μ β=μ β=μ,当除了极大值极小值的数据改变时他的缩放比例和平移量也会改变。
引入归一化,是由于不同的特征之间,其量纲或者量纲单位往往不同,变化区间也处于不同的数量级,若不进行归一化,可能导致某些指标被忽略,影响到数据分析的结果。
例如影响房价的两个特征,面积和房间数量,面积有80,90,100等,房间数有1,2,3等,这两个指标度量方式根本不在一个数量级上。
为了消除特征之间的量纲影响,需要进行归一化处理,以解决特征指标之间的可比性,原始数据,经归一化处理后,各个指标处于同一种数量级,可以直接对比评价。
上图是代表数据是否均一化的最优解寻解过程(圆圈可以理解为等高线)。左图表示未经归一化操作的寻解过程,右图表示经过归一化后的寻解过程。
当使用梯度下降法寻求最优解时,很有可能走“之字型”路线(垂直等高线走),从而导致需要迭代很多次才能收敛;而右图对两个原始特征进行了归一化,其对应的等高线显得很圆,在梯度下降进行求解时能较快的收敛。
因此如果机器学习模型使用梯度下降法求最优解时,归一化往往非常有必要,否则很难收敛甚至不能收敛。
x ′ = x − m i n ( x ) m a x ( x ) − m i n ( x ) x^{\prime} = \frac{x-min(x)}{max(x) - min(x)} x′=max(x)−min(x)x−min(x)
适用范围:比较适用在数值比较集中的情况。
缺点:如果 max 和 min 不稳定,很容易使得归一化结果不稳定,使得后续使用效果也不稳定。
x ′ = x − μ σ x^{\prime} = \frac{x-\mu}{\sigma} x′=σx−μ
含义:经过处理的数据符合标准正态分布,即均值为 0,标准差为 1 其中 μ \mu μ 为所有样本数据的均值, σ \sigma σ 为所有样本数据的标准差。
非线性归一化
适用范围:经常用在数据分化比较大的场景,有些数值很大,有些很小。通过一些数学函数,将原始值进行映射。该方法包括 l o g log log、指数,正切等。
这是一个争议比较大的方法,LRN技术主要是深度学习训练时的一种提高准确度的技术方法。其中caffe、tensorflow等里面是很常见的方法,其跟激活函数是有区别的,LRN一般是在激活、池化后进行的一种处理方法。LRN归一化技术首次在AlexNet模型中提出这个概念。通过实验确实证明它可以提高模型的泛化能力,但是提升的很少,以至于后面不再使用,甚至有人觉得它是一个“伪命题”,因而它饱受争议,在此不做过多介绍。
详细的描述大家可以参考这篇论文:http://www.cs.toronto.edu/~hinton/absps/imagenet.pdf
在之前的神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理。要知道,虽然我们对输入数据进行了归一化处理,但是输入数据经过 σ ( W X + b ) \sigma(WX+b) σ(WX+b) 这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大。如果我们能在网络的中间也进行归一化处理,是否对网络的训练起到改进作用呢?答案是肯定的。
这种在神经网络中间层也进行归一化处理,使训练效果更好的方法,就是批归一化Batch Normalization(BN)。
那么使用Batch Normalization到底有哪些优点呢?
下面给出 BN 算法在训练时的过程
输入:上一层输出结果 X = x 1 , x 2 , . . . , x m X = {x_1, x_2, ..., x_m} X=x1,x2,...,xm,学习参数 γ , β \gamma, \beta γ,β
算法流程:
μ β = 1 m ∑ i = 1 m ( x i ) \mu_{\beta} = \frac{1}{m} \sum_{i=1}^m(x_i) μβ=m1i=1∑m(xi)
其中, m m m 是此次训练样本 batch 的大小。
σ β 2 = 1 m ∑ i = 1 m ( x i − μ β ) 2 \sigma_{\beta}^2 = \frac{1}{m} \sum_{i=1}^m (x_i - \mu_{\beta})^2 σβ2=m1i=1∑m(xi−μβ)2
x ^ i = x i + μ β σ β 2 + ϵ \hat x_i = \frac{x_i + \mu_{\beta}}{\sqrt{\sigma_{\beta}^2} + \epsilon} x^i=σβ2+ϵxi+μβ
其中 ϵ \epsilon ϵ 是为了避免分母为 0 而加进去的接近于 0 的很小值
y i = γ x ^ i + β y_i = \gamma \hat x_i + \beta yi=γx^i+β
其中, γ , β \gamma, \beta γ,β 为可学习参数。
注:上述是 BN 训练时的过程,但是当在投入使用时,往往只是输入一个样本,没有所谓的均值 μ β \mu_{\beta} μβ 和标准差 σ β 2 \sigma_{\beta}^2 σβ2。此时,均值 μ β \mu_{\beta} μβ 是计算所有 batch μ β \mu_{\beta} μβ 值的平均值得到,标准差 σ β 2 \sigma_{\beta}^2 σβ2 采用每个batch σ β 2 \sigma_{\beta}^2 σβ2 的无偏估计得到。
Group Normalization是何恺明团队2018年提出来的,GN优化了BN在比较小的mini-batch情况下表现不太好的劣势。
Group Normalization(GN)首先将 Channels 划分为多个 groups,再计算每个 group 内的均值和方差,以进行归一化。 GN的计算与Batch Size无关,因此对于高精度图片小BatchSize的情况也是非常稳定的。
BN/LRN/GN都是在数据的层面上做的归一化,而Weight Normalization(WN)是对网络权值 W W W做的归一化。WN的做法是将权值向量 w w w在其欧氏范数和其方向上解耦成了参数向量 v 和参数标量 g 后使用SGD分别优化这两个参数。WN也是和样本量无关的,所以可以应用在batchsize较小以及RNN等动态网络中;另外BN使用的基于mini-batch的归一化统计量代替全局统计量,相当于在梯度计算中引入了噪声。而WN则没有这个问题,所以在生成模型,强化学习等噪声敏感的环境中WN的效果也要优于BN。
WN没有额外参数,不需要额外的存储空间来保存 mini batch 的均值和方差,这样更节约显存。同时WN的计算效率也要优于要计算归一化统计量的BN。
但是,WN不具备BN把每一层的输出Y固定在一个变化范围的作用。因此采用WN的时候要特别注意参数初始值的选择。
在具体实践中,原始论文是将批归一化放在激活函数之前的,但学术界和工业界也有不少人曾表示倾向于将批归一化放在激活函数之后(Keras作者Francois,Kaggle首席科学家jeremy Howard等),从近两年的论文来看,有一大部分是将批归一化放在激活层之后,比如MobileNet V2,ShuffleNet V2等,到底放在什么位置,还是一个比较有争议的问题。
特征提取
微调首先要弄清楚一个概念:特征提取。
用于图像分类的卷积神经网络包括两部分:一系列的卷积层和池化层(卷积基) + 一个密集连接分类器。对于卷积神经网络而言,特征提取就是取出之前训练好的网络的卷积基,用新数据训练一个新的分类器。那么为什么要重复使用之前的卷积基,而要训练新的分类器呢?这是因为卷积基学到的东西更加通用,而分类器学到的东西则针对于模型训练的输出类别,并且密集连接层舍弃了空间信息。
卷积基的通用性取决于该层在模型中的深度。模型中更靠近输入的层提取的特征更通用,更靠近输出的层提取的特征更抽象。
在特征提取时,应冻结卷积基,不对其进行训练,即训练过程中不改变卷积基的权重,只训练最后的dense层。在keras中,冻结方法为将卷积基每层的trainable属性设为False。
模型微调
模型微调与特征提取互为补充。对于用于特征提取的冻结的卷积基,微调是指将其靠近输出的几层解冻,并将这几层与分类器联合训练,让模型更加适用于当前要解决的问题。
微调案例:CNN 在图像识别这一领域取得了巨大的进步。如果想将 CNN 应用到我们自己的数据集上,这时通常就会面临一个问题:我们的 dataset 都不会特别大,一般不会超过 1 万张,甚至更少,每一类图片只有几十或者十几张。这时候,直接应用这些数据训练一个网络的想法就不可行了,因为深度学习成功的一个关键性因素就是大量带标签数据组成的训练集。如果只利用手头上这点数据,即使我们利用非常好的网络结构,也达不到很高的 performance。这时候,fine-tuning 的思想就可以很好解决我们的问题:我们通过对 ImageNet 上训练出来的模型(如CaffeNet,VGGNet,ResNet) 进行微调(即冻结卷积基,将靠近出口的部分解冻训练),然后应用到我们自己的数据集上。
方式一:只预测,不训练。
特点:相对快、简单,针对那些已经训练好,现在要实际对未知数据进行标注的项目,非常高效;
方式二:训练,但只训练最后分类层。
特点:fine-tuning的模型最终的分类以及符合要求,现在只是在他们的基础上进行类别降维。
方式三:完全训练,分类层+之前卷积层都训练
特点:跟状态二的差异很小,当然状态三比较耗时和需要训练GPU资源,不过非常适合fine-tuning到自己想要的模型里面,预测精度相比状态二也提高不少。
是否微调以及微调的方法要根据自己的数据集大小、数据集与预训练模型数据集的相似程度来选择。
不同情况下的微调方法:
数据量少,相似度高:修改最后几层;
数据量少,相似度低:保留预训练模型的前几层,训练后面的层;
数据量大,相似度高:这是最理想的情况。使用预训练的权重初始化模型,重新训练整个模型;
数据量大,相似度低:直接重新训练整个模型。
权重初始化(weight initialization)又称参数初始化,在深度学习模型训练过程的本质是对weight(即参数 W)进行更新,但是在最开始训练的时候是无法更新的,这需要每个参数有相应的初始值。在进行权重初始化后,神经网络就可以对权重参数w不停地迭代更新,以达到较好的性能。
在神经网络中全零初始化是我们要避免的,它无法训练网络。因为全零初始化后,神经网络训练时,在反向传播时梯度相同,参数更新也一样,最后会出现输出层两个权值相同,隐层神经元参数相同,也就是说神经网络失去了特征学习的能力。
通俗点说,在神经网络中考虑梯度下降的时候,设想你在爬山,但身处直线形的山谷中,两边是对称的山峰。由于对称性,你所在之处的梯度只能沿着山谷的方向,不会指向山峰;你走了一步之后,情况依然不变。结果就是你只能收敛到山谷中的一个极大值,而走不到山峰上去。
随机初始化是很多人经常使用的方法,一般初始化的权重为高斯或均匀分布中随机抽取的值。然而这是有弊端的,一旦随机分布选择不当,就会导致网络优化陷入困境。
把权重初始化为较小的值,如均值为0,方差为0.01的高斯分布,随着层数的增加,输出值迅速向0靠拢,在后几层中,几乎所有的输出值 x 都很接近0。高斯初始化,给权重较小的值,这种更新方式在小型网络中很常见,然而当网络deep的时候,会出现梯度消失的情况。
但是如果把权重初始成一个比较大的值,如均值为0,方差为1的高斯分布,几乎所有的值集中在-1或1附近,则会造成前向传播时,神经元要么被抑制,要么被饱和。当神经网络的层数增多时,会发现越往后面的层的激活函数(tanh在-1和1附近的gradient都接近0)的输出值几乎都接近于0。梯度更新时,梯度非常接近于0,就会导致梯度消失。
也称为Glorot初始化,因为发明人为Xavier Glorot。Xavier initialization是 Glorot 等人为了解决随机初始化的问题提出来的另一种初始化方法,他们的思想就是尽可能的让输入和输出服从相同的分布,这样就能够避免后面层的激活函数的输出值趋向于0。
Xavier初始化在sigmoid和tanh激活函数上有很好的表现,但是在Relu激活函数上表现很差。
He初始化是何凯明等提出的一种鲁棒的神经网络参数初始化方法,可以保证信息在前向传播和反向传播过程中能够有效流动,使不同层的输入信号的方差大致相等。He初始化对应的是非线性激活函数(Relu 和 Prelu)。
将偏差初始化为零是可能的,也是很常见的,因为非对称性破坏是由权重的小随机数导致的。因为 ReLU 具有非线性特点,所以有些人喜欢使用将所有的偏差设定为小的常数值如 0.01,因为这样可以确保所有的 ReLU 单元在最开始就激活触发(fire)并因此能够获得和传播一些梯度值。然而,这是否能够提供持续的改善还不太清楚(实际上一些结果表明这样做反而使得性能更加糟糕),所以更通常的做法是简单地将偏差初始化为 0。
好的初始化方法可以防止前向传播过程中的信息消失,也可以解决反向传递过程中的梯度消失。
激活函数选择双曲正切或者Sigmoid时,建议使用Xaizer初始化方法。
激活函数选择ReLU或Leakly ReLU时,推荐使用He初始化方法。
在机器学习中,监督式学习通过定义一个模型,并根据训练集上的数据估计最优参数。梯度下降法是一个广泛被用来最小化模型误差的参数优化算法。梯度下降法通过多次迭代,并在每一步中最小化成本函数(cost 来估计模型的参数。学习率 (learning rate),在迭代过程中会控制模型的学习进度。
在梯度下降法中,都是给定的统一的学习率,整个优化过程中都以确定的步长进行更新, 在迭代优化的前期中,学习率较大,则前进的步长就会较长,这时便能以较快的速度进行梯度下降,而在迭代优化的后期,逐步减小学习率的值,减小步长,这样将有助于算法的收敛,更容易接近最优解。故而如何对学习率的更新成为了研究者的关注点。
参数名称 | 参数说明 |
---|---|
learning_rate | 初始学习率 |
global_step | 用于衰减计算的全局步数,非负,用于逐步计算衰减指数 |
decay_steps | 衰减步数,必须是正值,决定衰减周期 |
decay_rate | 衰减率 |
end_learning_rate | 最低的最终学习率 |
cycle | 学习率下降后是否重新上升 |
alpha | 最小学习率 |
num_periods | 衰减余弦部分的周期数 |
initial_variance | 噪声的初始方差 |
variance_decay | 衰减噪声的方差 |
在模型优化中,常用到的几种学习率衰减方法有:分段常数衰减、多项式衰减、指数衰减、自然指数衰减、余弦衰减、线性余弦衰减、噪声线性余弦衰减。
以指数衰减方式进行学习率的更新,学习率的大小和训练次数指数相关,其更新规则为:
d e c a y e d _ l e a r n i n g _ r a t e = l e a r n i n g _ r a t e ∗ d e c a y _ r a t e g l o b a l _ s t e p d e c a y _ s t e p s decayed{\_}learning{\_}rate =learning{\_}rate*decay{\_}rate^{\frac{global{\_step}}{decay{\_}steps}} decayed_learning_rate=learning_rate∗decay_ratedecay_stepsglobal_step
这种衰减方式简单直接,收敛速度快,是最常用的学习率衰减方式,如下图所示,绿色的为学习率随训练次数的指数衰减方式,红色的即为分段常数衰减,它在一定的训练区间内保持学习率不变。
它与指数衰减方式相似,不同的在于它的衰减底数是 e e e,故而其收敛的速度更快,一般用于相对比较容易训练的网络,便于较快的收敛,其更新规则如下
d e c a y e d _ l e a r n i n g _ r a t e = l e a r n i n g _ r a t e ∗ e − d e c a y _ r a t e g l o b a l _ s t e p decayed{\_}learning{\_}rate =learning{\_}rate*e^{\frac{-decay{\_rate}}{global{\_}step}} decayed_learning_rate=learning_rate∗eglobal_step−decay_rate
下图为为分段常数衰减、指数衰减、自然指数衰减三种方式的对比图,红色的即为分段常数衰减图,阶梯型曲线。蓝色线为指数衰减图,绿色即为自然指数衰减图,很明可以看到自然指数衰减方式下的学习率衰减程度要大于一般指数衰减方式,有助于更快的收敛。
应用多项式衰减的方式进行更新学习率,这里会给定初始学习率和最低学习率取值,然后将会按照给定的衰减方式将学习率从初始值衰减到最低值,其更新规则如下式所示。
g l o b a l _ s t e p = m i n ( g l o b a l _ s t e p , d e c a y _ s t e p s ) global{\_}step=min(global{\_}step,decay{\_}steps) global_step=min(global_step,decay_steps)
d e c a y e d _ l e a r n i n g _ r a t e = ( l e a r n i n g _ r a t e − e n d _ l e a r n i n g _ r a t e ) ∗ ( 1 − g l o b a l _ s t e p d e c a y _ s t e p s ) p o w e r + e n d _ l e a r n i n g _ r a t e decayed{\_}learning{\_}rate =(learning{\_}rate-end{\_}learning{\_}rate)* \left( 1-\frac{global{\_step}}{decay{\_}steps}\right)^{power} \\ +end{\_}learning{\_}rate decayed_learning_rate=(learning_rate−end_learning_rate)∗(1−decay_stepsglobal_step)power+end_learning_rate
需要注意的是,有两个机制,降到最低学习率后,到训练结束可以一直使用最低学习率进行更新,另一个是再次将学习率调高,使用 decay_steps 的倍数,取第一个大于 global_steps 的结果,如下式所示.它是用来防止神经网络在训练的后期由于学习率过小而导致的网络一直在某个局部最小值附近震荡,这样可以通过在后期增大学习率跳出局部极小值。
d e c a y _ s t e p s = d e c a y _ s t e p s ∗ c e i l ( g l o b a l _ s t e p d e c a y _ s t e p s ) decay{\_}steps = decay{\_}steps*ceil \left( \frac{global{\_}step}{decay{\_}steps}\right) decay_steps=decay_steps∗ceil(decay_stepsglobal_step)
如下图所示,红色线代表学习率降低至最低后,一直保持学习率不变进行更新,绿色线代表学习率衰减到最低后,又会再次循环往复的升高降低。
余弦衰减就是采用余弦的相关方式进行学习率的衰减,衰减图和余弦函数相似。其更新机制如下式所示:
g l o b a l _ s t e p = m i n ( g l o b a l _ s t e p , d e c a y _ s t e p s ) global{\_}step=min(global{\_}step,decay{\_}steps) global_step=min(global_step,decay_steps)
c o s i n e _ d e c a y = 0.5 ∗ ( 1 + c o s ( π ∗ g l o b a l _ s t e p d e c a y _ s t e p s ) ) cosine{\_}decay=0.5*\left( 1+cos\left( \pi* \frac{global{\_}step}{decay{\_}steps}\right)\right) cosine_decay=0.5∗(1+cos(π∗decay_stepsglobal_step))
d e c a y e d = ( 1 − α ) ∗ c o s i n e _ d e c a y + α decayed=(1-\alpha)*cosine{\_}decay+\alpha decayed=(1−α)∗cosine_decay+α
d e c a y e d _ l e a r n i n g _ r a t e = l e a r n i n g _ r a t e ∗ d e c a y e d decayed{\_}learning{\_}rate=learning{\_}rate*decayed decayed_learning_rate=learning_rate∗decayed
如下图所示,红色即为标准的余弦衰减曲线,学习率从初始值下降到最低学习率后保持不变。蓝色的线是线性余弦衰减方式曲线,它是学习率从初始学习率以线性的方式下降到最低学习率值。绿色噪声线性余弦衰减方式。
在模型优化中,常用到的几种学习率衰减方法有:分段常数衰减、多项式衰减、指数衰减、自然指数衰减、余弦衰减、线性余弦衰减、噪声线性余弦衰减
分段常数衰减需要事先定义好的训练次数区间,在对应区间置不同的学习率的常数值,一般情况刚开始的学习率要大一些,之后要越来越小,要根据样本量的大小设置区间的间隔大小,样本量越大,区间间隔要小一点。下图即为分段常数衰减的学习率变化图,横坐标代表训练次数,纵坐标代表学习率。
下面介绍一些实际训练中具体的学习率调整方式
这是最简单常用的学习率调整方法,每过step_size轮,将此前的学习率乘以gamma。
scheduler=lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
MultiStepLR同样也是一个非常常见的学习率调整策略,它会在每个milestone时,将此前学习率乘以gamma。
scheduler = lr_scheduler.MultiStepLR(optimizer, milestones=[30,80], gamma=0.5)
ExponentialLR是指数型下降的学习率调节器,每一轮会将学习率乘以gamma,所以这里千万注意gamma不要设置的太小,不然几轮之后学习率就会降到0。
scheduler=lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
LinearLR是线性学习率,给定起始factor和最终的factor,LinearLR会在中间阶段做线性插值,比如学习率为0.1,起始factor为1,最终的factor为0.1,那么第0次迭代,学习率将为0.1,最终轮学习率为0.01。下面设置的总轮数total_iters为80,所以超过80时,学习率恒为0.01。
scheduler=lr_scheduler.LinearLR(optimizer,start_factor=1,end_factor=0.1,total_iters=80)
CyclicLR的参数要更多一些,它的曲线看起来就像是不断的上坡与下坡,base_lr为谷底的学习率,max_lr为顶峰的学习率,step_size_up是从谷底到顶峰需要的轮数,step_size_down时从顶峰到谷底的轮数。至于为啥这样设置,可以参见论文,简单来说最佳学习率会在base_lr和max_lr,CyclicLR不是一味衰减而是出现增大的过程是为了避免陷入鞍点。
scheduler=lr_scheduler.CyclicLR(optimizer,base_lr=0.1,max_lr=0.2,step_size_up=30,step_size_down=10)
OneCycleLR顾名思义就像是CyclicLR的一周期版本,它也有多个参数,max_lr就是最大学习率,pct_start是学习率上升部分所占比例,一开始的学习率为max_lr/div_factor,最终的学习率为max_lr/final_div_factor,总的迭代次数为total_steps。
scheduler=lr_scheduler.OneCycleLR(optimizer,max_lr=0.1,pct_start=0.5,total_steps=120,div_factor=10,final_div_factor=10)
CosineAnnealingLR是余弦退火学习率,T_max是周期的一半,最大学习率在optimizer中指定,最小学习率为eta_min。这里同样能够帮助逃离鞍点。值得注意的是最大学习率不宜太大,否则loss可能出现和学习率相似周期的上下剧烈波动。
scheduler=lr_scheduler.CosineAnnealingLR(optimizer,T_max=20,eta_min=0.05)
这里相对负责一些,公式如下,其中T_0是第一个周期,会从optimizer中的学习率下降至eta_min,之后的每个周期变成了前一周期乘以T_mult。
scheduler=lr_scheduler.CosineAnnealingWarmRestarts(optimizer, T_0=20, T_mult=2, eta_min=0.01)
LambdaLR其实没有固定的学习率曲线,名字中的lambda指的是可以将学习率自定义为一个有关epoch的lambda函数,比如下面我们定义了一个指数函数,实现了ExponentialLR的功能。
scheduler=lr_scheduler.LambdaLR(optimizer,lr_lambda=lambda epoch:0.9**epoch)
SequentialLR可以将多个学习率调整策略按照顺序串联起来,在milestone时切换到下一个学习率调整策略。下面就是将一个指数衰减的学习率和线性衰减的学习率结合起来。
scheduler=lr_scheduler.SequentialLR(optimizer,schedulers=[lr_scheduler.ExponentialLR(optimizer, gamma=0.9),lr_scheduler.LinearLR(optimizer,start_factor=1,end_factor=0.1,total_iters=80)],milestones=[50])
ChainedScheduler和SequentialLR类似,也是按照顺序调用多个串联起来的学习率调整策略,不同的是ChainedScheduler里面的学习率变化是连续的。
scheduler=lr_scheduler.ChainedScheduler([lr_scheduler.LinearLR(optimizer,start_factor=1,end_factor=0.5,total_iters=10),lr_scheduler.ExponentialLR(optimizer, gamma=0.95)])
ConstantLRConstantLR非常简单,在total_iters轮内将optimizer里面指定的学习率乘以factor,total_iters轮外恢复原学习率。
scheduler=lr_scheduler.ConstantLRConstantLR(optimizer,factor=0.5,total_iters=80)
正则化可以理解为规则化,规则就等同于一种限制。在损失函数中加入正则化项可以限制他们的拟合能力,正则化就是为了防止过拟合。
深度学习可能存在过拟合问题,有两个解决方法,一个是正则化,另一个是准备更多的数据,这是非常可靠的方法,但你可能无法时时刻刻准备足够多的训练数据或者获取更多数据的成本很高,但正则化通常有助于避免过拟合或减少你的网络误差。
假如我们要构建的模型是能够区分图中的红色与白色部分,看上图三种模型对训练集的拟合状态:
第1种模型:欠拟合(underfitting),此模型不能很好的区分图中的红色与白色部分。
第2种模型:拟合状态刚好,虽然有个别红色部分未被区分但考虑到实际测试集中会有噪声的存在,其拟合程度就刚刚好。
第3种模型:过拟合(overfitting),此种模型对于训练集的拟合程度非常高,导致其泛化能力("泛化"指的是一个假设模型能够应用到新样本的能力)较低。而且实际测试集中会有噪声的存在,在后续的测试集中得到的准确率也不高,这也会令模型的复杂度提高,让计算复杂,并不能起到理想的作用。
我们就可以使用正则化来解决过拟合,他的大致工作原理如下:
我们的目的是拟合图中的数据,对于第一幅图我们使用一个2次函数来拟合数据,这样看起来效果还不错,当我们使用一个高次函数来拟合数据时,像第二幅图,这样对于这个数据拟合的效果更加好,但这并不是我们想要的模型,因为它过度拟合了数据,我们可以想到这是由于高次项的出现,所以我们要对高次项的系数予以惩罚。
我们在损失函数的后面加上 1000 乘以 θ 3 θ_3 θ3 的平方,再加上 1000 乘以 θ 4 θ_4 θ4的平方,这里的1000只是一个随机值。即
min θ 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 + 1000 θ 3 2 + 1000 θ 4 2 \min _{\theta} \frac{1}{2 m} \sum_{i=1}^{m}\left(h_{\theta}\left(x^{(i)}\right)-y^{(i)}\right)^{2}+1000θ_3^2+1000θ_4^2 θmin2m1i=1∑m(hθ(x(i))−y(i))2+1000θ32+1000θ42
在我们如果要求损失函数的最小值,就得让 θ 3 θ_3 θ3和 θ 4 θ_4 θ4的值非常小,因为损失函数中加入了有关他们的两项,如果 θ 3 θ_3 θ3和 θ 4 θ_4 θ4的值非常大的话,损失函数的值也会变得非常大,所以 θ 3 θ_3 θ3 θ 4 θ_4 θ4的值趋近于0.
也即拟合函数中的 θ 3 θ_3 θ3 和 θ 4 θ_4 θ4 两项的值近似为0,所以拟合函数就趋近于2次函数,这样以来,拟合函数的拟合程度就刚刚好了
这里我们只是有目的的对 θ 3 θ_3 θ3 和 θ 4 θ_4 θ4 两项进行了惩罚,那如果不知道拟合函数中哪些系数是高次项系数哪?
我们就要对所有项的系数都进行惩罚了,也即我们在损失函数中加入一项(正则化项):
λ ∑ j = 1 n θ j 2 \lambda \sum_{j=1}^{n} \theta_{j}^{2} λj=1∑nθj2
这里并没有惩罚 θ 0 θ_0 θ0 ,这只会造成很小的差异。 对所有项的系数都进行惩罚相比之下还是对于高次项的惩罚程度更大。
其中 λ \lambda λ叫做正则化参数, λ \lambda λ越大则惩罚力度也越大,但 λ \lambda λ并不是越大越好当 λ \lambda λ太大时就会造成拟合函数中的参数太小以至于拟合函数就等于 θ 0 θ_0 θ0变成一条直线,造成欠拟合。
此外正则化还分为L1正则和L2正则,又叫L1范数和L2范数,其定义如下:
L1范数:
∥ x ∥ 1 = ∑ i = 1 N ∣ x i ∣ \|x\|_{1}=\sum_{i=1}^{N}\left|x_{i}\right| ∥x∥1=i=1∑N∣xi∣
即向量元素绝对值之和
L2范数:
∥ x ⃗ ∥ 2 = ∑ i = 1 N ∣ x i ∣ 2 \|\vec{x}\|_{2}=\sqrt{\sum_{i=1}^{N}\left|x_{i}\right|^{2}} ∥x∥2=i=1∑N∣xi∣2
即向量的每个元素的平方和再开方
Lp范数:
L p = ∥ x ⃗ ∥ p = ∑ i = 1 N ∣ x i ∣ p p L_{p}=\|\vec{x}\|_{p}=\sqrt[p]{\sum_{i=1}^{N}\left|x_{i}\right|^{p}} Lp=∥x∥p=pi=1∑N∣xi∣p
Dropout操作是指在网络的训练阶段,每次迭代时会从基础网络中随机丢弃一定比例的神经元,然后在修改后的网络上进行数据的前向传播和误差的反向传播。注意:模型在测试阶段会恢复全部的神经元。Dropout是一种常见的正则化方法,可以缓解网络的过拟合问题。
Dropout可以随机删除网络中的神经单元,它为什么可以通过正则化发挥如此大的作用呢?
直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和之前讲的L2正则化类似;实施dropout的结果实它会压缩权重,并完成一些预防过拟合的外层正则化;L2对不同权重的衰减是不同的,它取决于激活函数倍增的大小。
另一种理解就是,Dropout能够减少神经元之间复杂的共适性关系。由于Dropout每次丢弃的神经元是随机选择的,所以每次保留下来的网络会包含不同的神经元,这样在训练过程中,网络权值的更新不会依赖隐节点之间的固定关系。换句话说网络中每一个神经元不会对另一个神经元非常敏感,这使得网络能够学习到一些更加泛化的特征。
dropout 的一大缺点是成本函数无法被明确定义。因为每次迭代都会随机消除一些神经元结点的影响,因此无法确保成本函数单调递减。
明显增加训练时间,因为引入 dropout 之后相当于每次只是训练的原先网络的一个子网络,为了达到同样的精度需要的训练次数会增多。dropout 的缺点就在于训练时间是没有 dropout 网络的 2-3 倍。
[1]周志华. 《机器学习》
[2]李航.《统计学习方法(第2版)》
[3]诸葛越 、葫芦娃.《百面机器学习》
[4]弗朗索瓦·肖莱.《Python深度学习》
[5]伊恩·古德费洛.《深度学习》
[6]谈继勇.《深度学习》
[7]KnowingAI知智.哔哩哔哩
[8] 深度学习超参数介绍及调参
[9] https://www.zhihu.com/question/67366051/answer/262087707
[10] https://blog.csdn.net/weiman1/article/details/125647517?spm=1001.2014.3001.5506