大家好,又见面了,我是你们的朋友全栈君。
文章目录
- 1 基本概念
-
- 1.1 神经网络组成?
- 1.2 神经网络有哪些常用模型结构?
- 1.3 如何选择深度学习开发平台?
- 1.4 为什么深层神经网络难以训练?
- 1.5 深度学习和机器学习的异同?
- 2 网络操作与计算
-
- 2.1 前向传播与反向传播?
- 2.2 如何计算神经网络的输出?
- 2.3 如何计算卷积神经网络输出值?
- 2.4 如何计算 Pooling 层输出值输出值?
- 2.5 实例理解反向传播
- 2.6 神经网络更“深”有什么意义?
- 3 超参数
-
- 3.1 什么是超参数?
- 3.2 如何寻找超参数的最优值?
- 3.3 超参数搜索一般过程?
- 4 激活函数
-
- 4.1、什么是激活函数
- 4.2 为什么要使用激活函数?
- 4.3 为什么激活函数需要非线性函数?
- 4.4 常见的激活函数及导数
- 4.5 激活函数有哪些性质?
- 4.6 如何选择激活函数?
- 4.7 使用 ReLu 激活函数的优点?
- 4.8 什么时候可以用线性激活函数?
- 4.9 怎样理解 Relu(< 0 时)是非线性激活函数?
- 4.10 Softmax 定义及作用
- 4.11 Softmax 函数如何应用于多分类?
- 4.12 交叉熵代价函数定义及其求导推导
- 4.13 为什么Tanh收敛速度比Sigmoid快?
- 5 Batch_Size
-
- 5.1 什么是BatchSize
- 5.2 为什么需要 Batch_Size?
- 5.3 如何设置Batch_Size 的值?
- 5.4 在合理范围内,增大Batch_Size有何好处?
- 5.5 盲目增大 Batch_Size 有何坏处?
- 5.6 调节 Batch_Size 对训练效果影响到底如何?
- 6 归一化
-
- 6.1 什么是归一化?
- 6.2 为什么要归一化?
- 6.3 为什么归一化能提高求解最优解速度?
- 6.4 3D 图解未归一化
- 6.5 归一化有哪些类型?
- 6.6 局部响应归一化作用
- 6.7 理解局部响应归一化
- 6.8 什么是批归一化(Batch Normalization)
- 6.9 批归一化(BN)算法的优点
- 6.10 批归一化(BN)算法流程
- 6.11 批归一化和群组归一化比较
- 6.12 Weight Normalization和Batch Normalization比较
- 6.13 Batch Normalization在什么时候用比较合适?
- 7 权重偏差初始化
-
- 7.1 全都初始化为 0
- 7.2 全都初始化为同样的值
- 7.3 初始化为小的随机数
- 7.4 用 1 / n 1/sqrt n 1/n 校准方差
- 7.5 稀疏初始化(Sparse Initialazation)
- 7.6 初始化偏差
- 8 学习率
-
- 8.1 学习率的作用
- 8.2 学习率衰减常用参数有哪些
- 8.3 分段常数衰减
- 8.4 指数衰减
- 8.5 自然指数衰减
- 8.6 多项式衰减
- 8.7 余弦衰减
- 9 Dropout 系列问题
-
- 9.1 为什么要正则化?
- 9.2 为什么正则化有利于预防过拟合?
- 9.3 理解dropout正则化
- 9.4 dropout率的选择
- 9.5 dropout有什么缺点?
- 10 深度学习中图像数据常用的增强方法?
1 基本概念
1.1 神经网络组成?
人工神经网络(Artificial Neural Networks,简写为ANNs)是一种模仿动物神经网络行为特征,进行分布式并行信息处理的算法数学模型。这种网络依靠系统的复杂程度,通过调整内部大量节点之间相互连接的关系,从而达到处理信息的目的,并具有自学习和自适应的能力。神经网络类型众多,其中最为重要的是多层感知机。为了详细地描述神经网络,我们先从最简单的神经网络说起。
感知机
感知机是1957年,由Rosenblatt提出会,是神经网络和支持向量机的基础。
感知机是有生物学上的一个启发,他的参照对象和理论依据可以参照下图:(我们的大脑可以认为是一个神经网络,是一个生物的神经网络,在这个生物的神经网络里边呢,他的最小单元我们可以认为是一个神经元,一个neuron,这些很多个神经元连接起来形成一个错综复杂的网络,我们把它称之为神经网络。当然我们现在所说的,在深度学习包括机器学习指的神经网络Neural Networks实际上指的是人工神经网络Artificial Neural Networks,简写为ANNs。我们只是简化了。我们人的神经网络是由这样一些神经元来构成的,那么这个神经元他的一些工作机制呢就是通过这样一个下面图的结构,首先接收到一些信号,这些信号通过这些树突(dendrite)组织,树突组织接收到这些信号送到细胞里边的细胞核(nucleus),这些细胞核对接收到的这些信号,这些信号是以什么形式存在的呢?这些信号比如说眼睛接收到的光学啊,或者耳朵接收到的声音信号,到树突的时候会产生一些微弱的生物电,那么就形成这样的一些刺激,那么在细胞核里边对这些收集到的接收到的刺激进行综合的处理,当他的信号达到了一定的阈值之后,那么他就会被激活,就会产生一个刺激的输出,那么就会形成一个我们大脑接收到的进一步的信号,那么他是通过轴突这样的输出计算的,这就是我们人脑的一个神经元进行感知的时候大致的一个工作原理。)
简单的感知机如下图所示:
其中 x 1 x_1 x1, x 2 x_2 x2, x n x_n xn为感知机的输入,输入里一个常量1,实际上这个常量1就相当于 w 0 w0 w0乘以1,实际上就是 w 0 w0 w0, w 0 ∗ 1 = w 0 w0*1=w0 w0∗1=w0,后边 w 0 w0 w0的计算是和输入是无关的,相当于 w 0 w0 w0就是一个偏置,相当于i从1开始一直到n,还加上一个 w 0 w0 w0,其输出为:
o u t p u t = { 0 , i f ∑ i w i x i ⩽ t h r e s h o l d 1 , i f ∑ i w i x i > t h r e s h o l d output = left{ begin{aligned} 0, quad if sum_i w_i x_i leqslant threshold \ 1, quad if sum_i w_i x_i > threshold end{aligned} right. output=⎩⎪⎪⎨⎪⎪⎧0,if i∑wixi⩽threshold1,if i∑wixi>threshold
假如把感知机想象成一个加权投票机制,比如 3 位评委给一个歌手打分,打分分别为 4 4 4分、 1 1 1分、 − 3 -3 −3分,这 3 3 3位评分的权重分别是 1 、 3 、 2 1、3、2 1、3、2,则该歌手最终得分为 4 × 1 1 × 3 ( − 3 ) × 2 = 1 4 times 1 1 times 3 (-3) times 2 = 1 4×1 1×3 (−3)×2=1。按照比赛规则,选取的 t h r e s h o l d threshold threshold为 3 3 3,说明只有歌手的综合评分大于 3 3 3时,才可顺利晋级。对照感知机,该选手被淘汰,因为:
∑ i w i x i < t h r e s h o l d = 3 , o u t p u t = 0 sum_i w_i x_i < threshold=3, output = 0 i∑wixi<threshold=3,output=0
用 − b -b −b 代替 t h r e s h o l d threshold threshold,输出变为:
o u t p u t = { 0 , i f w ⋅ x b ⩽ 0 1 , i f w ⋅ x b > 0 output = left{ begin{aligned} 0, quad if boldsymbol{w} cdot boldsymbol{x} b leqslant 0 \ 1, quad if boldsymbol{w} cdot boldsymbol{x} b > 0 end{aligned} right. output={ 0,if w⋅x b⩽01,if w⋅x b>0
设置合适的 x boldsymbol{x} x 和 b b b,一个简单的感知机单元的与非门表示如下:
当输入为 0 0 0, 1 1 1时,感知机输出为 0 × ( − 2 ) 1 × ( − 2 ) 3 = 1 0 times (-2) 1 times (-2) 3 = 1 0×(−2) 1×(−2) 3=1。
复杂一些的感知机由简单的感知机单元组合而成:
多层感知机
多层感知机由感知机推广而来,最主要的特点是有多个神经元层,因此也叫深度神经网络。相比于单独的感知机,多层感知机的第 i i i层的每个神经元和第 i − 1 i-1 i−1层的每个神经元都有连接。
输出层可以不止有 1 1 1个神经元。隐藏层可以只有 1 1 1层,也可以有多层。输出层为多个神经元的神经网络例如下图所示:
1.2 神经网络有哪些常用模型结构?
下图包含了大部分常用的模型:
1.3 如何选择深度学习开发平台?
现有的深度学习开源平台主要有 PyTorch, MXNet, CNTK, Theano, TensorFlow, Keras, PaddlePaddle等。那如何选择一个适合自己的平台呢?
参考1:与现有编程平台、技能整合的难易程度
主要是前期积累的开发经验和资源,比如编程语言,前期数据集存储格式等。
参考2: 与相关机器学习、数据处理生态整合的紧密程度
深度学习研究离不开各种数据处理、可视化、统计推断等软件包。考虑建模之前,是否具有方便的数据预处理工具?建模之后,是否具有方便的工具进行可视化、统计推断、数据分析。
参考3:对数据量及硬件的要求和支持
深度学习在不同应用场景的数据量是不一样的,这也就导致我们可能需要考虑分布式计算、多GPU计算的问题。例如,对计算机图像处理研究的人员往往需要将图像文件和计算任务分部到多台计算机节点上进行执行。当下每个深度学习平台都在快速发展,每个平台对分布式计算等场景的支持也在不断演进。
参考4:深度学习平台的成熟程度
成熟程度的考量是一个比较主观的考量因素,这些因素可包括:社区的活跃程度;是否容易和开发人员进行交流;当前应用的势头。
参考5:平台利用是否多样性?
有些平台是专门为深度学习研究和应用进行开发的,有些平台对分布式计算、GPU 等构架都有强大的优化,能否用这些平台/软件做其他事情?比如有些深度学习软件是可以用来求解二次型优化;有些深度学习平台很容易被扩展,被运用在强化学习的应用中。
一般来说,现在做科学研究和竞赛用Pytorch较多,工业级的用Tensorflow较多。
1.4 为什么深层神经网络难以训练?
- 梯度消失 梯度消失是指通过隐藏层从后向前看,梯度会变的越来越小,说明前面层的学习会显著慢于后面层的学习,所以学习会卡住,除非梯度变大。 梯度消失的原因受到多种因素影响,例如学习率的大小,网络参数的初始化,激活函数的边缘效应等。在深层神经网络中,每一个神经元计算得到的梯度都会传递给前一层,较浅层的神经元接收到的梯度受到之前所有层梯度的影响。如果计算得到的梯度值非常小,随着层数增多,求出的梯度更新信息将会以指数形式衰减,就会发生梯度消失。下图是不同隐含层的学习速率:
- 梯度爆炸 在深度网络或循环神经网络(Recurrent Neural Network, RNN)等网络结构中,梯度可在网络更新的过程中不断累积,变成非常大的梯度,导致网络权重值的大幅更新,使得网络不稳定;在极端情况下,权重值甚至会溢出,变为 N a N NaN NaN值,再也无法更新。
- 权重矩阵的退化导致模型的有效自由度减少。 参数空间中学习的退化速度减慢,导致减少了模型的有效维数,网络的可用自由度对学习中梯度范数的贡献不均衡,随着相乘矩阵的数量(即网络深度)的增加,矩阵的乘积变得越来越退化。在有硬饱和边界的非线性网络中(例如 ReLU 网络),随着深度增加,退化过程会变得越来越快。Duvenaud等人2014年的论文里展示了关于该退化过程的可视化:
随着深度的增加,输入空间(左上角所示)会在输入空间中的每个点处被扭曲成越来越细的单丝,只有一个与细丝正交的方向影响网络的响应。沿着这个方向,网络实际上对变化变得非常敏感。
1.5 深度学习和机器学习的异同?
机器学习:利用计算机、概率论、统计学等知识,输入数据,让计算机学会新知识。机器学习的过程,就是训练数据去优化目标函数。
深度学习:是一种特殊的机器学习,具有强大的能力和灵活性。它通过学习将世界表示为嵌套的层次结构,每个表示都与更简单的特征相关,而抽象的表示则用于计算更抽象的表示。
传统的机器学习需要定义一些手工特征,从而有目的的去提取目标信息, 非常依赖任务的特异性以及设计特征的专家经验。而深度学习可以从大数据中先学习简单的特征,并从其逐渐学习到更为复杂抽象的深层特征,不依赖人工的特征工程,这也是深度学习在大数据时代受欢迎的一大原因。
2 网络操作与计算
2.1 前向传播与反向传播?
神经网络的计算主要有两种:前向传播(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。然后调整这些过程中的权值,再不断进行前向传播和反向传播的过程,最终得到一个比较好的结果。
2.2 如何计算神经网络的输出?
如上图,输入层有三个节点,我们将其依次编号为 1、2、3;隐藏层的 4 个节点,编号依次为 4、5、6、7;最后输出层的两个节点编号为 8、9。比如,隐藏层的节点 4,它和输入层的三个节点 1、2、3 之间都有连接,其连接上的权重分别为是 w 41 , w 42 , w 43 w_{41}, w_{42}, w_{43} w41,w42,w43。
为了计算节点 4 的输出值,我们必须先得到其所有上游节点(也就是节点 1、2、3)的输出值。节点 1、2、3 是输入层的节点,所以,他们的输出值就是输入向量本身。按照上图画出的对应关系,可以看到节点 1、2、3 的输出值分别是 x 1 , x 2 , x 3 x_1, x_2, x_3 x1,x2,x3。
a 4 = σ ( w T ⋅ a ) = σ ( w 41 x 4 w 42 x 2 w 43 a 3 w 4 b ) a_4 = sigma(w^T cdot a) = sigma(w_{41}x_4 w_{42}x_2 w_{43}a_3 w_{4b}) a4=σ(wT⋅a)=σ(w41x4 w42x2 w43a3 w4b)
其中 w 4 b w_{4b} w4b是节点 4 的偏置项。
同样,我们可以继续计算出节点 5、6、7 的输出值 a 5 , a 6 , a 7 a_5, a_6, a_7 a5,a6,a7。
计算输出层的节点 8 的输出值 y 1 y_1 y1:
y 1 = σ ( w T ⋅ a ) = σ ( w 84 a 4 w 85 a 5 w 86 a 6 w 87 a 7 w 8 b ) y_1 = sigma(w^T cdot a) = sigma(w_{84}a_4 w_{85}a_5 w_{86}a_6 w_{87}a_7 w_{8b}) y1=σ(wT⋅a)=σ(w84a4 w85a5 w86a6 w87a7 w8b)
其中 w 8 b w_{8b} w8b是节点 8 的偏置项。
同理,我们还可以计算出 y 2 y_2 y2。这样输出层所有节点的输出值计算完毕,我们就得到了在输入向量 x 1 , x 2 , x 3 , x 4 x_1, x_2, x_3, x_4 x1,x2,x3,x4时,神经网络的输出向量 y 1 , y 2 y_1, y_2 y1,y2。这里我们也看到,输出向量的维度和输出层神经元个数相同。
2.3 如何计算卷积神经网络输出值?
假设有一个 5*5 的图像,使用一个 3*3 的 filter 进行卷积,想得到一个 3*3 的 Feature Map,如下所示:
x i , j x_{i,j} xi,j表示图像第 i i i行第 j j j列元素。 w m , n w_{m,n} wm,n表示 filter 第 m m m行第 n n n列权重。 w b w_b wb表示 f i l t e r filter filter的偏置项。 表 a i , j a_i,_j ai,j示 feature map 第 i i i行第 j j j列元素。 f f f表示激活函数,这里以 R e L U ReLU ReLU函数为例。
卷积计算公式如下:
a i , j = f ( ∑ m = 0 2 ∑ n = 0 2 w m , n x i m , j n w b ) a_{i,j} = f(sum_{m=0}^2 sum_{n=0}^2 w_{m,n} x_{i m, j n} w_b ) ai,j=f(m=0∑2n=0∑2wm,nxi m,j n wb)
当步长为 1 1 1时,计算 feature map 元素 a 0 , 0 a_{0,0} a0,0如下:
a 0 , 0 = f ( ∑ m = 0 2 ∑ n = 0 2 w m , n x 0 m , 0 n w b ) = r e l u ( w 0 , 0 x 0 , 0 w 0 , 1 x 0 , 1 w 0 , 2 x 0 , 2 w 1 , 0 x 1 , 0 w 1 , 1 x 1 , 1 w 1 , 2 x 1 , 2 w 2 , 0 x 2 , 0 w 2 , 1 x 2 , 1 w 2 , 2 x 2 , 2 ) = 1 0 1 0 1 0 0 0 1 = 4 a_{0,0} = f(sum_{m=0}^2 sum_{n=0}^2 w_{m,n} x_{0 m, 0 n} w_b ) = relu(w_{0,0} x_{0,0} w_{0,1} x_{0,1} w_{0,2} x_{0,2} w_{1,0} x_{1,0} \w_{1,1} x_{1,1} w_{1,2} x_{1,2} w_{2,0} x_{2,0} w_{2,1} x_{2,1} w_{2,2} x_{2,2}) \ = 1 0 1 0 1 0 0 0 1 \ = 4 a0,0=f(m=0∑2n=0∑2wm,nx0 m,0 n wb)=relu(w0,0x0,0 w0,1x0,1 w0,2x0,2 w1,0x1,0 w1,1x1,1 w1,2x1,2 w2,0x2,0 w2,1x2,1 w2,2x2,2)=1 0 1 0 1 0 0 0 1=4
其计算过程图示如下:
以此类推,计算出全部的Feature Map。
当步幅为 2 时,Feature Map计算如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KisImtuW-1628659360465)(img/ch3/3.2.3.5.png)]
注:图像大小、步幅和卷积后的Feature Map大小是有关系的。它们满足下面的关系:
W 2 = ( W 1 − F 2 P ) / S 1 H 2 = ( H 1 − F 2 P ) / S 1 W_2 = (W_1 – F 2P)/S 1\ H_2 = (H_1 – F 2P)/S 1 W2=(W1−F 2P)/S 1H2=(H1−F 2P)/S 1
其中 W 2 W_2 W2, 是卷积后 Feature Map 的宽度; W 1 W_1 W1是卷积前图像的宽度; F F F是 filter 的宽度; P P P是 Zero Padding 数量,Zero Padding 是指在原始图像周围补几圈 0 0 0,如果 P P P的值是 1 1 1,那么就补 1 1 1圈 0 0 0; S S S是步幅; H 2 H_2 H2卷积后 Feature Map 的高度; H 1 H_1 H1是卷积前图像的宽度。
举例:假设图像宽度 W 1 = 5 W_1 = 5 W1=5,filter 宽度 F = 3 F=3 F=3,Zero Padding P = 0 P=0 P=0,步幅 S = 2 S=2 S=2, Z Z Z则
$$ W_2 = (W_1 – F 2P)/S 1
= (5-3 0)/2 1
= 2 $$
说明 Feature Map 宽度是2。同样,我们也可以计算出 Feature Map 高度也是 2。
如果卷积前的图像深度为 D D D,那么相应的 filter 的深度也必须为 D D D。深度大于 1 的卷积计算公式:
a i , j = f ( ∑ d = 0 D − 1 ∑ m = 0 F − 1 ∑ n = 0 F − 1 w d , m , n x d , i m , j n w b ) a_{i,j} = f(sum_{d=0}^{D-1} sum_{m=0}^{F-1} sum_{n=0}^{F-1} w_{d,m,n} x_{d,i m,j n} w_b) ai,j=f(d=0∑D−1m=0∑F−1n=0∑F−1wd,m,nxd,i m,j n wb)
其中, D D D是深度; F F F是 filter 的大小; w d , m , n w_{d,m,n} wd,m,n表示 filter 的第 d d d层第 m m m行第 n n n列权重; a d , i , j a_{d,i,j} ad,i,j表示 feature map 的第 d d d层第 i i i行第 j j j列像素;其它的符号含义前面相同,不再赘述。
每个卷积层可以有多个 filter。每个 filter 和原始图像进行卷积后,都可以得到一个 Feature Map。卷积后 Feature Map 的深度(个数)和卷积层的 filter 个数相同。下面的图示显示了包含两个 filter 的卷积层的计算。 7 ∗ 7 ∗ 3 7*7*3 7∗7∗3输入,经过两个 3 ∗ 3 ∗ 3 3*3*3 3∗3∗3filter 的卷积(步幅为 2 2 2),得到了 3 ∗ 3 ∗ 2 3*3*2 3∗3∗2的输出。图中的 Zero padding 是 1 1 1,也就是在输入元素的周围补了一圈 0 0 0。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xaRZ2mKr-1628659360466)(https://gitee.com/wanghao1090220084/images/raw/master/img/3.2.3.6.png)]
以上就是卷积层的计算方法。这里面体现了局部连接和权值共享:每层神经元只和上一层部分神经元相连(卷积计算规则),且 filter 的权值对于上一层所有神经元都是一样的。对于包含两个 3 ∗ 3 ∗ 3 3 * 3 * 3 3∗3∗3的 fitler 的卷积层来说,其参数数量仅有 ( 3 ∗ 3 ∗ 3 1 ) ∗ 2 = 56 (3 * 3 * 3 1) * 2 = 56 (3∗3∗3 1)∗2=56个,且参数数量与上一层神经元个数无关。与全连接神经网络相比,其参数数量大大减少了。
2.4 如何计算 Pooling 层输出值输出值?
Pooling 层主要的作用是下采样,通过去掉 Feature Map 中不重要的样本,进一步减少参数数量。Pooling 的方法很多,最常用的是 Max Pooling。Max Pooling 实际上就是在 n*n 的样本中取最大值,作为采样后的样本值。下图是 2*2 max pooling:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AK07prOA-1628659360466)(img/ch3/3.2.4.1.png)]
除了 Max Pooing 之外,常用的还有 Average Pooling ——取各样本的平均值。 对于深度为 D D D的 Feature Map,各层独立做 Pooling,因此 Pooling 后的深度仍然为 D D D。
2.5 实例理解反向传播
一个典型的三层神经网络如下所示:
其中 Layer L 1 L_1 L1是输入层,Layer L 2 L_2 L2是隐含层,Layer L 3 L_3 L3是输出层。
假设输入数据集为 D = x 1 , x 2 , . . . , x n D={x_1, x_2, …, x_n} D=x1,x2,...,xn,输出数据集为 y 1 , y 2 , . . . , y n y_1, y_2, …, y_n y1,y2,...,yn。
如果输入和输出是一样,即为自编码模型。如果原始数据经过映射,会得到不同于输入的输出。
假设有如下的网络层:
输入层包含神经元 i 1 , i 2 i_1, i_2 i1,i2,偏置 b 1 b_1 b1;隐含层包含神经元 h 1 , h 2 h_1, h_2 h1,h2,偏置 b 2 b_2 b2,输出层为 o 1 , o 2 o_1, o_2 o1,o2, w i w_i wi为层与层之间连接的权重,激活函数为 s i g m o i d sigmoid sigmoid函数。对以上参数取初始值,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mh7zIbvw-1628659360468)(img/ch3/3.2.5.3.png)]
其中:
- 输入数据 i 1 = 0.05 , i 2 = 0.10 i1=0.05, i2 = 0.10 i1=0.05,i2=0.10
- 输出数据 o 1 = 0.01 , o 2 = 0.99 o1=0.01, o2=0.99 o1=0.01,o2=0.99;
- 初始权重 w 1 = 0.15 , w 2 = 0.20 , w 3 = 0.25 , w 4 = 0.30 , w 5 = 0.40 , w 6 = 0.45 , w 7 = 0.50 , w 8 = 0.55 w1=0.15, w2=0.20, w3=0.25,w4=0.30, w5=0.40, w6=0.45, w7=0.50, w8=0.55 w1=0.15,w2=0.20,w3=0.25,w4=0.30,w5=0.40,w6=0.45,w7=0.50,w8=0.55
- 目标:给出输入数据 i 1 , i 2 i1,i2 i1,i2( 0.05 0.05 0.05和 0.10 0.10 0.10),使输出尽可能与原始输出 o 1 , o 2 o1,o2 o1,o2,( 0.01 0.01 0.01和 0.99 0.99 0.99)接近。
前向传播
- 输入层 –> 输出层
计算神经元 h 1 h1 h1的输入加权和:
$$ net_{h1} = w_1 * i_1 w_2 * i_2 b_1 * 1
net_{h1} = 0.15 * 0.05 0.2 * 0.1 0.35 * 1 = 0.3775 $$
神经元 h 1 h1 h1的输出 o 1 o1 o1:(此处用到激活函数为 sigmoid 函数):
o u t h 1 = 1 1 e − n e t h 1 = 1 1 e − 0.3775 = 0.593269992 out_{h1} = frac{1}{1 e^{-net_{h1}}} = frac{1}{1 e^{-0.3775}} = 0.593269992 outh1=1 e−neth11=1 e−0.37751=0.593269992
同理,可计算出神经元 h 2 h2 h2的输出 o 1 o1 o1:
o u t h 2 = 0.596884378 out_{h2} = 0.596884378 outh2=0.596884378
- 隐含层–>输出层:
计算输出层神经元 o 1 o1 o1和 o 2 o2 o2的值:
n e t o 1 = w 5 ∗ o u t h 1 w 6 ∗ o u t h 2 b 2 ∗ 1 net_{o1} = w_5 * out_{h1} w_6 * out_{h2} b_2 * 1 neto1=w5∗outh1 w6∗outh2 b2∗1
n e t o 1 = 0.4 ∗ 0.593269992 0.45 ∗ 0.596884378 0.6 ∗ 1 = 1.105905967 net_{o1} = 0.4 * 0.593269992 0.45 * 0.596884378 0.6 * 1 = 1.105905967 neto1=0.4∗0.593269992 0.45∗0.596884378 0.6∗1=1.105905967
o u t o 1 = 1 1 e − n e t o 1 = 1 1 e 1.105905967 = 0.75136079 out_{o1} = frac{1}{1 e^{-net_{o1}}} = frac{1}{1 e^{1.105905967}} = 0.75136079 outo1=1 e−neto11=1 e1.1059059671=0.75136079
这样前向传播的过程就结束了,我们得到输出值为 [ 0.75136079 , 0.772928465 ] [0.75136079 , 0.772928465] [0.75136079,0.772928465],与实际值 [ 0.01 , 0.99 ] [0.01 , 0.99] [0.01,0.99]相差还很远,现在我们对误差进行反向传播,更新权值,重新计算输出。
**反向传播 **
1.计算总误差
总误差:(这里使用Square Error)
E t o t a l = ∑ 1 2 ( t a r g e t − o u t p u t ) 2 E_{total} = sum frac{1}{2}(target – output)^2 Etotal=∑21(target−output)2
但是有两个输出,所以分别计算 o 1 o1 o1和 o 2 o2 o2的误差,总误差为两者之和:
E o 1 = 1 2 ( t a r g e t o 1 − o u t o 1 ) 2 = 1 2 ( 0.01 − 0.75136507 ) 2 = 0.274811083 E_{o1} = frac{1}{2}(target_{o1} – out_{o1})^2 = frac{1}{2}(0.01 – 0.75136507)^2 = 0.274811083 Eo1=21(targeto1−outo1)2=21(0.01−0.75136507)2=0.274811083.
E o 2 = 0.023560026 E_{o2} = 0.023560026 Eo2=0.023560026.
E t o t a l = E o 1 E o 2 = 0.274811083 0.023560026 = 0.298371109 E_{total} = E_{o1} E_{o2} = 0.274811083 0.023560026 = 0.298371109 Etotal=Eo1 Eo2=0.274811083 0.023560026=0.298371109.
2.隐含层 –> 输出层的权值更新:
以权重参数 w 5 w5 w5为例,如果我们想知道 w 5 w5 w5对整体误差产生了多少影响,可以用整体误差对 w 5 w5 w5求偏导求出:(链式法则)
∂ E t o t a l ∂ w 5 = ∂ E t o t a l ∂ o u t o 1 ∗ ∂ o u t o 1 ∂ n e t o 1 ∗ ∂ n e t o 1 ∂ w 5 frac{partial E_{total}}{partial w5} = frac{partial E_{total}}{partial out_{o1}} * frac{partial out_{o1}}{partial net_{o1}} * frac{partial net_{o1}}{partial w5} ∂w5∂Etotal=∂outo1∂Etotal∗∂neto1∂outo1∗∂w5∂neto1
下面的图可以更直观的看清楚误差是怎样反向传播的:
2.6 神经网络更“深”有什么意义?
前提:在一定范围内。
- 在神经元数量相同的情况下,深层网络结构具有更大容量,分层组合带来的是指数级的表达空间,能够组合成更多不同类型的子结构,这样可以更容易地学习和表示各种特征。
- 隐藏层增加则意味着由激活函数带来的非线性变换的嵌套层数更多,就能构造更复杂的映射关系。
3 超参数
3.1 什么是超参数?
超参数 : 在机器学习的上下文中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。通常情况下,需要对超参数进行优化,给学习机选择一组最优超参数,以提高学习的性能和效果。
超参数通常存在于:
代码语言:javascript复制1. 定义关于模型的更高层次的概念,如复杂性或学习能力。
2. 不能直接从标准模型培训过程中的数据中学习,需要预先定义。
3. 可以通过设置不同的值,训练不同的模型和选择更好的测试值来决定
超参数具体来讲比如算法中的学习率(learning rate)、梯度下降法迭代的数量(iterations)、隐藏层数目(hidden layers)、隐藏层单元数目、激活函数( activation function)都需要根据实际情况来设置,这些数字实际上控制了最后的参数和的值,所以它们被称作超参数。
3.2 如何寻找超参数的最优值?
在使用机器学习算法时,总有一些难调的超参数。例如权重衰减大小,高斯核宽度等等。这些参数需要人为设置,设置的值对结果产生较大影响。常见设置超参数的方法有:
- 猜测和检查:根据经验或直觉,选择参数,一直迭代。
- 网格搜索:让计算机尝试在一定范围内均匀分布的一组值。
- 随机搜索:让计算机随机挑选一组值。
- 贝叶斯优化:使用贝叶斯优化超参数,会遇到贝叶斯优化算法本身就需要很多的参数的困难。
- MITIE方法,好初始猜测的前提下进行局部优化。它使用BOBYQA算法,并有一个精心选择的起始点。由于BOBYQA只寻找最近的局部最优解,所以这个方法是否成功很大程度上取决于是否有一个好的起点。在MITIE的情况下,我们知道一个好的起点,但这不是一个普遍的解决方案,因为通常你不会知道好的起点在哪里。从好的方面来说,这种方法非常适合寻找局部最优解。稍后我会再讨论这一点。
- 最新提出的LIPO的全局优化方法。这个方法没有参数,而且经验证比随机搜索方法好。
3.3 超参数搜索一般过程?
超参数搜索一般过程:
- 将数据集划分成训练集、验证集及测试集。
- 在训练集上根据模型的性能指标对模型参数进行优化。
- 在验证集上根据模型的性能指标对模型的超参数进行搜索。
- 步骤 2 和步骤 3 交替迭代,最终确定模型的参数和超参数,在测试集中验证评价模型的优劣。
其中,搜索过程需要搜索算法,一般有:网格搜索、随机搜过、启发式智能搜索、贝叶斯搜索。
4 激活函数
4.1、什么是激活函数
激活函数(Activation functions)对于人工神经网络 模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用。它们将非线性特性引入到我们的网络中。如下图,在神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数。引入激活函数是为了增加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yISAsq4B-1628659360470)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
4.2 为什么要使用激活函数?
- 激活函数对模型学习、理解非常复杂和非线性的函数具有重要作用。
- 激活函数可以引入非线性因素。如果不使用激活函数,则输出信号仅是一个简单的线性函数。线性函数一个一级多项式,线性方程的复杂度有限,从数据中学习复杂函数映射的能力很小。没有激活函数,神经网络将无法学习和模拟其他复杂类型的数据,例如图像、视频、音频、语音等。
- 激活函数可以把当前特征空间通过一定的线性映射转换到另一个空间,让数据能够更好的被分类。
4.3 为什么激活函数需要非线性函数?
- 假若网络中全部是线性部件,那么线性的组合还是线性,与单独一个线性分类器无异。这样就做不到用非线性来逼近任意函数。
- 使用非线性激活函数 ,以便使网络更加强大,增加它的能力,使它可以学习复杂的事物,复杂的表单数据,以及表示输入输出之间非线性的复杂的任意函数映射。使用非线性激活函数,能够从输入输出之间生成非线性映射。
4.4 常见的激活函数及导数
- sigmoid 激活函数
函数的定义为:
其值域为 (0,1) 。函数图像如下:
特点: 它能够把输入的连续实值变换为0和1之间的输出,特别的,如果是非常大的负数,那么输出就是0;如果是非常大的正数,输出就是1. 缺点: sigmoid函数曾经被使用的很多,不过近年来,用它的人越来越少了。主要是因为它固有的一些 缺点。 缺点1:在深度神经网络中梯度反向传递时导致梯度爆炸和梯度消失,其中梯度爆炸发生的概率非常小,而梯度消失发生的概率比较大。首先来看Sigmoid函数的导数,如下图所示:
缺点2:不是以 0 0 0为对称轴(这点在tahn函数有所改善)
sigmoid函数及其导数的实现
代码语言:javascript复制import numpy as np
import matplotlib.pyplot as plt
#解决中文显示问题
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def d_sigmoid(x):
y = 1 / (1 np.exp(-x))
dy=y*(1-y)
return dy
def sigmoid(x):
y = 1 / (1 np.exp(-x))
return y
def plot_sigmoid():
# param:起点,终点,间距
x = np.arange(-8, 8, 0.2)
plt.subplot(1, 2, 1)
plt.title('sigmoid') # 第一幅图片标题
y = sigmoid(x)
plt.plot(x, y)
plt.subplot(1, 2, 2)
y = d_sigmoid(x)
plt.plot(x, y)
plt.title('sigmoid导数')
plt.show()
if __name__ == '__main__':
plot_sigmoid()
- tanh激活函数
函数的定义为:
其值域为 (-1,1) 。函数图像如下:
导数: f ′ ( x ) = 1 − ( f ( x ) ) 2 {f}'(x)=1-(f(x))^{2} f′(x)=1−(f(x))2,函数图像如下:
tanh读作Hyperbolic Tangent,它解决了Sigmoid函数的不是zero-centered输出问题,然而,梯度消失(gradient vanishing)的问题和幂运算的问题仍然存在。
优点和缺点
- 优点:
- 解决了Sigmoid的输出不关于零点对称的问题
- 也具有Sigmoid的优点平滑,容易求导
- 缺点:
- 激活函数运算量大(包含幂的运算
- Tanh的导数图像虽然最大之变大,使得梯度消失的问题得到一定的缓解,但是不能根本解决这个问题
tanh函数及其代码实现:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def tanh(x):
"""tanh函数"""
return ((np.exp(x) - np.exp(-x)) / (np.exp(x) np.exp(-x)))
def dx_tanh(x):
"""tanh函数的导数"""
return 1 - tanh(x) * tanh(x)
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
fx = tanh(x)
dx_fx = dx_tanh(x)
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('tanh 函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('tanh函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx_fx)
plt.show()
- Relu激活函数
它保留了 step 函数的生物学启发(只有输入超出阈值时神经元才激活),不过当输入为正的时候,导数不为零,从而允许基于梯度的学习(尽管在 x=0 的时候,导数是未定义的)。使用这个函数能使计算变得很快,因为无论是函数还是其导数都不包含复杂的数学运算。然而,当输入为负值的时候,ReLU 的学习速度可能会变得很慢,甚至使神经元直接无效,因为此时输入小于零而梯度为零,从而其权重无法得到更新,在剩下的训练过程中会一直保持静默。函数的定义为:f(x)=max(0,x),值阈[0, ∞ infty ∞] 。函数图像如下:
导数: f ′ ( x ) = { 1 i f x > 0 0 i f x < = 0 {f}'(x)=left{begin{matrix} 1 &if x>0 & \ 0 &if x<=0 & end{matrix}right. f′(x)={ 10ifx>0ifx<=0 函数图像如下:
优点:
1.相比起Sigmoid和tanh,ReLU在SGD中能够快速收敛,这是因为它线性(linear)、非饱和(non-saturating)的形式。
2.Sigmoid和tanh涉及了很多很expensive的操作(比如指数),ReLU可以更加简单的实现。
3.有效缓解了梯度消失的问题。
4.在没有无监督预训练的时候也能有较好的表现。
缺点:
- ReLU的输出不是zero-centered
- Dead ReLU Problem,指的是某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。有两个主要原因可能导致这种情况产生: (1) 非常不幸的参数初始化,这种情况比较少见 (2) learning rate太高导致在训练过程中参数更新太大,不幸使网络进入这种状态。解决方法是可以采用Xavier初始化方法,以及避免将learning rate设置太大或使用adagrad等自动调节learning rate的算法。
尽管存在这两个问题,ReLU目前仍是最常用的activation function,在搭建人工神经网络的时候推荐优先尝试!
函数及导数代码:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def relu(x):
"""relu函数"""
# temp = np.zeros_like(x)
# if_bigger_zero = (x > temp)
# return x * if_bigger_zero
return np.where(x<0,0,x)
def dx_relu(x):
"""relu函数的导数"""
# temp = np.zeros_like(x)
# if_bigger_equal_zero = (x >= temp)
# return if_bigger_equal_zero * np.ones_like(x)
return np.where(x < 0, 0, 1)
# ---------------------------------------------
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
fx = relu(x)
dx_fx = dx_relu(x)
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Relu函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Relu函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx_fx)
plt.show()
- Leaky ReLU函数(PReLU)
函数的定义为: f ( x ) = m a x ( a x , x ) f(x)=max(ax,x) f(x)=max(ax,x)。函数图像如下:
导数: f ′ ( x ) = { 1 i f x > 0 0.01 i f x < = 0 {f}'(x)=left{begin{matrix} 1 & if x>0 & \ 0.01& if x<=0 & end{matrix}right. f′(x)={ 10.01ifx>0ifx<=0函数图像如下:
特点:与 ReLu 相比 ,leak 给所有负值赋予一个非零斜率, leak是一个很小的常数 a i large a_{i} ai,这样保留了一些负轴的值,使得负轴的信息不会全部丢失。
函数及导数代码:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def leaky_relu(x):
"""leaky relu函数"""
return np.where(x<0,0.01*x,x)
def dx_leaky_relu(x):
"""leaky relu函数的导数"""
return np.where(x < 0, 0.01, 1)
# ---------------------------------------------
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
fx = leaky_relu(x)
dx_fx = dx_leaky_relu(x)
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Leaky ReLu函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Leaky Relu函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx_fx)
plt.show()
与Leaky ReLU相似的还有PReLU和RReLU,下图是他们的比较:
PReLU中的 a i a_{i} ai是根据数据变化的;
Leaky ReLU中的 a i a_{i} ai是固定的;
RReLU中的 a j i a_{ji} aji是一个在一个给定的范围内随机抽取的值,这个值在测试环节就会固定下来。
- ELU激活函数
函数定义: f ( x ) = { x , i f x ≥ 0 a ( e x − 1 ) , i f x < 0 f(x)=left{begin{matrix} x,&if & xgeq 0\ a(e^{x}-1), &if &x< 0 end{matrix}right. f(x)={ x,a(ex−1),ififx≥0x<0
函数图像如下:
导数: f ′ = { 1 i f x ≥ 0 f ( x ) a i f x < 0 {f}’=left{begin{matrix} 1 &if & xgeq 0\ f(x) a &if &x< 0 end{matrix}right. f′={ 1f(x) aififx≥0x<0
函数图像如下:
特点:
- 融合了sigmoid和ReLU,左侧具有软饱和性,右侧无饱和性。
- 右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。
- ELU的输出均值接近于零,所以收敛速度更快。
- 在 ImageNet上,不加 Batch Normalization 30 层以上的 ReLU 网络会无法收敛,PReLU网络在MSRA的Fan-in (caffe )初始化下会发散,而 ELU 网络在Fan-in/Fan-out下都能收敛。
函数及导数代码:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def ELU(x):
"""ELU函数"""
return np.where(x<0,np.exp(x)-1,x)
def dx_ELU(x):
"""ELU函数的导数"""
return np.where(x < 0, np.exp(x), 1)
# ---------------------------------------------
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
fx = ELU(x)
dx_fx = dx_ELU(x)
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('ELU函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('ELU函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx_fx)
plt.show()
- Mish激活函数
函数定义: f ( x ) = x ∗ t a n h ( l n ( 1 e x ) ) f(x)=x*tanh(ln(1 e^{x})) f(x)=x∗tanh(ln(1 ex)),函数图像如下:
导数:
函数图像如下:
特点:
特点:无上界(unbounded above)、有下界(bounded below)、平滑(smooth)和非单调(nonmonotonic)。 无上界:可以防止网络饱和,即梯度消失。 有下界:提升网络的正则化效果。 平滑:首先在0值点连续相比ReLU可以减少一些不可预料的问题,其次可以使网络更容易优化并且提高泛化性能。 非单调:可以使一些小的负输入也被保留为负输出,提高网络的可解释能力和梯度流 优点:平滑、非单调、上无界、有下界 缺点:引入了指数函数,增加了计算量
函数及导数代码:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def sech(x):
"""sech函数"""
return 2 / (np.exp(x) np.exp(-x))
def sigmoid(x):
"""sigmoid函数"""
return 1 / (1 np.exp(-x))
def softplus(x):
"""softplus函数"""
return np.log10(1 np.exp(x))
def tanh(x):
"""tanh函数"""
return ((np.exp(x) - np.exp(-x)) / (np.exp(x) np.exp(-x)))
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
fx = x * tanh(softplus(x))
dx_fx = sech(softplus(x))*sech(softplus(x))*x*sigmoid(x) fx/x
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Mish函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Mish函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx_fx)
plt.show()
- Swish 激活函数
函数定义为: f ( x ) = x ∗ s i g m o i d ( β x ) f(x) = x*sigmoid(beta x) f(x)=x∗sigmoid(βx),其函数图像如下:
其导数:
[外链图
函数图像如下:
特点:
特点:Swish 具备无上界有下界、平滑、非单调的特性。 优点:ReLU有无上界和有下界的特点,而Swish相比ReLU又增加了平滑和非单调的特点,这使得其在ImageNet上的效果更好。 缺点:引入了指数函数,增加了计算量
函数及导数代码:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def sech(x):
"""sech函数"""
return 2 / (np.exp(x) np.exp(-x))
def sigmoid(x):
"""sigmoid函数"""
return 1 / (1 np.exp(-x))
def s(x):
"""sigmoid函数"""
return 1 / (1 np.exp(-b*x))
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
b = 1
fx = x / (1 np.exp(-b * x))
dx_fx = b * fx s(x) * (1 - b * fx)
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Swish函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('Swish函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx_fx)
plt.show()
- SiLU激活函数
函数定义为: f ( x ) = x ⋅ s i g m o i d ( x ) f(x)=xcdot sigmoid (x) f(x)=x⋅sigmoid(x),其函数图形如下:
导数为: f ′ ( x ) = f ( x ) s i g m o i d ( x ) ( 1 − f ( x ) ) {f}'(x)=f(x) sigmoid (x)(1-f(x)) f′(x)=f(x) sigmoid(x)(1−f(x)),其函数图像如下:
函数及导数代码:
代码语言:javascript复制from matplotlib import pyplot as plt
import numpy as np
# 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def sigmoid(x):
y = 1 / (1 np.exp(-x))
return y
def silu(x):
return x*sigmoid(x)
def dx_silu(x):
return silu(x) sigmoid(x)*(1-silu(x))
if __name__ == '__main__':
x = np.arange(-10, 10, 0.01)
b = 1
fx = silu(x)
dx=dx_silu(x)
plt.subplot(1, 2, 1)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('SiLU函数')
plt.xlabel('x')
plt.ylabel('fx')
plt.plot(x, fx)
plt.subplot(1, 2, 2)
ax = plt.gca() # 得到图像的Axes对象
ax.spines['right'].set_color('none') # 将图像右边的轴设为透明
ax.spines['top'].set_color('none') # 将图像上面的轴设为透明
ax.xaxis.set_ticks_position('bottom') # 将x轴刻度设在下面的坐标轴上
ax.yaxis.set_ticks_position('left') # 将y轴刻度设在左边的坐标轴上
ax.spines['bottom'].set_position(('data', 0)) # 将两个坐标轴的位置设在数据点原点
ax.spines['left'].set_position(('data', 0))
plt.title('SiLU函数的导数')
plt.xlabel('x')
plt.ylabel('dx_fx')
plt.plot(x, dx)
plt.show()
4.5 激活函数有哪些性质?
- 非线性: 当激活函数是非线性的,一个两层的神经网络就可以基本上逼近所有的函数。但如果激活函数是恒等激活函数的时候,即 f ( x ) = x f(x)=x f(x)=x,就不满足这个性质,而且如果 MLP 使用的是恒等激活函数,那么其实整个网络跟单层神经网络是等价的;
- 可微性: 当优化方法是基于梯度的时候,就体现了该性质;
- 单调性: 当激活函数是单调的时候,单层网络能够保证是凸函数; 4. f ( x ) ≈ x f(x)≈x f(x)≈x: 当激活函数满足这个性质的时候,如果参数的初始化是随机的较小值,那么神经网络的训练将会很高效;如果不满足这个性质,那么就需要详细地去设置初始值;
- 输出值的范围: 当激活函数输出值是有限的时候,基于梯度的优化方法会更加稳定,因为特征的表示受有限权值的影响更显著;当激活函数的输出是无限的时候,模型的训练会更加高效,不过在这种情况下,一般需要更小的 Learning Rate。
4.6 如何选择激活函数?
选择一个适合的激活函数并不容易,需要考虑很多因素,通常的做法是,如果不确定哪一个激活函数效果更好,可以把它们都试试,然后在验证集或者测试集上进行评价。然后看哪一种表现的更好,就去使用它。
以下是常见的选择情况:
- 如果输出是 0、1 值(二分类问题),则输出层选择 sigmoid 函数,然后其它的所有单元都选择 Relu 函数。
- 如果在隐藏层上不确定使用哪个激活函数,那么通常会使用 Relu 激活函数。有时,也会使用 tanh 激活函数,但 Relu 的一个优点是:当是负值的时候,导数等于 0。
- sigmoid 激活函数:除了输出层是一个二分类问题基本不会用它。
- tanh 激活函数:tanh 是非常优秀的,几乎适合所有场合。
- ReLu 激活函数:最常用的默认函数,如果不确定用哪个激活函数,就使用 ReLu 或者 Leaky ReLu,再去尝试其他的激活函数。
- 如果遇到了一些死的神经元,我们可以使用 Leaky ReLU 函数。
4.7 使用 ReLu 激活函数的优点?
- 在区间变动很大的情况下,ReLu 激活函数的导数或者激活函数的斜率都会远大于 0,在程序实现就是一个 if-else 语句,而 sigmoid 函数需要进行浮点四则运算,在实践中,使用 ReLu 激活函数神经网络通常会比使用 sigmoid 或者 tanh 激活函数学习的更快。
- sigmoid 和 tanh 函数的导数在正负饱和区的梯度都会接近于 0,这会造成梯度弥散,而 Relu 和Leaky ReLu 函数大于 0 部分都为常数,不会产生梯度弥散现象。
- 需注意,Relu 进入负半区的时候,梯度为 0,神经元此时不会训练,产生所谓的稀疏性,而 Leaky ReLu 不会产生这个问题。
4.8 什么时候可以用线性激活函数?
- 输出层,大多使用线性激活函数。
- 在隐含层可能会使用一些线性激活函数。
- 一般用到的线性激活函数很少。
4.9 怎样理解 Relu(< 0 时)是非线性激活函数?
Relu 激活函数图像如下:
根据图像可看出具有如下特点:
- 单侧抑制;
- 相对宽阔的兴奋边界;
- 稀疏激活性; ReLU 函数从图像上看,是一个分段线性函数,把所有的负值都变为 0,而正值不变,这样就成为单侧抑制。 因为有了这单侧抑制,才使得神经网络中的神经元也具有了稀疏激活性。 稀疏激活性:从信号方面来看,即神经元同时只对输入信号的少部分选择性响应,大量信号被刻意的屏蔽了,这样可以提高学习的精度,更好更快地提取稀疏特征。当 x < 0 x<0 x<0时,ReLU 硬饱和,而当 x > 0 x>0 x>0时,则不存在饱和问题。ReLU 能够在 x > 0 x>0 x>0时保持梯度不衰减,从而缓解梯度消失问题。
4.10 Softmax 定义及作用
Softmax 是一种形如下式的函数: P ( i ) = e x p ( θ i T x ) ∑ k = 1 K e x p ( θ i T x ) P(i) = frac{exp(theta_i^T x)}{sum_{k=1}^{K} exp(theta_i^T x)} P(i)=∑k=1Kexp(θiTx)exp(θiTx) 其中, θ i theta_i θi和 x x x是列向量, θ i T x theta_i^T x θiTx可能被换成函数关于 x x x的函数 f i ( x ) f_i(x) fi(x)
通过 softmax 函数,可以使得 P ( i ) P(i) P(i)的范围在 [ 0 , 1 ] [0,1] [0,1]之间。在回归和分类问题中,通常 θ theta θ是待求参数,通过寻找使得 P ( i ) P(i) P(i)最大的 θ i theta_i θi作为最佳参数。
但是,使得范围在 [ 0 , 1 ] [0,1] [0,1] 之间的方法有很多,为啥要在前面加上以 e e e的幂函数的形式呢?参考 logistic 函数: P ( i ) = 1 1 e x p ( − θ i T x ) P(i) = frac{1}{1 exp(-theta_i^T x)} P(i)=1 exp(−θiTx)1 这个函数的作用就是使得 P ( i ) P(i) P(i)在负无穷到 0 的区间趋向于 0, 在 0 到正无穷的区间趋向 1,。同样 softmax 函数加入了 e e e的幂函数正是为了两极化:正样本的结果将趋近于 1,而负样本的结果趋近于 0。这样为多类别提供了方便(可以把 P ( i ) P(i) P(i)看做是样本属于类别的概率)。可以说,Softmax 函数是 logistic 函数的一种泛化。
softmax 函数可以把它的输入,通常被称为 logits 或者 logit scores,处理成 0 到 1 之间,并且能够把输出归一化到和为 1。这意味着 softmax 函数与分类的概率分布等价。它是一个网络预测多酚类问题的最佳输出激活函数。
4.11 Softmax 函数如何应用于多分类?
softmax 用于多分类过程中,它将多个神经元的输出,映射到 ( 0 , 1 ) (0,1) (0,1)区间内,可以看成概率来理解,从而来进行多分类!
假设我们有一个数组, V i V_i Vi表示 V V V 中的第 i i i个元素,那么这个元素的 softmax 值就是
S i = e V i ∑ j e V j S_i = frac{e^{V_i}}{sum_j e^{V_j}} Si=∑jeVjeVi
从下图看,神经网络中包含了输入层,然后通过两个特征层处理,最后通过 softmax 分析器就能得到不同条件下的概率,这里需要分成三个类别,最终会得到 y = 0 , y = 1 , y = 2 y=0, y=1, y=2 y=0,y=1,y=2的概率值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6BbtnCB6-1628659360507)(https://gitee.com/wanghao1090220084/images/raw/master/img/3.4.9.1.png)]
继续看下面的图,三个输入通过 softmax 后得到一个数组 [ 0.05 , 0.10 , 0.85 ] [0.05 , 0.10 , 0.85] [0.05,0.10,0.85],这就是 soft 的功能。
更形象的映射过程如下图所示:
softmax 直白来说就是将原来输出是 3 , 1 , − 3 3,1,-3 3,1,−3通过 softmax 函数一作用,就映射成为 ( 0 , 1 ) (0,1) (0,1)的值,而这些值的累和为 1 1 1(满足概率的性质),那么我们就可以将它理解成概率,在最后选取输出结点的时候,我们就可以选取概率最大(也就是值对应最大的)结点,作为我们的预测目标!
4.12 交叉熵代价函数定义及其求导推导
神经元的输出就是 a = σ(z),其中 z = ∑ w j i j b z=sum w_{j}i_{j} b z=∑wjij b是输⼊的带权和。
C = − 1 n ∑ [ y l n a ( 1 − y ) l n ( 1 − a ) ] C=-frac{1}{n}sum[ylna (1-y)ln(1-a)] C=−n1∑[ylna (1−y)ln(1−a)]
其中 n 是训练数据的总数,求和是在所有的训练输⼊ x 上进⾏的, y 是对应的⽬标输出。
表达式是否解决学习缓慢的问题并不明显。实际上,甚⾄将这个定义看做是代价函数也不是显⽽易⻅的!在解决学习缓慢前,我们来看看交叉熵为何能够解释成⼀个代价函数。
将交叉熵看做是代价函数有两点原因。
第⼀,它是⾮负的, C > 0。可以看出:式子中的求和中的所有独⽴的项都是负数的,因为对数函数的定义域是 (0,1),并且求和前⾯有⼀个负号,所以结果是非负。
第⼆,如果对于所有的训练输⼊ x,神经元实际的输出接近⽬标值,那么交叉熵将接近 0。
假设在这个例⼦中, y = 0 ⽽ a ≈ 0。这是我们想到得到的结果。我们看到公式中第⼀个项就消去了,因为 y = 0,⽽第⼆项实际上就是 − ln(1 − a) ≈ 0。反之, y = 1 ⽽ a ≈ 1。所以在实际输出和⽬标输出之间的差距越⼩,最终的交叉熵的值就越低了。(这里假设输出结果不是0,就是1,实际分类也是这样的)
综上所述,交叉熵是⾮负的,在神经元达到很好的正确率的时候会接近 0。这些其实就是我们想要的代价函数的特性。其实这些特性也是⼆次代价函数具备的。所以,交叉熵就是很好的选择了。但是交叉熵代价函数有⼀个⽐⼆次代价函数更好的特性就是它避免了学习速度下降的问题。为了弄清楚这个情况,我们来算算交叉熵函数关于权重的偏导数。我们将 a = ς ( z ) a={varsigma}(z) a=ς(z)代⼊到 公式中应⽤两次链式法则,得到:
KaTeX parse error: No such environment: eqnarray at position 7: begin{̲e̲q̲n̲a̲r̲r̲a̲y̲}̲frac{partial …
根据 ς ( z ) = 1 1 e − z varsigma(z)=frac{1}{1 e^{-z}} ς(z)=1 e−z1的定义,和⼀些运算,我们可以得到 ς ′ ( z ) = ς ( z ) ( 1 − ς ( z ) ) {varsigma}'(z)=varsigma(z)(1-varsigma(z)) ς′(z)=ς(z)(1−ς(z))。化简后可得:
∂ C ∂ w j = 1 n ∑ x j ( ς ( z ) − y ) frac{partial C}{partial w_{j}}=frac{1}{n}sum x_{j}({varsigma}(z)-y) ∂wj∂C=n1∑xj(ς(z)−y)
这是⼀个优美的公式。它告诉我们权重学习的速度受到 ς ( z ) − y varsigma(z)-y ς(z)−y,也就是输出中的误差的控制。更⼤的误差,更快的学习速度。这是我们直觉上期待的结果。特别地,这个代价函数还避免了像在⼆次代价函数中类似⽅程中 ς ′ ( z ) {varsigma}'(z) ς′(z)导致的学习缓慢。当我们使⽤交叉熵的时候, ς ′ ( z ) {varsigma}'(z) ς′(z)被约掉了,所以我们不再需要关⼼它是不是变得很⼩。这种约除就是交叉熵带来的特效。实际上,这也并不是⾮常奇迹的事情。我们在后⾯可以看到,交叉熵其实只是满⾜这种特性的⼀种选择罢了。
根据类似的⽅法,我们可以计算出关于偏置的偏导数。我这⾥不再给出详细的过程,你可以轻易验证得到:
∂ C ∂ b = 1 n ∑ ( ς ( z ) − y ) frac{partial C}{partial b}=frac{1}{n}sum ({varsigma}(z)-y) ∂b∂C=n1∑(ς(z)−y)
再⼀次, 这避免了⼆次代价函数中类似 ς ′ ( z ) {varsigma}'(z) ς′(z)项导致的学习缓慢。
4.13 为什么Tanh收敛速度比Sigmoid快?
首先看如下两个函数的求导:
t a n h , ( x ) = 1 − t a n h ( x ) 2 ∈ ( 0 , 1 ) tanh^{,}(x)=1-tanh(x)^{2}in (0,1) tanh,(x)=1−tanh(x)2∈(0,1)
s , ( x ) = s ( x ) ∗ ( 1 − s ( x ) ) ∈ ( 0 , 1 4 ] s^{,}(x)=s(x)*(1-s(x))in (0,frac{1}{4}] s,(x)=s(x)∗(1−s(x))∈(0,41]
由上面两个公式可知tanh(x)梯度消失的问题比sigmoid轻,所以Tanh收敛速度比Sigmoid快。
注:梯度消失(gradient vanishing)或者爆炸(gradient explosion)是激活函数以及当前权重耦合产生的综合结果: 设任意激活函数为 σ ( ⋅ ) sigma(cdot) σ(⋅),k 1层网络输出为 f k 1 = σ ( W f k ) f_{k 1}=sigma(Wf_k) fk 1=σ(Wfk),求导得到 ∂ h t 1 ∂ h t = d i a g ( σ ′ ( W h t ) ) W frac {partial h_{t 1}}{partial h_t}=diag(sigma'(Wh_t))W ∂ht∂ht 1=diag(σ′(Wht))W。可见求导结果同时会受到权重 W W W和激活函数的导数 σ ′ ( ⋅ ) sigma'(cdot) σ′(⋅)的影响,以sigmoid函数 σ ( X ) = 1 1 e − x sigma(X)=frac {1}{1 e^{-x}} σ(X)=1 e−x1为例,其导数为 σ ′ ( x ) = 1 1 e − x ( 1 − 1 1 e − x ) sigma'(x)=frac{1}{1 e^{-x}}(1-frac{1}{1 e^{-x}}) σ′(x)=1 e−x1(1−1 e−x1),其值恒大于零小于1,用链式法则求梯度回传时连续相乘使得结果趋于0,但是如果权重 W W W是较大的数值,使得 ∂ f t 1 ∂ f t frac {partial f_{t 1}}{partial f_t} ∂ft∂ft 1相乘结果大于1,则梯度回传时连续相乘则不会发生梯度消失。 综上,在讨论激活函数收敛速度或与梯度消失或者爆炸相关时,应同时考虑当前权重 W W W数值的影响。
5 Batch_Size
5.1 什么是BatchSize
Batch一般被翻译为批量,设置batch_size的目的让模型在训练过程中每次选择批量的数据来进行处理。一般机器学习或者深度学习训练过程中的目标函数可以简单理解为在每个训练集样本上得到的目标函数值的求和,然后根据目标函数的值进行权重值的调整,大部分时候是根据梯度下降法来进行参数更新的。
Batch Size的直观理解就是一次训练所选取的样本数。 Batch Size的大小影响模型的优化程度和速度。同时其直接影响到GPU内存的使用情况,假如你GPU内存不大,该数值最好设置小一点。
5.2 为什么需要 Batch_Size?
在没有使用Batch Size之前,这意味着网络在训练时,是一次把所有的数据(整个数据库)输入网络中,然后计算它们的梯度进行反向传播,由于在计算梯度时使用了整个数据库,所以计算得到的梯度方向更为准确。但在这情况下,计算得到不同梯度值差别巨大,难以使用一个全局的学习率,所以这时一般使用Rprop这种基于梯度符号的训练算法,单独进行梯度更新。 在小样本数的数据库中,不使用Batch Size是可行的,而且效果也很好。但是一旦是大型的数据库,一次性把所有数据输进网络,肯定会引起内存的爆炸。所以就提出Batch Size的概念。
5.3 如何设置Batch_Size 的值?
假如每次只训练一个样本,即 Batch_Size = 1。线性神经元在均方误差代价函数的错误面是一个抛物面,横截面是椭圆。对于多层神经元、非线性网络,在局部依然近似是抛物面。此时,每次修正方向以各自样本的梯度方向修正,横冲直撞各自为政,难以达到收敛。
既然 Batch_Size 为全数据集或者Batch_Size = 1都有各自缺点,那么如何设置一个合适的BatchSize呢? 这个和样本还有一定的关系,样本量少的时候会带来很大的方差,而这个大方差恰好会导致我们在梯度下降到很差的局部最优点(只是微微凸下去的最优点)和鞍点的时候不稳定,一不小心就因为一个大噪声的到来导致炸出了局部最优点。 与之相反的,当样本量很多时,方差很小,对梯度的估计要准确和稳定的多,因此反而在差劲的局部最优点和鞍点时反而容易自信的呆着不走了,从而导致神经网络收敛到很差的点上,跟出了bug一样的差劲。
batch的size设置的不能太大也不能太小,因此实际工程中最常用的就是mini-batch,一般size设置为几十或者几百。 对于二阶优化算法,减小batch换来的收敛速度提升远不如引入大量噪声导致的性能下降,因此在使用二阶优化算法时,往往要采用大batch哦。此时往往batch设置成几千甚至一两万才能发挥出最佳性能。 所以设置BatchSize要注意一下几点:
代码语言:javascript复制 1)batch数太小,而类别又比较多的时候,真的可能会导致loss函数震荡而不收敛,尤其是在你的网络比较复杂的时候。
2)随着batchsize增大,处理相同的数据量的速度越快。
3)随着batchsize增大,达到相同精度所需要的epoch数量越来越多。
4)由于上述两种因素的矛盾, Batch_Size 增大到某个时候,达到时间上的最优。
5)由于最终收敛精度会陷入不同的局部极值,因此 Batch_Size 增大到某些时候,达到最终收敛精度上的最优。
6)过大的batchsize的结果是网络很容易收敛到一些不好的局部最优点。同样太小的batch也存在一些问题,比如训练速度很慢,训练不容易收敛等。
7)具体的batch size的选取和训练集的样本数目相关。
8)GPU对2的幂次的batch可以发挥更佳的性能,因此设置成16、32、64、128…时往往要比设置为整10、整100的倍数时表现更优
我在设置BatchSize的时候,首先选择大点的BatchSize把GPU占满,观察Loss收敛的情况,如果不收敛,或者收敛效果不好则降低BatchSize,一般常用16,32,64等。
5.4 在合理范围内,增大Batch_Size有何好处?
内存利用率提高了,大矩阵乘法的并行化效率提高。 跑完一次 epoch(全数据集)所需的迭代次数减少,对于相同数据量的处理速度进一步加快。 在一定范围内,一般来说 Batch_Size 越大,其确定的下降方向越准,引起训练震荡越小。
5.5 盲目增大 Batch_Size 有何坏处?
内存利用率提高了,但是内存容量可能撑不住了。 跑完一次 epoch(全数据集)所需的迭代次数减少,要想达到相同的精度,其所花费的时间大大增加了,从而对参数的修正也就显得更加缓慢。 Batch_Size 增大到一定程度,其确定的下降方向已经基本不再变化。
5.6 调节 Batch_Size 对训练效果影响到底如何?
Batch_Size 太小,模型表现效果极其糟糕(error飙升)。 随着 Batch_Size 增大,处理相同数据量的速度越快。 随着 Batch_Size 增大,达到相同精度所需要的 epoch 数量越来越多。 由于上述两种因素的矛盾, Batch_Size 增大到某个时候,达到时间上的最优。 由于最终收敛精度会陷入不同的局部极值,因此 Batch_Size 增大到某些时候,达到最终收敛精度上的最优。
6 归一化
6.1 什么是归一化?
- 归纳统一样本的统计分布性。归一化在 0 − 1 0-1 0−1之间是统计的概率分布,归一化在 − 1 − − 1 -1– 1 −1−− 1之间是统计的坐标分布。
- 无论是为了建模还是为了计算,首先基本度量单位要同一,神经网络是以样本在事件中的统计分别几率来进行训练(概率计算)和预测,且 sigmoid 函数的取值是 0 到 1 之间的,网络最后一个节点的输出也是如此,所以经常要对样本的输出归一化处理。
- 归一化是统一在 0 − 1 0-1 0−1之间的统计概率分布,当所有样本的输入信号都为正值时,与第一隐含层神经元相连的权值只能同时增加或减小,从而导致学习速度很慢。
- 另外在数据中常存在奇异样本数据,奇异样本数据存在所引起的网络训练时间增加,并可能引起网络无法收敛。为了避免出现这种情况及后面数据处理的方便,加快网络学习速度,可以对输入信号进行归一化,使得所有样本的输入信号其均值接近于 0 或与其均方差相比很小。
6.2 为什么要归一化?
- 为了后面数据处理的方便,归一化的确可以避免一些不必要的数值问题。
- 为了程序运行时收敛加快。
- 同一量纲。样本数据的评价标准不一样,需要对其量纲化,统一评价标准。这算是应用层面的需求。
- 避免神经元饱和。啥意思?就是当神经元的激活在接近 0 或者 1 时会饱和,在这些区域,梯度几乎为 0,这样,在反向传播过程中,局部梯度就会接近 0,这会有效地“杀死”梯度。
- 保证输出数据中数值小的不被吞食。
- 有可能提高精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。
6.3 为什么归一化能提高求解最优解速度?
上图是代表数据是否均一化的最优解寻解过程(圆圈可以理解为等高线)。左图表示未经归一化操作的寻解过程,右图表示经过归一化后的寻解过程。
当使用梯度下降法寻求最优解时,很有可能走“之字型”路线(垂直等高线走),从而导致需要迭代很多次才能收敛;而右图对两个原始特征进行了归一化,其对应的等高线显得很圆,在梯度下降进行求解时能较快的收敛。
因此如果机器学习模型使用梯度下降法求最优解时,归一化往往非常有必要,否则很难收敛甚至不能收敛。
6.4 3D 图解未归一化
例子:
假设 w 1 w1 w1的范围在 [ − 10 , 10 ] [-10, 10] [−10,10],而 w 2 w2 w2的范围在 [ − 100 , 100 ] [-100, 100] [−100,100],梯度每次都前进 1 单位,那么在 w 1 w1 w1方向上每次相当于前进了 1 / 20 1/20 1/20,而在 w 2 w2 w2上只相当于 1 / 200 1/200 1/200!某种意义上来说,在 w 2 w2 w2上前进的步长更小一些,而 w 1 w1 w1在搜索过程中会比 w 2 w2 w2“走”得更快。
这样会导致,在搜索过程中更偏向于 w 1 w1 w1的方向。走出了“L”形状,或者成为“之”字形。
6.5 归一化有哪些类型?
- 线性归一化
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、指数,正切等。
6.6 局部响应归一化作用
LRN 是一种提高深度学习准确度的技术方法。LRN 一般是在激活、池化函数后的一种方法。
在 ALexNet 中,提出了 LRN 层,对局部神经元的活动创建竞争机制,使其中响应比较大对值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。
6.7 理解局部响应归一化
局部响应归一化原理是仿造生物学上活跃的神经元对相邻神经元的抑制现象(侧抑制),其公式如下:
b x , y i = a x , y i / ( k α ∑ j = m a x ( 0 , i − n / 2 ) m i n ( N − 1 , i n / 2 ) ( a x , y j ) 2 ) β b_{x,y}^i = a_{x,y}^i / (k alpha sum_{j=max(0, i-n/2)}^{min(N-1, i n/2)}(a_{x,y}^j)^2 )^beta bx,yi=ax,yi/(k αj=max(0,i−n/2)∑min(N−1,i n/2)(ax,yj)2)β
其中, 1) a a a:表示卷积层(包括卷积操作和池化操作)后的输出结果,是一个四维数组[batch,height,width,channel]。
- batch:批次数(每一批为一张图片)。
- height:图片高度。
- width:图片宽度。
- channel:通道数。可以理解成一批图片中的某一个图片经过卷积操作后输出的神经元个数,或理解为处理后的图片深度。
2) a x , y i a_{x,y}^i ax,yi表示在这个输出结构中的一个位置 [ a , b , c , d ] [a,b,c,d] [a,b,c,d],可以理解成在某一张图中的某一个通道下的某个高度和某个宽度位置的点,即第 a a a张图的第 d d d个通道下的高度为b宽度为c的点。
3) N N N:论文公式中的 N N N表示通道数 (channel)。
4) a a a, n / 2 n/2 n/2, k k k分别表示函数中的 input,depth_radius,bias。参数 k , n , α , β k, n, alpha, beta k,n,α,β都是超参数,一般设置 k = 2 , n = 5 , α = 1 ∗ e − 4 , β = 0.75 k=2, n=5, alpha=1*e-4, beta=0.75 k=2,n=5,α=1∗e−4,β=0.75
5) ∑ sum ∑: ∑ sum ∑叠加的方向是沿着通道方向的,即每个点值的平方和是沿着 a a a中的第 3 维 channel 方向的,也就是一个点同方向的前面 n / 2 n/2 n/2个通道(最小为第 0 0 0个通道)和后 n / 2 n/2 n/2个通道(最大为第 d − 1 d-1 d−1个通道)的点的平方和(共 n 1 n 1 n 1个点)。而函数的英文注解中也说明了把 input 当成是 d d d个 3 维的矩阵,说白了就是把 input 的通道数当作 3 维矩阵的个数,叠加的方向也是在通道方向。
简单的示意图如下:
6.8 什么是批归一化(Batch Normalization)
以前在神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理。要知道,虽然我们对输入数据进行了归一化处理,但是输入数据经过 σ ( W X b ) sigma(WX b) σ(WX b)这样的矩阵乘法以及非线性运算之后,其数据分布很可能被改变,而随着深度网络的多层运算之后,数据分布的变化将越来越大。如果我们能在网络的中间也进行归一化处理,是否对网络的训练起到改进作用呢?答案是肯定的。
这种在神经网络中间层也进行归一化处理,使训练效果更好的方法,就是批归一化Batch Normalization(BN)。
6.9 批归一化(BN)算法的优点
下面我们来说一下BN算法的优点:
- 减少了人为选择参数。在某些情况下可以取消 dropout 和 L2 正则项参数,或者采取更小的 L2 正则项约束参数;
- 减少了对学习率的要求。现在我们可以使用初始很大的学习率或者选择了较小的学习率,算法也能够快速训练收敛;
- 可以不再使用局部响应归一化。BN 本身就是归一化网络(局部响应归一化在 AlexNet 网络中存在)
- 破坏原来的数据分布,一定程度上缓解过拟合(防止每批训练中某一个样本经常被挑选到,文献说这个可以提高 1% 的精度)。
- 减少梯度消失,加快收敛速度,提高训练精度。
6.10 批归一化(BN)算法流程
下面给出 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 的无偏估计得到。
6.11 批归一化和群组归一化比较
名称 | 特点 |
---|---|
批量归一化(Batch Normalization,以下简称 BN) | 可让各种网络并行训练。但是,批量维度进行归一化会带来一些问题——批量统计估算不准确导致批量变小时,BN 的误差会迅速增加。在训练大型网络和将特征转移到计算机视觉任务中(包括检测、分割和视频),内存消耗限制了只能使用小批量的 BN。 |
群组归一化 Group Normalization (简称 GN) | GN 将通道分成组,并在每组内计算归一化的均值和方差。GN 的计算与批量大小无关,并且其准确度在各种批量大小下都很稳定。 |
比较 | 在 ImageNet 上训练的 ResNet-50上,GN 使用批量大小为 2 时的错误率比 BN 的错误率低 10.6% ;当使用典型的批量时,GN 与 BN 相当,并且优于其他标归一化变体。而且,GN 可以自然地从预训练迁移到微调。在进行 COCO 中的目标检测和分割以及 Kinetics 中的视频分类比赛中,GN 可以胜过其竞争对手,表明 GN 可以在各种任务中有效地取代强大的 BN。 |
6.12 Weight Normalization和Batch Normalization比较
Weight Normalization 和 Batch Normalization 都属于参数重写(Reparameterization)的方法,只是采用的方式不同。
Weight Normalization 是对网络权值 W进行 normalization,因此也称为 Weight Normalization;
Batch Normalization 是对网络某一层输入数据进行 normalization。
Weight Normalization相比Batch Normalization有以下三点优势:
- Weight Normalization 通过重写深度学习网络的权重W的方式来加速深度学习网络参数收敛,没有引入 minbatch 的依赖,适用于 RNN(LSTM)网络(Batch Normalization 不能直接用于RNN,进行 normalization 操作,原因在于:1) RNN 处理的 Sequence 是变长的;2) RNN 是基于 time step 计算,如果直接使用 Batch Normalization 处理,需要保存每个 time step 下,mini btach 的均值和方差,效率低且占内存)。
- Batch Normalization 基于一个 mini batch 的数据计算均值和方差,而不是基于整个 Training set 来做,相当于进行梯度计算式引入噪声。因此,Batch Normalization 不适用于对噪声敏感的强化学习、生成模型(Generative model:GAN,VAE)使用。相反,Weight Normalization 对通过标量 g g g和向量 v v v对权重 W W W进行重写,重写向量 v v v是固定的,因此,基于 Weight Normalization 的 Normalization 可以看做比 Batch Normalization 引入更少的噪声。
- 不需要额外的存储空间来保存 mini batch 的均值和方差,同时实现 Weight Normalization 时,对深度学习网络进行正向信号传播和反向梯度计算带来的额外计算开销也很小。因此,要比采用 Batch Normalization 进行 normalization 操作时,速度快。 但是 Weight Normalization 不具备 Batch Normalization 把网络每一层的输出 Y 固定在一个变化范围的作用。因此,采用 Weight Normalization 进行 Normalization 时需要特别注意参数初始值的选择。
6.13 Batch Normalization在什么时候用比较合适?
(贡献者:黄钦建-华南理工大学)
在CNN中,BN应作用在非线性映射前。在神经网络训练时遇到收敛速度很慢,或梯度爆炸等无法训练的状况时可以尝试BN来解决。另外,在一般使用情况下也可以加入BN来加快训练速度,提高模型精度。
BN比较适用的场景是:每个mini-batch比较大,数据分布比较接近。在进行训练之前,要做好充分的shuffle,否则效果会差很多。另外,由于BN需要在运行过程中统计每个mini-batch的一阶统计量和二阶统计量,因此不适用于动态的网络结构和RNN网络。
7 权重偏差初始化
7.1 全都初始化为 0
偏差初始化陷阱: 都初始化为 0。
产生陷阱原因:因为并不知道在训练神经网络中每一个权重最后的值,但是如果进行了恰当的数据归一化后,我们可以有理由认为有一半的权重是正的,另一半是负的。令所有权重都初始化为 0,如果神经网络计算出来的输出值是一样的,神经网络在进行反向传播算法计算出来的梯度值也一样,并且参数更新值也一样。更一般地说,如果权重初始化为同一个值,网络就是对称的。
形象化理解:在神经网络中考虑梯度下降的时候,设想你在爬山,但身处直线形的山谷中,两边是对称的山峰。由于对称性,你所在之处的梯度只能沿着山谷的方向,不会指向山峰;你走了一步之后,情况依然不变。结果就是你只能收敛到山谷中的一个极大值,而走不到山峰上去。
7.2 全都初始化为同样的值
偏差初始化陷阱: 都初始化为一样的值。 以一个三层网络为例: 首先看下结构
它的表达式为:
a 1 ( 2 ) = f ( W 11 ( 1 ) x 1 W 12 ( 1 ) x 2 W 13 ( 1 ) x 3 b 1 ( 1 ) ) a_1^{(2)} = f(W_{11}^{(1)} x_1 W_{12}^{(1)} x_2 W_{13}^{(1)} x_3 b_1^{(1)}) a1(2)=f(W11(1)x1 W12(1)x2 W13(1)x3 b1(1))
a 2 ( 2 ) = f ( W 21 ( 1 ) x 1 W 22 ( 1 ) x 2 W 23 ( 1 ) x 3 b 2 ( 1 ) ) a_2^{(2)} = f(W_{21}^{(1)} x_1 W_{22}^{(1)} x_2 W_{23}^{(1)} x_3 b_2^{(1)}) a2(2)=f(W21(1)x1 W22(1)x2 W23(1)x3 b2(1))
a 3 ( 2 ) = f ( W 31 ( 1 ) x 1 W 32 ( 1 ) x 2 W 33 ( 1 ) x 3 b 3 ( 1 ) ) a_3^{(2)} = f(W_{31}^{(1)} x_1 W_{32}^{(1)} x_2 W_{33}^{(1)} x_3 b_3^{(1)}) a3(2)=f(W31(1)x1 W32(1)x2 W33(1)x3 b3(1))
h W , b ( x ) = a 1 ( 3 ) = f ( W 11 ( 2 ) a 1 ( 2 ) W 12 ( 2 ) a 2 ( 2 ) W 13 ( 2 ) a 3 ( 2 ) b 1 ( 2 ) ) h_{W,b}(x) = a_1^{(3)} = f(W_{11}^{(2)} a_1^{(2)} W_{12}^{(2)} a_2^{(2)} W_{13}^{(2)} a_3^{(2)} b_1^{(2)}) hW,b(x)=a1(3)=f(W11(2)a1(2) W12(2)a2(2) W13(2)a3(2) b1(2))
x a 1 ( 2 ) = f ( W 11 ( 1 ) x 1 W 12 ( 1 ) x 2 W 13 ( 1 ) x 3 b 1 ( 1 ) ) a 2 ( 2 ) = f ( W 21 ( 1 ) x 1 W 22 ( 1 ) x 2 W 23 ( 1 ) x 3 xa_1^{(2)} = f(W_{11}^{(1)} x_1 W_{12}^{(1)} x_2 W_{13}^{(1)} x_3 b_1^{(1)})a_2^{(2)} = f(W_{21}^{(1)} x_1 W_{22}^{(1)} x_2 W_{23}^{(1)} x_3 xa1(2)=f(W11(1)x1 W12(1)x2 W13(1)x3 b1(1))a2(2)=f(W21(1)x1 W22(1)x2 W23(1)x3
如果每个权重都一样,那么在多层网络中,从第二层开始,每一层的输入值都是相同的了也就是 a 1 = a 2 = a 3 = . . . . a1=a2=a3=…. a1=a2=a3=....,既然都一样,就相当于一个输入了,为啥呢??
如果是反向传递算法(如果这里不明白请看上面的连接),其中的偏置项和权重项的迭代的偏导数计算公式如下
$$ frac{partial}{partial W_{ij}^{(l)}} J(W,b;x,y) = a_j^{(l)} delta_i^{(l 1)}
frac{partial}{partial b_{i}^{(l)}} J(W,b;x,y) = delta_i^{(l 1)} $$
δ delta δ的计算公式
δ i ( l ) = ( ∑ j = 1 s t 1 W j i ( l ) δ j ( l 1 ) ) f ′ ( z i ( l ) ) delta_i^{(l)} = (sum_{j=1}^{s_{t 1}} W_{ji}^{(l)} delta_j^{(l 1)} ) f^{prime}(z_i^{(l)}) δi(l)=(j=1∑st 1Wji(l)δj(l 1))f′(zi(l))
如果用的是 sigmoid 函数
f ′ ( z i ( l ) ) = a i ( l ) ( 1 − a i ( l ) ) f^{prime}(z_i^{(l)}) = a_i^{(l)}(1-a_i^{(l)}) f′(zi(l))=ai(l)(1−ai(l))
把后两个公式代入,可以看出所得到的梯度下降法的偏导相同,不停的迭代,不停的相同,不停的迭代,不停的相同…,最后就得到了相同的值(权重和截距)。
7.3 初始化为小的随机数
将权重初始化为很小的数字是一个普遍的打破网络对称性的解决办法。这个想法是,神经元在一开始都是随机的、独一无二的,所以它们会计算出不同的更新,并将自己整合到整个网络的各个部分。一个权重矩阵的实现可能看起来像 W = 0.01 ∗ n p . r a n d o m . r a n d n ( D , H ) W=0.01∗np.random.randn(D,H) W=0.01∗np.random.randn(D,H),其中 randn 是从均值为 0 的单位标准高斯分布进行取样。通过这个公式(函数),每个神经元的权重向量初始化为一个从多维高斯分布取样的随机向量,所以神经元在输入空间中指向随机的方向(so the neurons point in random direction in the input space). 应该是指输入空间对于随机方向有影响)。其实也可以从均匀分布中来随机选取小数,但是在实际操作中看起来似乎对最后的表现并没有太大的影响。
备注:并不是数字越小就会表现的越好。比如,如果一个神经网络层的权重非常小,那么在反向传播算法就会计算出很小的梯度(因为梯度 gradient 是与权重成正比的)。在网络不断的反向传播过程中将极大地减少“梯度信号”,并可能成为深层网络的一个需要注意的问题。
7.4 用 1 / n 1/sqrt n 1/n 校准方差
上述建议的一个问题是,随机初始化神经元的输出的分布有一个随输入量增加而变化的方差。结果证明,我们可以通过将其权重向量按其输入的平方根(即输入的数量)进行缩放,从而将每个神经元的输出的方差标准化到 1。也就是说推荐的启发式方法 (heuristic) 是将每个神经元的权重向量按下面的方法进行初始化: w = n p . r a n d o m . r a n d n ( n ) / n w=np.random.randn(n)/sqrt n w=np.random.randn(n)/n ,其中 n 表示输入的数量。这保证了网络中所有的神经元最初的输出分布大致相同,并在经验上提高了收敛速度。
7.5 稀疏初始化(Sparse Initialazation)
另一种解决未校准方差问题的方法是把所有的权重矩阵都设为零,但是为了打破对称性,每个神经元都是随机连接地(从如上面所介绍的一个小的高斯分布中抽取权重)到它下面的一个固定数量的神经元。一个典型的神经元连接的数目可能是小到 10 个。
7.6 初始化偏差
将偏差初始化为零是可能的,也是很常见的,因为非对称性破坏是由权重的小随机数导致的。因为 ReLU 具有非线性特点,所以有些人喜欢使用将所有的偏差设定为小的常数值如 0.01,因为这样可以确保所有的 ReLU 单元在最开始就激活触发(fire)并因此能够获得和传播一些梯度值。然而,这是否能够提供持续的改善还不太清楚(实际上一些结果表明这样做反而使得性能更加糟糕),所以更通常的做法是简单地将偏差初始化为 0.
8 学习率
8.1 学习率的作用
在机器学习中,监督式学习通过定义一个模型,并根据训练集上的数据估计最优参数。梯度下降法是一个广泛被用来最小化模型误差的参数优化算法。梯度下降法通过多次迭代,并在每一步中最小化成本函数(cost 来估计模型的参数。学习率 (learning rate),在迭代过程中会控制模型的学习进度。
在梯度下降法中,都是给定的统一的学习率,整个优化过程中都以确定的步长进行更新, 在迭代优化的前期中,学习率较大,则前进的步长就会较长,这时便能以较快的速度进行梯度下降,而在迭代优化的后期,逐步减小学习率的值,减小步长,这样将有助于算法的收敛,更容易接近最优解。故而如何对学习率的更新成为了研究者的关注点。 在模型优化中,常用到的几种学习率衰减方法有:分段常数衰减、多项式衰减、指数衰减、自然指数衰减、余弦衰减、线性余弦衰减、噪声线性余弦衰减
8.2 学习率衰减常用参数有哪些
参数名称 | 参数说明 |
---|---|
learning_rate | 初始学习率 |
global_step | 用于衰减计算的全局步数,非负,用于逐步计算衰减指数 |
decay_steps | 衰减步数,必须是正值,决定衰减周期 |
decay_rate | 衰减率 |
end_learning_rate | 最低的最终学习率 |
cycle | 学习率下降后是否重新上升 |
alpha | 最小学习率 |
num_periods | 衰减余弦部分的周期数 |
initial_variance | 噪声的初始方差 |
variance_decay | 衰减噪声的方差 |
8.3 分段常数衰减
分段常数衰减需要事先定义好的训练次数区间,在对应区间置不同的学习率的常数值,一般情况刚开始的学习率要大一些,之后要越来越小,要根据样本量的大小设置区间的间隔大小,样本量越大,区间间隔要小一点。下图即为分段常数衰减的学习率变化图,横坐标代表训练次数,纵坐标代表学习率。
8.4 指数衰减
以指数衰减方式进行学习率的更新,学习率的大小和训练次数指数相关,其更新规则为: 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 这种衰减方式简单直接,收敛速度快,是最常用的学习率衰减方式,如下图所示,绿色的为学习率随 训练次数的指数衰减方式,红色的即为分段常数衰减,它在一定的训练区间内保持学习率不变。
8.5 自然指数衰减
它与指数衰减方式相似,不同的在于它的衰减底数是 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 下图为为分段常数衰减、指数衰减、自然指数衰减三种方式的对比图,红色的即为分段常数衰减图,阶梯型曲线。蓝色线为指数衰减图,绿色即为自然指数衰减图,很明可以看到自然指数衰减方式下的学习率衰减程度要大于一般指数衰减方式,有助于更快的收敛。
8.6 多项式衰减
应用多项式衰减的方式进行更新学习率,这里会给定初始学习率和最低学习率取值,然后将会按照 给定的衰减方式将学习率从初始值衰减到最低值,其更新规则如下式所示。 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) 如下图所示,红色线代表学习率降低至最低后,一直保持学习率不变进行更新,绿色线代表学习率衰减到最低后,又会再次循环往复的升高降低。
8.7 余弦衰减
余弦衰减就是采用余弦的相关方式进行学习率的衰减,衰减图和余弦函数相似。其更新机制如下式所示: 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 cosleft( 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
如下图所示,红色即为标准的余弦衰减曲线,学习率从初始值下降到最低学习率后保持不变。蓝色的线是线性余弦衰减方式曲线,它是学习率从初始学习率以线性的方式下降到最低学习率值。绿色噪声线性余弦衰减方式。
总结:常用的有余弦退火和等间隔,其他的在不同的场合也有使用,学习率详细的调整参考博客:六种PyTorch学习率调整策略(含代码)_AI浩-CSDN博客
9 Dropout 系列问题
9.1 为什么要正则化?
- 深度学习可能存在过拟合问题——高方差,有两个解决方法,一个是正则化,另一个是准备更多的数据,这是非常可靠的方法,但你可能无法时时刻刻准备足够多的训练数据或者获取更多数据的成本很高,但正则化通常有助于避免过拟合或减少你的网络误差。
- 如果你怀疑神经网络过度拟合了数据,即存在高方差问题,那么最先想到的方法可能是正则化,另一个解决高方差的方法就是准备更多数据,这也是非常可靠的办法,但你可能无法时时准备足够多的训练数据,或者,获取更多数据的成本很高,但正则化有助于避免过度拟合,或者减少网络误差。
9.2 为什么正则化有利于预防过拟合?
左图是高偏差,右图是高方差,中间是Just Right,这几张图我们在前面课程中看到过。
9.3 理解dropout正则化
Dropout可以随机删除网络中的神经单元,它为什么可以通过正则化发挥如此大的作用呢?
直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和之前讲的L2正则化类似;实施dropout的结果实它会压缩权重,并完成一些预防过拟合的外层正则化;L2对不同权重的衰减是不同的,它取决于激活函数倍增的大小。
9.4 dropout率的选择
- 经过交叉验证,隐含节点 dropout 率等于 0.5 的时候效果最好,原因是 0.5 的时候 dropout 随机生成的网络结构最多。
- dropout 也可以被用作一种添加噪声的方法,直接对 input 进行操作。输入层设为更接近 1 的数。使得输入变化不会太大(0.8)
- 对参数 w w w的训练进行球形限制 (max-normalization),对 dropout 的训练非常有用。
- 球形半径 c c c是一个需要调整的参数,可以使用验证集进行参数调优。
- dropout 自己虽然也很牛,但是 dropout、max-normalization、large decaying learning rates and high momentum 组合起来效果更好,比如 max-norm regularization 就可以防止大的learning rate 导致的参数 blow up。
- 使用 pretraining 方法也可以帮助 dropout 训练参数,在使用 dropout 时,要将所有参数都乘以 1 / p 1/p 1/p。
9.5 dropout有什么缺点?
dropout一大缺点就是代价函数J不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。定义明确的代价函数J每次迭代后都会下降,因为我们所优化的代价函数J实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制这样的图片。我通常会关闭dropout函数,将keep-prob的值设为1,运行代码,确保J函数单调递减。然后打开dropout函数,希望在dropout过程中,代码并未引入bug。我觉得你也可以尝试其它方法,虽然我们并没有关于这些方法性能的数据统计,但你可以把它们与dropout方法一起使用。
10 深度学习中图像数据常用的增强方法?
Color Jittering:对颜色的数据增强:图像亮度、饱和度、对比度变化(此处对色彩抖动的理解不知是否得当);
PCA Jittering:首先按照RGB三个颜色通道计算均值和标准差,再在整个训练集上计算协方差矩阵,进行特征分解,得到特征向量和特征值,用来做PCA Jittering;
Random Scale:尺度变换;
Random Crop:采用随机图像差值方式,对图像进行裁剪、缩放;包括Scale Jittering方法(VGG及ResNet模型使用)或者尺度和长宽比增强变换;
Horizontal/Vertical Flip:水平/垂直翻转;
Shift:平移变换;
Rotation/Reflection:旋转/仿射变换;
Noise:高斯噪声、模糊处理;
Label Shuffle:类别不平衡数据的增广;
推荐一个图像增强库:Albumentations,使用方法见博客:图像增强库Albumentations使用总结_AI浩-CSDN博客
PDF版本已经做了更新,并上传到CSDN,需要的可自行下载: 五万字总结,深度学习基础.pdf
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/143570.html原文链接:https://javaforall.cn
如果您是在找激活码,但输入激活码后激活失败,最新激活码地址:https://javaforall.cn/127239.html