TensorFlow可微分编程实践3---向量微分和Jacobian矩阵

在这篇博文中,我们将利用TensorFlow Eager Execution API来实现一个完整多层感知器(MLP)模型。在具体实现多层感知器模型之前,我们首先来看,怎样用TensorFlow Eager Execution API来求向量与矩阵运算的导数。
我们知道在多层感知器模型中,最基本的运算是由第 l1 l − 1 层输出信号求出第 l l 层神经元的输入信号,公式如下所示:

(3.2.001)zl=Wlal1+bl

为了下面讨论方便,我们假设第 l1 l − 1 层有3个神经元,第 l l 层有2个神经元,式(3.2.001)中的各个值定义如下。
l1层输出信号:

al1=1.02.03.0(3.2.002) (3.2.002) a l − 1 = [ 1.0 2.0 3.0 ]

l1 l − 1 层到第 l l 层连接权值矩阵:
(3.2.003)Wl=[4.05.06.07.08.09.0]

l l 层偏置值:
(3.2.004)bl=[1001.01002.0]

为了进行学习,我们需要求出以下导数: zlal1 ∂ z l ∂ a l − 1 zlbl ∂ z l ∂ b l zlWl ∂ z l ∂ W l ,我们分别来进行讨论。
我们首先来看第一项,根据Jacobian矩阵定义得:
zlal1=zl1al11zl2al11...zlNlal11zl1al12zl2al12...zlNlal12............zl1al1Nl1zl2al1Nl1...zlNlal1Nl1=Wl1,1Wl2,1...WlNl,1Wl1,2Wl2,2...WlNl,2............Wl1,Nl1Wl2,Nl1...WlNl,Nl1=Wl(3.2.005) (3.2.005) ∂ z l ∂ a l − 1 = [ ∂ z 1 l ∂ a 1 l − 1 ∂ z 1 l ∂ a 2 l − 1 . . . ∂ z 1 l ∂ a N l − 1 l − 1 ∂ z 2 l ∂ a 1 l − 1 ∂ z 2 l ∂ a 2 l − 1 . . . ∂ z 2 l ∂ a N l − 1 l − 1 . . . . . . . . . . . . ∂ z N l l ∂ a 1 l − 1 ∂ z N l l ∂ a 2 l − 1 . . . ∂ z N l l ∂ a N l − 1 l − 1 ] = [ W 1 , 1 l W 1 , 2 l . . . W 1 , N l − 1 l W 2 , 1 l W 2 , 2 l . . . W 2 , N l − 1 l . . . . . . . . . . . . W N l , 1 l W N l , 2 l . . . W N l , N l − 1 l ] = W l

我们接下来再来求对第 l l 层偏置求微分:
(3.2.006)zlbl=[z1lb1lz1lb2l...z1lbNllz2lb1lz2lb2l...z2lbNll............zNllb1lzNllb2l...zNllbNll]=[10...001...............00...1]

下面是一个向量对矩阵求偏导,而我们对这个操作没有定义,所以我们需要以一种变通的方式来进行,我们将 Wl W l 视为由 w(i)RNl1 w ( i ) ∈ R N l − 1 的行向量组成:
Wl=(w(1))T(w(2))T...(w(Nl))T=wlRNl(3.2.007) (3.2.007) W l = [ ( w ( 1 ) ) T ( w ( 2 ) ) T . . . ( w ( N l ) ) T ] = w l ∈ R N l

其实 w(i) w ( i ) 是指向第 l l 层第i个神经元所有连接权值组成的向量。
有了式(3.2.007)的定义,我们就可以将 Wl W l 视为向量,这样根据Jacobian矩阵定义:
zlWl=zlwl=zl1w(1)zl2w(1)...zlNlw(1)zl1w(2)zl2w(2)...zlNlw(2)............zl1w(Nl)zl2w(Nl)...zlNlw(Nl)(3.2.008) (3.2.008) ∂ z l ∂ W l = ∂ z l ∂ w l = [ z 1 l w ( 1 ) z 1 l w ( 2 ) . . . z 1 l w ( N l ) z 2 l w ( 1 ) z 2 l w ( 2 ) . . . z 2 l w ( N l ) . . . . . . . . . . . . z N l l w ( 1 ) z N l l w ( 2 ) . . . z N l l w ( N l ) ]

与前面不同的是,式(3.2.008)的矩阵中的每个元素都是一个标量对向量的求偏导,根据我们上篇博文介绍,标量对向量求偏导,结果为一个行向量,我们以 zliw(j) z i l w ( j ) 为例进行讨论。
如果 ij i ≠ j 时, w(j) w ( j ) 是指向第 l l 层第j个神经元的,不与第 l l 行第i个神经元相接,因此所有偏层均为0,如下所示:
zliw(j)=[00...0]RNl1(3.2.009) (3.2.009) z i l w ( j ) = [ 0 0 . . . 0 ] ∈ R N l − 1

如果 i=j i = j 时, w(j) w ( j ) 是由指向第 l l 层第i个神经元的所有连接权值组成的,根据输入信号定义可得:
zliw(j)=[zliWlj,1zliWlj,2...zliWlj,Nl1]=[al11al12...al1Nl1](3.2.010) (3.2.010) z i l w ( j ) = [ ∂ z i l ∂ W j , 1 l ∂ z i l ∂ W j , 2 l . . . ∂ z i l ∂ W j , N l − 1 l ] = [ a 1 l − 1 a 2 l − 1 . . . a N l − 1 l − 1 ]

因此式(3.2.008)矩阵的每个元素为指向纸里的 RNl1 R N l − 1 向量,当不在对角线上时,所有元素值为零,当在对角线上时,元素为第 l1 l − 1 层输出值。
下面我们来看,怎样通过TensorFlow Eager Excecution API来求出这些偏导的值。

@tf.custom_gradient
def f002(W, a, b):
    def grad_fn(dy):
        ws = W.shape
        pz_pW = np.zeros((2, 2, 3))
        a1 = tf.reshape(a, [3])
        for idx in range(ws[0]):
            pz_pW[idx][idx] = a1
        diag = tf.ones([W.shape[0]])
        d_b = tf.matrix_diag(diag)
        return tf.constant(pz_pW), W, d_b
    return tf.matmul(W, a) + b, grad_fn

def test001(args={}):
    tf.enable_eager_execution()
    tfe = tf.contrib.eager

    W = tf.constant([[4.0, 5.0, 6.0],[7.0, 8.0, 9.0]])
    a = tf.reshape(tf.constant([1.0, 2.0, 3.0]), [3, 1])
    i_debug = 2
    if 1 == i_debug:
        f003(W, a)
        return 
    b = tf.reshape(tf.constant([1001.0, 1002.0]), [2, 1])
    z = f002(W, a, b)
    print('z=Wa+b={0}'.format(z))
    grad_f1 = tfe.gradients_function(f002)
    dv = grad_f1(W, a, b)
    print('pz_pW={0}'.format(dv[0].numpy()))
    print('pz_pa={0}'.format(dv[1].numpy()))
    print('pz_pb={0}'.format(dv[2].numpy()))
    print('v0.0.1')

在上面的代码中,需要说明的是第16行,将向量 a a 定义为3行1列的矩阵形式,这是为了与连接权值矩阵做矩阵相乘。
在前向计算阶段,可以直接调用TensorFlow的矩阵乘法和加法,我们就可以取得正确的结果,但是在反向求导阶段,如果我们直接利用TensorFlow进行求导,例如求 zlal1 ∂ z l ∂ a l − 1 时,TensorFlow返回结果维数与 al1 a l − 1 相同,而Jacobian矩阵的维数为 RNl×Nl1 R N l × N l − 1 ,所以我们需要自己定义求导函数,根据上面的理论分析, zlWl ∂ z l ∂ W l 是一个3维的张量,维数为 RNl×Nl×Nl1 R N l × N l × N l − 1 ,我们可以将其视为一个 RNl×Nl R N l × N l 的矩阵,矩阵中每个元素均为一个数组,长度为 Nl1 N l − 1 ,且除对角线上的元素外,数组元素为0,而对角线上的元素,数组元素为第 l1 l − 1 导神经元的输出值。 zlal1 ∂ z l ∂ a l − 1 为第 l1 l − 1 层到第 l l 层的连接权值矩阵Wl,而 zlbl ∂ z l ∂ b l RNl×Nl R N l × N l 的单位阵,运行结果如下所示:
这里写图片描述
至此我们完成了第 l1 l − 1 层到第 l l <script type="math/tex" id="MathJax-Element-58">l</script>层正向传输和反向求导工作,基本上按照数学理论要求,我们就可以完全处理一个多层感知器模型了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值