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

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

z1i=W1i,1x1+W1i,2x2+...+W1i,jxj+...+W1i,784x784+b1i(1) (1) z i 1 = W i , 1 1 x 1 + W i , 2 1 x 2 + . . . + W i , j 1 x j + . . . + W i , 784 1 x 784 + b i 1

或者简写为:
z1i=j=1784W1i,jxj+b1i(2) (2) z i 1 = ∑ j = 1 784 W i , j 1 x j + b i 1

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

我们将第1层神经元的偏置值 b1i b i 1 与串在一起形成一个向量,如下所示:
b1=b11b12...b1512 b 1 = [ b 1 1 b 2 1 . . . b 512 1 ]

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

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

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

我们假设第1层第i个神经元的激活函数为ReLU函数,则其输出为:
a1i=ReLU(z1i)(4) (4) a i 1 = R e L U ( z i 1 )

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

将式( e000001 e 000001 )代入得到:
a1=ReLU(z1)=ReLU(W1x+b1)(e000002) (e000002) a 1 = R e L U ( z 1 ) = R e L U ( W 1 ⋅ x + b 1 )

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

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

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

第2层也就是输出层的激活函数为Softmax函数:
yi=a2i=ez2iN2j=1ez2j(8) (8) y i = a i 2 = e z i 2 ∑ j = 1 N 2 e z j 2

其向量形式表示为:
yi=ez21N2j=1ez2jez22N2j=1ez2j...ez2N2N2j=1ez2j(9) (9) y i = [ e z 1 2 ∑ j = 1 N 2 e z j 2 e z 2 2 ∑ j = 1 N 2 e z j 2 . . . e z N 2 2 ∑ j = 1 N 2 e z j 2 ]

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

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

  • 向量运算的微分
    我们先来定义向量微分,假设有向量 yRm y ∈ R m 和向量 xRn x ∈ R n ,微分 yx ∂ y ∂ x 定义为:

    yx=y1x1y2x1...ymx1y1x2y2x2...ymx2............y1xny2xn...ymxn(11) (11) ∂ y ∂ x = [ ∂ y 1 ∂ x 1 ∂ y 1 ∂ x 2 . . . ∂ y 1 ∂ x n ∂ y 2 ∂ x 1 ∂ y 2 ∂ x 2 . . . ∂ y 2 ∂ x n . . . . . . . . . . . . ∂ y m ∂ x 1 ∂ y m ∂ x 2 . . . ∂ y m ∂ x n ]

    这就是Jacobian矩阵 jRm×n j ∈ R m × n

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

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

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

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

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

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

yz2=y1z21y2z21...yN2z21y1z22y2z22...yN2z22............y1z2N2y2z2N2...yN2z2N2(16) (16) ∂ y ∂ z 2 = [ ∂ y 1 ∂ z 1 2 ∂ y 1 ∂ z 2 2 . . . ∂ y 1 ∂ z N 2 2 ∂ y 2 ∂ z 1 2 ∂ y 2 ∂ z 2 2 . . . ∂ y 2 ∂ z N 2 2 . . . . . . . . . . . . ∂ y N 2 ∂ z 1 2 ∂ y N 2 ∂ z 2 2 . . . ∂ y N 2 ∂ z N 2 2 ]

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

yz2=σ(z21)0...00σ(z22)...0............00...σ(z210)(17) (17) ∂ y ∂ z 2 = [ σ ′ ( z 1 2 ) 0 . . . 0 0 σ ′ ( z 2 2 ) . . . 0 . . . . . . . . . . . . 0 0 . . . σ ′ ( z 10 2 ) ]

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

我们知道:
z2i=W2i,1a11+W2i,2a12+...+W2i,ja1j+...+W2i,N1a1N1 z i 2 = W i , 1 2 a 1 1 + W i , 2 2 a 2 1 + . . . + W i , j 2 a j 1 + . . . + W i , N 1 2 a N 1 1

则其对第1层第j个神经元输出信号求导:
z2ia1j=W2i,j ∂ z i 2 ∂ a j 1 = W i , j 2

所以式(e000004)的最终结果为:
z2a1=z21a11z22a11...z2N2a11z21a12z22a12...z2N2a12............z21a1N1z22a1N1...z2N2a1N1=W21,1W22,1...W2N2,1W21,2W22,2...W2N2,2............W21,N1W22,N1...W2N2,N1=W2(e000004) (e000004) ∂ z 2 ∂ a 1 = [ ∂ z 1 2 ∂ a 1 1 ∂ z 1 2 ∂ a 2 1 . . . ∂ z 1 2 ∂ a N 1 1 ∂ z 2 2 ∂ a 1 1 ∂ z 2 2 ∂ a 2 1 . . . ∂ z 2 2 ∂ a N 1 1 . . . . . . . . . . . . ∂ z N 2 2 ∂ a 1 1 ∂ z N 2 2 ∂ a 2 1 . . . ∂ z N 2 2 ∂ a N 1 1 ] = [ W 1 , 1 2 W 1 , 2 2 . . . W 1 , N 1 2 W 2 , 1 2 W 2 , 2 2 . . . W 2 , N 1 2 . . . . . . . . . . . . W N 2 , 1 2 W N 2 , 2 2 . . . W N 2 , N 1 2 ] = W 2

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

其中第 k k 个列向量wk为:
wk=W21,kW22,k...W2N2,k(19) (19) w k = [ W 1 , k 2 W 2 , k 2 . . . W N 2 , k 2 ]

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

式中的每一项均为向量对向量的导数,其为Jacobian矩阵,因为 z2RN2 z 2 ∈ R N 2 ,且 wkRN2 w k ∈ R N 2 ,根据Jacobian矩阵定义, z2wkRN2×N2 ∂ z 2 ∂ w k ∈ R N 2 × N 2 的矩阵,如下所示:
z2wk=z21wk1z22wk1...z2N2wk1z21wk2z22wk2...z2N2wk2............z21wkkz22wkk...z2N2wkk............z21wkN2z22wkN2...z2N2wkN2(21) (21) ∂ z 2 ∂ w k = [ ∂ z 1 2 ∂ w 1 k ∂ z 1 2 ∂ w 2 k . . . ∂ z 1 2 ∂ w k k . . . ∂ z 1 2 ∂ w N 2 k ∂ z 2 2 ∂ w 1 k ∂ z 2 2 ∂ w 2 k . . . ∂ z 2 2 ∂ w k k . . . ∂ z 2 2 ∂ w N 2 k . . . . . . . . . . . . . . . . . . ∂ z N 2 2 ∂ w 1 k ∂ z N 2 2 ∂ w 2 k . . . ∂ z N 2 2 ∂ w k k . . . ∂ z N 2 2 ∂ w N 2 k ]

由此可知其为 RN2×N2 R N 2 × N 2 的方阵,对其中第 i i 行第j列元素:
z2iwkj=z2iW2j,k(e000005) (e000005) ∂ z i 2 ∂ w j k = ∂ z i 2 ∂ W j , k 2

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值