神经网络的 BP 算法学习

lrm928 7年前
   <p>首先用图来表达函数,对于以下的函数:</p>    <p><img src="https://simg.open-open.com/show/8860fe339a94cc2bf3f4a0a578f6c46b.png"></p>    <p>令:</p>    <p><img src="https://simg.open-open.com/show/af2679b1b788616dbd71eae9fcd9f3b7.png"></p>    <p>则相应的图表示为:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/04a073372f905670d97ead1e78358e52.png"></p>    <p style="text-align: center;">图-1</p>    <p>上图中,圆形框是函数表达式,边表示圆形框的输入和输出来源。将图r节点的输出记为r, 则r节点的输入为p节点和q节点的输出,记为 p,q。</p>    <p>那么如何求z(z节点的输出)关于图中任意节点输出的梯度呢?</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/7e5c8801b0eeacea908f0b295194cca5.png"></p>    <p style="text-align: center;">图-2</p>    <p>对于任意的x节点,找到所有与x节点直接关联的节点p,q,x 只能通过p,q去影响z。那么z和x的关系可以表示如下z=z(p,q),p=p(x),q=q(x),根据复合函数求导法则:</p>    <p><img src="https://simg.open-open.com/show/0891d70ddb9c66e650df466907fb7eb8.png"></p>    <p>这就是BP算法的核心公式。</p>    <p>在实际使用中,有前向和后向两个流程,以图-1为例:</p>    <p>在前向流程中,依次计算:</p>    <p>在后向计算过程中,计算过程如下:</p>    <p><img src="https://simg.open-open.com/show/902dd44e3e133c41f595e4101ae749fc.png"> <img src="https://simg.open-open.com/show/edad240d6857312dcd445828656be13b.png"></p>    <p>可见当某一节点的后续节点都走完回传过程后,那么该节点的梯度就计算出来了,然后轮到该节点,直接做回传就可以了。</p>    <p>在BP过程中,最重要的事情就是维护梯度回传的先后顺序。在TensorFlow中,每个tensor就表示了一个函数节点,tensor之间的运算,例如mat、add这些都会生成新的tensor,这种关系最终都在记在graph中,在梯度求导的时候,根据图中节点的顺序,从后至前计算梯度,只要确保:回传某节点的梯度时,该节点的后续依赖节点已经回传完毕就行了。</p>    <p>需要注意的几个地方:</p>    <p>1)梯度的计算可能引起歧义,如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/b28f14361c2d238e0e3d6f28692fe4c4.png"></p>    <p>根据上面的求导法则</p>    <p><img src="https://simg.open-open.com/show/cbd5974aa663dd86a369e9baea34963e.png"></p>    <p>假设:</p>    <p>p=x*x,</p>    <p>q=p*x,</p>    <p>z=p+q,</p>    <p>显然z=x*x+(x*x)*x,则∂z/∂x实际为2*x+3*x*x。</p>    <p>在这个公式中(∂q/∂x)为q对x_2这个输入的导数,x_2的值正好为x,即(∂q/(∂(x_2)))=p=x*x。(∂z/∂p)是z关于p节点输出的导数,即(∂z/∂p=∂z/∂z*∂z/(∂(p_1))+∂z/∂q  ∂q/(∂(p_2)))=(1*1+1*x)=(1+x),则(∂z/∂x)=(1+x)*(2*x)+(1)*(x*x)=2*x+3*x*x。所以计算导数的时候一定要分清楚,是要对某节点的输入求导还是对某节点的输出求导。</p>    <p>为方便起见,在loss节点后增加一个节点C=loss,如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/c8805b6399d8f435314f7f264c0faeef.png"></p>    <p>记(∂C/∂w)表示C关于w节点输出的导数,这是回传算法要求的。其他的,例如:(∂q/∂x)表示q关于x到q的输入端上的导数,这在建立节点的时候前向的时候就能算出来。</p>    <p>则上图的求导过程如下:</p>    <p><img src="https://simg.open-open.com/show/dd474ea04cf0cd77dc6cc3187ba47d37.png"></p>    <p>2)对于某些RNN,unroll之后,可能具有以下的形式,其求导过程如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/a7d3c8a2e35039ef6ddc24135f3eb9f2.png"></p>    <p><img src="https://simg.open-open.com/show/61a0d588899c8ddbf3ebeea13a0079b4.png"></p>    <p>3)上图中所有的节点是以标量为例的,实际上也可以为向量,特别注意下面两种向量的求导形式:</p>    <p>(a)矩阵形式</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/ed39033df62a9b6e8bca9d00343ddf34.png"></p>    <p><img src="https://simg.open-open.com/show/af16cf3837dcca5701c0aefd4a919eb1.png"></p>    <p>(b)点乘形式</p>    <p><img src="https://simg.open-open.com/show/60ed8a2bca734ee712cdc9f3229ee7b9.png"><br> <img src="https://simg.open-open.com/show/86ba8e24008a31e558dfcd9639601fae.png"></p>    <p>有了上面的基础后,就可以看看几种常见的神经网络的推导</p>    <p>1)普通的神经网络</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/5a8ca93bfffa339cc74ed464b4843c9c.png"> <img src="https://simg.open-open.com/show/4cd633161ae6825e7967e1286ff7b825.png"></p>    <p>2)LSTM的BPTT推导,一个典型的LSTM示意图如下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/44887cfdb2469ce4bb063f7ace09f671.jpg"></p>    <p><img src="https://simg.open-open.com/show/2641065d4623c4c2354fe8e22a9b74a3.png"></p>    <p>将其中的函数块都变为节点,一个典型的seq2seq网络就如下图所示:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/e107dca3d8c3270ab206e0f83566ff19.jpg"></p>    <p>依赖关系如下:</p>    <p>lstm_cell[t-1]->lstm_cell[t]->logits[t]->prob[t]</p>    <p>prob[0],…,prob[T-1]->loss</p>    <p>基本的前向过程如下:</p>    <p>对任意t:</p>    <p>几个控制门的输出:</p>    <p>c_hat=W_c x_(t-1)+U_c h_(t-1)+b_c</p>    <p>c=tanh(c_hat)</p>    <p>i_hat=W_i x_(t-1)+U_i h_(t-1)+b_i</p>    <p>i=sigmoid(i_hat)</p>    <p>f_hat=W_f x_(t-1)+U_f h_(t-1)+b_f</p>    <p>f=sigmoid(f_hat)</p>    <p>o_hat=W_o x_(t-1)+U_o h_(t-1)+b_o</p>    <p>o=sigmoid(o_hat)</p>    <p>状态S的更新:</p>    <p>S[t]=c⊙i+S[t-1]⊙f</p>    <p>输出H的计算:</p>    <p>H[t]=o⊙tan⁡h(S[t-1])</p>    <p>未归一化的概率计算:</p>    <p>logits[t]=W_p H[t]+b_p</p>    <p>归一化的概率计算:</p>    <p>prob[t]=softmax(logits[t])</p>    <p>损失函数的计算:</p>    <p>loss+=Y_t⊙(-log⁡(prob[t])</p>    <p>按照之前BP的法则,计算loss关于各个节点输出的梯度,如下:</p>    <p><img src="https://simg.open-open.com/show/09e971f86a987ebfe9fcdc9b7a7af8ab.jpg"></p>    <p>其余节点的计算都很简单,就不列出计算公式了。BP的时候,需要从T步向第1步计算。具体的代码参见:</p>    <p>http://git.oschina.net/bamuyy/lstm/blob/master/lstm.py?dir=0&filepath=lstm.py&oid=7dfd64de7ca2ab53ae79008ac9bbc054dcba2f5d&sha=5fd268b98baf62fc4ec3ebd3cadcc130f85894e2</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/b66e31e54f59cffd0d5263f53460f013.jpg"></p>    <p> </p>    <p> </p>    <p>来自:http://mp.weixin.qq.com/s?__biz=MjM5ODIzNDQ3Mw==&mid=2649966184&idx=1&sn=b3347f1a508ccbf799c3bf124d670131&chksm=beca366e89bdbf78fe1bf48b91c6b48b68f988a67fa99dac694748591060c9484543713aedec&scene=0</p>    <p> </p>