2024年五万字总结,深度学习基础。(1),2024年最新面试题题目

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

注:图像大小、步幅和卷积后的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−1​m=0∑F−1​n=0∑F−1​wd,m,n​xd,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)接近。

前向传播

  1. 输入层 --> 输出层

计算神经元 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−neth1​1​=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

  1. 隐含层–>输出层:

计算输出层神经元 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−neto1​1​=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 什么是超参数?


超参数 : 在机器学习的上下文中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。通常情况下,需要对超参数进行优化,给学习机选择一组最优超参数,以提高学习的性能和效果。

​ 超参数通常存在于:

  1. 定义关于模型的更高层次的概念,如复杂性或学习能力。

  2. 不能直接从标准模型培训过程中的数据中学习,需要预先定义。

  3. 可以通过设置不同的值,训练不同的模型和选择更好的测试值来决定

​ 超参数具体来讲比如算法中的学习率(learning rate)、梯度下降法迭代的数量(iterations)、隐藏层数目(hidden layers)、隐藏层单元数目、激活函数( activation function)都需要根据实际情况来设置,这些数字实际上控制了最后的参数和的值,所以它们被称作超参数。

3.2 如何寻找超参数的最优值?


​ 在使用机器学习算法时,总有一些难调的超参数。例如权重衰减大小,高斯核宽度等等。这些参数需要人为设置,设置的值对结果产生较大影响。常见设置超参数的方法有:

  1. 猜测和检查:根据经验或直觉,选择参数,一直迭代。

  2. 网格搜索:让计算机尝试在一定范围内均匀分布的一组值。

  3. 随机搜索:让计算机随机挑选一组值。

  4. 贝叶斯优化:使用贝叶斯优化超参数,会遇到贝叶斯优化算法本身就需要很多的参数的困难。

  5. MITIE方法,好初始猜测的前提下进行局部优化。它使用BOBYQA算法,并有一个精心选择的起始点。由于BOBYQA只寻找最近的局部最优解,所以这个方法是否成功很大程度上取决于是否有一个好的起点。在MITIE的情况下,我们知道一个好的起点,但这不是一个普遍的解决方案,因为通常你不会知道好的起点在哪里。从好的方面来说,这种方法非常适合寻找局部最优解。稍后我会再讨论这一点。

  6. 最新提出的LIPO的全局优化方法。这个方法没有参数,而且经验证比随机搜索方法好。

3.3 超参数搜索一般过程?


超参数搜索一般过程:

  1. 将数据集划分成训练集、验证集及测试集。

  2. 在训练集上根据模型的性能指标对模型参数进行优化。

  3. 在验证集上根据模型的性能指标对模型的超参数进行搜索。

  4. 步骤 2 和步骤 3 交替迭代,最终确定模型的参数和超参数,在测试集中验证评价模型的优劣。

其中,搜索过程需要搜索算法,一般有:网格搜索、随机搜过、启发式智能搜索、贝叶斯搜索。

4 激活函数

=================================================================

4.1、什么是激活函数


激活函数(Activation functions)对于人工神经网络 模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用。它们将非线性特性引入到我们的网络中。如下图,在神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数。引入激活函数是为了增加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。

图1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yISAsq4B-1628659360470)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]

4.2 为什么要使用激活函数?


  1. 激活函数对模型学习、理解非常复杂和非线性的函数具有重要作用。

  2. 激活函数可以引入非线性因素。如果不使用激活函数,则输出信号仅是一个简单的线性函数。线性函数一个一级多项式,线性方程的复杂度有限,从数据中学习复杂函数映射的能力很小。没有激活函数,神经网络将无法学习和模拟其他复杂类型的数据,例如图像、视频、音频、语音等。

  3. 激活函数可以把当前特征空间通过一定的线性映射转换到另一个空间,让数据能够更好的被分类。

4.3 为什么激活函数需要非线性函数?


  1. 假若网络中全部是线性部件,那么线性的组合还是线性,与单独一个线性分类器无异。这样就做不到用非线性来逼近任意函数。

  2. 使用非线性激活函数 ,以便使网络更加强大,增加它的能力,使它可以学习复杂的事物,复杂的表单数据,以及表示输入输出之间非线性的复杂的任意函数映射。使用非线性激活函数,能够从输入输出之间生成非线性映射。

4.4 常见的激活函数及导数


  • sigmoid 激活函数

函数的定义为:

img

其值域为 (0,1) 。函数图像如下:

img

特点:

它能够把输入的连续实值变换为0和1之间的输出,特别的,如果是非常大的负数,那么输出就是0;如果是非常大的正数,输出就是1.

缺点:

sigmoid函数曾经被使用的很多,不过近年来,用它的人越来越少了。主要是因为它固有的一些 缺点。

缺点1:在深度神经网络中梯度反向传递时导致梯度爆炸和梯度消失,其中梯度爆炸发生的概率非常小,而梯度消失发生的概率比较大。首先来看Sigmoid函数的导数,如下图所示:

img

缺点2:不是以 0 0 0为对称轴(这点在tahn函数有所改善)

sigmoid函数及其导数的实现

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激活函数

函数的定义为:

img

其值域为 (-1,1) 。函数图像如下:

img

导数: f ′ ( x ) = 1 − ( f ( x ) ) 2 {f}'(x)=1-(f(x))^{2} f′(x)=1−(f(x))2,函数图像如下:

img

tanh读作Hyperbolic Tangent,它解决了Sigmoid函数的不是zero-centered输出问题,然而,梯度消失(gradient vanishing)的问题和幂运算的问题仍然存在。

优点和缺点

  • 优点:

  • 解决了Sigmoid的输出不关于零点对称的问题

  • 也具有Sigmoid的优点平滑,容易求导

  • 缺点:

  • 激活函数运算量大(包含幂的运算

  • Tanh的导数图像虽然最大之变大,使得梯度消失的问题得到一定的缓解,但是不能根本解决这个问题

tanh函数及其代码实现:

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 ∞] 。函数图像如下:

img

导数: 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)={10​ifx>0ifx<=0​​ 函数图像如下:

img

优点

1.相比起Sigmoid和tanh,ReLU在SGD中能够快速收敛,这是因为它线性(linear)、非饱和(non-saturating)的形式。

2.Sigmoid和tanh涉及了很多很expensive的操作(比如指数),ReLU可以更加简单的实现。

3.有效缓解了梯度消失的问题。

4.在没有无监督预训练的时候也能有较好的表现。

缺点

  1. ReLU的输出不是zero-centered

  2. Dead ReLU Problem,指的是某些神经元可能永远不会被激活,导致相应的参数永远不能被更新。有两个主要原因可能导致这种情况产生: (1) 非常不幸的参数初始化,这种情况比较少见 (2) learning rate太高导致在训练过程中参数更新太大,不幸使网络进入这种状态。解决方法是可以采用Xavier初始化方法,以及避免将learning rate设置太大或使用adagrad等自动调节learning rate的算法。

尽管存在这两个问题,ReLU目前仍是最常用的activation function,在搭建人工神经网络的时候推荐优先尝试!

函数及导数代码:

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)。函数图像如下:

img

导数: 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.01​ifx>0ifx<=0​​函数图像如下:

img

特点:与 ReLu 相比 ,leak 给所有负值赋予一个非零斜率, leak是一个很小的常数 a i \large a_{i} ai​,这样保留了一些负轴的值,使得负轴的信息不会全部丢失。

函数及导数代码:

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,下图是他们的比较:

img

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 & x\geq 0\\ a(e^{x}-1), &if &x< 0 \end{matrix}\right. f(x)={x,a(ex−1),​ifif​x≥0x<0​

函数图像如下:

img

导数: f ′ = { 1 i f x ≥ 0 f ( x ) + a i f x < 0 {f}'=\left\{\begin{matrix} 1 &if & x\geq 0\\ f(x)+a &if &x< 0 \end{matrix}\right. f′={1f(x)+a​ifif​x≥0x<0​

函数图像如下:

img

特点:

  • 融合了sigmoid和ReLU,左侧具有软饱和性,右侧无饱和性。

  • 右侧线性部分使得ELU能够缓解梯度消失,而左侧软饱能够让ELU对输入变化或噪声更鲁棒。

  • ELU的输出均值接近于零,所以收敛速度更快。

  • 在 ImageNet上,不加 Batch Normalization 30 层以上的 ReLU 网络会无法收敛,PReLU网络在MSRA的Fan-in (caffe )初始化下会发散,而 ELU 网络在Fan-in/Fan-out下都能收敛。

函数及导数代码:

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)),函数图像如下:

img

导数:

img

函数图像如下:

img

特点:

特点:无上界(unbounded above)、有下界(bounded below)、平滑(smooth)和非单调(nonmonotonic)。

无上界:可以防止网络饱和,即梯度消失。

有下界:提升网络的正则化效果。

平滑:首先在0值点连续相比ReLU可以减少一些不可预料的问题,其次可以使网络更容易优化并且提高泛化性能。

非单调:可以使一些小的负输入也被保留为负输出,提高网络的可解释能力和梯度流

优点:平滑、非单调、上无界、有下界

缺点:引入了指数函数,增加了计算量

函数及导数代码:

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))xsigmoid(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),其函数图像如下:

img

其导数:

img[外链图

函数图像如下:

img

特点:

特点:Swish 具备无上界有下界、平滑、非单调的特性。

优点:ReLU有无上界和有下界的特点,而Swish相比ReLU又增加了平滑和非单调的特点,这使得其在ImageNet上的效果更好。

缺点:引入了指数函数,增加了计算量

函数及导数代码:

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)=x\cdot sigmoid (x) f(x)=x⋅sigmoid(x),其函数图形如下:

img

导数为: 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)),其函数图像如下:

img

函数及导数代码:

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 激活函数有哪些性质?


  1. 非线性: 当激活函数是非线性的,一个两层的神经网络就可以基本上逼近所有的函数。但如果激活函数是恒等激活函数的时候,即 f ( x ) = x f(x)=x f(x)=x,就不满足这个性质,而且如果 MLP 使用的是恒等激活函数,那么其实整个网络跟单层神经网络是等价的;

  2. 可微性: 当优化方法是基于梯度的时候,就体现了该性质;

  3. 单调性: 当激活函数是单调的时候,单层网络能够保证是凸函数;

  4. f ( x ) ≈ x f(x)≈x f(x)≈x: 当激活函数满足这个性质的时候,如果参数的初始化是随机的较小值,那么神经网络的训练将会很高效;如果不满足这个性质,那么就需要详细地去设置初始值;

  5. 输出值的范围: 当激活函数输出值是有限的时候,基于梯度的优化方法会更加稳定,因为特征的表示受有限权值的影响更显著;当激活函数的输出是无限的时候,模型的训练会更加高效,不过在这种情况下,一般需要更小的 Learning Rate。

4.6 如何选择激活函数?


​ 选择一个适合的激活函数并不容易,需要考虑很多因素,通常的做法是,如果不确定哪一个激活函数效果更好,可以把它们都试试,然后在验证集或者测试集上进行评价。然后看哪一种表现的更好,就去使用它。

以下是常见的选择情况:

  1. 如果输出是 0、1 值(二分类问题),则输出层选择 sigmoid 函数,然后其它的所有单元都选择 Relu 函数。

  2. 如果在隐藏层上不确定使用哪个激活函数,那么通常会使用 Relu 激活函数。有时,也会使用 tanh 激活函数,但 Relu 的一个优点是:当是负值的时候,导数等于 0。

  3. sigmoid 激活函数:除了输出层是一个二分类问题基本不会用它。

  4. tanh 激活函数:tanh 是非常优秀的,几乎适合所有场合。

  5. ReLu 激活函数:最常用的默认函数,如果不确定用哪个激活函数,就使用 ReLu 或者 Leaky ReLu,再去尝试其他的激活函数。

  6. 如果遇到了一些死的神经元,我们可以使用 Leaky ReLU 函数。

4.7 使用 ReLu 激活函数的优点?


  1. 在区间变动很大的情况下,ReLu 激活函数的导数或者激活函数的斜率都会远大于 0,在程序实现就是一个 if-else 语句,而 sigmoid 函数需要进行浮点四则运算,在实践中,使用 ReLu 激活函数神经网络通常会比使用 sigmoid 或者 tanh 激活函数学习的更快。

  2. sigmoid 和 tanh 函数的导数在正负饱和区的梯度都会接近于 0,这会造成梯度弥散,而 Relu 和Leaky ReLu 函数大于 0 部分都为常数,不会产生梯度弥散现象。

  3. 需注意,Relu 进入负半区的时候,梯度为 0,神经元此时不会训练,产生所谓的稀疏性,而 Leaky ReLu 不会产生这个问题。

4.8 什么时候可以用线性激活函数?


  1. 输出层,大多使用线性激活函数。

  2. 在隐含层可能会使用一些线性激活函数。

  3. 一般用到的线性激活函数很少。

4.9 怎样理解 Relu(< 0 时)是非线性激活函数?


Relu 激活函数图像如下:

根据图像可看出具有如下特点:

  1. 单侧抑制;

  2. 相对宽阔的兴奋边界;

  3. 稀疏激活性;

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=1K​exp(θiT​x)exp(θiT​x)​

​ 其中, θ i \theta_i θi​和 x x x是列向量, θ i T x \theta_i^T x θiT​x可能被换成函数关于 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(−θiT​x)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​=∑j​eVj​eVi​​

​ 从下图看,神经网络中包含了输入层,然后通过两个特征层处理,最后通过 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=∑wj​ij​+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要注意一下几点:

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 什么是归一化?


  1. 归纳统一样本的统计分布性。归一化在 0 − 1 0-1 0−1之间是统计的概率分布,归一化在 − 1 − − + 1 -1–+1 −1−−+1之间是统计的坐标分布。

  2. 无论是为了建模还是为了计算,首先基本度量单位要同一,神经网络是以样本在事件中的统计分别几率来进行训练(概率计算)和预测,且 sigmoid 函数的取值是 0 到 1 之间的,网络最后一个节点的输出也是如此,所以经常要对样本的输出归一化处理。

  3. 归一化是统一在 0 − 1 0-1 0−1之间的统计概率分布,当所有样本的输入信号都为正值时,与第一隐含层神经元相连的权值只能同时增加或减小,从而导致学习速度很慢。

  4. 另外在数据中常存在奇异样本数据,奇异样本数据存在所引起的网络训练时间增加,并可能引起网络无法收敛。为了避免出现这种情况及后面数据处理的方便,加快网络学习速度,可以对输入信号进行归一化,使得所有样本的输入信号其均值接近于 0 或与其均方差相比很小。

最后

Python崛起并且风靡,因为优点多、应用领域广、被大牛们认可。学习 Python 门槛很低,但它的晋级路线很多,通过它你能进入机器学习、数据挖掘、大数据,CS等更加高级的领域。Python可以做网络应用,可以做科学计算,数据分析,可以做网络爬虫,可以做机器学习、自然语言处理、可以写游戏、可以做桌面应用…Python可以做的很多,你需要学好基础,再选择明确的方向。这里给大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

👉Python所有方向的学习路线👈

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。

👉Python必备开发工具👈

工欲善其事必先利其器。学习Python常用的开发软件都在这里了,给大家节省了很多时间。

👉Python全套学习视频👈

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了。

👉实战案例👈

学python就与学数学一样,是不能只看书不做题的,直接看步骤和答案会让人误以为自己全都掌握了,但是碰到生题的时候还是会一筹莫展。

因此在学习python的过程中一定要记得多动手写代码,教程只需要看一两遍即可。

👉大厂面试真题👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 27
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值