提到人工智能算法,人工神经网络(ANN)是一个绕不过去的话题。可是对于新手,每每容易被ANN中一堆复杂的概念公式搞得头大,最后只能作到感性的认识,而没法深刻的理解。正好最近笔者本人也在经历这个痛苦的过程,本着真理越辩越明的态度,索性坐下来认真的把这些头大的问题梳理一番,试试看能不能搞清楚ANN背后的数学原理。算法
其实ANN 的流程归纳来讲倒不是很复杂,以最简单的前馈神经网络为例,无非就是编程
其中四、五、6里的内容是须要反复迭代的(注意6是双重循环)。网络
在ANN的一堆操做里,梯度降低算法是一个相对独立的过程,不妨就让咱们从梯度降低算法开始吧。架构
其实这个问题很是简单,只是你们被梯度降低复杂的过程搞蒙了,忘记了它的本质。梯度降低算法自始至终都在干一件事——就是找到函数的极值点,固然确切的说是极小值点。可是,这种方法不一样于以往咱们在高等数学里学到的找极值点的方法。那么咱们首先就要问,求极值的经典方法不香吗?框架
要回答这个问题,让咱们先快速回顾一下在中学和大学里学到的传统的求极值点的方法。机器学习
对于一元函数来讲,极值可能出如今一阶导函数为0的点(驻点)或是导数不存在的点。ide
例如要找到 f(x) = x^2 + 3x 的极值点
求导得 dy/dx = 2x + 3
令 dy/dx = 0
就获得 x = -1.5 时,导函数为0。
注意,上面找到的只是可能的极值点,也就是极值存在的必要条件。还须要验证一下充分条件,才能肯定极值。这时,能够判断二阶导的正负性、或是断定一阶导在可能的极值点两边的正负状况。回到咱们的例子函数
当 x < -1.5 时,2x + 3 < 0
当 x > -1.5 时,2x + 3 > 0
说明函数在x = -1.5 附近先降低、后上升
该点是一个极小值点
对于二元函数,状况更复杂一些。首先要找出该函数的驻点和偏导数不存在的点,这些点仍然只是可能的极值点。而二元函数的驻点须要同时知足两个偏导数为0的条件,即学习
显然,这里的驻点是须要解这样一个二元方程才能求得的。对于驻点分别求出其3个二阶偏导数的值,再根据一些规则才能判断是否是极值点。人工智能
还需注意这个判断规则是不一样于一元函数的,由于一元函数极值的充分条件只须要考察一个二阶偏导数,而这里则须要综合考察二元函数的3个二阶偏导数,计算量明显增大了。对于偏导数不存在的情形还须要特判。
综合以上,咱们能够看出使用经典方法虽然能准确的解出极值点,但当函数自变量的个数不少时,用这种方法求解极值点还真的不香。好比:
基于此,为了找出多元函数的极值点,咱们还需另寻他法。这种方法要简单易行,特别是要能简单的向任意元函数推广,并且这种方法要可以适应计算机数值计算的特色,毕竟咱们这套程序确定是要放在电脑上跑的。而这就是传说中的梯度降低算法。
梯度的概念其实也不难,但为了让尽量多的人明白这一律念,咱们仍是从一元函数开始吧。不过如今咱们的目标是——用纯粹数值计算的方法,从函数上的某一点出发,找到函数的极值。这里咱们只考察极小值。
1.2.1 一元函数找极值:从枚举试探法到梯度降低法
以函数为例,让咱们看看如何找到极值。
既然是从函数上的某一点出发,那么不妨设想咱们在x = 1 的地方,这个地方是否是极小值点呢,咱们能够试探一下。
向右走0.5,发现f(1.5) > f(1),说明这个方向是上升的方向,不该该选择这个方向;
向左走0.5,发现f(0.5) < f(1),说明这个方向是降低的方向,选择这个方向;
再向左走0.5,发现f(0) < f(0.5),说明这个方向是降低的方向,选择这个方向;
再向左走0.5,发现f(-0.5) > f(0),说明这个方向是上升的方向,不该该选择这个方向。
至此,咱们能够将x = 0做为极小值点。
回顾这个过程,咱们将寻找极小值点的过程抽象以下:
固然这里还有几点值得注意
对于第二点,咱们能够引出梯度的定义了。
梯度是一个向量,它总指向当前函数值增加最快的方向。而它的模长则是这个最快的增加率(导数)的值。想要获得梯度向量,也很简单,它在x, y, z……等方向上的份量(坐标)就是相应的导数值。因而咱们求导就能够了。
对于一元函数,函数变化的方向只有两个,咱们定义一种一维的向量来表示梯度,好比5i,-5i。i前的数为正时,表明向量指向x轴正向;i前的数为负时,表明向量指向x轴负向。由下图能够看出,按照上述定义规定的梯度向量天然的指向了函数增加的方向,是否是很神奇。
因为梯度的方向正是函数增加最快的方向,因此梯度的逆方向就成了函数降低最快的方向。固然对于一元函数来讲,没有最快的方向的概念,由于毕竟就两个方向而已,根本没得比。不过有了梯度,咱们就能够进一步简化上述寻找极小值点的过程:
仍以函数,起始点x = 1为例,让咱们看看如何用梯度找到极值。
初始x = 1, 步长step = 0.5
#在咱们的例子里,梯度的计算式为2xi。i是指向x轴正向的单位向量
求x = 1处的梯度为2i,梯度反方向为-i #注意这里咱们只关注梯度的方向,至于梯度的模长则没必要在乎
沿此方向走一步,x新 = x旧 + step * 负梯度方向上单位向量的坐标 = 1 + 0.5 * (-1) = 0.5
求x = 0.5处的梯度为1i,梯度反方向为-i
沿此方向再走一步,x新 = 0.5 - 0.5 * 1 = 0
求x = 0处的梯度为0,说明到达极值点
以上就是用梯度找极小值点的过程,也就是梯度降低算法所作的事情,其实不难理解对吧。
能够看出,相比于枚举试探法,梯度降低法明显智能了许多,它直接给出了正确的方向,不须要咱们一步步试探了。此外,使用梯度降低法没必要再关注具体的函数值,只须要把注意力放在导数上,并且只关注一阶导数便可。
在后面,咱们还将给上面提到的步长step换一个高大上的名字——学习率,这样就彻底是机器学习里的叫法了。
这里用到梯度的时候,我进行了单位化操做,其实也能够不进行这一步,这样当函数变化比较剧烈的时候,移动的距离就比较多;函数变化比较平缓的时候,移动的距离就比较短。好比,在咱们这个例子里,只需一轮迭代就能获得结果了。
初始x = 1, 学习率step = 0.5
#在咱们的例子里,梯度的计算式为2xi。i是指向x轴正向的单位向量
求x = 1处的梯度为2i,梯度反方向为-2i #注意这里咱们既关注梯度的方向,也关注梯度的模长
沿此方向走一步,x新 = x旧 + step * 负梯度的坐标 = 1 + 0.5 * (-2) = 0
求x = 0处的梯度为0,说明到达极值点
好了说完了梯度,对于前面第三点提到的找不到极值的情形,咱们举两个具体的例子
仍是函数,若是起始点选为0.4,而学习率仍为0.5,在采用单位化梯度向量的情形下,则没法找到事实上的极小值点
对于这种状况,咱们能够经过减少学习率使结果尽量精确,例如咱们将学习率设置为0.1,就仍然能获得精确的结果。事实上,在实际操做中,通常也会把学习率设置为0.1。
而对于这种有多个极值点的函数,这种方法是无法找到所有极值点的,更遑论找到全局的极值点了。这时,咱们能够在算法里加入一些随机性,使其有必定几率跳出可能陷入的局部极值点。
1.2.2 多元函数的梯度
前面说过梯度降低算法的好处之一在于能够很方便的向多维推广,如今咱们以二元函数为例,看看梯度是如何帮助咱们找到极值点的。
此次咱们的函数变成了,起始点选择为(-5, -5),学习率仍设置为0.5。如今咱们的目标是从这个点出发,找到该函数的极值点,咱们知道这个极值点应该是(0, 0)。
这里与一元函数有几点不一样:
好了,如今算法开始:
起始点坐标(-5,-5), 学习率step = 0.5
#在咱们的例子里,梯度的计算式为2xi + 2yj。i和j分别是指向x轴正向和y轴正向的单位向量
求点(-5,-5)处的梯度为-10i-10j,负梯度为10i+10j,写成坐标形式就是(10,10)
在点(-5,-5)处沿此梯度走一步
根据公式 向量坐标 = 终点坐标 - 起点坐标,得终点坐标 = 起点坐标 + 向量坐标
这里,终点坐标是(x新, y新),起点坐标是(x旧, y旧) = (-5, -5)
向量坐标是负梯度坐标 = (10,10),再考虑学习率step,就能够获得
(x新, y新) = (-5,-5) + 0.5 * (10, 10) = (0, 0)
求(0, 0)处的梯度为零向量,说明到达极值点
将上述过程抽象,咱们就获得了梯度降低算法的所有逻辑:
咱们要找函数的极小值点(使函数取值尽量小的那一组自变量),由于,梯度的方向是函数值增加速度最快的方向,因此,沿着梯度的反方向函数值降低最快。
所以,只要沿着梯度的反方向一步步逼近就有可能找到那一组使函数取值尽量小的自变量。
如何沿着梯度的反方向一步步逼近呢?
咱们随机指定一个起点坐标(一组自变量取值),而后沿着梯度的方向求出未知的终点坐标,梯度是一个向量,自己也具备坐标
经过上面的迭代公式,不管是多少元的函数,它的一个个自变量们都会比较快的接近极值点(或者其近似)。这样咱们就能够找到一组自变量值,使得函数值尽量的小。
1.2.3 小结
若是是第一次听到人工神经网络这个名词,难免会以为比较高大上,好像咱们已经能够模仿神秘的神经系统了。其实它只是一个数学模型而已。固然ANN的效果是使人眼前一亮的,好像计算机一会儿真的有了人的能力,能够识人、识物。
但其实稍加抽象便能发现,这个东西无非就是个分类器,它的输入是一张图片,或者确切的说就是一堆表明像素点的数值,而输出则是一个类别。
因此说白了,所谓的人工神经网络其实就是一个超大规模的函数。
这就比如飞机和鸟儿的关系。让飞机飞起来靠的不是依葫芦画瓢造一我的工鸟,而是靠流体力学中的原理创建数学模型,而后计算得出飞机的尺寸、造型,并设计相应的发动机。
盗一张老师ppt里的图说明问题,能够看出ANN中的每个节点(也就是所谓的神经元)就是这样一个简单的线性函数模型。
固然经过激活函数咱们能够制造一点非线性的因素,以提升模型的表达能力。这样的话下面的神经元就表明这样一个函数
其中,,这里w1, w2, w3, b都是参数,x1, x2, x3是函数的输入,也就是因变量。
经常使用的激活函数在这里(仍然盗用老师的ppt,捂脸逃~)
以上就是所谓的人工神经元或者叫人造神经元,不少不少这样的神经元按必定规则相连就构成了ANN,因此我才说ANN就是一个超大规模的函数而已。
是否是和你想象中的高大上的神经元截然不同,可是咱们如今所谓的人工智能其实就是这样的数学模型而已。不管是简单的图像分类器仍是打败人类的AlphaGo,都是靠这样的数学计算算出来结果的,而不是靠什么化腐朽为神奇的力量。
知道了ANN的本质,如今就让咱们看看获得一个ANN须要怎么作?这里,请留意咱们会遇到不一样功能的函数,千万不要搞混了。
既然ANN是一个超大规模的函数,那么首先咱们作的就是搭建起这个函数的架构,也就是设计人工神经网络的架构。 这时这个函数就有一堆参数待定了。 接下来咱们准备一堆训练数据训练ANN,也就是把上面提到的待定参数都给他肯定了。 模型完成,可使用。
显然,最关键的是第三步——肯定未知参数。
这里首先解释训练数据,咱们知道ANN是一个分类器也是一个函数,这个函数读取一些输入值,通过复杂的计算后获得输出值,这些输出值能够被解释为类别。而训练数据就是输入值和最后的输出值都已知的一组数据,换句话说就是已知一组函数的自变量和因变量的对应关系。
再说的明白点,咱们的任务就是,已知函数的架构、函数的一组输入值和输出值,但不知道函数的一些参数,如今要推出这些未知参数。我把这里咱们要求出的这个函数称之为目标函数。因而,一言以蔽之,咱们的任务就是求出目标函数的未知参数。
为了完成这个任务,咱们引出另外一个重要的概念——损失函数。
2.2.1 损失函数
在这里,咱们玩一点当心机。注意了,这里很关键!!!
既然咱们已知目标函数的一组输入和输出,而未知其参数,那么咱们不妨将计就计将这些未知参数直接视为因变量,而将目标函数的输入直接代入进去,这样咱们不就获得了一个自变量是目标函数的全部未知参数且函数总体彻底已知的函数了吗?
这时,若是能找到一组合适的未知参数,这个函数应该能输出和已知输入对应的输出彻底一致的值。
因而咱们能够经过做差比较定义损失函数了
上图给出了损失函数的两种形式(除此以外还有交叉熵损失函数等其余类型),通常须要根据不一样的任务类型选取适当的损失函数。为了便于初学者理解,后文将以第二种均方偏差的形式作讲解。这里之因此出现了求和符号,是由于ANN的输出端可能对应了不止一个函数,这些函数能够分别表示把一张图片分红不一样类别的几率。后面咱们引入一个直观的例子,一看便知。
这里必定要注意,损失函数看起来虽然还有目标函数的影子,但实际已经彻底不一样了。咱们列表比较一下
目标函数 | 损失函数 | |
---|---|---|
表现形式 | f | loss |
生成方式 | 事先搭好框架,再经过训练得出待定参数 | 将目标函数的输出与实际值做差获得框架,而后代入一个具体的训练样例(包括输入值与标签值) |
自变量 | ——神经网络的输入值(实际场合中能够是一张图片的全部像素值) | ——目标函数的待定参数 |
函数值(因变量)含义 | 属于不一样分类的几率 | 预测值与实际值的差值(越小越好) |
特色 | 咱们最终想要获得的函数,能够用来做图像分类。是线性函数与非线性函数的组合,规模很大,自变量与参数都不少 | 用来求出目标函数的过渡函数。非负,最小值为0,通常要使用梯度降低法找到极值点 |
举个例子看看函数变异的过程吧。设原函数为,这是一个关于的二元函数,其中a, b, c均是常数,也能够叫待定参数。如今咱们给出一组具体的函数输入值好比,令把它们代入函数,而且将a, b, c视为变量,则函数变成了关于a, b, c的三元函数,记做。
综上,求目标函数的过程,就变成了寻找损失函数极小值点的过程,而寻找极小值点不正能够用上面介绍的梯度降低法实现吗?
2.2.2 一个实例:关于链式求导和偏差反向传播(BP)
行文至此,有关ANN的重要概念,咱们还剩下链式求导和偏差反向传播(BP)没有说起,让咱们用一个实例融会贯通一下。
考虑下面这个简单的ANN:
这个ANN只有4个神经元,分别是。它输出两个目标函数,均是输入变量的函数,分别由神经元输出。能够记为
这里给加上帽子,表示这两个函数(即目标函数)的函数值是预测值,区别于训练数据给出的实际标签值。而均是目标函数的待定参数,这里咱们假定神经元均采用sigmoid激活函数,即,而神经元不采用激活函数。
如今定义损失函数为
注意接下来咱们会将具体的一组输入变量带进去,这样损失函数就被视做觉得自变量的多元函数(具体的自变量变化过程参见上文描述)。其中, 是中间变量,它们均是觉得自变量的多元函数。
如今只要给出一个包含输入输出数据的训练样例,损失函数就成为不含未知参数的彻底肯定的函数。而咱们要作的就是找到这个损失函数的极小值。
按照梯度降低算法的推导,此时咱们只要按照下面的步骤就能够找出这个极小值:
其中二、3里的内容是须要反复迭代的。
如今,咱们以其中的几个参数为例,看看在调整过程当中会遇到什么新问题。
先试试调整吧,这时咱们须要求出损失函数对自变量的偏导数值(注意是数值,不是表达式),为此写出它的依赖关系:
这里,Loss函数依赖于变量,但与无关。回想多元函数求偏导数的规则,咱们对求导时,应将视为常数。
而依赖于变量,所以这里应按照复合函数求导法则,即传说中的链式求导法则,先让Loss函数对变量求导,再令对求导,即:
其实从神经网络的图中能够很清楚的看出求导链。
这里有几个要点:
求出损失函数对的偏导数值,咱们就能够按照梯度降低算法推导的公式,调整这个参数了!
如今,再来看看靠前的参数是怎么调整的,咱们以和为例
仍是老规矩,对照神经网络图,先写出它的依赖关系:
能够看出和,分别是函数和的变量,而函数均与和有关,因此Loss函数须要对均求偏导。
依然按照链式求导法则对求偏导,有:
对求偏导,有:
注意红框圈出来的部分是否是有些眼熟?
事实上,这一部分已经在调整后层参数的时候计算过了(请回看计算时的计算公式)。所以在编程时,可让程序保存中间结果,这里直接拿来用。
如今纵观整个过程,咱们惊奇的发现,对于ANN,当咱们须要使用它时,是从最前面给出输入,而后一步步日后计算得出这个庞大复杂函数的输出的;而当咱们须要训练它时,则是从最后面的参数开始,一步步向前求导,调整各个参数的。而且计算前面的参数时通常都会用到以前计算过的中间结果。
这样,ANN调整参数的过程就能够看做是一个偏差反向传播(BP)的过程。
之因此会这样反向传播,是由于神经网络中靠后的参数依赖的中间变量少、复合层数少,而靠前的参数则通过层层复合,求导链会拉的很长。
2.2.3 最后的一点小问题
以上咱们将求偏导的过程整个过了一遍,而求偏导只是梯度降低算法的一环。
程序跑起来以后,咱们会对每个训练样例,一遍遍的求偏导,直到基本上找到极小值点。
也就是说,按照梯度降低算法,每个训练样例都会最终给出一组参数值。
一个训练集中显然会有多个训练样例,所以最终会获得好多组各不相同的参数值。
而ANN的训练目标是肯定一组参数值,获得一个有必定效果的很复杂的函数,
怎么解决?
对每个参数,咱们能够将不一样训练样例得出的不一样值求一个平均。也能够构建一个更大的损失函数,即将每个训练样例生成的损失函数求和,而后用梯度降低算法找到这个累计损失函数的极小值点。
这样,咱们就能经过训练,最终肯定ANN这个大函数的全部待定参数,而后用它来作一些神奇的事情。