关于第5周反向传播算法的一些争论与思考
第5周介绍了神经网络的反向传播算法。由于介绍得比较简要,很多地方没有讲透(众:You can you up!),后来C站论坛里有几个网友开始争论其中的公式有点奇怪,究竟是讲错了还是另有原因(最后结论似乎应该是没讲错)。全程围观的耿先生记录了相关的一些要点。
背景:
反向传播算法就是说好比你有一个神经网络,输入层 -> 隐藏层 -> 输出层酱紫。我们现在把所有系数初始化,把训练集里的 x 都代入到输入层,经过层层计算最后得到一组输出值。这时候我们就可以和实际的值 y 作比较,计算cost函数。假设我们用的是逻辑回归来研究分类问题,那么cost函数就应该是:
(摘自课程维基页。下同)
学 过前几周应该会知道这个公式是怎么来的。前半部分和前几周逻辑回归的cost函数区别在于多了一个 K。因为逻辑回归最后的结果是一个输出值,0 到 1 之间。而上述神经网络最后的结果是一排值,每一个都是 0 到 1 之间,所以多了一次求和。后半部分其实就是把所有的统统求平方加起来。
cost函数有了之后我们就可以使用梯度下降法,那么就需要计算梯度gradient。这就要用到反向传播算法了。简单来说就是,我们从最后的输出层的值(可以叫或者叫)开始,计算每一个值的值。这个值描述了当前的计算结果和实际结果 y 之间的偏差量error。
对于输出层,课程告诉我们,就等于。争论就发生在这里,我们下面会详细说是什么争论。现在先把背景知识介绍完。
对于其它层,则需要从后一层的,倒推前一层的。公式是:
这个公式体现了什么思想呢?我们说了表现了每一个结点的值与实际值之间的偏差。对于输出层看似很好理解,减一下得到的就是偏差。不过实际上这里面暗藏玄机。详见下文(此处为悬念1)。
而 对于中间层,所谓“偏差”应该这么理解:对于后一层某一个结点上出现的偏差,前一层的每一个结点都要承担一部分责任,所以我们可以把后一层结点的偏差量按 照系数比例分配给前一层的结点。同时,前一层的某一个结点,对后一层的每一个节点都承担着责任,那么它的“总责任”就是这些所有的小责任之和。如果你仔细 想想这个矩阵乘法的计算过程,会发现正好就是上面说的这个责任分配与求和的过程。
但是后面乘的 g' 项是从哪里来的呢?我们先不说,详见下文(此处为悬念2)。我们只要知道,由于这里用的是逻辑回归,g(z)函数就是逻辑函数。g' 是指对它求导。求导之后我们会发现:,而根据定义又有,所以我们就把上式展开为:
这样我们就得到了每一层的。之后再用前一层的“每一个”结点的值 a,乘以后一层的“每一个”结点的,就得到了一个矩阵。写成公式是。这个矩阵的规模和前后两层之间的系数矩阵是一致的,因为它们都是由前一层的每一个结点指向后一层的每一个结点。这个矩阵有什么意义呢?实际上,前一层的结点 i 的值,乘以后一层结点 j 的值,得到的就是cost函数对于的偏微分。为什么呢?详见下文(悬念3)。总之,这其实就是我们要求的梯度。
注意,上面这部分(从“简单来说就是……”那句话往后)是针对“一条”输入数据所进行的计算。而我们的训练集里通常有很多条,比如有 m 条数据。我们针对每一条都进行上述计算,最后把每一条的都累加起来再除以 m,得到的就是我们真正需要的梯度矩阵。然后就可以使用梯度下降法寻找最优了。
争议:
在C站课程论坛上,有人发了一个帖子,指出输出层的计算公式有问题。课程给出的公式是:
Andrew Ng在这里并没有详细解释这个计算公式是怎么来的。实际上它是cost函数求导求出来的,具体求法详见下文(悬念4)。该贴楼主和楼主的小伙伴认为 Andrew Ng犯了一个错误,误将线性回归的cost函数用在了逻辑回归计算中。因为逻辑回归的cost函数是长最上面那个鬼样子。为了便于看清楚我们将它简化一下 (忽略求和,忽略后半部分项):
对它求导后得到的应该是:
(注意。)
而只有线性回归的cost函数:
求导之后才能得到:
几位TA觉得他们说的好有道理无法反驳,只能不断地说我信任Prof Ng男神是不会错的!最后还是楼主的小伙伴自己发了个帖子指出了真正的问题出在哪里。
原来还是他们理解错了公式。并非简单地对cost函数求导就可以。它真正的计算方法是:详见下文(众:你去死吧!)……
真相:
问题的焦点集中在了真正的计算公式上面。维基百科“反向传播算法”页面给出了这个过程。我们下面来看看到底是怎么求的。我们之前留下的悬念1、2、3、4,也将一次性解决。
让我们回到我们的初心。这一切究竟是为了什么?啊!是为了求cost函数对于每一个的偏微分,也就是求:
(其中分母上的那坨东东是指:第 l 层的结点 i,指向其下一层也就是 l+1 层的结点 j 的系数。)
(评论中有人指出这里似乎 i 和 j 的意义弄反了。我查了一下似乎是反了,表示前一层结点的字母应该写在后面。如果第 l 层是结点 i,第 l+1 层是结点 j,的下标应该是 j,i 这样子。如果这里反了的话上面背景部分的倒数第二段估计也弄反了。如果你需要参考这篇文章里的公式,请留意一下这个问题。我暂时先不改了。)
再回顾一下我们计算输出值时的一些定义:,,。
为了使用这些定义,我们可以将上面的偏微分式子展开为:
(此处使用技能:链式法则x2)
把求一次偏微分变成了求三次偏微分。
右边这三项,我们先看最后一项。根据定义我们知道:
所以:
JOB DONE...........33%
顺便说一句,右边前两项的乘积,就是课程里引入的值!这就回答了悬念 3 提出的问题:为什么得到后一层的值之后,要乘以前一层的 a 值来得到偏微分?答案是:因为它们分别是偏微分式子展开后的两个乘数。
接下来我们看第二项:这不就是对sigmoid函数求导吗?之前我们遇到过的g'(z),它的出处原来是这里!
我们经过计算后会得到这样一个式子:
恰好,算出来也是这个式子,所以我们就用后面这个更好算的式子来计算了。这就是计算公式的后半部分,的来源。
JOB DONE...........67%
现在来到第一项偏微分。对于中间层的结点,这个偏微分并不好算。(也能算,需要继续使用链式法则展开成更多的项!最后算出来就是上面计算公式的前半部分。)但是我们只想知道关于最后输出层的情况。那就简单多了!上面争议部分中,那位楼主的小伙伴已经给出了cost函数对于输出层求偏微分的结果:
JOB DONE...........100%
那么说到底输出层的到底等于多少?
等于前两项的乘积:
原来地球是这个样子滴……
结论:
Andrew Ng给的公式没有错。他只是把复杂的推倒,不是,推导过程省略了。但是这样一来容易产生误解。很多人以为输出层的偏差量就是计算值减实际值这么简单,其实 是碰巧才这么简单的。还有很多人说为什么这个值和很多别的网站,包括维基百科上说的不一样啊?因为很多别的网站包括维基百科,cost函数用的是线性回归 的那种,。它的偏微分就和逻辑回归的cost函数有差别了。具体地说,就差在分母的那一项上。
tl;nr:男神没搞错。微积分很复杂。而猫咪依旧是可爱的。