假设有一个 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个,且参数数量与上一层神经元个数无关。与全连接神经网络相比,其参数数量大大减少了。
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。
一个典型的三层神经网络如下所示:
其中 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
下面的图可以更直观的看清楚误差是怎样反向传播的:
前提:在一定范围内。
-
在神经元数量相同的情况下,深层网络结构具有更大容量,分层组合带来的是指数级的表达空间,能够组合成更多不同类型的子结构,这样可以更容易地学习和表示各种特征。
-
隐藏层增加则意味着由激活函数带来的非线性变换的嵌套层数更多,就能构造更复杂的映射关系。
================================================================
超参数 : 在机器学习的上下文中,超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。通常情况下,需要对超参数进行优化,给学习机选择一组最优超参数,以提高学习的性能和效果。
超参数通常存在于:
-
定义关于模型的更高层次的概念,如复杂性或学习能力。
-
不能直接从标准模型培训过程中的数据中学习,需要预先定义。
-
可以通过设置不同的值,训练不同的模型和选择更好的测试值来决定
超参数具体来讲比如算法中的学习率(learning rate)、梯度下降法迭代的数量(iterations)、隐藏层数目(hidden layers)、隐藏层单元数目、激活函数( activation function)都需要根据实际情况来设置,这些数字实际上控制了最后的参数和的值,所以它们被称作超参数。
在使用机器学习算法时,总有一些难调的超参数。例如权重衰减大小,高斯核宽度等等。这些参数需要人为设置,设置的值对结果产生较大影响。常见设置超参数的方法有:
-
猜测和检查:根据经验或直觉,选择参数,一直迭代。
-
网格搜索:让计算机尝试在一定范围内均匀分布的一组值。
-
随机搜索:让计算机随机挑选一组值。
-
贝叶斯优化:使用贝叶斯优化超参数,会遇到贝叶斯优化算法本身就需要很多的参数的困难。
-
MITIE方法,好初始猜测的前提下进行局部优化。它使用BOBYQA算法,并有一个精心选择的起始点。由于BOBYQA只寻找最近的局部最优解,所以这个方法是否成功很大程度上取决于是否有一个好的起点。在MITIE的情况下,我们知道一个好的起点,但这不是一个普遍的解决方案,因为通常你不会知道好的起点在哪里。从好的方面来说,这种方法非常适合寻找局部最优解。稍后我会再讨论这一点。
-
最新提出的LIPO的全局优化方法。这个方法没有参数,而且经验证比随机搜索方法好。
超参数搜索一般过程:
-
将数据集划分成训练集、验证集及测试集。
-
在训练集上根据模型的性能指标对模型参数进行优化。
-
在验证集上根据模型的性能指标对模型的超参数进行搜索。
-
步骤 2 和步骤 3 交替迭代,最终确定模型的参数和超参数,在测试集中验证评价模型的优劣。
其中,搜索过程需要搜索算法,一般有:网格搜索、随机搜过、启发式智能搜索、贝叶斯搜索。
=================================================================
激活函数(Activation functions)对于人工神经网络 模型去学习、理解非常复杂和非线性的函数来说具有十分重要的作用。它们将非线性特性引入到我们的网络中。如下图,在神经元中,输入的 inputs 通过加权,求和后,还被作用了一个函数,这个函数就是激活函数。引入激活函数是为了增加神经网络模型的非线性。没有激活函数的每层都相当于矩阵相乘。就算你叠加了若干层之后,无非还是个矩阵相乘罢了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yISAsq4B-1628659360470)(data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==)]
-
激活函数对模型学习、理解非常复杂和非线性的函数具有重要作用。
-
激活函数可以引入非线性因素。如果不使用激活函数,则输出信号仅是一个简单的线性函数。线性函数一个一级多项式,线性方程的复杂度有限,从数据中学习复杂函数映射的能力很小。没有激活函数,神经网络将无法学习和模拟其他复杂类型的数据,例如图像、视频、音频、语音等。
-
激活函数可以把当前特征空间通过一定的线性映射转换到另一个空间,让数据能够更好的被分类。
-
假若网络中全部是线性部件,那么线性的组合还是线性,与单独一个线性分类器无异。这样就做不到用非线性来逼近任意函数。
-
使用非线性激活函数 ,以便使网络更加强大,增加它的能力,使它可以学习复杂的事物,复杂的表单数据,以及表示输入输出之间非线性的复杂的任意函数映射。使用非线性激活函数,能够从输入输出之间生成非线性映射。
- sigmoid 激活函数
函数的定义为:
其值域为 (0,1) 。函数图像如下:
特点:
它能够把输入的连续实值变换为0和1之间的输出,特别的,如果是非常大的负数,那么输出就是0;如果是非常大的正数,输出就是1.
缺点:
sigmoid函数曾经被使用的很多,不过近年来,用它的人越来越少了。主要是因为它固有的一些 缺点。
缺点1:在深度神经网络中梯度反向传递时导致梯度爆炸和梯度消失,其中梯度爆炸发生的概率非常小,而梯度消失发生的概率比较大。首先来看Sigmoid函数的导数,如下图所示:
缺点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激活函数
函数的定义为:
其值域为 (-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函数及其代码实现:
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,在搭建人工神经网络的时候推荐优先尝试!
函数及导数代码:
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,这样保留了一些负轴的值,使得负轴的信息不会全部丢失。
函数及导数代码:
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 & x\geq 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 & x\geq 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下都能收敛。
函数及导数代码:
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可以减少一些不可预料的问题,其次可以使网络更容易优化并且提高泛化性能。
非单调:可以使一些小的负输入也被保留为负输出,提高网络的可解释能力和梯度流
优点:平滑、非单调、上无界、有下界
缺点:引入了指数函数,增加了计算量
函数及导数代码:
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),其函数图像如下:
其导数:
[外链图
函数图像如下:
特点:
特点: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),其函数图形如下:
导数为: 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)),其函数图像如下:
函数及导数代码:
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()
-
非线性: 当激活函数是非线性的,一个两层的神经网络就可以基本上逼近所有的函数。但如果激活函数是恒等激活函数的时候,即 f ( x ) = x f(x)=x f(x)=x,就不满足这个性质,而且如果 MLP 使用的是恒等激活函数,那么其实整个网络跟单层神经网络是等价的;
-
可微性: 当优化方法是基于梯度的时候,就体现了该性质;
-
单调性: 当激活函数是单调的时候,单层网络能够保证是凸函数;
-
f ( x ) ≈ x f(x)≈x f(x)≈x: 当激活函数满足这个性质的时候,如果参数的初始化是随机的较小值,那么神经网络的训练将会很高效;如果不满足这个性质,那么就需要详细地去设置初始值;
-
输出值的范围: 当激活函数输出值是有限的时候,基于梯度的优化方法会更加稳定,因为特征的表示受有限权值的影响更显著;当激活函数的输出是无限的时候,模型的训练会更加高效,不过在这种情况下,一般需要更小的 Learning Rate。
选择一个适合的激活函数并不容易,需要考虑很多因素,通常的做法是,如果不确定哪一个激活函数效果更好,可以把它们都试试,然后在验证集或者测试集上进行评价。然后看哪一种表现的更好,就去使用它。
以下是常见的选择情况:
-
如果输出是 0、1 值(二分类问题),则输出层选择 sigmoid 函数,然后其它的所有单元都选择 Relu 函数。
-
如果在隐藏层上不确定使用哪个激活函数,那么通常会使用 Relu 激活函数。有时,也会使用 tanh 激活函数,但 Relu 的一个优点是:当是负值的时候,导数等于 0。
-
sigmoid 激活函数:除了输出层是一个二分类问题基本不会用它。
-
tanh 激活函数:tanh 是非常优秀的,几乎适合所有场合。
-
ReLu 激活函数:最常用的默认函数,如果不确定用哪个激活函数,就使用 ReLu 或者 Leaky ReLu,再去尝试其他的激活函数。
-
如果遇到了一些死的神经元,我们可以使用 Leaky ReLU 函数。
-
在区间变动很大的情况下,ReLu 激活函数的导数或者激活函数的斜率都会远大于 0,在程序实现就是一个 if-else 语句,而 sigmoid 函数需要进行浮点四则运算,在实践中,使用 ReLu 激活函数神经网络通常会比使用 sigmoid 或者 tanh 激活函数学习的更快。
-
sigmoid 和 tanh 函数的导数在正负饱和区的梯度都会接近于 0,这会造成梯度弥散,而 Relu 和Leaky ReLu 函数大于 0 部分都为常数,不会产生梯度弥散现象。
-
需注意,Relu 进入负半区的时候,梯度为 0,神经元此时不会训练,产生所谓的稀疏性,而 Leaky ReLu 不会产生这个问题。
-
输出层,大多使用线性激活函数。
-
在隐含层可能会使用一些线性激活函数。
-
一般用到的线性激活函数很少。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Python开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注Python)
最后
不知道你们用的什么环境,我一般都是用的Python3.6环境和pycharm解释器,没有软件,或者没有资料,没人解答问题,都可以免费领取(包括今天的代码),过几天我还会做个视频教程出来,有需要也可以领取~
给大家准备的学习资料包括但不限于:
Python 环境、pycharm编辑器/永久激活/翻译插件
python 零基础视频教程
Python 界面开发实战教程
Python 爬虫实战教程
Python 数据分析实战教程
python 游戏开发实战教程
Python 电子书100本
Python 学习路线规划
会产生梯度弥散现象。
- 需注意,Relu 进入负半区的时候,梯度为 0,神经元此时不会训练,产生所谓的稀疏性,而 Leaky ReLu 不会产生这个问题。
-
输出层,大多使用线性激活函数。
-
在隐含层可能会使用一些线性激活函数。
-
一般用到的线性激活函数很少。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Python工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Python开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-Y7BuSKAS-1711820750384)]
[外链图片转存中…(img-1taolfu6-1711820750384)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Python开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注Python)
[外链图片转存中…(img-tIDVzs7v-1711820750384)]
最后
不知道你们用的什么环境,我一般都是用的Python3.6环境和pycharm解释器,没有软件,或者没有资料,没人解答问题,都可以免费领取(包括今天的代码),过几天我还会做个视频教程出来,有需要也可以领取~
给大家准备的学习资料包括但不限于:
Python 环境、pycharm编辑器/永久激活/翻译插件
python 零基础视频教程
Python 界面开发实战教程
Python 爬虫实战教程
Python 数据分析实战教程
python 游戏开发实战教程
Python 电子书100本
Python 学习路线规划