自然語言處理領域的核心:序列學習
生活中的所有事物都是與時間相關的,也就形成了一個序列。為了對序列數(shù)據(jù)(文本、演講、視頻等)我們可以使用神經(jīng)網(wǎng)絡并導入整個序列,但是這樣我們的數(shù)據(jù)輸入尺寸是固定的,局限性就很明顯。如果重要的時序特征事件恰好落在輸入窗以外,就會產(chǎn)生更大的問題。所以我們需要的是:
-
能對任意長度序列做逐個元素讀取的神經(jīng)網(wǎng)絡(比如視頻就是一系列的圖片;我們每次給神經(jīng)網(wǎng)絡一張圖);
-
有記憶的神經(jīng)網(wǎng)絡,能夠記得若干個時間步以前的事件、這些問題和需求已經(jīng)催生出多中不同的循環(huán)神經(jīng)網(wǎng)絡。
圖1:長短期記憶(LSTM)單元。LSTM有四個輸入權重和四個循環(huán)權重。Peepholes是記憶細胞和門之間的額外連接,但他們對性能提升幫助不到,所以常被忽略。
循環(huán)神經(jīng)網(wǎng)絡
若我們想讓一個常規(guī)的神經(jīng)網(wǎng)絡解決兩個數(shù)相加的問題,那我們只需要輸入兩個數(shù)字,再訓練兩數(shù)之和的預測即可。如果現(xiàn)在有3個數(shù)要相加,那么我們可以:
-
拓展網(wǎng)絡架構,添加輸入和權重,再重新訓練;
-
把第一次的輸出(即兩數(shù)之和)和第三個數(shù)作為輸入,再返回給網(wǎng)絡。
方案(2)顯然更好,因為我們希望避免重新訓練整個網(wǎng)絡(網(wǎng)絡已經(jīng)“知道”如何將兩個數(shù)相加)。如果我們的任務變成:先對兩數(shù)做加法,再減去兩個不同的數(shù),那這個方案又不好使了。即使我們使用額外的權重,也不能保證正確的輸出。相反,我們可以嘗試“修改程序”,把網(wǎng)絡由“加法”變成“減法”。通過隱藏層的加權可以實現(xiàn)這一步(見圖2),如此便讓網(wǎng)絡的內核隨著每個新的輸入而變化。網(wǎng)絡將學習著在相加兩個數(shù)之后,把程序從“加法”變成“減法”,然后就解決了問題。
我們甚至可以泛化這一方法,傳遞給網(wǎng)絡兩個數(shù)字,再傳入一個“特殊”的數(shù)字——代表著數(shù)學運算“加法”,“減法”或“乘法”。實踐當中這樣或許不盡完美,但也能得到大體正確的結果了。不過這里的主要問題倒不在于得到正確結果,而是我們可以訓練循環(huán)神經(jīng)網(wǎng)絡,使之能夠學習任意輸入序列所產(chǎn)生的特殊輸出,這就威力大了。
例如,我們可以教網(wǎng)絡學會詞語的序列。Soumith Chintala和Wojciech Zaremba寫了一篇優(yōu)秀的博客講述用RNN做自然語言處理。RNN也可以用于生成序列。Andrej Karpathy寫了這篇[有趣而生動的博客],展示了字詞級別的RNN,可以模仿各種文風,從莎士比亞,到Linux源碼,再到給小孩兒起名。
長短期記憶(Long Short Term Memory, LSTM)
長短期記憶單元使用自連接的線性單元,權重為常數(shù)1.0。這使得流入自循環(huán)的值(前向傳播)或梯度(反向傳播)可以保持不變(乘以1.0的輸入或誤差還是原來的值;前一時間步的輸出或誤差也和下一時間步的輸出相同),因而所有的值和梯度都可以在需要的時候準確回調。這個自循環(huán)的單元,記憶細胞,提供了一種可以儲存信息的記憶功能,對之前的若干個時間步當中有效。這對很多任務都極其有效,比如文本數(shù)據(jù),LSTM可以存儲前一段的信息,并對當前段落的序列應用這些信息。
另外,深度網(wǎng)絡中一個很普遍的問題叫作“梯度消失”問題,也即,梯度隨著層數(shù)增多而越來越小。有了LSTM中的記憶細胞,就有了連續(xù)的梯度流(誤差保持原值),從而消除了梯度消失問題,能夠學習幾百個時間步那么長的序列。
然而有時我們會想要拋掉舊有信息,替換以更新、更相關的信息。同時我們又不想釋放無效信息干擾其余部分的網(wǎng)絡。為了解決這個問題,LSTM單元擁有一個遺忘門,在不對網(wǎng)絡釋放信息的情況下刪除自循環(huán)單元內的信息(見圖1)。遺忘門將記憶細胞里的值乘以0~1之間的數(shù)字,其中0表示遺忘,1表示保持原樣。具體的數(shù)值宥當前輸入和上一時間步的LSTM單元輸出決定。
在其他時間,記憶細胞還需要保持多個時間步內不變,為此LSTM增加了另一道門,輸入門(或寫入門)。當輸入門關閉時,新信息就不會流入,原有信息得到保護。
另一個門將記憶細胞的輸出值乘以0(抹除輸出)~1()之間的數(shù),當多個記憶相互競爭時這很有用:一個記憶細胞可能說:“我的記憶非常重要!所以我現(xiàn)在就要釋放”,但是網(wǎng)絡卻可能說:“你的記憶是很重要,不過現(xiàn)在又其他更重要的記憶細胞,所以我給你的輸出門賦予一個微小的數(shù)值,給其他門大數(shù)值,這樣他們會勝出”。
LSTM單元的連接方式初看可能有些復雜,你需要一些時間去理解。但是當你分別考察各個部件的時候,會發(fā)現(xiàn)其結構其實跟普通的循環(huán)神經(jīng)網(wǎng)絡沒啥兩樣——輸入和循環(huán)權重流向所有的門,連接到自循環(huán)記憶細胞。
想要更深入地了解LSTM并認識整個架構,我推薦閱讀:LSTM: A Search Space Odyssey和original LSTM paper。
詞嵌入(Word Embedding)
圖3:菜譜的二維詞嵌入空間,這里我們局部放大了“南歐”的聚類群
想象"cat"和其他所有與"cat"相關聯(lián)的詞匯,你可能會想到"kitten","feline"。再想一些不那么相似,但是又比"car"要相似得多的,比如"lion","tiger","dog","animal"或者動詞"purring","mewing","sleeping"等等。
再想象一個三維的空間,我們把詞"cat"放在正中間。上面提到的詞語當中,與"cat"相似的,空間位置也離得更近;比如"kitty","feline"就離中央很近;"tiger"和"lion"就稍微遠一點;"dog"再遠一點;而"car"就不知遠到哪里去了??梢钥磮D3這個詞嵌入二維空間的例子。
如果我們我們用向量來代表空間里的每一個詞,那么每個向量就由3個坐標構成,比如"cat"是(0, 0, 0),"kitty"可能是(0,1, 0,2, -0,3)而"car"則是(10, 0, -15)。這個向量空間,就是詞嵌入空間,每個詞對應的三個坐標可以用做算法的輸入數(shù)據(jù)。
典型的詞嵌入空間含有上千個詞和上百個維度,人類是很難直觀理解的,但是相似的詞距離近這個規(guī)律仍然成立。對于機器來說,這是一種很好的詞匯表征,可以提高自然語言處理能力。
如果你想要學習更多詞嵌入的內容,以及如何應用于創(chuàng)建模型“理解”語言,推薦閱讀:Understanding Natural Language with Deep Neural Networks Using Torch,作者:Soumith Chintala和Wojciech Zaremba。
編碼-解碼
讓我們暫時停下自然語言處理,來想象一個西紅柿,想象那些適合西紅柿的配料或菜肴。如果你的想法和那些網(wǎng)上最常見的菜譜差不多,那你想到的可能是諸如奶酪和薩拉米;帕爾馬干酪、羅勒、通心粉;或其他配料比如橄欖油、百里香和西芹等等。(換作中國人來想,肯定是雞蛋)。這些配料主要都是意大利、地中海菜系。
還是那個西紅柿,如果要吃墨西哥菜系,你想到的可能是豆子、玉米、辣椒、芫荽葉或鱷梨。
你剛才所想的,就是把詞匯“西紅柿”的表征變換成了新的表征:“墨西哥菜里的西紅柿”。
“編碼”(Encoder)做的是同樣的事,它通過變換詞匯的表征,把輸入詞匯逐個變換為新的“思維向量”。就像給“西紅柿”加入了上下文“墨西哥菜”,這是“編碼-解碼”架構的第一步。
編碼-解碼架構的第二步是基于這樣一個事實:不同的語種在詞嵌入空間里,具有相似的幾何結構,即便對同一個事物,描述用詞完全不同。比如在德語里“貓”是"Katze",狗是"Hund",與英語截然不同,但是兩個詞之間的關系確實一樣。Karze與Hund的關系,跟Car與Dog的關系完全一致,換言之,即使詞匯本身不同,他們背后的“思維向量”確實一樣的。當然也有些詞匯很難用其他語言表達(比如中文里的“緣分”之類),但是這種情況比較稀罕,總體上是成立的。
基于以上思想,我們就可以構建解碼網(wǎng)絡了。我們把英語編碼器產(chǎn)生的“思維向量”傳遞給德語解碼器。德語解碼器會把這些思維向量或關系變換映射到德語詞嵌入空間里,然后就會產(chǎn)生一句話,保持英語句子里的關系。如此我們就有了一個能做翻譯的網(wǎng)絡,這個思想目前仍在發(fā)展,結果雖然不完美,但卻在極快提高,不久就會成為翻譯的最佳方法。