TensorFlow可微分编程实践3---交叉熵与代价函数微分

在上篇博文中,我们讲述怎样处理第 l1 l − 1 层到第 l l 层的前向传输和反向求导,我们还没有讲述关于输出层的处理技术。在这里,我们还以MNIST手写数字识别为例,网络计算图如下所示:
这里写图片描述
当我们计算出输出层的输出yR10时,表示输入图像 x x 是0~9这10个数字的概率。此时输入图像 x x 对应的正确结果 y^R10 y ^ ∈ R 10 ,假设该数为 r r ,则y^r=1,其余维0,即 y^={0,0,...,1,...,0} y ^ = { 0 , 0 , . . . , 1 , . . . , 0 } ,其中主1的是第 r r 维。
我们首先处理损失函数,这里我们假设不考虑添加调整项的情况,我们的代价函数取交叉熵(cross entropy)函数,根据交叉熵定义:

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

对离散值情况,交叉熵(cross entropy)可以表示为:

H(p,q)=k=1Kp(k)logq(k)(2) (2) 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 维为正确数字,则代价函数的值为:
(3)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](4) (4) ∂ C ∂ y = [ 0 0 . . . − 1 y r . . . 0 ]

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

yz2=y1z21y2z21...yN2z21y1z22y2z22...yN2z22............y1z2N2y2z2N2...yN2z2N2(5) (5) ∂ 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 ]

接下来 z2W2 ∂ z 2 ∂ W 2 z2a1 ∂ z 2 ∂ a 1 z2b2 ∂ z 2 ∂ b 2 就是上一篇博文中讲述的内容。这里我们简单讲解下代价函数和代价函数反向求导的问题。代码如下所示:

@tf.custom_gradient
def cross_entropy(y, y_):
    # 找出y_中不等于0的下标值
    idx = np.nonzero(y_)[0][0]
    def grad_fn(dy):
        grad_C = np.zeros(y.shape[0])
        grad_C[idx] = - 1.0 / y[idx]
        return tf.constant(grad_C)
    return -math.log(y[idx]), grad_fn

def test003(args={}):
    tf.enable_eager_execution()
    tfe = tf.contrib.eager
    print('代价函数求导...')
    y = np.zeros((10))
    for idx in range(10):
        y[idx] = 0.01
    y[2] = 0.31
    y[3] = 0.11
    y[8] = 0.21
    y[1] = 0.11
    y[4] = 0.21
    y_ = np.array([0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
    print('y:{0}'.format(y))
    print('y_:{0}'.format(y_))
    C = cross_entropy(y, y_)
    print('代价函数值:{0}'.format(C.numpy()))
    grad_C1 = tfe.gradients_function(cross_entropy)
    pC_py = grad_C1(y, y_)
    print('pC_py:{0}'.format(pC_py[0].numpy()))

运行结果如下所示:
这里写图片描述
在求 y2z2 ∂ y 2 ∂ z 2 时,根据我们的定义,输出层采用的是交叉熵(Cross Entropy)函数,形式为:

yi=ez2iN2k=1ez2k(3.3.001) (3.3.001) y i = e z i 2 ∑ k = 1 N 2 e z k 2

下面我们来求 yiz2j ∂ y i ∂ z j 2 ,我们分为 ij i ≠ j i=j i = j 两种情况来讨论。
i=j i = j 时:
yiz2i=z2i(ez2iN2k=1ez2k)=ez2iN2k=1ez2k(ez2iN2k=1ez2k)2(3.3.002) (3.3.002) ∂ y i ∂ z i 2 = ∂ ∂ z i 2 ( e z i 2 ∑ k = 1 N 2 e z k 2 ) = e z i 2 ∑ k = 1 N 2 e z k 2 − ( e z i 2 ∑ k = 1 N 2 e z k 2 ) 2

ij i ≠ j 时:
yiz2j=z2j(ez2iN2k=1ez2k)=ez2iez2j(N2k=1ez2k)2(3.3.002) (3.3.002) ∂ y i ∂ z j 2 = ∂ ∂ z j 2 ( e z i 2 ∑ k = 1 N 2 e z k 2 ) = − e z i 2 e z j 2 ( ∑ k = 1 N 2 e z k 2 ) 2

按照上面的公式,我们可以求出 yz2R10×10 ∂ y ∂ z 2 ∈ R 10 × 10 的方阵。
根据定义有:
CW2=Cyyz2z2W2(3.3.003) (3.3.003) ∂ C ∂ W 2 = ∂ C ∂ y ⋅ ∂ y ∂ z 2 ⋅ ∂ z 2 ∂ W 2

其维数为 R1×10×R10×10×R10×10×512=R1×10×512 R 1 × 10 × R 10 × 10 × R 10 × 10 × 512 = R 1 × 10 × 512 ,即可得到每个第1层到第2层连接权值的导数,根据梯度下降算法,就可以求出新的连接权值了。
到目前为止,我们已经将所有多层感知器(MLP)模式中用到的技术,全部讲述完成了,有了这些基本知识之后,我们就可以搭建一个完整的多层感知器(MLP)模型了,在下一节中我们将搭建一个最基本的多层感知器模型用于MNIST手写数字识别。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值