2024年最全五万字总结,深度学习基础。,php生命周期面试题

(1)Python所有方向的学习路线(新版)

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

最近我才对这些路线做了一下新的更新,知识体系更全面了。

在这里插入图片描述

(2)Python学习视频

包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。

在这里插入图片描述

(3)100多个练手项目

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。

在这里插入图片描述

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

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

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

其中:

  • 输入数据 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 或与其均方差相比很小。

6.2 为什么要归一化?


  1. 为了后面数据处理的方便,归一化的确可以避免一些不必要的数值问题。

  2. 为了程序运行时收敛加快。

  3. 同一量纲。样本数据的评价标准不一样,需要对其量纲化,统一评价标准。这算是应用层面的需求。

  4. 避免神经元饱和。啥意思?就是当神经元的激活在接近 0 或者 1 时会饱和,在这些区域,梯度几乎为 0,这样,在反向传播过程中,局部梯度就会接近 0,这会有效地“杀死”梯度。

  5. 保证输出数据中数值小的不被吞食。

  6. 有可能提高精度。一些分类器需要计算样本之间的距离(如欧氏距离),例如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 归一化有哪些类型?


  1. 线性归一化

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 不稳定,很容易使得归一化结果不稳定,使得后续使用效果也不稳定。

  1. 标准差标准化

x ′ = x − μ σ x^{\prime} = \frac{x-\mu}{\sigma} x′=σx−μ​

​ 含义:经过处理的数据符合标准正态分布,即均值为 0,标准差为 1 其中 μ \mu μ为所有样本数据的均值, σ \sigma σ为所有样本数据的标准差。

  1. 非线性归一化

适用范围:经常用在数据分化比较大的场景,有些数值很大,有些很小。通过一些数学函数,将原始值进行映射。该方法包括 l o g log log、指数,正切等。

6.6 局部响应归一化作用


​ LRN 是一种提高深度学习准确度的技术方法。LRN 一般是在激活、池化函数后的一种方法。

​ 在 ALexNet 中,提出了 LRN 层,对局部神经元的活动创建竞争机制,使其中响应比较大对值变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。

做了那么多年开发,自学了很多门编程语言,我很明白学习资源对于学一门新语言的重要性,这些年也收藏了不少的Python干货,对我来说这些东西确实已经用不到了,但对于准备自学Python的人来说,或许它就是一个宝藏,可以给你省去很多的时间和精力。

别在网上瞎学了,我最近也做了一些资源的更新,只要你是我的粉丝,这期福利你都可拿走。

我先来介绍一下这些东西怎么用,文末抱走。


(1)Python所有方向的学习路线(新版)

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

最近我才对这些路线做了一下新的更新,知识体系更全面了。

在这里插入图片描述

(2)Python学习视频

包含了Python入门、爬虫、数据分析和web开发的学习视频,总共100多个,虽然没有那么全面,但是对于入门来说是没问题的,学完这些之后,你可以按照我上面的学习路线去网上找其他的知识资源进行进阶。

在这里插入图片描述

(3)100多个练手项目

我们在看视频学习的时候,不能光动眼动脑不动手,比较科学的学习方法是在理解之后运用它们,这时候练手项目就很适合了,只是里面的项目比较多,水平也是参差不齐,大家可以挑自己能做的项目去练练。

在这里插入图片描述

(4)200多本电子书

这些年我也收藏了很多电子书,大概200多本,有时候带实体书不方便的话,我就会去打开电子书看看,书籍可不一定比视频教程差,尤其是权威的技术书籍。

基本上主流的和经典的都有,这里我就不放图了,版权问题,个人看看是没有问题的。

(5)Python知识点汇总

知识点汇总有点像学习路线,但与学习路线不同的点就在于,知识点汇总更为细致,里面包含了对具体知识点的简单说明,而我们的学习路线则更为抽象和简单,只是为了方便大家只是某个领域你应该学习哪些技术栈。

在这里插入图片描述

(6)其他资料

还有其他的一些东西,比如说我自己出的Python入门图文类教程,没有电脑的时候用手机也可以学习知识,学会了理论之后再去敲代码实践验证,还有Python中文版的库资料、MySQL和HTML标签大全等等,这些都是可以送给粉丝们的东西。

在这里插入图片描述

这些都不是什么非常值钱的东西,但对于没有资源或者资源不是很好的学习者来说确实很不错,你要是用得到的话都可以直接抱走,关注过我的人都知道,这些都是可以拿到的。

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值