本笔记参考《深度学习入门:基于python的理论与实践》的内容,本文语言通俗易懂,适合小白!
书接上回,我们怎么做一个异或门?
不知道的伙伴可以先去看看前面一篇笔记【深度学习入门笔记01 - CSDN App】
多层感知机
前一篇笔记介绍了感知机,与门,或门,与非门,异或门。
我们知道与门的功能【两个输入均为1才行】,或门的功能是【两个输入有一个1就行】,与非门的功能是【两个输入有一个0就行】。
我们要创造的异或门是【有一个输入为0,一个输入1才行】
我们使用这样的结构:
其实我们只要判断输入是不是有1,是不是有0。如果两个都有,我们就输出1。因此,最右边的问号应该是填与门(两个肯定句,输出肯定句),然后左边的两个问号一个用来判断是不是有1,也就是用或门【有一个1就行的门】来判断,一个来判断是不是有0.也就是用与非门【有一个0就行的门】来判断。如果与非门和或门都输出为1,则最右边的与门输出为1。于是我们创造了异或门。
(图2-1)(与门,或门,与非门的感知机都是这种形式,只不过w1,w2,b有不同的组合)
图2-13所示的感知机与前面介绍的与门、或门的感知机形状不同(图2-1),与门、或门是单层感知机,而异或门是2层感知机。叠加了多层的感知机也称为多层感知机(multi-layered perceptron)
从与非门到计算机
计算机是处理信息的机器。向计算机中输入一些信息后,它会按照某种既定的方法进行处理,然后输出结果。所谓“按照某种既定的方法进行处理”是指,计算机和感知机一样,也有输入和输出,会按照某个既定的规则进行计算。
计算机是处理信息的机器。向计算机中输入一些信息后,它会按照某种既定的方法进行处理,然后输出结果。所谓“按照某种既定的方法进行处理”是指,计算机和感知机一样,也有输入和输出,会按照某个既定的规则进行计算。
说到仅通过与非门的组合就能实现计算机,大家也许一下子很难相信。建议有兴趣的读者看一下《计算机系统要素:从零开始构建现代计算机》。这本书以深入理解计算机为主题,论述了通过NAND构建可运行俄罗斯方块的计算机的过程。此书能让读者真实体会到,通过简单的NAND元件就可以实现计算机这样复杂的系统。
从感知机到神经网络
我们把最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层。中间层有时也称为隐藏层。“隐藏”一词的意思是,隐藏层的神经元(和输入层、输出层不同)肉眼看不见。另外,本书中把输入层到输出层依次称为第0层、第1层、第2层(层号之所以从0开始,是为了方便后面基于Python进行实现)。图3-1中,第0层对应输入层,第1层对应中间层,第2层对应输出层。
(图3-1)
图3-1中的网络一共由3层神经元构成,但实质上只有2层神经元有权重,因此将其称为“2层网络”。请注意,有的书也会根据构成网络的层数,把图3-1的网络称为“3层网络”。
激活函数
用阶跃函数来表示分情况的动作
如图,这里耍一个小花招,添加一个1的输入,这样就把偏置也显示在图中了。
我们用一个函数来表示这种分情况的动作(超过0则输出1,否则输出0)。引入新函数h(x),
y=h(w1x1+w2x2+b)
刚才登场的h(x)函数会将输入信号的总和转换为输出信号,这种函数一般称为激活函数(activation function)
写的更详细一些,a表示输入信号的总和,y表示输出信号。总之,我们用更加数学的语言,来表示之前的语言。
之前是这样的
式(3.3)表示的激活函数以阈值为界,一旦输入超过阈值,就切换输出。这样的函数称为“阶跃函数”。因此,可以说感知机中使用了阶跃函数作为激活函数。也就是说,在激活函数的众多候选函数中,感知机使用了阶跃函数。那么,如果感知机使用其他函数作为激活函数的话会怎么样呢?实际上,如果将激活函数从阶跃函数换成其他函数,就可以进入神经网络的世界了。
阶跃函数的python实践
这个实现简单、易于理解,但是参数x只能接受实数(浮点数)。也就是说,允许形如step_function(3.0)的调用,但不允许参数取NumPy数组,例如step_function(np.array([1.0, 2.0]))。为支持NumPy数组的实现。为此,可以考虑下述实现。
上述函数的内容只有两行。由于使用了NumPy中的“技巧”,可能会有点难理解。下面我们通过Python解释器的例子来看一下这里用了什么技巧。下面这个例子中准备了NumPy数组x,并对这个NumPy数组进行了不等号运算
对NumPy数组进行不等号运算后,数组的各个元素都会进行不等号运算,生成一个布尔型数组。这里,数组x中大于0的元素被转换为True,小于等于0的元素被转换为False,从而生成一个新的数组y。数组y是一个布尔型数组,但是我们想要的阶跃函数是会输出int型的0或1的函数。因此,需要把数组y的元素类型从布尔型转换为int型。
阶跃函数的绘制
np.arange(-5.0, 5.0, 0.1)在-5.0到5.0的范围内,以0.1为单位,生成NumPy数组([-5.0, -4.9,…, 4.9])。step_function()以该NumPy数组为参数,对数组的各个元素执行阶跃函数运算,并以数组形式返回运算结果。对数组x、y进行绘图,结果如图3-6所示。
神经网络的激活函数
sigmoid函数
sigmoid函数的实现
这里,np.exp(-x)对应exp(-x)。这个实现没有什么特别难的地方,但是要注意参数x为NumPy数组时,结果也能被正确计算。 之所以sigmoid函数的实现能支持NumPy数组,秘密就在于NumPy的广播功能。
numpy数组的广播功能
NumPy中,形状不同的数组之间也可以进行运算。在这个过程中,如图1-1所示,标量10被扩展成了2×2的形状,然后再与矩阵A进行乘法运算。这个巧妙的功能称为广播(broadcast)
虽然这里的例子用的是乘法,但是加减乘除就是一样的。比如例1的“*”换成“-”号,然后对应位置相减。
刚才的sigmoid函数的实现也是如此,因为np.exp(-x)会生成NumPy数组,所以1 / (1 + np.exp(-x))的运算将会在NumPy数组的各个元素间进行。
sigmoid函数的绘制
为什么激活函数不能用线性函数
阶跃函数和sigmoid函数还有其他共同点,就是两者均为非线性函数。sigmoid函数是一条曲线,阶跃函数是一条像阶梯一样的折线,两者都属于非线性的函数。
线性函数的问题在于,不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”。
为了具体地(稍微直观地)理解这一点,我们来思考下面这个简单的例子。这里我们考虑把线性函数h(x) = cx作为激活函数,把y(x) =h(h(h(x)))的运算对应3层神经网络(注意这里只是一个近似实际神经网络比它要复杂,但不影响结论。)。这个运算会进行y(x) = c×c×c×x的乘法运算,但是同样的处理可以由y(x)=ax(注意,a=c^3)这一次乘法运算(即没有隐藏层的神经网络)来表示。
我的思考:
我再举一个比这个例子稍微复杂一点的。
ReLU函数
ReLU的实现:
这里使用了NumPy的maximum函数。maximum函数会从输入的数值中选择较大的那个值进行输出