在这篇博文中,我们将探讨怎样通过可微分编程技术,实现深度学习中最常用的多层感知器(MLP)模型。我们在这里使用TensorFlow Eager Execution API,并使用多层感知器模型来进行MNIST手写数字识别任务。如果我们单纯想尝试一下自动微分和可微分编程,以及如何用TensorFlow来调用这些技术,我们可以使用TensorFlow内置类来做这个工作,但是这样大家就无从了解实现的细节了,对于深刻掌握可微分编程来说是不利的。因此我们在这篇博文,会尝试从头开始,利用自动微分技术,实现一个简单的多层感知器模型。
我们可以构造一个最简的多层感知器(MLP)模型,来做MNIST手写数字识别工作,如下所示:
TensorFlow可微分编程实践3---计算图模型
因为MNIST图片为28×28的黑白图片,所以输入向量为x inR784,这里的n=784,即共有784维。对第i个样本,我们用x(i)来表示,在本例中,为了讨论问题方便,我们省略的上标仅用x表示,但是大家要注意这代表的是某一个样本。对于图中的每个像素点,我们将28行串接起来,组成一个784个的长数列,用下标表示某个像素点的取值,例如第2行第5列的下标为28×2+5=61,可以用x61来表示。
输入层与第1层采用全连接方式,第1层第i个节点的输入值我们用zi1,其为输入层所有神经元的输出值,与该神经元与第1层第i个神经元连接权值相乘再相加的结果,我们假设输入层第j个神经元指向第1层第i个神经元的连接权值用Wi,j1表示,上标代表为第1层,下标第一个代表是第1层第i个神经元,第二个代表是输入层第j个神经元,我们可以得出第1层第i个神经元的输入值公式:

(1)zi1=Wi,11x1+Wi,21x2+...+Wi,j1xj+...+Wi,7841x784+bi1

或者简写为:
(2)zi1=j=1784Wi,j1xj+bi1

我们通常将所有第1层神经元的输入值串起来形成一个向量,如下所示:
z1=[z11z21...z5121]

我们将第1层神经元的偏置值bi1与串在一起形成一个向量,如下所示:
b1=[b11b21...b5121]

我们将输入层与第1层的连接权值表示为矩阵形式,如下所示:
W1=[W1,11W1,21...W1,7841W2,11W2,21...W2,7841............W512,11W512,21...W512,7841]

输入信号也表示为向量形式:
(3)x=[x1x2...x584]

则第1层神经元的输入信号可以表示矩阵向量的运算,如下所示:
(e000001)z1=W1x+b1

我们假设第1层第i个神经元的**函数为ReLU函数,则其输出为:
(4)ai1=ReLU(zi1)

我们同样将第1层所有神经元的输出串在一起形成一个向量,如下所示:
(5)a1=ReLU(z1)

将式(e000001)代入得到:
(e000002)a1=ReLU(z1)=ReLU(W1x+b1)

以上我们讨论的是输入导到第1层,我们可以很容易的将其推广为从第l1到第l层:
(e000003)al=ReLU(zl)=ReLU(Wlal1+bl)

我们用Nl1代表第l1层神经元数量,用Nl表示第l层神经元数量,则第l1层输出信号al1RNl1,第l1层到第l层连接权值矩阵WlRNl×Nl1,第l层偏置值blRNl,第l层输入信息zlRNl,第l层的输出值alRNl
前向传播各层计算公式一样,直到我们的输出层(这里是第2层),我们有10个神经元,分别代表取0~9这10个数字的概率,**函数采用Softmax函数,取概率最大的那个作为整个网络的分类结果。
神经网络的训练可以采用BP算法,这里有很多成熟的算法库可用。但是我们在这里要采用计算的方式来讲解,同时我们在讲解了计算图的基本原理之后,我们会用TensorFlow Eager Execution API,采用可微分编程方式,实现这一经典算法。
采用计算图方式的话,我们需要引入一种网络的另一种表示方式,如图所示:
TensorFlow可微分编程实践3---计算图模型
我们将输入信号向量x、输入层到第1层的连接权值矩阵W1、第1层神经元偏置值向量b1放在图的最左侧,将这三个值进行如下运算:
(6)z1=W1x+b1

经过计算得到节点z1,我们再经过**函数得到第1层神经元输出信号a1=ReLU(z1),得到a1节点。
我们将第1层输出信号a1、第1层到第2层连接权值矩阵W2、第2层神经元偏置值向量b2放在一起,经过如下运算:
(7)z2=W2a1+b2

第2层也就是输出层的**函数为Softmax函数:
(8)yi=ai2=ezi2j=1N2ezj2

其向量形式表示为:
(9)yi=[ez12j=1N2ezj2ez22j=1N2ezj2...ezN22j=1N2ezj2]

而我们的希望的结果表示为:
(10)y^i=[0010...0]

如上所示,其用one-hot向量形式表示,即只有正确的数字处为1,其余位置为0,例如本例中,就代表其识别结果应该为2。

  • 向量运算的微分
    我们先来定义向量微分,假设有向量yRm和向量xRn,微分yx定义为:

    (11)yx=[y1x1y1x2...y1xny2x1y2x2...y2xn............ymx1ymx2...ymxn]

    这就是Jacobian矩阵jRm×n

  • 代价函数求导
    我们首先从计算图最右侧开始反向求导,如图所示:
    TensorFlow可微分编程实践3---计算图模型
    我们首先处理损失函数,这里我们假设不考虑添加调整项的情况,我们的代价函数取交叉熵(cross entropy)函数,根据交叉熵定义:

    (12)H(p,q)=Ep(logq)=H(p)+KL(pq)

    对离散值情况,交叉熵(cross entropy)可以表示为:
    (13)H(p,q)=k=1Kp(k)logq(k)

    在这里我们设正确值y^的分布为p,而计算值y=a2的分布为q,假设共有K=10个类别,并且假设第r维为正确数字,则代价函数的值为:
    (14)C=H(p,q)=k=1Kp(k)logq(k)=(0logy1+0logy2+...+1logyr+...+0logy10)=logyr

    我们可以将代价函数值视为R1的向量,我们对y求偏导,根据Jacobian矩阵定义,结果为R1×N2=R1×10的1行10列的矩阵。结果如下所示:
    (15)Cy=[00...1yr...0]

    其只有正确数字对应的第r维不为0,其余均为零。
    接下来我们来求:yz2,因为y和$\boldsymbol{a}^2均为向量,可以直接使用Jacobian矩阵定义得:

(16)yz2=[y1z12y1z22...y1zN22y2z12y2z22...y2zN22............yN2z12yN2z22...yN2zN22]

式中N2=10为第2层即输出层神经元个数。由此可见yz2RN2×N2(R10×10)的方阵。
如果我们输出层采用σ函数,那么第i个神经元的输出只与其输入有关,与其他神经元无关,因此该矩阵就变为一个对角阵,如下所示:

(17)yz2=[σ(z12)0...00σ(z22)...0............00...σ(z102)]

但是我们在这里使用的是Softmax**函数,每个输出与该层所有神经元的输入均有关,所以其不是对角阵。
接下来我们计算z2a1,根据Jacobian矩阵定义得:
(e000004)z2a1=[z12a11z12a21...z12aN11z22a11z22a21...z22aN11............zN22a11zN22a21...zN22aN11]

我们知道:
zi2=Wi,12a11+Wi,22a21+...+Wi,j2aj1+...+Wi,N12aN11

则其对第1层第j个神经元输出信号求导:
zi2aj1=Wi,j2

所以式(e000004)的最终结果为:
(e000004)z2a1=[z12a11z12a21...z12aN11z22a11z22a21...z22aN11............zN22a11zN22a21...zN22aN11]=[W1,12W1,22...W1,N12W2,12W2,22...W2,N12............WN2,12WN2,22...WN2,N12]=W2

这个结果与我们直接对z2=W2a1+b2a1求导得W2一致。
接下来我们要求的z2W2,这里是向量对矩阵求偏导,结果将是一个张量(Tensor)。
我们可以将连接权值矩阵W2视为由列向量组成:
(18)W2=[w1w2...wN1]

其中第k个列向量wk为:
(19)wk=[W1,k2W2,k2...WN2,k2]

这时z2W2就可以转化为对一系列连接权值矩阵组成的列向量求导,就变为列向量求导,如下所示:
(20)z2W2=[z2w1z2w2...z2wN1]

式中的每一项均为向量对向量的导数,其为Jacobian矩阵,因为z2RN2,且wkRN2,根据Jacobian矩阵定义,z2wkRN2×N2的矩阵,如下所示:
(21)z2wk=[z12w1kz12w2k...z12wkk...z12wN2kz22w1kz22w2k...z22wkk...z22wN2k..................zN22w1kzN22w2k...zN22wkk...zN22wN2k]

由此可知其为RN2×N2的方阵,对其中第i行第j列元素:
(e000005)zi2wjk=zi2Wj,k2

在式(e000005)中,如果ij,此时连接权值不指向第i个神经元,因此值为0。当i=j时,Wi,k2是与第1层的第k个神经元的输出ak1相乘,因此其导数为ak1,当i=j时对应的是式(e000005)的对角线,因此其为对角阵,而且其值均为ak1,如下所示:
(22)[ak10...00ak1...0............00...ak1]

余下部分的偏导求法和上面的方法相同,我们在这里就不再一一列举了。读者可以自行补齐。
到此我们基本把多层感知器模型的计算图讲完了,下一步就是利用TensorFlow Eager Execution API来实现这个模型,我们将在下一篇博文中进行介绍。

相关文章:

  • 2021-10-28
  • 2021-04-11
  • 2022-12-23
  • 2022-02-18
  • 2021-04-21
  • 2021-10-23
  • 2021-04-04
  • 2021-08-27
猜你喜欢
  • 2021-08-31
  • 2021-12-05
  • 2022-12-23
  • 2021-06-10
  • 2021-12-01
  • 2021-09-26
相关资源
相似解决方案