往期回顧
在前面的文章中棍潘,我們介紹了循環(huán)神經(jīng)網(wǎng)絡(luò)恃鞋,它可以用來處理包含序列結(jié)構(gòu)的信息崖媚。然而,除此之外恤浪,信息往往還存在著諸如樹結(jié)構(gòu)畅哑、圖結(jié)構(gòu)等更復(fù)雜的結(jié)構(gòu)。對于這種復(fù)雜的結(jié)構(gòu)水由,循環(huán)神經(jīng)網(wǎng)絡(luò)就無能為力了敢课。本文介紹一種更為強(qiáng)大、復(fù)雜的神經(jīng)網(wǎng)絡(luò):遞歸神經(jīng)網(wǎng)絡(luò) (Recursive Neural Network, RNN)绷杜,以及它的訓(xùn)練算法BPTS (Back Propagation Through Structure)直秆。顧名思義,遞歸神經(jīng)網(wǎng)絡(luò)(巧合的是鞭盟,它的縮寫和循環(huán)神經(jīng)網(wǎng)絡(luò)一樣圾结,也是RNN)可以處理諸如樹、圖這樣的遞歸結(jié)構(gòu)齿诉。在文章的最后筝野,我們將實(shí)現(xiàn)一個(gè)遞歸神經(jīng)網(wǎng)絡(luò),并介紹它的幾個(gè)應(yīng)用場景粤剧。
遞歸神經(jīng)網(wǎng)絡(luò)是啥
因?yàn)樯窠?jīng)網(wǎng)絡(luò)的輸入層單元個(gè)數(shù)是固定的歇竟,因此必須用循環(huán)或者遞歸的方式來處理長度可變的輸入。循環(huán)神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)了前者抵恋,通過將長度不定的輸入分割為等長度的小塊焕议,然后再依次的輸入到網(wǎng)絡(luò)中,從而實(shí)現(xiàn)了神經(jīng)網(wǎng)絡(luò)對變長輸入的處理弧关。一個(gè)典型的例子是盅安,當(dāng)我們處理一句話的時(shí)候,我們可以把一句話看作是詞組成的序列世囊,然后别瞭,每次向循環(huán)神經(jīng)網(wǎng)絡(luò)輸入一個(gè)詞,如此循環(huán)直至整句話輸入完畢株憾,循環(huán)神經(jīng)網(wǎng)絡(luò)將產(chǎn)生對應(yīng)的輸出蝙寨。如此,我們就能處理任意長度的句子了嗤瞎。入下圖所示:
然而墙歪,有時(shí)候把句子看做是詞的序列是不夠的,比如下面這句話『兩個(gè)外語學(xué)院的學(xué)生』:
上圖顯示了這句話的兩個(gè)不同的語法解析樹猫胁∠湟冢可以看出來這句話有歧義,不同的語法解析樹則對應(yīng)了不同的意思弃秆。一個(gè)是『兩個(gè)外語學(xué)院的/學(xué)生』届惋,也就是學(xué)生可能有許多髓帽,但他們來自于兩所外語學(xué)校;另一個(gè)是『兩個(gè)/外語學(xué)院的學(xué)生』脑豹,也就是只有兩個(gè)學(xué)生郑藏,他們是外語學(xué)院的。為了能夠讓模型區(qū)分出兩個(gè)不同的意思瘩欺,我們的模型必須能夠按照樹結(jié)構(gòu)去處理信息必盖,而不是序列,這就是遞歸神經(jīng)網(wǎng)絡(luò)的作用俱饿。當(dāng)面對按照樹/圖結(jié)構(gòu)處理信息更有效的任務(wù)時(shí)歌粥,遞歸神經(jīng)網(wǎng)絡(luò)通常都會(huì)獲得不錯(cuò)的結(jié)果。
遞歸神經(jīng)網(wǎng)絡(luò)可以把一個(gè)樹/圖結(jié)構(gòu)信息編碼為一個(gè)向量拍埠,也就是把信息映射到一個(gè)語義向量空間中失驶。這個(gè)語義向量空間滿足某類性質(zhì),比如語義相似的向量距離更近枣购。也就是說嬉探,如果兩句話(盡管內(nèi)容不同)它的意思是相似的,那么把它們分別編碼后的兩個(gè)向量的距離也相近棉圈;反之涩堤,如果兩句話的意思截然不同,那么編碼后向量的距離則很遠(yuǎn)分瘾。如下圖所示:
從上圖我們可以看到胎围,遞歸神經(jīng)網(wǎng)絡(luò)將所有的詞、句都映射到一個(gè)2維向量空間中芹敌。句子『the country of my birth』和句子『the place where I was born』的意思是非常接近的痊远,所以表示它們的兩個(gè)向量在向量空間中的距離很近。另外兩個(gè)詞『Germany』和『France』因?yàn)楸硎镜亩际堑攸c(diǎn)氏捞,它們的向量與上面兩句話的向量的距離,就比另外兩個(gè)表示時(shí)間的詞『Monday』和『Tuesday』的向量的距離近得多冒版。這樣液茎,通過向量的距離,就得到了一種語義的表示辞嗡。
上圖還顯示了自然語言可組合的性質(zhì):詞可以組成句捆等、句可以組成段落、段落可以組成篇章续室,而更高層的語義取決于底層的語義以及它們的組合方式栋烤。遞歸神經(jīng)網(wǎng)絡(luò)是一種表示學(xué)習(xí),它可以將詞挺狰、句明郭、段买窟、篇按照他們的語義映射到同一個(gè)向量空間中,也就是把可組合(樹/圖結(jié)構(gòu))的信息表示為一個(gè)個(gè)有意義的向量薯定。比如上面這個(gè)例子始绍,遞歸神經(jīng)網(wǎng)絡(luò)把句子"the country of my birth"表示為二維向量[1,5]。有了這個(gè)『編碼器』之后话侄,我們就可以以這些有意義的向量為基礎(chǔ)去完成更高級的任務(wù)(比如情感分析等)亏推。如下圖所示,遞歸神經(jīng)網(wǎng)絡(luò)在做情感分析時(shí)年堆,可以比較好的處理否定句吞杭,這是勝過其他一些模型的:
在上圖中,藍(lán)色表示正面評價(jià)变丧,紅色表示負(fù)面評價(jià)芽狗。每個(gè)節(jié)點(diǎn)是一個(gè)向量,這個(gè)向量表達(dá)了以它為根的子樹的情感評價(jià)锄贷。比如"intelligent humor"是正面評價(jià)译蒂,而"care about cleverness wit or any other kind of intelligent humor"是中性評價(jià)。我們可以看到谊却,模型能夠正確的處理doesn't的含義柔昼,將正面評價(jià)轉(zhuǎn)變?yōu)樨?fù)面評價(jià)。
盡管遞歸神經(jīng)網(wǎng)絡(luò)具有更為強(qiáng)大的表示能力炎辨,但是在實(shí)際應(yīng)用中并不太流行捕透。其中一個(gè)主要原因是,遞歸神經(jīng)網(wǎng)絡(luò)的輸入是樹/圖結(jié)構(gòu)碴萧,而這種結(jié)構(gòu)需要花費(fèi)很多人工去標(biāo)注乙嘀。想象一下,如果我們用循環(huán)神經(jīng)網(wǎng)絡(luò)處理句子破喻,那么我們可以直接把句子作為輸入虎谢。然而,如果我們用遞歸神經(jīng)網(wǎng)絡(luò)處理句子曹质,我們就必須把每個(gè)句子標(biāo)注為語法解析樹的形式婴噩,這無疑要花費(fèi)非常大的精力。很多時(shí)候羽德,相對于遞歸神經(jīng)網(wǎng)絡(luò)能夠帶來的性能提升几莽,這個(gè)投入是不太劃算的。
我們已經(jīng)基本了解了遞歸神經(jīng)網(wǎng)絡(luò)是做什么用的宅静,接下來章蚣,我們將探討它的算法細(xì)節(jié)。
遞歸神經(jīng)網(wǎng)絡(luò)的前向計(jì)算
接下來姨夹,我們詳細(xì)介紹一下遞歸神經(jīng)網(wǎng)絡(luò)是如何處理樹/圖結(jié)構(gòu)的信息的纤垂。在這里矾策,我們以處理樹型信息為例進(jìn)行介紹。
遞歸神經(jīng)網(wǎng)絡(luò)的輸入是兩個(gè)子節(jié)點(diǎn)(也可以是多個(gè))洒忧,輸出就是將這兩個(gè)子節(jié)點(diǎn)編碼后產(chǎn)生的父節(jié)點(diǎn)蝴韭,父節(jié)點(diǎn)的維度和每個(gè)子節(jié)點(diǎn)是相同的。如下圖所示:
和
分別是表示兩個(gè)子節(jié)點(diǎn)的向量熙侍,
是表示父節(jié)點(diǎn)的向量榄鉴。子節(jié)點(diǎn)和父節(jié)點(diǎn)組成一個(gè)全連接神經(jīng)網(wǎng)絡(luò),也就是子節(jié)點(diǎn)的每個(gè)神經(jīng)元都和父節(jié)點(diǎn)的每個(gè)神經(jīng)元兩兩相連蛉抓。我們用矩陣
表示這些連接上的權(quán)重庆尘,它的維度將是
,其中巷送,
表示每個(gè)節(jié)點(diǎn)的維度驶忌。父節(jié)點(diǎn)的計(jì)算公式可以寫成:
在上式中笑跛,tanh是激活函數(shù)(當(dāng)然也可以用其它的激活函數(shù))付魔,是偏置項(xiàng)飞蹂,它也是一個(gè)維度為
的向量几苍。如果讀過前面的文章,相信大家已經(jīng)非常熟悉這些計(jì)算了陈哑,在此不做過多的解釋了妻坝。
然后,我們把產(chǎn)生的父節(jié)點(diǎn)的向量和其他子節(jié)點(diǎn)的向量再次作為網(wǎng)絡(luò)的輸入惊窖,再次產(chǎn)生它們的父節(jié)點(diǎn)刽宪。如此遞歸下去,直至整棵樹處理完畢界酒。最終圣拄,我們將得到根節(jié)點(diǎn)的向量,我們可以認(rèn)為它是對整棵樹的表示毁欣,這樣我們就實(shí)現(xiàn)了把樹映射為一個(gè)向量售担。在下圖中,我們使用遞歸神經(jīng)網(wǎng)絡(luò)處理一棵樹署辉,最終得到的向量,就是對整棵樹的表示:
舉個(gè)例子岩四,我們使用遞歸神將網(wǎng)絡(luò)將『兩個(gè)外語學(xué)校的學(xué)生』映射為一個(gè)向量哭尝,如下圖所示:
最后得到的向量就是對整個(gè)句子『兩個(gè)外語學(xué)校的學(xué)生』的表示。由于整個(gè)結(jié)構(gòu)是遞歸的剖煌,不僅僅是根節(jié)點(diǎn)材鹦,事實(shí)上每個(gè)節(jié)點(diǎn)都是以其為根的子樹的表示逝淹。比如,在左邊的這棵樹中桶唐,向量
是短語『外語學(xué)院的學(xué)生』的表示栅葡,而向量
是短語『外語學(xué)院的』的表示。
式1就是遞歸神經(jīng)網(wǎng)絡(luò)的前向計(jì)算算法尤泽。它和全連接神經(jīng)網(wǎng)絡(luò)的計(jì)算沒有什么區(qū)別欣簇,只是在輸入的過程中需要根據(jù)輸入的樹結(jié)構(gòu)依次輸入每個(gè)子節(jié)點(diǎn)。
需要特別注意的是坯约,遞歸神經(jīng)網(wǎng)絡(luò)的權(quán)重和偏置項(xiàng)
在所有的節(jié)點(diǎn)都是共享的。
遞歸神經(jīng)網(wǎng)絡(luò)的訓(xùn)練
遞歸神經(jīng)網(wǎng)絡(luò)的訓(xùn)練算法和循環(huán)神經(jīng)網(wǎng)絡(luò)類似闹丐,兩者不同之處在于横殴,前者需要將殘差從根節(jié)點(diǎn)反向傳播到各個(gè)子節(jié)點(diǎn),而后者是將殘差
從當(dāng)前時(shí)刻
反向傳播到初始時(shí)刻
卿拴。
下面衫仑,我們介紹適用于遞歸神經(jīng)網(wǎng)絡(luò)的訓(xùn)練算法,也就是BPTS算法堕花。
誤差項(xiàng)的傳遞
首先文狱,我們先推導(dǎo)將誤差從父節(jié)點(diǎn)傳遞到子節(jié)點(diǎn)的公式,如下圖:
定義為誤差函數(shù)E相對于父節(jié)點(diǎn)
的加權(quán)輸入
的導(dǎo)數(shù)航徙,即:
設(shè)是父節(jié)點(diǎn)的加權(quán)輸入如贷,則
在上述式子里到踏,杠袱、
、
都是向量窝稿,而
是矩陣楣富。為了看清楚它們的關(guān)系,我們將其展開:
在上面的公式中伴榔,表示父節(jié)點(diǎn)p的第i個(gè)分量纹蝴;
表示
子節(jié)點(diǎn)的第i個(gè)分量;
表示
子節(jié)點(diǎn)的第i個(gè)分量踪少;
表示子節(jié)點(diǎn)
的第k個(gè)分量到父節(jié)點(diǎn)p的第i個(gè)分量的的權(quán)重塘安。根據(jù)上面展開后的矩陣乘法形式,我們不難看出援奢,對于子節(jié)點(diǎn)
來說兼犯,它會(huì)影響父節(jié)點(diǎn)所有的分量。因此,我們求誤差函數(shù)E對
的導(dǎo)數(shù)時(shí)切黔,必須用到全導(dǎo)數(shù)公式砸脊,也就是:
有了上式,我們就可以把它表示為矩陣形式纬霞,從而得到一個(gè)向量化表達(dá):
其中凌埂,矩陣是從矩陣W中提取部分元素組成的矩陣。其單元為:
上式看上去可能會(huì)讓人暈菜诗芜,從下圖瞳抓,我們可以直觀的看到到底是啥。首先我們把W矩陣拆分為兩個(gè)矩陣
和
绢陌,如下圖所示:
顯然挨下,子矩陣和
分別對應(yīng)子節(jié)點(diǎn)
和
的到父節(jié)點(diǎn)
權(quán)重。則矩陣
為:
也就是說脐湾,將誤差項(xiàng)反向傳遞到相應(yīng)子節(jié)點(diǎn)的矩陣
就是其對應(yīng)權(quán)重矩陣
的轉(zhuǎn)置臭笆。
現(xiàn)在,我們設(shè)是子節(jié)點(diǎn)
的加權(quán)輸入秤掌,
是子節(jié)點(diǎn)c的激活函數(shù)愁铺,則:
這樣,我們得到:
如果我們將不同子節(jié)點(diǎn)對應(yīng)的誤差項(xiàng)
連接成一個(gè)向量
闻鉴。那么茵乱,上式可以寫成:
式2就是將誤差項(xiàng)從父節(jié)點(diǎn)傳遞到其子節(jié)點(diǎn)的公式。注意孟岛,上式中的也是將兩個(gè)子節(jié)點(diǎn)的加權(quán)輸入
和
連在一起的向量瓶竭。
有了傳遞一層的公式,我們就不難寫出逐層傳遞的公式渠羞。
上圖是在樹型結(jié)構(gòu)中反向傳遞誤差項(xiàng)的全景圖斤贰,反復(fù)應(yīng)用式2,在已知的情況下次询,我們不難算出
為:
在上面的公式中荧恍,,
表示取向量
屬于節(jié)點(diǎn)p的部分屯吊。
權(quán)重梯度的計(jì)算
根據(jù)加權(quán)輸入的計(jì)算公式:
其中鸭津,表示第l層的父節(jié)點(diǎn)的加權(quán)輸入黄锤,
表示第l層的子節(jié)點(diǎn)结执。
是權(quán)重矩陣,
是偏置項(xiàng)淮腾。將其展開可得:
那么糟需,我們可以求得誤差函數(shù)在第l層對權(quán)重的梯度為:
上式是針對一個(gè)權(quán)重項(xiàng)的公式,現(xiàn)在需要把它擴(kuò)展為對所有的權(quán)重項(xiàng)的公式谷朝。我們可以把上式寫成矩陣的形式(在下面的公式中,m=2n):
式3就是第l層權(quán)重項(xiàng)的梯度計(jì)算公式武花。我們知道圆凰,由于權(quán)重是在所有層共享的,所以和循環(huán)神經(jīng)網(wǎng)絡(luò)一樣体箕,遞歸神經(jīng)網(wǎng)絡(luò)的最終的權(quán)重梯度是各個(gè)層權(quán)重梯度之和专钉。即:
因?yàn)?strong>循環(huán)神經(jīng)網(wǎng)絡(luò)的證明過程已經(jīng)在零基礎(chǔ)入門深度學(xué)習(xí)(4) - 卷積神經(jīng)網(wǎng)絡(luò)一文中給出,因此累铅,遞歸神經(jīng)網(wǎng)絡(luò)『為什么最終梯度是各層梯度之和』的證明就留給讀者自行完成啦跃须。
接下來,我們求偏置項(xiàng)的梯度計(jì)算公式菇民。先計(jì)算誤差函數(shù)對第l層偏置項(xiàng)
的梯度:
把上式擴(kuò)展為矩陣的形式:
式5是第l層偏置項(xiàng)的梯度第练,那么最終的偏置項(xiàng)梯度是各個(gè)層偏置項(xiàng)梯度之和,即:
權(quán)重更新
如果使用梯度下降優(yōu)化算法,那么權(quán)重更新公式為:
其中勋眯,是學(xué)習(xí)速率常數(shù)婴梧。把式4帶入到上式,即可完成權(quán)重的更新客蹋。同理浮还,偏置項(xiàng)的更新公式為:
把式6帶入到上式,即可完成偏置項(xiàng)的更新。
這就是遞歸神經(jīng)網(wǎng)絡(luò)的訓(xùn)練算法BPTS华糖。由于我們有了前面幾篇文章的基礎(chǔ)十办,相信讀者們理解BPTS算法也會(huì)比較容易再扭。
遞歸神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)
現(xiàn)在罢荡,我們實(shí)現(xiàn)一個(gè)處理樹型結(jié)構(gòu)的遞歸神經(jīng)網(wǎng)絡(luò)。
在文件的開頭昂羡,加入如下代碼:
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import numpy as np
from cnn import IdentityActivator
上述四行代碼非常簡單,沒有什么需要解釋的潜支。IdentityActivator激活函數(shù)是在我們介紹卷積神經(jīng)網(wǎng)絡(luò)時(shí)寫的貌笨,現(xiàn)在引用一下它遭商。
我們首先定義一個(gè)樹節(jié)點(diǎn)結(jié)構(gòu)劫流,這樣,我們就可以用它保存卷積神經(jīng)網(wǎng)絡(luò)生成的整棵樹:
class TreeNode(object):
def __init__(self, data, children=[], children_data=[]):
self.parent = None
self.children = children
self.children_data = children_data
self.data = data
for child in children:
child.parent = self
接下來,我們把遞歸神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)代碼都放在RecursiveLayer類中,下面是這個(gè)類的構(gòu)造函數(shù):
# 遞歸神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)
class RecursiveLayer(object):
def __init__(self, node_width, child_count,
activator, learning_rate):
'''
遞歸神經(jīng)網(wǎng)絡(luò)構(gòu)造函數(shù)
node_width: 表示每個(gè)節(jié)點(diǎn)的向量的維度
child_count: 每個(gè)父節(jié)點(diǎn)有幾個(gè)子節(jié)點(diǎn)
activator: 激活函數(shù)對象
learning_rate: 梯度下降算法學(xué)習(xí)率
'''
self.node_width = node_width
self.child_count = child_count
self.activator = activator
self.learning_rate = learning_rate
# 權(quán)重?cái)?shù)組W
self.W = np.random.uniform(-1e-4, 1e-4,
(node_width, node_width * child_count))
# 偏置項(xiàng)b
self.b = np.zeros((node_width, 1))
# 遞歸神經(jīng)網(wǎng)絡(luò)生成的樹的根節(jié)點(diǎn)
self.root = None
下面是前向計(jì)算的實(shí)現(xiàn):
def forward(self, *children):
'''
前向計(jì)算
'''
children_data = self.concatenate(children)
parent_data = self.activator.forward(
np.dot(self.W, children_data) + self.b
)
self.root = TreeNode(parent_data, children
, children_data)
forward函數(shù)接收一系列的樹節(jié)點(diǎn)對象作為輸入康栈,然后贰逾,遞歸神經(jīng)網(wǎng)絡(luò)將這些樹節(jié)點(diǎn)作為子節(jié)點(diǎn)氯迂,并計(jì)算它們的父節(jié)點(diǎn)。最后言缤,將計(jì)算的父節(jié)點(diǎn)保存在self.root變量中嚼蚀。
上面用到的concatenate函數(shù),是將各個(gè)子節(jié)點(diǎn)中的數(shù)據(jù)拼接成一個(gè)長向量管挟,其代碼如下:
def concatenate(self, tree_nodes):
'''
將各個(gè)樹節(jié)點(diǎn)中的數(shù)據(jù)拼接成一個(gè)長向量
'''
concat = np.zeros((0,1))
for node in tree_nodes:
concat = np.concatenate((concat, node.data))
return concat
下面是反向傳播算法BPTS的實(shí)現(xiàn):
def backward(self, parent_delta):
'''
BPTS反向傳播算法
'''
self.calc_delta(parent_delta, self.root)
self.W_grad, self.b_grad = self.calc_gradient(self.root)
def calc_delta(self, parent_delta, parent):
'''
計(jì)算每個(gè)節(jié)點(diǎn)的delta
'''
parent.delta = parent_delta
if parent.children:
# 根據(jù)式2計(jì)算每個(gè)子節(jié)點(diǎn)的delta
children_delta = np.dot(self.W.T, parent_delta) * (
self.activator.backward(parent.children_data)
)
# slices = [(子節(jié)點(diǎn)編號(hào)轿曙,子節(jié)點(diǎn)delta起始位置,子節(jié)點(diǎn)delta結(jié)束位置)]
slices = [(i, i * self.node_width,
(i + 1) * self.node_width)
for i in range(self.child_count)]
# 針對每個(gè)子節(jié)點(diǎn)哮独,遞歸調(diào)用calc_delta函數(shù)
for s in slices:
self.calc_delta(children_delta[s[1]:s[2]],
parent.children[s[0]])
def calc_gradient(self, parent):
'''
計(jì)算每個(gè)節(jié)點(diǎn)權(quán)重的梯度拳芙,并將它們求和,得到最終的梯度
'''
W_grad = np.zeros((self.node_width,
self.node_width * self.child_count))
b_grad = np.zeros((self.node_width, 1))
if not parent.children:
return W_grad, b_grad
parent.W_grad = np.dot(parent.delta, parent.children_data.T)
parent.b_grad = parent.delta
W_grad += parent.W_grad
b_grad += parent.b_grad
for child in parent.children:
W, b = self.calc_gradient(child)
W_grad += W
b_grad += b
return W_grad, b_grad
在上述算法中皮璧,calc_delta函數(shù)和calc_gradient函數(shù)分別計(jì)算各個(gè)節(jié)點(diǎn)的誤差項(xiàng)以及最終的梯度舟扎。它們都采用遞歸算法,先序遍歷整個(gè)樹悴务,并逐一完成每個(gè)節(jié)點(diǎn)的計(jì)算睹限。
下面是梯度下降算法的實(shí)現(xiàn)(沒有weight decay),這個(gè)非常簡單:
def update(self):
'''
使用SGD算法更新權(quán)重
'''
self.W -= self.learning_rate * self.W_grad
self.b -= self.learning_rate * self.b_grad
以上就是遞歸神經(jīng)網(wǎng)絡(luò)的實(shí)現(xiàn)讯檐,總共100行左右羡疗,和上一篇文章的LSTM相比簡單多了。
最后别洪,我們用梯度檢查來驗(yàn)證程序的正確性:
def gradient_check():
'''
梯度檢查
'''
# 設(shè)計(jì)一個(gè)誤差函數(shù)叨恨,取所有節(jié)點(diǎn)輸出項(xiàng)之和
error_function = lambda o: o.sum()
rnn = RecursiveLayer(2, 2, IdentityActivator(), 1e-3)
# 計(jì)算forward值
x, d = data_set()
rnn.forward(x[0], x[1])
rnn.forward(rnn.root, x[2])
# 求取sensitivity map
sensitivity_array = np.ones((rnn.node_width, 1),
dtype=np.float64)
# 計(jì)算梯度
rnn.backward(sensitivity_array)
# 檢查梯度
epsilon = 10e-4
for i in range(rnn.W.shape[0]):
for j in range(rnn.W.shape[1]):
rnn.W[i,j] += epsilon
rnn.reset_state()
rnn.forward(x[0], x[1])
rnn.forward(rnn.root, x[2])
err1 = error_function(rnn.root.data)
rnn.W[i,j] -= 2*epsilon
rnn.reset_state()
rnn.forward(x[0], x[1])
rnn.forward(rnn.root, x[2])
err2 = error_function(rnn.root.data)
expect_grad = (err1 - err2) / (2 * epsilon)
rnn.W[i,j] += epsilon
print 'weights(%d,%d): expected - actural %.4e - %.4e' % (
i, j, expect_grad, rnn.W_grad[i,j])
return rnn
下面是梯度檢查的結(jié)果,完全正確挖垛,OH YEAH痒钝!
遞歸神經(jīng)網(wǎng)絡(luò)的應(yīng)用
自然語言和自然場景解析
在自然語言處理任務(wù)中秉颗,如果我們能夠?qū)崿F(xiàn)一個(gè)解析器,將自然語言解析為語法樹送矩,那么毫無疑問蚕甥,這將大大提升我們對自然語言的處理能力。解析器如下所示:
可以看出栋荸,遞歸神經(jīng)網(wǎng)絡(luò)能夠完成句子的語法分析菇怀,并產(chǎn)生一個(gè)語法解析樹。
除了自然語言之外晌块,自然場景也具有可組合的性質(zhì)爱沟。因此,我們可以用類似的模型完成自然場景的解析匆背,如下圖所示:
兩種不同的場景钥顽,可以用相同的遞歸神經(jīng)網(wǎng)絡(luò)模型來實(shí)現(xiàn)。我們以第一個(gè)場景靠汁,自然語言解析為例。
我們希望將一句話逐字輸入到神經(jīng)網(wǎng)絡(luò)中闽铐,然后蝶怔,神經(jīng)網(wǎng)絡(luò)返回一個(gè)解析好的樹。為了做到這一點(diǎn)兄墅,我們需要給神經(jīng)網(wǎng)絡(luò)再加上一層踢星,負(fù)責(zé)打分。分?jǐn)?shù)越高隙咸,說明兩個(gè)子節(jié)點(diǎn)結(jié)合更加緊密沐悦,分?jǐn)?shù)越低,說明兩個(gè)子節(jié)點(diǎn)結(jié)合更松散五督。如下圖所示:
一旦這個(gè)打分函數(shù)訓(xùn)練好了(也就是矩陣U的各項(xiàng)值變?yōu)楹线m的值)藏否,我們就可以利用貪心算法來實(shí)現(xiàn)句子的解析。第一步充包,我們先將詞按照順序兩兩輸入神經(jīng)網(wǎng)絡(luò)副签,得到第一組打分:
我們發(fā)現(xiàn),現(xiàn)在分?jǐn)?shù)最高的是第一組基矮,The cat淆储,說明它們的結(jié)合是最緊密的。這樣家浇,我們可以先將它們組合為一個(gè)節(jié)點(diǎn)本砰。然后,再次兩兩計(jì)算相鄰子節(jié)點(diǎn)的打分:
現(xiàn)在钢悲,分?jǐn)?shù)最高的是最后一組点额,the mat舔株。于是,我們將它們組合為一個(gè)節(jié)點(diǎn)咖楣,再兩兩計(jì)算相鄰節(jié)點(diǎn)的打分:
這時(shí)督笆,我們發(fā)現(xiàn)最高的分?jǐn)?shù)是on the mat,把它們組合為一個(gè)節(jié)點(diǎn)诱贿,繼續(xù)兩兩計(jì)算相鄰節(jié)點(diǎn)的打分......最終娃肿,我們就能夠得到整個(gè)解析樹:
現(xiàn)在,我們困惑這樣牛逼的打分函數(shù)score是怎樣訓(xùn)練出來的呢珠十?我們需要定義一個(gè)目標(biāo)函數(shù)料扰。這里,我們使用Max-Margin目標(biāo)函數(shù)焙蹭。它的定義如下:
在上式中晒杈,、
分別表示第i個(gè)訓(xùn)練樣本的輸入和標(biāo)簽孔厉,注意這里的標(biāo)簽
是一棵解析樹拯钻。
就是打分函數(shù)s對第i個(gè)訓(xùn)練樣本的打分。因?yàn)橛?xùn)練樣本的標(biāo)簽肯定是正確的撰豺,我們希望s對它的打分越高越好粪般,也就是
越大越好。
是所有可能的解析樹的集合污桦,而
則是對某個(gè)可能的解析樹
的打分亩歹。
是對錯(cuò)誤的懲罰。也就是說凡橱,如果某個(gè)解析樹
和標(biāo)簽
是一樣的小作,那么
為0,如果網(wǎng)絡(luò)的輸出錯(cuò)的越離譜稼钩,那么懲罰項(xiàng)
的值就越高顾稀。
表示所有樹里面最高得分。在這里变抽,懲罰項(xiàng)相當(dāng)于Margin础拨,也就是我們雖然希望打分函數(shù)s對正確的樹打分比對錯(cuò)誤的樹打分高,但也不要高過Margin的值绍载。我們優(yōu)化
诡宗,使目標(biāo)函數(shù)取最小值,即:
下面是懲罰函數(shù)的定義:
上式中击儡,N(y)是樹y節(jié)點(diǎn)的集合塔沃;subTree(d)是以d為節(jié)點(diǎn)的子樹。上式的含義是阳谍,如果以d為節(jié)點(diǎn)的子樹沒有出現(xiàn)在標(biāo)簽中蛀柴,那么函數(shù)值+1螃概。最終,懲罰函數(shù)的值鸽疾,是樹y中沒有出現(xiàn)在樹
中的子樹的個(gè)數(shù)吊洼,再乘上一個(gè)系數(shù)k。其實(shí)也就是關(guān)于兩棵樹差異的一個(gè)度量制肮。
是對一個(gè)樣本最終的打分冒窍,它是對樹y每個(gè)節(jié)點(diǎn)打分的總和。
具體細(xì)節(jié)豺鼻,讀者可以查閱『參考資料3』的論文综液。
小結(jié)
我們在系列文章中已經(jīng)介紹的全連接神經(jīng)網(wǎng)絡(luò)、卷積神經(jīng)網(wǎng)絡(luò)儒飒、循環(huán)神經(jīng)網(wǎng)絡(luò)和遞歸神經(jīng)網(wǎng)絡(luò)谬莹,在訓(xùn)練時(shí)都使用了監(jiān)督學(xué)習(xí)(Supervised Learning)作為訓(xùn)練方法。在監(jiān)督學(xué)習(xí)中桩了,每個(gè)訓(xùn)練樣本既包括輸入特征附帽,也包括標(biāo)記
,即樣本
井誉。然而士葫,很多情況下,我們無法獲得形如
的樣本送悔,這時(shí),我們就不能采用監(jiān)督學(xué)習(xí)的方法爪模。在接下來的幾篇文章中欠啤,我們重點(diǎn)介紹另外一種學(xué)習(xí)方法:增強(qiáng)學(xué)習(xí)(Reinforcement Learning)。在了解增強(qiáng)學(xué)習(xí)的主要算法之后屋灌,我們還將介紹著名的圍棋軟件AlphaGo洁段,它是一個(gè)把監(jiān)督學(xué)習(xí)和增強(qiáng)學(xué)習(xí)進(jìn)行完美結(jié)合的案例。
參考資料
- CS224d: Deep Learning for Natural Language Processing
- Learning Task-Dependent Distributed Representations by Back Propagation Through Structure
- Parsing Natural Scenes and Natural Language with Recursive Neural Networks
相關(guān)文章
零基礎(chǔ)入門深度學(xué)習(xí)(1) - 感知器
零基礎(chǔ)入門深度學(xué)習(xí)(2) - 線性單元和梯度下降
零基礎(chǔ)入門深度學(xué)習(xí)(3) - 神經(jīng)網(wǎng)絡(luò)和反向傳播算法
零基礎(chǔ)入門深度學(xué)習(xí)(4) - 卷積神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(5) - 循環(huán)神經(jīng)網(wǎng)絡(luò)
零基礎(chǔ)入門深度學(xué)習(xí)(6) - 長短時(shí)記憶網(wǎng)絡(luò)(LSTM)