机器学习入门——Python神经网络编程②

python神经网络编程②-基于MNIST数据集

  • 本人通过异步图书《python神经网络编程》这部书来学习,所以此博客内容大概为这本书的概括。
  • 这时Python神经网络编程的第二讲,第一讲传送门:python神经网络编程①

基础知识储备:

  • 矩阵乘法
  • 反向传播误差、权重更新
  • 实际如何更新权重

1.矩阵乘法

    先前我们手工对两层、每一层只有两节点的神经网络进行计算。对人类而言,这样的工作量也是足够大的,但是如果对五层,每层100个节点的网络进行相同的计算,那是非常令人头秃的操作,单单是写下所有的计算,也是一个艰巨的任务。
    题目既然是“矩阵乘法”,那么矩阵如何帮助我们简化运算呢?其实,矩阵在两个方面帮助了我们。首先矩阵允许我们压缩所有的这些计算,把它们变成一种非常简单的缩写形式。第二个好处是,许多计算机编程语言理解如何与矩阵一起工作,计算机编程语言能够认识到实际的工作是重复性的,因此能够高效高速地进行计算。
    关于矩阵的点乘和叉乘这里不再赘述,相信大家在线代课上都学过啦……

    下面只说明如何应用到我们的计算当中,相信大家从上一篇博客里也应该能看出来如何应用吧。就是这样:
机器学习入门——Python神经网络编程②
    如上图第一个矩阵包含两层节点之间的权重。第二个矩阵包含第一层输入层的信号。通过两个矩阵相乘,我们得到的答案是输入到第二层节点组合调节后的信号。因此,我们可以使用矩阵乘法表示所有的计算,计算出组合调节后的信号x,输入到第二层的节点中。我们可以使用下式非常简洁的表示出这个关系:                                X=WIX=W*I
    此处W是权重矩阵,I是输入矩阵,X是组合调节后的信号,即输入到第二层的结果矩阵。
    当计算出第二层的结果矩阵之后,对于第二层的最终输出:                                        O=sigmoid(X)O=sigmoid(X)

    理解了上面的公式之后,我们就可以进行前后层之间的运算了,比如说我们有三层网络,我们简单的再次进行矩阵乘法,使用第二层的输出作为第三层的输入。当然,这个输出应该使用权重系数进行调节并进行组合。

2.反向传播误差、权重更新

    所谓误差即是节点生成的答案与所知正确答案之间的差值,我们根据误差可以进行调整相关参数,从而减小误差。那么对于我们神经网络而言,输出和误差是多个节点共同作用的结果,那么我们如何更新链接权重来减小误差呢?

    当只有一个节点前馈信号到输出节点,事情要简单的多。如果有两个节点,我们如何使用输出误差值呢?
    如果使用了所有的误差值,只对一个权重进行更新,这种做法忽略了其他链接以及权重,毫无意义。多条链接都对这个误差有影响,只有一条连接造成了误差,这种机会微乎其微,如果我们改变了已经正确的权重而使情况变得更糟,那么下一次迭代中,这个权重就会得到改进,因此神经网络并没有失去什么。
    说了这么多,那么如何计算误差,更新权重呢?一种思想就是等分误差,还有一种思想就是不等分误差。
    等分误差的思想就是在所有造成误差的节点中平分误差,如下图所示:
机器学习入门——Python神经网络编程②
    不等分误差的思想是:我们为较大链接权重的链接分配更多的误差,之所以这么做,是因为这些链接对造成误差的贡献较大。如下图所示:
机器学习入门——Python神经网络编程②
    由上图,有两个节点对输出节点的信号做出了贡献,它们的链接权重分别是3.0和1.0.如果按权重比例分割误差,那么我们就可以观察到输出误差的3/43/4应该可以用于更新第一个较大的权重,误差的1/41/4可以用来更新较小的权重。我们可以将同样的思想扩展到多个节点。如果我们拥有100个节点链接到输出节点,那么我们要在这100条链接之间,按照每条链接对误差所作贡献的比例(由链接权重的大小表示),分割误差

多个输出节点反向传播误差:

    在未经训练过的神经网络中,两个节点都有误差的的情况是非常普遍的,在网络中,我们需要使用这两个误差值来告知如何调整内部链接权重。我们可以使用与先前一样的方法,也就是跨越造成误差的多条链接,按照权重比例,分割输出节点的误差。如下图展示了具有2个输入节点和2个输出节点的简单网络:
机器学习入门——Python神经网络编程②
    如上图,我们将第一个输出节点的误差标记为e1e_1,第二个输出节点的误差标记为e2e_2。他们的值等于我们由训练数据提供的所期望的输出值tt与实际输出值oo之间的差。从图中,我们还可以发现按照所链接的比例,也就是权重W1,1W_{1,1}W2,1W_{2,1},我们对e1e_1进行了分割分割。类似地,我们按照权重W1,1W_{1,1}W2,1W_{2,1}的比例分割了e2e_2。这种分割方式如下面公式所示:W1,1W1,1+W2,1\frac{W_{1,1}}{W_{1,1}+W_{2,1}}W2,1W1,1+W2,1\frac{W_{2,1}}{W_{1,1}+W_{2,1}}

    通过上面的介绍,我们可能对两层的网络的误差分割有了了解,那么对多于两层的网络怎么办呢?我们如何更新权重呢?

反向传播误差到更多层中:

    下图显示了具有3层的简单神经网络,一个输入层、一个阴藏层和一个最终输出层。
机器学习入门——Python神经网络编程②
    从右手边的最终输出层开始,往回工作,我们可以看到,我们使用在输出层的误差值引导调整馈送到最终层的链接权重。更一般的,我们将输出误差标记为eoutpute_{output},将在输出层和隐藏层之间的链接权重标记为WhoW_{ho}。通过将误差值按权重的比例进行分割,我们计算出与每条链接相关的特定误差值。
    对于额外的新层所需要做的事情。简单说来,我们采用与隐藏层节点相关联的这些误差ehiddene_{hidden},再次将这些误差按照输入层和隐藏层之间的链接权重wihw_{ih}进行分割。如果神经网络有多个层,那么我们就从最终输出层往回工作,对每一层重复应用相同的思路。

    对于输出层节点的eoutpute_{output},我们首先使用了输出误差。那么,对于隐藏层节点ehiddene_{hidden},我们使用什么误差呢?对于隐藏层的节点,我们没有目标值或所希望的输出值。我们只有最终输出层节点的目标值,这个目标值来自于训练样本数据。观察上图,对于隐藏层第一个节点具有两个链接,这两个链接将这个节点连接到两个输出层节点。我们知道,沿着各个链接可以分割输出误差,就像我们先前所做的一样。这意味着,对于中间层节点的每个连接,我们得到了某种误差值。我们可以重组这两个链接的误差,形成这个节点的误差。

    我们需要隐藏层节点的误差以此来更新前一层的权重,我们称这个误差为ehiddene_{hidden},但是我们并不需要明确地回答这些误差是什么,我们的训练样本数据只给出了最终输出节点的目标值,因此不能说这个误差等于中间层节点所需目标输出值与实际输出值之间的差。
    我们可以使用先前看到的误差反向传播,为链接重组分割的误差。因此,第一个隐层层节点的误差是与这个节点前向连接所有链接中分割误差的和。在上图中,我们得到了在权重为W1,1W_{1,1}的链接上的输出误差eoutput,1e_{output,1}的一部分,同时也得到了在权重为W1,2W_{1,2}的链接上第二个输出节点的输出误差eoutput,2e_{output,2}的一部分。
    ehidden,1=W1,1W1,2e_{hidden,1}=链接W_{1,1}和链接W_{1,2}上的分割误差之和
            =eoutput,1W1,1W1,1+W2,1+W1,2W1,2+W2,2=e_{output,1}*\frac{W_{1,1}}{W_{1,1}+W_{2,1}}+\frac{W_{1,2}}{W_{1,2}+W_{2,2}}

使用矩阵乘法进行误差传播

    使用矩阵乘法进行误差传播的计算与前面计算输出的思想类似,这里不再赘述,只写出最终结果:
        errorhidden=WhiddenoutputTerroroutputerror_{hidden}=W^T_{hidden_output}*error_{output}

3.实际如何更新权重

    梯度下降法是求解函数最小值的一种很好的方法,当函数非常复杂,并且不能轻易使用数学代数求解时,这种方法就发挥了很大的作用。
    神经网络的误差是内部链接权重的函数,改进神经网络,意味着通过改变权重减少这种误差。对于我们来说,直接选择合适的权重太难了,我们可以使用梯度下降的方法改进权重,即:通过误差函数的梯度下降,采取小步长,迭代地改进权重。所迈出的每一步的方向都是在当前位置向下斜率最大的方向。

    由于这个数学知识不太好表述,而且我又注意到****上有大量的讲解,这里就不再赘述。

    下一节开始使用python来进行训练自己的网络,然后识别手写数字