TensorFlow可微分编程实践3---计算图模型

本文通过构建计算图的方式,详细介绍了如何使用自动微分技术实现一个多层感知器(MLP)模型,并应用于MNIST手写数字识别任务。

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

z1i=W1i,1x1+W1i,2x2+...+W1i,jxj+...+W1i,784x784+b1i(1)(1)zi1=Wi,11x1+Wi,21x2+...+Wi,j1xj+...+Wi,7841x784+bi1

或者简写为:
z1i=j=1784W1i,jxj+b1i(2)(2)zi1=∑j=1784Wi,j1xj+bi1

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

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

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

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

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

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

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

将式(e000001e000001)代入得到:
a1=ReLU(z1)=ReLU(W1x+b1)(e000002)(e000002)a1=ReLU(z1)=ReLU(W1⋅x+b1)

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

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

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

第2层也就是输出层的激活函数为Softmax函数:
yi=a2i=ez2iN2j=1ez2j(8)(8)yi=ai2=ezi2∑j=1N2ezj2

其向量形式表示为:
yi=ez21N2j=1ez2jez22N2j=1ez2j...ez2N2N2j=1ez2j(9)(9)yi=[ez12∑j=1N2ezj2ez22∑j=1N2ezj2...ezN22∑j=1N2ezj2]

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

如上所示,其用one-hot向量形式表示,即只有正确的数字处为1,其余位置为0,例如本例中,就代表其识别结果应该为2。
  • 向量运算的微分
    我们先来定义向量微分,假设有向量yRmy∈Rm和向量xRnx∈Rn,微分yx∂y∂x定义为:

    yx=y1x1y2x1...ymx1y1x2y2x2...ymx2............y1xny2xn...ymxn(11)(11)∂y∂x=[∂y1∂x1∂y1∂x2...∂y1∂xn∂y2∂x1∂y2∂x2...∂y2∂xn............∂ym∂x1∂ym∂x2...∂ym∂xn]

    这就是Jacobian矩阵jRm×nj∈Rm×n
  • 代价函数求导
    我们首先从计算图最右侧开始反向求导,如图所示:
    这里写图片描述
    我们首先处理损失函数,这里我们假设不考虑添加调整项的情况,我们的代价函数取交叉熵(cross entropy)函数,根据交叉熵定义:

    H(p,q)=Ep(logq)=H(p)+KL(pq)(12)(12)H(p,q)=Ep(−log⁡q)=H(p)+KL(p‖q)

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

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

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

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

yz2=y1z21y2z21...yN2z21y1z22y2z22...yN2z22............y1z2N2y2z2N2...yN2z2N2(16)(16)∂y∂z2=[∂y1∂z12∂y1∂z22...∂y1∂zN22∂y2∂z12∂y2∂z22...∂y2∂zN22............∂yN2∂z12∂yN2∂z22...∂yN2∂zN22]

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

yz2=σ(z21)0...00σ(z22)...0............00...σ(z210)(17)(17)∂y∂z2=[σ′(z12)0...00σ′(z22)...0............00...σ′(z102)]

但是我们在这里使用的是Softmax激活函数,每个输出与该层所有神经元的输入均有关,所以其不是对角阵。
接下来我们计算z2a1∂z2∂a1,根据Jacobian矩阵定义得:
z2a1=z21a11z22a11...z2N2a11z21a12z22a12...z2N2a12............z21a1N1z22a1N1...z2N2a1N1(e000004)(e000004)∂z2∂a1=[∂z12∂a11∂z12∂a21...∂z12∂aN11∂z22∂a11∂z22∂a21...∂z22∂aN11............∂zN22∂a11∂zN22∂a21...∂zN22∂aN11]

我们知道:
z2i=W2i,1a11+W2i,2a12+...+W2i,ja1j+...+W2i,N1a1N1zi2=Wi,12a11+Wi,22a21+...+Wi,j2aj1+...+Wi,N12aN11

则其对第1层第j个神经元输出信号求导:
z2ia1j=W2i,j∂zi2∂aj1=Wi,j2

所以式(e000004)的最终结果为:
z2a1=z21a11z22a11...z2N2a11z21a12z22a12...z2N2a12............z21a1N1z22a1N1...z2N2a1N1=W21,1W22,1...W2N2,1W21,2W22,2...W2N2,2............W21,N1W22,N1...W2N2,N1=W2(e000004)(e000004)∂z2∂a1=[∂z12∂a11∂z12∂a21...∂z12∂aN11∂z22∂a11∂z22∂a21...∂z22∂aN11............∂zN22∂a11∂zN22∂a21...∂zN22∂aN11]=[W1,12W1,22...W1,N12W2,12W2,22...W2,N12............WN2,12WN2,22...WN2,N12]=W2

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

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

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

式中的每一项均为向量对向量的导数,其为Jacobian矩阵,因为z2RN2z2∈RN2,且wkRN2wk∈RN2,根据Jacobian矩阵定义,z2wkRN2×N2∂z2∂wk∈RN2×N2的矩阵,如下所示:
z2wk=z21wk1z22wk1...z2N2wk1z21wk2z22wk2...z2N2wk2............z21wkkz22wkk...z2N2wkk............z21wkN2z22wkN2...z2N2wkN2(21)(21)∂z2∂wk=[∂z12∂w1k∂z12∂w2k...∂z12∂wkk...∂z12∂wN2k∂z22∂w1k∂z22∂w2k...∂z22∂wkk...∂z22∂wN2k..................∂zN22∂w1k∂zN22∂w2k...∂zN22∂wkk...∂zN22∂wN2k]

由此可知其为RN2×N2RN2×N2的方阵,对其中第ii行第j列元素:
z2iwkj=z2iW2j,k(e000005)(e000005)∂zi2∂wjk=∂zi2∂Wj,k2

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值