吳恩達深度學習筆記(五)

自然語言處理NLP:序列模型

循環神經網絡RNN

爲什麼選擇序列模型?
循環神經網絡(RNN)之類的模型在語音識別、自然語言處理和其他領域中引起變革。序列模型能夠應用在許多領域,例如:語音識別、音樂生成器、情感分類、DNA序列分析、機器翻譯、視頻動作識別、命名實體識別等。

在進行語音識別時,給定了一個輸入音頻片段xx,並要求輸出對應的文字記錄yy. 這個例子裏輸入和輸出數據都是序列模型,因爲xx是一個按時播放的音頻片段,輸出yy是一系列單詞。

音樂生成問題是使用序列數據的另一個例子,在這個例子中,只有輸出數據yy是序列,而輸入數據可以是空集,也可以是個單一的整數,這個數可能指代想要生成的音樂風格,也可能是想要生成的那首曲子的頭幾個音符。

在處理情感分類時,輸入數據xx是序列,會得到類似這樣的輸入:“There is nothing to like in this movie.”,這句評論對應幾星?

系列模型在DNA序列分析中也十分有用,DNA可以用A、C、G、T四個字母來表示。所以給定一段DNA序列,標記出哪部分是匹配某種蛋白質。

在機器翻譯過程中,得到這樣的輸入句:“Voulez-vou chante avecmoi?”,然後輸出另一種語言的翻譯結果。

在進行視頻行爲識別時,可能會得到一系列視頻幀,然後識別其中的行爲。

在進行命名實體識別時,可能會給定一個句子要識別出句中的人名。

所以這些問題都可以被稱作使用標籤數據(x,y)(x,y)作爲訓練集的監督學習。但從這一系列例子中可以看出序列問題有很多不同類型。有些問題裏,輸入數據xx和輸出數據yy都是序列,xxyy有時也會不一樣長。在另一些問題裏,只有xx或者只有yy是序列。

數學符號
下面以命名實體識別爲例,介紹序列模型的命名規則。建立一個能夠自動識別句中人名位置的序列模型,它的輸入語句是這樣的:“Harry Potter and Herminoe Granger invented a new spell.”,這就是一個命名實體識別問題,這常用於搜索引擎,比如說索引過去24小時內所有新聞報道提及的人名。命名實體識別系統可以用來查找不同類型的文本中的人名、公司名、時間、地點、國家名和貨幣名等等。

現在給定這樣的輸入數據xx,假如想要一個序列模型輸出yy,使得輸入的每個單詞都對應一個輸出值,同時這個能夠表明輸入的單詞是否是人名的一部分。y[110110000]\begin{matrix} y:[1& 1& 0& 1& 1& 0& 0& 0& 0]\\ \end{matrix}

技術上來說這也許不是最好的輸出形式,還有更加複雜的輸出形式,它不僅能夠表明輸入詞是否是人名的一部分,還能夠知道這個人名在這個句子裏從哪裏開始到哪裏結束。

這個輸入數據是9個單詞組成的序列,所以最終會有9個特徵集合來表示這9個單詞,並按序列中的位置進行索引,x<1>,x<2>,,x<9>x^{<1>},x^{<2>},\cdots,x^{<9>}來索引不同的位置,用x<t>x^{<t>}來索引這個序列的中間位置。tt意味着它們是時序序列。x[x<1>x<2>x<3>x<4>x<5>x<6>x<7>x<8>x<9>]\begin{matrix} x:[x^{<1>}& x^{<2>}& x^{<3>}& x^{<4>}& x^{<5>}& x^{<6>}& x^{<7>}& x^{<8>}& x^{<9>}]\\ \end{matrix}

輸出數據也一樣,還是用y<1>,y<2>,,y<9>y^{<1>},y^{<2>},\cdots,y^{<9>}來表示輸出數據。同時用TxT_x來表示輸入序列的長度,這個例子中輸入是9個單詞,所以Tx=9T_x=9。用TyT_y來表示輸出序列的長度。這個例子中,Tx=TyT_x=T_y.

ii個訓練樣本的序列中第tt個元素用x(i)<t>x^{(i)<t>}來表示。如果TxT_x是序列長度,那麼訓練集裏不同的訓練樣本就會有不同的長度,所以Tx(i)T_x^{(i)}就代表第ii個訓練樣本的輸入序列長度。

想要表示一個句子裏的單詞,第一件事是做一張詞表,有時也稱爲詞典,意思是列一列表示方法中用到的單詞。這個詞表中的第一個詞是a,第二個單詞是Aaron,然後更下面一些是單詞and,再後面會找到Harry,然後找到Potter,這樣一直到最後,詞典裏最後一個單詞可能是Zulu。[aaaronandharrypotterzulu]123674075683010000\left[ \begin{array}{c} a\\ aaron\\ \vdots\\ and\\ \vdots\\ harry\\ \vdots\\ potter\\ \vdots\\ zulu\\ \end{array} \right] \begin{array}{c} 1\\ 2\\ \vdots\\ 367\\ \vdots\\ 4075\\ \vdots\\ 6830\\ \vdots\\ 10000\\ \end{array}

所以在這個例子中用了10000個單詞大小的詞典,這對現代自然語言處理應用來說太小了。對於一般規模的商業應用來說30000到50000詞大小的詞典比較常見,而且有些大型互聯網公司會用百萬詞,甚至更大的詞典。

如果選定了10000詞的詞典,構建這個詞典的一個方法是遍歷訓練集,並且找到前10000個常用詞,也可以去瀏覽一些網絡詞典,獲得英語裏最常用的10000個單詞,接下來可以用one-hot表示法來表示詞典裏的每個單詞。

在這裏x<1>x^{<1>}表示Harry這個單詞,它就是一個第4075行是1,其餘值都是0的列向量。同樣x<2>x^{<2>}是個第6830行是1,其餘位置都是0的向量。and在詞典裏排第367,所以x<3>x^{<3>}就是第367行是1,其餘值都是0的向量。如果詞典大小是10000,那麼這裏的每個向量都是10000維的。

所以這種表示方法中,x<t>x^{<t>}指代句子裏的任意詞,它就是個one-hot向量,因爲它只有一個值是1,其餘值都是0,所以會有9個one-hot向量來表示這個句中的9個單詞,目的是用這樣的表示方式表示xx,用序列模型在xx和目標輸出yy之間學習建立一個映射。

如果遇到了一個不在詞表中的單詞,答案就是創建一個新的標記,也就是一個叫做Unknow Word的僞造單詞,用作爲標記,來表示不在詞表中的單詞。

循環神經網絡模型
使用標準神經網絡,我們有9個輸入單詞。這9個輸入單詞,可能是9個one-hot向量,然後將它們輸入到一個標準神經網絡中,經過一些隱藏層,最終會輸出9個值爲0或1的項,它表明每個輸入單詞是否是人名的一部分。

這個方法並不好,主要有兩個問題:

  • Inputs, outputs can be different lengths in different examples.
  • Doesn’t share features learned across different positions of text.

一是輸入和輸出數據在不同例子中可以有不同的長度,不是所有的例子都是Tx=TyT_x=T_y。即使每個句子都有最大長度,也許能夠填充(pad)或零填充(zero pad)使每個輸入語句都達到最大長度,也就是說設定一個最大序列長度,對每個輸入和輸出序列補零並統一到最大長度。但仍然看起來不是一個好的表達方式。

二是它並不共享從文本的不同位置上學到的特徵。具體來說,如果神經網絡已經學習到了在位置1出現的Harry可能是人名的一部分,那麼如果Harry出現在其他位置,比如x<t>x^{<t>}時,它也能夠自動識別其爲人名的一部分的話,這就很棒了。這可能類似於在卷積神經網絡中,希望將部分圖片裏學到的內容快速推廣到圖片的其他部分,我們希望對序列數據也有相似的效果,用一個更好的表達方式能夠減少模型中參數的數量。

x<1>,,x<t>,,x<Tx>x^{<1>},\cdots,x^{<t>},\cdots,x^{<T_x>}這些都是10000維的one-hot向量,因此這會是十分龐大的輸入層。如果總的輸入大小是最大單詞數(最大序列長度)乘以10000,那麼第一層的權重矩陣就會有着巨量的參數。

下面建立循環神經網絡RNN。如果以從左到右的順序讀一個句子,第一個單詞就是x<1>x^{<1>},將第一個詞輸入一個神經網絡層,第一個神經網絡的隱藏層,我們可以讓神經網絡嘗試預測輸出,判斷這是否是人名的一部分。當它讀到句中的第二個單詞時,假設是x<2>x^{<2>},它不是僅用x<2>x^{<2>}就預測出y^<2>\hat{y}^{<2>},它也會輸入一些來自時間步1的信息。具體而言,時間步1的激活值就會傳遞到時間步2。然後,在下一個時間步,循環神經網絡輸入了單詞x<3>x^{<3>},然後它嘗試預測輸出了預測結果y^<3>\hat{y}^{<3>},一直到最後一個時間步,輸入了x<Tx>x^{<T_x>},然後輸出了y^<Ty>\hat{y}^{<T_y>}。在這個例子中Tx=TyT_x=T_y,如果TxTyT_x\ne T_y,這個結構會需要作出一些改變。所以在每一個時間步中,循環神經網絡傳遞一個激活值到下一個時間步中用於計算。

要開始整個流程,在零時刻需要構造一個激活值a<0>a^{<0>},這通常是零向量。有些研究人員會隨機用其他方法初始化a<0>a^{<0>},不過使用零向量作爲零時刻的僞激活值是最常見的選擇,因此把它輸入神經網絡。

循環神經網絡是從左向右掃描數據,同時每個時間步的參數也是共享的,我們用WaxW_{ax}來表示管理着從x<1>x^{<1>}到隱藏層的連接的一系列參數,每個時間步使用的都是相同的參數WaxW_{ax}。而激活值也就是水平聯繫(horizontal connections),是由參數WaaW_{aa}決定的,同時每一個時間步都使用相同的參數WaaW_{aa},同樣輸出結果由參數WyaW_{ya}決定。

在這個循環神經網絡中,在預測y^<3>\hat{y}^{<3>}時,不僅要使用x<3>x^{<3>}的信息,還要使用來自x<1>x^{<1>}x<2>x^{<2>}的信息。這個循環神經網絡的一個缺點就是它只使用了這個序列中之前的信息來做出預測,尤其當預測y^<3>\hat{y}^{<3>}時,它沒有用到x<4>x^{<4>}x<5>x^{<5>}x<6>x^{<6>}等等的信息。如果給定了這個句子,He said, “Teddy Roosevelt was a great President.”,爲了判斷Teddy是否是人名的一部分,僅僅知道句中前兩個詞是完全不夠的,還需要知道句中後部分的信息,這也是十分有用的,因爲句子也可能是這樣的,He said, “Teddy bears are on sale!”。因此如果只給定前三個單詞,是不可能確切地知道Teddy是否是人名的一部分。

所以這樣特定的神經網絡結構的一個限制是它在某一時刻的預測僅使用了從序列之前的輸入信息並沒有使用序列中後部分的信息,雙向循環神經網絡(BRNN)可以處理這個問題。

一般開始先輸入a<0>a^{<0>},它是一個零向量。接着就是前向傳播過程,先計算激活值a<1>a^{<1>},然後再計算y<1>y^{<1>}
a<1>=g1(Waaa<0>+Waxx<1>+ba)y^<1>=g2(Wyaa<1>+by)\begin{aligned} a^{<1>} &= g_1(W_{aa}a^{<0>}+W_{ax}x^{<1>}+b_a) \\ \hat{y}^{<1>} &= g_2(W_{ya}a^{<1>}+b_y) \end{aligned}

對於WaxW_{ax},第二個下標xx意味着WaxW_{ax}要乘以某個xx類型的量,然後第一個下標aa表示它是用來計算某個aa類型的變量。同樣的,WyaW_{ya}乘上了某個aa類型的量,用來計算出某個y^\hat{y}類型的量。

循環神經網絡用的激活函數經常是tanh,不過有時候也會用ReLU,但是tanh是更通常的選擇,我們有其他方法來避免梯度消失問題。選用哪個激活函數是取決於輸出yy,如果它是一個二分類問題,那麼會用Sigmoid函數作爲激活函數,如果是kk類別分類問題的話,那麼可以選用Softmax作爲激活函數。

更一般的情況下,在tt時刻:a<t>=g1(Waaa<t1>+Waxx<t>+ba)y^<t>=g2(Wyaa<t>+by)\begin{aligned} a^{<t>} &= g_1(W_{aa}a^{<t-1>}+W_{ax}x^{<t>}+b_a) \\ \hat{y}^{<t>} &= g_2(W_{ya}a^{<t>}+b_y) \end{aligned}所以這些等式定義了神經網絡的前向傳播,可以從零向量a<0>a^{<0>}開始,然後用a<0>a^{<0>}x<1>x^{<1>}來計算出a<1>a^{<1>}y^<1>\hat{y}^{<1>},然後用x<2>x^{<2>}a<1>a^{<1>}一起算出a<2>a^{<2>}y^<2>\hat{y}^{<2>}等等,從左到右完成前向傳播。

寫成矩陣形式:a<t>=g1(Wa[a<t1>x<t>]+ba)y^<t>=g2(Wya<t>+by)\begin{aligned} a^{<t>} &= g_1(W_{a}\left[ \begin{array}{c} a^{<t-1>}\\ x^{<t>}\\ \end{array} \right]+b_a) \\ \hat{y}^{<t>} &= g_2(W_{y}a^{<t>}+b_y) \end{aligned}其中Wa=[WaaWax]W_a=\left[ \begin{matrix} W_{aa}& W_{ax}\\ \end{matrix} \right],視頻中[a<t1>x<t>]\left[ \begin{array}{c} a^{<t-1>}\\ x^{<t>}\\ \end{array} \right]寫爲[a<t1>,x<t>]\left[ \begin{matrix} a^{<t-1>},x^{<t>}\\ \end{matrix} \right].

通過時間的反向傳播
當在編程框架中實現循環神經網絡時,編程框架(PyTorch、TensorFlow等)通常會自動處理反向傳播。在循環神經網絡中,對反向傳播的運行有一個粗略的認識還是非常有用的。反向傳播的計算方向與前向傳播基本上是相反的。回顧前向傳播:

爲了計算反向傳播,先定義一個元素損失函數:L<t>(y^<t>,y<t>)=y<t>logy^<t>(1y<t>)log(1y^<t>)\mathcal{L}^{<t>}(\hat{y}^{<t>},y^{<t>})=-y^{<t>}log\hat{y}^{<t>}-(1-y^{<t>})log(1-\hat{y}^{<t>})

它對應的是序列中一個具體的詞,如果它是某個人的名字,那麼y<t>{y}^{<t>}的值就是1,然後神經網絡將輸出這個詞是名字的概率值,比如0.1。將它定義爲標準邏輯迴歸損失函數,也叫交叉熵損失函數(Cross Entropy Loss)。所以這是關於單個位置上或者說某個時間步tt上某個單詞的預測值的損失函數。

現在來定義整個序列的損失函數:L(y^,y)=t=1TxL<t>(y^<t>,y<t>)\mathcal{L}(\hat{y},y)=\sum_{t=1}^{T_x}{\mathcal{L}^{<t>}(\hat{y}^{<t>},y^{<t>})}

反向傳播算法需要在相反的方向上進行計算和傳遞信息,最終就是把前向傳播的箭頭都反過來,在這之後就可以計算出所有合適的量,然後就可以通過導數相關的參數,用梯度下降法來更新參數。

在這個反向傳播的過程中,最重要的信息傳遞或者說最重要的遞歸運算就是這個從右到左的運算,這個算法有一個很別緻的名字,叫做“通過(穿越)時間反向傳播(backpropagation through time)”。取這個名字的原因是對於前向傳播,需要從左到右進行計算,在這個過程中,時刻tt不斷增加。而對於反向傳播,需要從右到左進行計算,就像時間倒流。

反向傳播過程就是從右到左分別計算L(y^,y)\mathcal{L}(\hat{y},y)對參數Wa,Wy,ba,byW_a,W_y,b_a,b_y的偏導數。

RNN反向傳播示意圖(有時間手推一波)

不同類型的循環神經網絡
事實上,對於其他一些應用,TxT_xTyT_y並不一定相等。比如音樂生成這個例子,TxT_x可以是長度爲1甚至爲空集。再比如電影情感分類,輸出yy可以是1到5的整數,而輸入是一個序列。還有一些情況,輸入長度和輸出長度不同,他們都是序列但長度不同,比如機器翻譯,一個法語句子和一個英語句子不同數量的單詞卻能表達同一個意思。

所以應該修改基本的RNN結構來處理這些問題,參考Andrej Karpathy的博客,一篇叫做“The Unreasonable Effectiveness of Recurrent Neural Networks”的文章。

這個就叫做“多對多”(many-to-many)的結構,因爲輸入序列有很多的輸入,而輸出序列也有很多輸出。

處理情感分類問題,這裏xx可能是一段文本,比如一個電影的評論,“These is nothing to like in this movie.”,所以xx就是一個序列,而yy可能是從1到5的一個數字,或者是0或1,這代表正面評價和負面評價,而數字1到5代表電影是1星,2星,3星,4星還是5星。我們可以簡化神經網絡的結構,輸入x<1>x^{<1>}x<2>x^{<2>},一次輸入一個單詞,我們不再在每個時間上都有輸出了,而是讓這個RNN網絡讀入整個句子,然後在最後一個時間上得到輸出。所以這個神經網絡叫做“多對一”(many-to-one)結構,因爲它有很多輸入,很多的單詞,然後輸出一個數字。

爲了完整性,還要補充一個“一對一”(one-to-one)的結構,這個可能沒有那麼重要,這就是一個小型的標準的神經網絡,輸入xx然後得到輸出yy

除了“多對一”的結構,也可以有“一對多”(one-to-many)的結構。音樂生成,目標是使用一個神經網絡輸出一些音符。對應於一段音樂,輸入xx可以是一個整數,表示想要的音樂類型或者是想要的音樂的第一個音符,並且如果什麼都不想輸入,xx可以是空的輸入,可設爲0向量。

這樣這個神經網絡的結構,首先是輸入xx,然後得到RNN的輸出,第一個值,然後就沒有輸入了,再得到第二個輸出,接着輸出第三個值等等,一直到合成這個音樂作品的最後一個音符,這裏也可以寫上輸入a<0>a^{<0>}。當生成序列時通常會把第一個合成的輸出也餵給下一層,所以實際的網絡結構最終就像上圖這個樣子。

對於“多對多”的結構還有就是輸入和輸出長度不同的情況。而對於像機器翻譯這樣的應用,輸入句子的單詞的數量,比如說一個法語的句子,和輸出句子的單詞數量,比如翻譯成英語,這兩個句子的長度可能不同,所以還需要一個新的神經網絡結構如下圖所示。這個網絡的結構有兩個不同的部分,這是一個編碼器,獲取輸入,比如法語句子,這是解碼器,它會讀取整個句子,然後輸出翻譯成其他語言的結果。

這就是一個“多對多”結構的例子,嚴格來說,還有一種結構,就是“注意力”(attention based)結構,但是根據現在畫的這些圖不好理解這個模型。

總結一下這些各種各樣的RNN結構,第一個是“一對一"的結構,當去掉a<0>a^{<0>}時它就是一種標準類型的神經網絡。還有一種“一對多”的結構(第二個),比如音樂生成或者序列生成。還有“多對一”,這是情感分類的例子。還有“多對多”的結構,命名實體識別就是“多對多”的例子,其中Tx=TyT_x=T_y。最後還有一種“多對多”結構的其他版本,如機器翻譯,TxTyT_x\ne T_y.

現在瞭解了大部分基本的模塊,這些差不多就是所有的神經網絡了,除了序列生成。用這些RNN的基本模塊,把它們組合在一起就可以構建各種各樣的模型。

語言模型和序列生成
在自然語言處理中,構建語言模型是最基礎的也是最重要的工作之一,並且能用RNN很好地實現。

比如做一個語音識別系統,某句語音有兩種翻譯:

  • The apple and pair salad was delicious.
  • The apple and pear salad was delicious.

應該更像第二種,事實上,這就是一個好的語音識別系統要幫助輸出的東西,即使這兩句話聽起來是如此相似。而讓語音識別系統去選擇第二個句子的方法就是使用一個語言模型,利用語言模型得到各自語句的概率,選擇概率最大的語句作爲正確的翻譯。

一個語音識別模型可能算出第一句話的概率是P(Theappleandpairsalad)=3.2×1013P(The\,apple\,and\,pair\, salad)=3.2\times10^{-13}而第二句話的概率是P(Theappleandpearsalad)=5.7×1010P(The\,apple\,and\,pear\,salad)=5.7\times10^{-10}比較這兩個概率值,顯然說的話更像是第二種,因爲第二句話的概率比第一句高出1000倍以上,語音識別系統能夠在這兩句話中作出選擇。

語言模型會告訴你某個特定的句子它出現的概率是多少。概率計算表達式爲:P(y<1>,y<2>,,y<Ty>)P(y^{<1>},y^{<2>},\cdots,y^{<T_y>})語言模型是兩種系統的基本組成部分,一個是語音識別系統,還有機器翻譯系統,它要能正確輸出最接近的句子。而語言模型做的最基本工作就是輸入一個句子,準確地說是一個文本序列,y<1>y^{<1>}y<2>y^{<2>}一直到y<Ty>y^{<T_y>}。對於語言模型來說,用yy來表示這些序列比用xx來表示要更好,然後語言模型會估計某個句子序列中各個單詞出現的可能性。

爲了使用RNN建立出這樣的模型,首先需要一個訓練集,包含一個很大的英文文本語料庫(corpus)或者其它的語言,想用於構建模型的語言的語料庫。語料庫是自然語言處理的一個專有名詞,意思就是很長的或者說數量衆多的英文句子組成的文本。然後,對corpus的每句話進行切分詞(tokenize)。建立Vocabulary,對每個單詞進行one-hot編碼。

假如在訓練集中得到這麼一句話,“Cats average 15 hours of sleep a day.”,要做的第一件事就是將這個句子標記化,建立一個字典,然後將每個單詞都轉換成對應的one-hot向量,也就是字典中的索引。可能還有一件事就是要定義句子的結尾,一般的做法就是增加一個額外的標記< EOS >,它表示句子的結尾,如果想要模型能夠準確識別句子結尾的話,< EOS >標記可以被附加到訓練集中每一個句子的結尾。於是在本例中如果加了< EOS >標記,這句話就會有9個輸入,y<1>,y<2>,,y<9>y^{<1>},y^{<2>},\cdots,y^{<9>}. 在標記化的過程中,可以自行決定要不要把標點符號看成標記,在本例中,忽略了標點符號,所以只把day看成標誌,不包括後面的句號,如果想把句號或者其他符號也當作標誌,那麼可以將句號也加入字典中。

現在還有一個問題如果訓練集中有一些詞並不在字典裏,比如說字典有10000個最常用的英語單詞。現在這個句子“The Egyptian Mau is a bread of cat.”其中有一個詞Mau,它可能並不是預先的那10000個最常用的單詞,在這種情況下,可以把Mau替換成一個叫做< UNK >的代表未知詞的標誌,只針對< UNK >建立概率模型。

The Egyptian < UNK > is a bread of cat. < EOS >

完成標識化的過程後,這意味着輸入的句子都映射到了各個標誌上,或者說字典中的各個詞上。下一步要使用一個RNN來構建這些序列的概率模型。

例子:Cats average 15 hours of sleep a day. 在第0個時間步,要計算激活項a<1>a^{<1>},它是以x<1>x^{<1>}作爲輸入的函數,而x<1>x^{<1>}會被設爲全爲0的集合,也就是0向量。a<0>a^{<0>}也設爲0向量,於是a<1>a^{<1>}會通過Softmax進行一些預測來計算出第一個詞可能會是什麼,其結果就是y^<1>\hat{y}^{<1>},這一步其實是通過一個Softmax層來預測字典中的任意單詞會是第一個詞的概率,比如說第一個詞是aa的概率有多少P(a)P(a),第一個詞是Aaron的概率有多少P(aaron)P(aaron),第一個詞是cats的概率又有多少P(cats)P(cats),就這樣一直到Zulu是第一個詞的概率是多少P(zulu)P(zulu),還有第一個詞是UNK的概率有多少P(<UNK>)P(<UNK>),還有第一個詞是句子結尾標誌的概率有多少P(<EOS>)P(<EOS>),表示不必閱讀。所以y^<1>\hat{y}^{<1>}的輸出是Softmax的計算結果,它只是預測第一個詞的概率,而不去管結果是什麼。在這個例子中,最終會得到單詞Cats。所以Softmax層輸出會有10002個結果,因爲可能加上了未知詞,還有句子結尾這兩個額外的標誌。

然後RNN進入下個時間步,在下一時間步中,仍然使用激活項a<1>a^{<1>},在這步要做的是計算出第二個詞會是什麼。現在依然傳給它正確的第一個詞,告訴它第一個詞就是Cats,也就是y^<1>\hat{y}^{<1>},這裏y<1>=x<2>y^{<1>}=x^{<2>}。然後在第二個時間步中,輸出結果同樣經過Softmax層進行預測,RNN的職責就是預測這些詞的概率,而不會去管結果是什麼,它只會考慮之前得到的詞。所以在這種情況下會是average.

然後再進行RNN的下個時間步,現在要計算a<3>a^{<3>}。爲了預測第三個詞,也就是15,現在給它之前兩個詞,告訴它Cats average是句子的前兩個詞,所以這是下一個輸入,x<3>=y<2>x^{<3>}=y^{<2>},輸入average以後,現在要計算出序列中下一個詞是什麼,或者說計算出字典中每一個詞的概率,通過之前得到的Cats和average,在這種情況下,正確結果會是15,以此類推。

一直到最後,會停在第9個時間步,然後把x<9>x^{<9>}也就是y<8>y^{<8>}傳給它,也就是單詞day,這裏是a<9>a^{<9>},它會輸出y^<9>\hat{y}^{<9>},最後的得到結果會是< EOS >標誌,在這一步中,通過前面這些得到的單詞,不管它們是什麼,我們希望能預測出< EOS >句子結尾標誌的概率會很高。

所以RNN中的每一步都會考慮前面得到的單詞,比如給它前3個單詞,讓它給出下個詞的分佈,這就是RNN如何學習從左往右地每次預測一個詞。

接下來爲了訓練這個網絡,要定義代價函數。於是,在某個時間步tt,如果真正的詞是y<t>y^{<t>},而神經網絡的Softmax層預測結果值是y^<t>\hat{y}^{<t>},那麼這就是Softmax損失函數:L<t>(y^<t>,y<t>)=iyi<t>logy^i<t>\mathcal{L}^{<t>}(\hat{y}^{<t>},y^{<t>})=-\sum_i{y_i^{<t>}log\hat{y}_i^{<t>}}而總體損失函數:L=tL<t>(y^<t>,y<t>)\mathcal{L}=\sum_t{\mathcal{L}^{<t>}(\hat{y}^{<t>},y^{<t>})}也就是把所有單個預測的損失函數都相加起來。

如果用很大的訓練集來訓練這個RNN,就可以通過開頭一系列單詞來預測之後單詞的概率。即根據給出語句的前幾個單詞預測其餘部分,將語句補充完整,例如給出“Cats average 15”,RNN模型可能預測完整的語句是“Cats average 15 hours of sleep a day.”。

整個語句出現的概率等於語句中所有元素出現的條件概率乘積。現在有一個新句子,它是y<1>y^{<1>}y<2>y^{<2>}y<3>y^{<3>},它只包含3個詞,現在要計算出整個句子中各個單詞的概率,方法就是第一個Softmax層會告訴你y<1>y^{<1>}的概率,然後第二個Softmax層會告訴你在考慮y<1>y^{<1>}的情況下y<2>y^{<2>}的概率,然後第三個Softmax層告訴你在考慮y<1>y^{<1>}y<2>y^{<2>}的情況下y<3>y^{<3>}的概率,把這三個概率相乘,最後得到這個含3個詞的整個句子的概率:P(y<1>,y<2>,y<3>)=P(y<1>)P(y<2>y<1>)P(y<3>y<1>,y<2>)P(y^{<1>},y^{<2>},y^{<3>})=P(y^{<1>})P(y^{<2>}|y^{<1>})P(y^{<3>}|y^{<1>},y^{<2>})

對新序列採樣
在訓練一個序列模型之後,要想了解到這個模型學到了什麼,一種非正式的方法就是利用訓練好的RNN語言模型,進行新序列採樣,從而隨機產生新的語句。

一個序列模型模擬了任意特定單詞序列的概率P(y<1>,,y<Tx>)P(y^{<1>},\cdots,y^{<T_x>}),我們要做的就是對這些概率分佈進行採樣來生成一個新的單詞序列。

第一步是對模型生成的第一個詞進行採樣,於是輸入x<1>=0x^{<1>}=0a<0>=0a^{<0>}=0,現在第一個時間步得到的y^<1>\hat{y}^{<1>}是所有可能的輸出,是經過Softmax層後得到的概率,然後根據這個Softmax的分佈進行隨機採樣。Softmax分佈給的信息就是第一個詞是a的概率是多少,是aaron的概率是多少,是zulu的概率是多少,是UNK的概率是多少,然後對這個向量使用例如numpy命令,np.random.choice,來根據向量中這些概率的分佈進行採樣,這樣就能對第一個詞進行採樣了。

然後繼續下一個時間步,現在要做的是把剛剛採樣得到的y^<1>\hat{y}^{<1>}放到a<2>a^{<2>},作爲下一個時間步的輸入,所以不管在第一個時間步得到的是什麼詞,都要把它傳遞到下一個位置作爲輸入,然後Softmax層就會預測y^<2>\hat{y}^{<2>}是什麼。舉個例子,假如說對第一個詞進行抽樣後,得到的是The,The作爲第一個詞的情況很常見,然後把The當成x<2>x^{<2>},現在x<2>=y^<1>x^{<2>}=\hat{y}^{<1>},現在要計算出在第一詞是The的情況下,第二個詞應該是什麼P(_the)P(\_|the),然後得到的結果就是y^<2>\hat{y}^{<2>},然後再次用這個採樣函數來對y^<2>\hat{y}^{<2>}進行採樣。

然後再到下一個時間步,無論得到什麼樣的用one-hot碼錶示的選擇結果,都把它傳遞到下一個時間步,然後對第三個詞進行採樣。不管得到什麼都把它傳遞下去,一直這樣直到最後一個時間步。

如果代表句子結尾的標識在字典中,可以一直進行採樣直到得到< EOS >標識,這代表着已經抵達結尾,可以停止採樣了。另一種情況是,如果字典中沒有這個詞,可以決定從20個或100個或其他個單詞進行採樣,然後一直將採樣進行下去直到達到所設定的時間步。不過這種過程有時候會產生一些未知標識,如果要確保算法不會輸出這種標識,可以拒絕採樣過程中產生任何未知的標識,一旦出現就繼續在剩下的詞中進行重採樣,直到得到一個不是未知標識的詞。

這就是如何從RNN語言模型中生成一個隨機選擇的句子。直到現在我們所建立的是基於詞彙的RNN模型,意思就是字典中的詞都是英語單詞。

根據實際的應用,還可以構建一個基於字符的RNN結構(character level RNN),在這種情況下,字典包含從a到z的字母,可能還會有空格符,還可以有數字0到9,也可以再加上大寫的字母,還可以實際地看一看訓練集中可能會出現的字符,然後用這些字符組成字典。Vocabulary=[a,b,c,,z,.,;,,0,1,,9,A,B,,Z]Vocabulary=[a,b,c,⋯,z,.,;,\,,0,1,⋯,9,A,B,⋯,Z]

如果建立一個基於字符的語言模型,序列y^<1>\hat{y}^{<1>}y^<2>\hat{y}^{<2>}y^<3>\hat{y}^{<3>}在訓練數據中將會是單獨的字符。“Cat average 15 hours of sleep a day.”,在該例中C就是y^<1>\hat{y}^{<1>},a就是y^<2>\hat{y}^{<2>},t就是y^<3>\hat{y}^{<3>},空格符就是y^<4>\hat{y}^{<4>}等等。

使用基於字符的語言模型有優點也有缺點,優點就是不必擔心會出現未知的標識。不過基於字符的語言模型一個主要缺點就是最後會得到太多太長的序列,大多數英語句子只有10到20個的單詞,但可能包含很多字符。所以基於字符的語言模型在捕捉句子中的依賴關係也就是句子較前部分如何影響較後部分不如基於詞彙的語言模型那樣可以捕捉長範圍的關係,並且基於字符的語言模型訓練起來計算成本比較高昂。

自然語言處理的趨勢就是,絕大多數都是使用基於詞彙的語言模型,但隨着計算機性能越來越高,會有更多的應用。在一些特殊情況下,會開始使用基於字符的模型。但是這確實需要更昂貴的計算力來訓練,所以現在並沒有得到廣泛地使用,除了一些比較專門需要處理大量未知的文本或者未知詞彙的應用,還有一些要面對很多專有詞彙的應用。

在現有的方法下,現在可以構建一個RNN結構,看一看英文文本的語料庫,然後建立一個基於詞彙的或者基於字符的語言模型,然後從訓練的語言模型中進行採樣。

這裏有一些樣本,它們是從一個語言模型中採樣得到的,準確來說是基於字符的語言模型。如果模型是用新聞文章訓練的,它就會生成左邊這樣的文本,這有點像一篇不太合乎語法的新聞文本,這句“Concussion epidemic”,to be examined,確實有點像新聞報道。用莎士比亞的文章訓練後生成了右邊這篇東西。

循環神經網絡的梯度消失
基本的RNN算法還有一個很大的問題,就是梯度消失的問題。

現在舉個語言模型的例子,假如看到這個句子,“The cat, which already ate ……, was full.”,前後應該保持一致,因爲cat是單數,所以應該用was。“The cats, which ate ……, were full.”,cats是複數,所以用were。這個例子中的句子有長期的依賴,最前面的單詞對句子後面的單詞有影響。目前見到的基本的RNN模型,不擅長捕獲這種長期依賴效應。

對於訓練很深的網絡,討論過梯度消失的問題。比如說一個很深很深的網絡,對這個網絡從左到右做前向傳播然後再反向傳播。如果這是個很深的神經網絡,從輸出y^\hat{y}得到的梯度很難傳播回去,很難影響靠前層的權重和前面層的計算。

對於有同樣問題的RNN,首先從左到右前向傳播,然後反向傳播。但是反向傳播會很困難,因爲同樣的梯度消失的問題,後面層的輸出誤差(y^<Ty>\hat{y}^{<T_y>})很難影響前面層(時間步1和2)的計算。這就意味着,實際上很難讓一個神經網絡能夠意識到它要記住看到的是單數名詞還是複數名詞,然後在序列後面生成依賴單複數形式的was或者were。而且在英語句子裏面,中間的內容(上面句子省略號處)可以任意長。所以需要長時間記住單詞是單數還是複數,這樣後面的句子才能用到這些信息。

也正是這個原因,所以基本的RNN模型會有很多局部影響,意味着這個輸出y^<3>\hat{y}^{<3>}主要受y^<3>\hat{y}^{<3>}附近的值(x<1>,x<2>,x<3>x^{<1>},x^{<2>},x^{<3>})的影響,y^<Ty>\hat{y}^{<T_y>}基本上很難受到序列靠前的輸入(x<1>,x<2>x^{<1>},x^{<2>})的影響,這是因爲不管輸出是什麼,不管是對的還是錯的,這個區域都很難反向傳播到序列的前面部分,也因此網絡很難調整序列前面的計算。這是基本的RNN算法的一個缺點。

在講很深的神經網絡時,也提到了梯度爆炸,在反向傳播的時候,隨着層數的增多,梯度不僅可能指數型的下降,也可能指數型的上升。事實上梯度消失在訓練RNN時是首要的問題,儘管梯度爆炸也是會出現,但是梯度爆炸很明顯,因爲指數級大的梯度會讓參數變得極其大,以至於網絡參數崩潰。所以梯度爆炸很容易發現,因爲參數會大到崩潰,會看到很多NaN,或者不是數字的情況,這意味着網絡計算出現了數值溢出。如果發現了梯度爆炸的問題,一個解決方法就是用梯度修剪(Gradient Clipping)。梯度修剪就是觀察梯度向量,如果它大於某個閾值,縮放梯度向量(尺度縮小),保證它不會太大,這就是通過一些最大值來修剪的方法。所以如果遇到了梯度爆炸,如果導數值很大,或者出現了NaN,就用梯度修剪,這是相對比較魯棒的梯度爆炸的解決方法。

訓練很深的神經網絡時,隨着層數的增加,導數有可能指數型的下降或者指數型的增加,可能會遇到梯度消失或者梯度爆炸的問題。加入一個RNN處理1000個時間序列的數據集或者10000個時間序列的數據集,這就是一個1000層或者10000層的神經網絡,這樣的網絡就會遇到上述類型的問題。梯度爆炸基本上用梯度修剪就可以應對,但梯度消失比較棘手。

GRU單元
這節難度較大,有時間看文末的參考資料好好理解!
門控循環單元,它改變了RNN的隱藏層,使其可以更好地捕捉深層連接,並改善了梯度消失問題。

a<t>=g(Wa[a<t1>,x<t>]+ba)a^{<t>}=g(W_a[a^{<t-1>},x^{<t>}]+b_a),在RNN的時間tt處,計算激活值。輸入a<t1>a^{<t-1>},即上一個時間步的激活值,再輸入x<t>x^{<t>},再把這兩個並起來,然後乘上權重項,在這個線性計算之後,如果gg是一個tanh激活函數,再經過tanh計算之後,它會計算出激活值a<t>a^{<t>}。然後激活值將會傳Softmax單元,或者其他用於產生輸出y^<t>\hat{y}^{<t>}的東西。

GRU參考論文

The cat, which already ate……, was full.”,需要記得貓是單數的,“The cat was full.”或者是“The cats were full”。當我們從左到右讀這個句子,GRU單元將會有個新的變量稱爲cc,代表記憶細胞(memory cell),記憶細胞的作用是提供了記憶的能力,比如說一隻貓是單數還是複數,所以當它看到之後的句子的時候,它仍能夠判斷句子的主語是單數還是複數。於是在時間tt處,有記憶細胞c<t>c^{<t>},GRU實際上輸出了激活值a<t>a^{<t>}c<t>=a<t>c^{<t>}=a^{<t>}。於是想要使用不同的符號ccaa來表示記憶細胞的值和輸出的激活值,即使它們是一樣的。

所以這些等式表示了GRU單元的計算,在每個時間步,將用一個候選值c~<t>\tilde{c}^{<t>}重寫記憶細胞,替代了c<t>c^{<t>}的值。然後用tanh激活函數來計算,c~<t>=tanh(Wc[c<t1>,x<t>]+bc)\tilde{c}^{<t>}=tanh(W_c[c^{<t-1>},x^{<t>}]+b_c).

在GRU中真正重要的思想是有一個門叫做Γu\Gamma_uuu代表更新(update)門,這是一個0到1之間的值。實際上這個值是把這個式子帶入Sigmoid函數得到的,Γu=σ(Wu[c<t1>,x<t>]+bu)\Gamma_u=\sigma(W_u[c^{<t-1>},x^{<t>}]+b_u). Sigmoid函數的輸出值總是在0到1之間,對於大多數可能的輸入,Sigmoid函數的輸出總是非常接近0或者非常接近1。當Γu=1\Gamma_u=1時,代表更新;當Γu=0\Gamma_u=0時,代表記憶,保留之前的模塊輸出。這一點跟CNN中的ResNets的作用有點類似。

然後GRU的關鍵部分就是用c~\tilde{c}更新cc的等式。然後門決定是否要真的更新它。記憶細胞c<t>c^{<t>}將被設定爲0或者1,這取決於考慮的單詞在句子中是單數還是複數,因爲這裏是單數情況,所以先假定它被設爲了1,如果是複數的情況就把它設爲0。然後GRU單元將會一直記住c<t>c^{<t>}的值,直到(was)的位置,c<t>c^{<t>}的值還是1,這就告訴它,這是單數,所以用was。於是Γu\Gamma_u的作用就是決定什麼時候會更新這個值(cc),特別是當看到詞組the cat,這就是一個好時機去更新這個值。然後當使用完它的時候,“The cat, which already ate……, was full.”,然後就知道,不需要記住它了,可以忘記它了。

接下來要給GRU用的式子就是c<t>=Γuc~<t>+(1Γu)c<t1>c^{<t>}=\Gamma_u\ast\tilde{c}^{<t>}+(1-\Gamma_u)\ast c^{<t-1>}. 如果Γu=1\Gamma_u=1,則c<t>=c~<t>c^{<t>}=\tilde{c}^{<t>}. 對於所有在這中間的值,應該把門的值設爲0,即Γu=0\Gamma_u=0,不更新它,就用舊的值,c<t>=c<t1>c^{<t>}=c^{<t-1>}. 從左到右掃描這個句子,當門值爲0的時候(上圖中間Γu\Gamma_u一直爲0,表示一直不更新),就是說不更新它的時候,不要更新它,就用舊的值,也不要忘記這個值是什麼,這樣即使一直處理句子到(was的位置),c<t>c^{<t>}一直等於c<t1>c^{<t-1>},於是它仍然記得貓是單數的。

公式總結:

  1. c<t1>=a<t1>c^{<t-1>}=a^{<t-1>}
  2. c~<t>=tanh(Wc[c<t1>,x<t>]+bc)\tilde{c}^{<t>}=tanh(W_c[c^{<t-1>},x^{<t>}]+b_c)
  3. Γu=σ(Wu[c<t1>,x<t>]+bu)\Gamma_u=\sigma(W_u[c^{<t-1>},x^{<t>}]+b_u)
  4. c<t>=Γuc~<t>+(1Γu)c<t1>c^{<t>}=\Gamma_u\ast\tilde{c}^{<t>}+(1-\Gamma_u)\ast c^{<t-1>}

GRU單元輸入c<t1>c^{<t-1>},對於上一個時間步,先假設它正好等於a<t1>a^{<t-1>},所以把這個作爲輸入。然後x<t>x^{<t>}也作爲輸入,然後把這兩個用合適權重結合在一起,再用tanh計算,算出c~<t>\tilde{c}^{<t>},公式2,即c<t>c^{<t>}的替代值。

再用一個不同的參數集,通過sigmoid激活函數算出更新門Γu\Gamma_u,公式3。最後所有的值通過另一個運算符(空白框)結合,公式4。它把c<t1>c^{<t-1>}c~<t>\tilde{c}^{<t>}Γu\Gamma_u作爲輸入一起產生記憶細胞的新值c<t>c^{<t>},所以c<t>=a<t>c^{<t>}=a^{<t>}。也可以把這個代入Softmax或者其他預測的東西。

問題:若Γu=0\Gamma_u=0,豈不是a<t>=a<t1>a^{<t>}=a^{<t-1>}

這就是一個簡化過的GRU單元,它的優點就是通過門決定,當從左到右掃描一個句子的時候,這個時機是要更新某個記憶細胞,還是不更新,不更新直到真的需要使用記憶細胞的時候,這可能在句子之前就決定了。因爲Sigmoid的值,Γu\Gamma_u門很容易取到0值,或者說非常接近0。在這樣的情況下,這個更新式子就會變成c<t>=c<t1>c^{<t>}=c^{<t-1>},這非常有利於維持細胞(c<t>c^{<t>})的值,即使經過很多的時間步。這就是緩解梯度消失問題的關鍵,因此允許神經網絡運行在非常龐大的依賴詞上,比如說cat和was單詞即使被中間的很多單詞分割開。

一個細節c<t>c^{<t>}可以是一個向量,如果有100維的隱藏的激活值,那麼c<t>c^{<t>}也是100維的,c~<t>\tilde{c}^{<t>}也是相同的維度,Γu\Gamma_u也是相同的維度。\ast實際上就是元素對應的乘積(element-wise),所以如果門Γu\Gamma_u是一個100維的向量,裏面的值幾乎都是0或者1,就是說這100維的記憶細胞c<t>c^{<t>}就是要更新的比特

當然在實際應用中Γu\Gamma_u不會真的等於0或者1,有時候它是0到1的一箇中間值,但是這對於直觀思考是很方便的,就把它當成完全確切的0或者就是確切的1。元素對應的乘積做的就是告訴GRU單元哪個記憶細胞的向量維度在每個時間步要做更新,所以可以選擇保存一些比特不變,而去更新其他的比特。比如說可能需要一個比特來記憶貓是單數還是複數,其他比特來理解正在談論食物,因爲在談論喫飯或者食物,然後稍後可能就會談論“The cat was full.”,可以每個時間點只改變一些比特。(不理解)

對於完整的GRU單元要做的一個改變就是給記憶細胞的新候選值加上一個新的項,要添加一個門Γr\Gamma_r,可以認爲rr代表相關性(relevance)。這個Γu\Gamma_u門告訴你計算出的c<t>c^{<t>}的候選值c~<t>\tilde{c}^{<t>}c<t1>c^{<t-1>}有多大的相關性。計算這個門Γr\Gamma_r需要參數,一個新的參數矩陣WrW_rΓr=σ(Wr[c<t1>,x<t>]+br)\Gamma_r=\sigma(W_r[c^{<t-1>},x^{<t>}]+b_r).

公式總結:

  1. c<t1>=a<t1>c^{<t-1>}=a^{<t-1>}
  2. c~<t>=tanh(Wc[Γrc<t1>,x<t>]+bc)\tilde{c}^{<t>}=tanh(W_c[\Gamma_r\ast c^{<t-1>},x^{<t>}]+b_c)
  3. Γu=σ(Wu[c<t1>,x<t>]+bu)\Gamma_u=\sigma(W_u[c^{<t-1>},x^{<t>}]+b_u)
  4. Γr=σ(Wr[c<t1>,x<t>]+br)\Gamma_r=\sigma(W_r[c^{<t-1>},x^{<t>}]+b_r)
  5. c<t>=Γuc~<t>+(1Γu)c<t1>c^{<t>}=\Gamma_u\ast\tilde{c}^{<t>}+(1-\Gamma_u)\ast c^{<t-1>}

有很多方法可以來設計這些類型的神經網絡,然後爲什麼有Γr\Gamma_r?爲什麼不用簡單的版本?這是因爲多年來研究者們試驗過很多很多不同可能的方法來設計這些單元,去嘗試讓神經網絡有更深層的連接,去嘗試產生更大範圍的影響,還有解決梯度消失的問題,GRU就是其中一個研究者們最常使用的版本,也被發現在很多不同的問題上也是非常健壯和實用的。但是GRU是一個標準版本,也就是最常使用的。然後另一個常用的版本被稱爲LSTM,表示長短時記憶網絡,GRU和LSTM是在神經網絡結構中最常用的兩個具體實例。

如果看學術文章的話,有的時候會看到有些人使用另一種符號h~\tilde{h}uurrhh表示這些量(c~\tilde{c}Γu\Gamma_uΓr\Gamma_rcc)。GRU,即門控循環單元,這是RNN的其中之一。這個結構可以更好捕捉非常長範圍的依賴,讓RNN更加有效。

參考資料:
(1)GRU神經網絡
(2)人人都能看懂的GRU

長短期記憶LSTM
這節難度很大,有時間看文末的參考資料好好理解!!
對於GRU我們有:

  1. c~<t>=tanh(Wc[Γrc<t1>,x<t>]+bc)\tilde{c}^{<t>}=tanh(W_c[\Gamma_r\ast c^{<t-1>},x^{<t>}]+b_c)
  2. Γu=σ(Wu[c<t1>,x<t>]+bu)\Gamma_u=\sigma(W_u[c^{<t-1>},x^{<t>}]+b_u)(更新門)
  3. Γr=σ(Wr[c<t1>,x<t>]+br)\Gamma_r=\sigma(W_r[c^{<t-1>},x^{<t>}]+b_r)(相關門)
  4. c<t>=Γuc~<t>+(1Γu)c<t1>c^{<t>}=\Gamma_u\ast\tilde{c}^{<t>}+(1-\Gamma_u)\ast c^{<t-1>}
  5. a<t>=c<t>a^{<t>}=c^{<t>}

c~<t>\tilde{c}^{<t>}是代替記憶細胞的候選值,然後使用更新門Γu\Gamma_u來決定是否要用c~<t>\tilde{c}^{<t>}更新c<t>c^{<t>}.

LSTM是一個比GRU更加強大和通用的版本,論文Hochreiter & Schmidhuber 1997. Long Short-Term Memory,它在序列模型上有着巨大影響。這篇論文是挺難讀懂的,它在深度學習社羣有着重大的影響,深入討論了梯度消失的理論。

  1. c~<t>=tanh(Wc[a<t1>,x<t>]+bc)\tilde{c}^{<t>}=tanh(W_c[a^{<t-1>},x^{<t>}]+b_c)
  2. Γu=σ(Wu[a<t1>,x<t>]+bu)\Gamma_u=\sigma(W_u[a^{<t-1>},x^{<t>}]+b_u)
  3. Γf=σ(Wf[a<t1>,x<t>]+bf)\Gamma_f=\sigma(W_f[a^{<t-1>},x^{<t>}]+b_f)
  4. Γo=σ(Wo[a<t1>,x<t>]+bo)\Gamma_o=\sigma(W_o[a^{<t-1>},x^{<t>}]+b_o)
  5. c<t>=Γuc~<t>+Γfc<t1>c^{<t>}=\Gamma_u\ast \tilde{c}^{<t>}+\Gamma_f\ast c^{<t-1>}
  6. a<t>=Γotanh(c<t>)a^{<t>}=\Gamma_o\ast tanh(c^{<t>})

上面是LSTM主要的式子,在LSTM中不再有a<t>=c<t>a^{<t>}=c^{<t>}的情況,現在專門使用a<t>a^{<t>}或者a<t1>a^{<t-1>},而不是用c<t1>c^{<t-1>},也不用相關門Γr\Gamma_r. 像以前那樣有一個更新門Γu\Gamma_u和表示更新的參數WuW_u,LSTM的一個新特性是不只有一個更新門控制,要用遺忘門Γf\Gamma_f來取代1Γu1-\Gamma_u. 然後有一個新的輸出門Γo\Gamma_o. 於是記憶細胞的更新值爲上面公式5。所以這給了記憶細胞選擇權去維持舊的值c<t1>c^{<t-1>}或者就加上新的值c~<t>\tilde{c}^{<t>},這裏用了單獨的更新門Γu\Gamma_u和遺忘門Γf\Gamma_f. 最後a<t>=c<t>a^{<t>}=c^{<t>}的式子會變成上面公式6(視頻中少了tanh)。

下面這個圖的靈感來自於Christopher Olah的一篇博客,標題是《理解LSTM網絡》,翻譯版見此處。這裏的這張圖跟他博客上的圖是很相似的,但關鍵的不同可能是這張圖用了a<t1>a^{<t-1>}x<t>x^{<t>}來計算所有門值,即遺忘門Γf\Gamma_f,更新門Γu\Gamma_u以及輸出門Γo\Gamma_o. 然後也經過tanh函數來計算c~<t>\tilde{c}^{<t>},這些值被用複雜的方式組合在一起,比如說元素對應的乘積或者其他的方式來從之前的c<t1>c^{<t-1>}中獲得c<t>c^{<t>}.

如下圖所示,這是其中一個,再把它們連起來,就是把它們按時間次序連起來,這裏輸入x<1>x^{<1>},然後x<2>x^{<2>}x<3>x^{<3>},然後可以把這些單元依次連起來,這裏輸出了上一個時間的aaaa會作爲下一個時間步的輸入。上面這裏有條線,這條線顯示了只要正確地設置了遺忘門和更新門,LSTM是相當容易把c<0>c^{<0>}的值一直往下傳遞到右邊,比如c<3>=c<0>c^{<3>}=c^{<0>}。LSTM和GRU非常擅長於長時間記憶某個值,對於存在記憶細胞中的某個值,即使經過很長很長的時間步。

最常用的版本可能是門值不僅取決於a<t1>a^{<t-1>}x<t>x^{<t>},有時候也可以偷窺一下c<t1>c^{<t-1>}的值,這叫做偷窺孔連接(peephole connection)。“偷窺孔連接”意思就是門值不僅取決於a<t1>a^{<t-1>}x<t>x^{<t>},也取決於上一個記憶細胞c<t1>c^{<t-1>}的值,然後“偷窺孔連接”就可以結合這三個門(Γu\Gamma_uΓf\Gamma_fΓo\Gamma_o)來計算了。

  1. c~<t>=tanh(Wc[a<t1>,x<t>]+bc)\tilde{c}^{<t>}=tanh(W_c[a^{<t-1>},x^{<t>}]+b_c)
  2. Γu=σ(Wu[a<t1>,x<t>,c<t1>]+bu)\Gamma_u=\sigma(W_u[a^{<t-1>},x^{<t>},c^{<t-1>}]+b_u)
  3. Γf=σ(Wf[a<t1>,x<t>,c<t1>]+bf)\Gamma_f=\sigma(W_f[a^{<t-1>},x^{<t>},c^{<t-1>}]+b_f)
  4. Γo=σ(Wo[a<t1>,x<t>,c<t1>]+bo)\Gamma_o=\sigma(W_o[a^{<t-1>},x^{<t>},c^{<t-1>}]+b_o)
  5. c<t>=Γuc~<t>+Γfc<t1>c^{<t>}=\Gamma_u\ast \tilde{c}^{<t>}+\Gamma_f\ast c^{<t-1>}
  6. a<t>=Γotanh(c<t>)a^{<t>}=\Gamma_o\ast tanh(c^{<t>})

LSTM主要的區別在於一個技術上的細節,比如這有一個100維的向量,有一個100維的隱藏的記憶細胞單元,然後比如c<t1>c^{<t-1>}的第50個元素只會影響對應的那個門的第50個元素,所以關係是一對一的,於是並不是任意這100維的c<t1>c^{<t-1>}可以影響所有的門元素。相反的,c<t1>c^{<t-1>}的第一個元素只能影響門的第一個元素,第二個元素影響對應的第二個元素,如此類推。

LSTM反向傳播計算:(有空推導一遍!)

來源:http://www.ai-start.com/dl2017/html/lesson5-week1.html

在深度學習的歷史上,LSTM是更早出現的,而GRU是最近才發明出來的,它可能源於Pavia在更加複雜的LSTM模型中做出的簡化。研究者們在很多不同問題上嘗試了這兩種模型,看看在不同的問題不同的算法中哪個模型更好。

GRU的優點是這是個更加簡單的模型,所以更容易創建一個更大的網絡,而且它只有兩個門,在計算性上也運行得更快,然後它可以擴大模型的規模。

但是LSTM更加強大和靈活,因爲它有三個門。如果想選一個使用,LSTM在歷史進程上是個更優先的選擇,所以如果必須選一個,今天大部分的人還是會把LSTM作爲默認的選擇來嘗試。雖然最近幾年GRU獲得了很多支持,而且感覺越來越多的團隊也正在使用GRU,因爲它更加簡單,而且還效果還不錯,它更容易適應規模更加大的問題。無論是GRU還是LSTM,都可以用它們來構建捕獲更加深層連接的神經網絡。

參考資料:
(1)人人都能看懂的LSTM
(2)理解 LSTM 網絡
(3)如何從RNN起步,一步一步通俗理解LSTM
(4)LSTM的公式推導詳解
(5)LSTM長短期記憶神經網絡的學習與實現
(6)零基礎入門深度學習(6) - 長短時記憶網絡(LSTM)

雙向循環神經網絡
雙向RNN模型,這個模型可以在序列的某點處不僅可以獲取之前的信息,還可以獲取未來的信息。

  • He said, “Teddy bears are on sale!”
  • He said, “Teddy Roosevelt was a great President!”

在判斷第三個詞Teddy是不是人名的一部分時,光看句子前面部分是不夠的,爲了判斷y^<3>\hat{y}^{<3>}是0還是1,除了前3個單詞,還需要更多的信息,因爲根據前3個單詞無法判斷他們說的是Teddy bears,還是前美國總統Teddy Roosevelt,所以這是一個非雙向的或者說只有前向的RNN。

爲了簡單,先用3個輸入或者說一個只有3個單詞的句子,x<1>x^{<1>}x<3>x^{<3>}。從這裏開始的這個網絡會有一個前向的循環單元叫做a<1>\overrightarrow{a}^{<1>}a<2>\overrightarrow{a}^{<2>}a<3>\overrightarrow{a}^{<3>},向右的箭頭來表示前向的循環單元,並且它們這樣連接。這3個循環單元都有一個當前輸入xx輸入進去,得到預測的y^<1>\hat{y}^{<1>}y^<2>\hat{y}^{<2>}y^<3>\hat{y}^{<3>}.

增加一個反向循環層,這裏有a<1>\overleftarrow{a}^{<1>}a<2>\overleftarrow{a}^{<2>}a<3>\overleftarrow{a}^{<3>}左箭頭代表反向連接。

同樣,把網絡這樣向上連接,這個aa反向連接就依次反向向前連接。這個網絡就構成了一個無環圖。給定一個輸入序列x<1>x^{<1>}x<3>x^{<3>},這個序列首先計算前向的a<1>\overrightarrow{a}^{<1>},然後計算前向的a<2>\overrightarrow{a}^{<2>},接着a<3>\overrightarrow{a}^{<3>}。而反向序列從計算a<3>\overleftarrow{a}^{<3>}開始,反向進行,計算反向的a<2>\overleftarrow{a}^{<2>}. 計算的是網絡激活值,這不是反向而是前向的傳播,而圖中這個前向傳播一部分計算是從左到右,一部分計算是從右到左。計算完了反向的a<2>\overleftarrow{a}^{<2>},可以用這些激活值計算反向的a<1>\overleftarrow{a}^{<1>},把所有這些激活值都計算完了就可以計算預測結果了。

舉個例子,爲了預測結果,y^<t>=g(Wy[a<t>,a<t>]+by)\hat{y}^{<t>}=g(W_y[\overrightarrow{a}^{<t>},\overleftarrow{a}^{<t>}]+b_y). 比如要觀察時間3這裏的預測結果,信息從x<1>x^{<1>}過來,流經前向的a<1>\overrightarrow{a}^{<1>}到前向的a<2>\overrightarrow{a}^{<2>},到前向的a<3>\overrightarrow{a}^{<3>}再到y^<3>\hat{y}^{<3>},所以從x<1>x^{<1>}x<2>x^{<2>}x<3>x^{<3>}來的信息都會考慮在內,而從x<4>x^{<4>}來的信息會流過反向的a<4>\overleftarrow{a}^{<4>},到反向的a<3>\overleftarrow{a}^{<3>}再到y^<3>\hat{y}^{<3>}. 這樣使得時間3的預測結果不僅輸入了過去和現在的信息,還有未來的信息,這一步涉及了前向和反向的傳播信息以及未來的信息。

這就是雙向RNN,並且這些基本單元不僅僅是標準RNN單元,也可以是GRU單元或者LSTM單元。事實上,很多的NLP問題,對於大量有自然語言處理問題的文本,有LSTM單元的雙向RNN模型是用的最多的。所以如果有NLP問題,並且文本句子都是完整的,首先需要標定這些句子,一個有LSTM單元的雙向RNN模型。

通過這些改變,就可以用一個用RNN或GRU或LSTM構建的模型,並且能夠預測任意位置,即使在句子的中間,因爲模型能夠考慮整個句子的信息。這個雙向RNN網絡模型的缺點就是需要完整的數據的序列,才能預測任意位置。比如說要構建一個語音識別系統,那麼雙向RNN模型需要考慮整個語音表達,但是如果直接用這個去實現的話,需要等待這個人說完,然後獲取整個語音表達才能處理這段語音,並進一步做語音識別。對於實際的語音識別的應用通常會有更加複雜的模塊,而不是僅僅用我們見過的標準的雙向RNN模型。但是對於很多自然語言處理的應用,如果總是可以獲取整個句子,這個標準的雙向RNN算法實際上很高效

BRNN能夠同時對序列進行雙向處理,性能大大提高。但是計算量較大,且在處理實時語音時,需要等到完整的一句話結束時才能進行分析。

深層循環神經網絡
要學習非常複雜的函數,通常會把RNN的多個層堆疊在一起構建更深的模型。

一個標準的神經網絡,首先是輸入xx,然後堆疊上隱含層,所以這裏應該有激活值,比如說第一層是a[1]a^{[1]},接着堆疊上下一層,激活值a[2]a^{[2]},可以再加一層a[3]a^{[3]},然後得到預測值y^\hat{y}

我們不再用原來的a<0>a^{<0>}表示0時刻的激活值,而是用a[1]<0>a^{[1]<0>}來表示第一層,用a[l]<t>a^{[l]<t>}來表示第ll層第tt個時間點的激活值。然後我們把這些堆疊在上面,這就是一個有三個隱層的新的網絡。

看看這個值(a[2]<3>a^{[2]<3>})是怎麼算的。激活值a[2]<3>a^{[2]<3>}有兩個輸入,一個是從下面過來的輸入,還有一個是從左邊過來的輸入:a[2]<3>=g(Wa[2][a[2]<2>,a[1]<3>]+ba[2])a^{[2]<3>}=g(W_a^{[2]}[a^{[2]<2>},a^{[1]<3>}]+b_a^{[2]})這就是這個激活值的計算方法。參數Wa[2]W_a^{[2]}ba[2]b_a^{[2]}在這一層的計算裏都一樣,相對應地第一層也有自己的參數Wa[1]W_a^{[1]}ba[1]b_a^{[1]}.

由於時間的維度,RNN網絡會變得相當大,很少會看到這種網絡堆疊到100層。另外一種Deep RNNs結構是每個輸出層上還有一些垂直單元,就是把這裏的輸出去掉,然後換成一些深的層,這些層並不水平連接,只是一個深層的網絡,然後用來預測y^<1>\hat{y}^{<1>}. 同樣這裏也加上一個深層網絡,然後預測y^<2>\hat{y}^{<2>}。這種類型的網絡結構用的會稍微多一點,這種結構有三個循環單元,在時間上連接,接着一個網絡在後面接一個網絡,當然y^<3>\hat{y}^{<3>}y^<4>\hat{y}^{<4>}也一樣。

最簡單的RNN模型,也可以是GRU單元或者LSTM單元,並且也可以構建深層的雙向RNN網絡。由於深層的RNN訓練需要很多計算資源,需要很長的時間,儘管看起來沒有多少循環層,這個在時間上連接了三個深層的循環層。

自然語言處理與詞嵌入

詞彙表徵
詞嵌入(word embeddings)是語言表示的一種方式,可以讓算法自動的理解一些類似的詞,比如男人對女人,比如國王對王后,還有其他很多的例子。通過詞嵌入的概念就可以構建NLP應用了,即使模型標記的訓練集相對較小。

目前爲止一直都是用詞彙表來表示詞,可能是10000個單詞,我們一直用one-hot向量來表示詞。比如如果man在詞典裏是第5391個,那麼就可以表示成一個向量,只在第5391處爲1,我們用O5391O_{5391}代表這個量,這裏OO的代表one-hot. 接下來,如果woman是編號9853,那麼就可以用O9853O_{9853}來表示,這個向量只在9853處爲1,其他爲0,其他的詞king、queen、apple、orange都可以這樣表示出來,這種表示方法的一大缺點就是它把每個詞孤立起來,這樣使得算法對相關詞的泛化能力不強。

假如已經學習到了一個語言模型,當看到“I want a glass of orange ____",那麼下一個詞很可能是juice. 即使學習算法已經學到了“I want a glass of orange juice”這樣一個句子,但如果看到“I want a glass of apple ____”,因爲算法不知道apple和orange的關係很接近,就像man和woman,king和queen一樣。所以算法很難從已經知道的orange juice是一個常見的東西,而明白apple juice也是很常見的東西或者說常見的句子。這是因爲任何兩個one-hot向量的內積都是0,很難區分它們之間的差別,所以無法知道apple和orange要比king和orange,或者queen和orange相似的多。在NLP中,我們更希望能掌握不同單詞之間的相似程度。

(特徵表徵)用特徵化的表示來表示每個詞,man,woman,king,queen,apple,orange或者詞典裏的任何一個單詞,學習這些詞的特徵或者數值。使用一個特徵向量表徵單詞,特徵向量的每個元素都是對該單詞某一特徵的量化描述,量化範圍可以是[1,1][-1,1]之間。

舉個例子,比如想知道這些詞與Gender的關係。假定男性的性別爲-1,女性的性別爲+1,那麼man的性別值就是-1,而woman就是-1。最終根據經驗king就是-0.95,queen是+0.97,apple和orange沒有性別可言。

另一個特徵可以是這些詞有多Royal(高貴),所以這些詞,man,woman和高貴沒太關係,所以它們的特徵值接近0。而king和queen很高貴,apple和orange跟高貴也沒太大關係。

那麼Age呢?man和woman一般沒有年齡的意思,也許man和woman隱含着成年人的意思,但也可能是介於young和old之間,所以它們的值也接近0。而通常king和queen都是成年人,apple和orange跟年齡更沒什麼關係了。

還有一個特徵,這個詞是否是Food,man不是食物,woman不是食物,king和queen也不是,但apple和orange是食物。當然還可以有很多的其他特徵,從Size,Cost,這個東西是不是Alive,是不是一個Action,或者是不是Noun或者是不是Verb,等等。

假設有300個不同的特徵,這樣的話就有了這一列數字(300個),組成了一個300維的向量來表示man這個詞,用e5391e_{5391}這個符號來表示。同樣用e9853e_{9853}代表這個300維的向量,用來表示woman這個詞,其他的例子也一樣。現在,如果用這種表示方法來表示apple和orange這些詞,那麼apple和orange的這種表示肯定會非常相似,可能有些特徵不太一樣,比如orange的顏色和apple的不太一樣,但總的來說apple和orange的大部分特徵實際上都一樣,或者說都有相似的值。這樣對於已經知道orange juice的算法很大機率上也會明白apple juice這個東西,這樣對於不同的單詞算法會泛化得更好。這種特徵化單詞的操作被稱爲Word Embeddings,即單詞嵌入。

這種高維特徵的表示能夠比one-hot更好的表示不同的單詞。而最終學習的特徵不會像這裏一樣這麼好理解,新的特徵表示的東西肯定會更難搞清楚。儘管如此,接下來要學的特徵表示方法卻能使算法高效地發現apple和orange會比king和orange,queen和orange更加相似。

如果能夠學習到一個300維的特徵向量,或者說300維的詞嵌入,通常把這300維的數據嵌入到一個二維空間裏(降維),這樣就可以可視化了。常用的可視化算法是t-SNE算法,來自於論文:van der Maaten and Hinton., 2008. Visualizing data using t-SNE. 如果觀察這種詞嵌入的表示方法,會發現man和woman這些詞聚集在一塊,king和queen聚集在一塊,這些都是人,也都聚集在一起。動物都聚集在一起,水果也都聚集在一起,像1、2、3、4這些數字也聚集在一起。如果把這些生物看成一個整體,他們也聚集在一起。

這種詞嵌入算法對於相近的概念,學到的特徵也比較類似,在對這些概念可視化的時候,這些概念就比較相似,最終把它們映射爲相似的特徵向量。這種表示方式用的是在300維空間裏的特徵表示,這叫做嵌入(embeddings)。想象一個300維的空間,現在取每一個單詞比如orange,它對應一個300維的特徵向量,所以這個詞就被嵌在這個300維空間裏的一個點上了,apple這個詞就被嵌在這個300維空間的另一個點上了。爲了可視化,t-SNE算法把這個空間映射到低維空間,可以畫出一個2維圖像然後觀察,這就是這個術語嵌入的來源。

使用詞嵌入
例子:命名實體識別,假如有一個句子:“Sally Johnson is an orange farmer.”,會發現Sally Johnson就是一個人名,所以這裏的輸出爲1。之所以能確定Sally Johnson是一個人名,是因爲知道種橙子的農民一定是一個人,前面已經討論過用one-hot來表示這些單詞,x<1>x^{<1>}x<2>x^{<2>}等等。

但是如果用特徵化表示方法,那麼用詞嵌入作爲輸入訓練好的模型,如果看到一個新的輸入:“Robert Lin is an apple farmer.”,因爲知道orange和apple很相近,那麼算法很容易就知道Robert Lin也是一個人的名字。要是測試集裏是不太常見的詞比如“Robert Lin is a durian cultivator.” 如果對於一個命名實體識別任務,只有一個很小的標記的訓練集,訓練集裏甚至可能沒有durian或者cultivator這兩個詞。但是如果有一個已經學好的詞嵌入,它會告訴你durian是水果,就像orange一樣,並且cultivator,做培育工作的人其實跟farmer差不多,那麼就有可能從訓練集裏的“an orange farmer”歸納出“a durian cultivator”也是一個人。

詞嵌入能夠達到這種效果,其中一個原因就是學習詞嵌入的算法會考察非常大的文本集,也許是從網上找到的,這樣可以考察很大的數據集可以是1億個單詞,甚至達到100億也都是合理的,大量的無標籤的文本的訓練集。通過考察大量的無標籤文本,可以發現orange和durian相近,farmer和cultivator相近。因此學習這種嵌入表達,把它們都聚集在一塊,通過讀取大量的互聯網文本發現了orange和durian都是水果。

接下來可以把這個詞嵌入應用到命名實體識別任務當中,儘管只有一個很小的訓練集,也許訓練集裏有100000個單詞,甚至更小,這就可以使用遷移學習,把從互聯網上免費獲得的大量的無標籤文本中學習到的知識,能夠分辨orange、apple和durian都是水果的知識,然後把這些知識遷移到一個只有少量標記的訓練數據集的命名實體識別任務中。事實上應該用一個雙向的RNN。

總結一下,這是如何用詞嵌入做遷移學習的步驟:

  • Learn word embeddings from large text corpus. (1-100B words) (Or download pre-trained embedding online.)
  • Transfer embedding to new task with smaller training set. (say, 100k words)
  • Optional: Continue to fine tune the word embeddings with new data.

第一步,先從大量的文本集中學習詞嵌入。一個非常大的文本集,或者可以下載網上預訓練好的詞嵌入模型。

第二步,可以用這些詞嵌入模型把它遷移到新的只有少量標註訓練集的任務中,比如說用這個300維的詞嵌入來表示單詞。這樣做的一個好處就是可以用更低維度的特徵向量代替原來的10000維的one-hot向量,現在可以用一個300維更加緊湊的向量。

第三步,當在新的任務上訓練模型時,在命名實體識別任務上,只有少量的標記數據集上,可以自己選擇要不要繼續微調,用新的數據調整詞嵌入。實際中,只有這個第二步中有很大的數據集纔會這樣做。

當任務的訓練集相對較小時,詞嵌入的作用最明顯,所以它廣泛用於NLP領域(命名實體識別,文本摘要,文本解析,指代消解等)。

詞嵌入在語言模型、機器翻譯領域用的少一些,這些任務有大量的數據。在其他的遷移學習情形中也一樣,如果從某一任務A遷移到某個任務B,只有A中有大量數據,而B中數據少時,遷移的過程纔有用。

最後,詞嵌入和人臉編碼之間有奇妙的關係,對於人臉識別,我們訓練了一個Siamese網絡結構,這個網絡會學習不同人臉的一個128維表示,然後通過比較編碼結果來判斷兩個圖片是否是同一個人臉,這個詞嵌入的意思和這個差不多。在人臉識別領域用編碼這個詞來指代這些向量f(x(i))f(x^{(i)})f(x(j))f(x^{(j)}),人臉識別領域和這裏的詞嵌入有一個不同就是,在人臉識別中我們訓練一個網絡,任給一個人臉照片,甚至是沒有見過的照片,神經網絡都會計算出相應的一個編碼結果。我們學習詞嵌入則是有一個固定的詞彙表,比如10000個單詞,我們學習向量e1e_1e10000e_{10000},學習一個固定的編碼,每一個詞彙表的單詞的固定嵌入。這裏的術語編碼(encoding)和嵌入(embedding)可以互換,人臉識別中的算法未來可能涉及到海量的人臉照片,而自然語言處理有一個固定的詞彙表,而像一些沒有出現過的單詞就記爲未知單詞(< UNK >)。

用詞嵌入來實現這種類型的遷移學習,並且通過替換原來的one-hot表示,而是用之前的嵌入的向量,算法會泛化的更好,也可以從較少的標記數據中進行學習。

詞嵌入的特性
詞嵌入還有一個迷人的特性就是它還能幫助實現類比推理,儘管類比推理可能不是自然語言處理應用中最重要的,不過它能幫助人們理解詞嵌入做了什麼,以及詞嵌入能夠做什麼。

man如果對應woman,那麼king應該對應什麼?應該都能猜到king對應queen,有一種算法能自動推導出這種關係。

用一個四維向量e5391e_{5391}來表示man,把它稱爲emane_{man},而旁邊這個表示woman的嵌入向量,稱它爲ewomane_{woman},對king和queen也是用一樣的表示方法。在該例中假設用的是四維的嵌入向量。這些向量有一個有趣的特性,就是對向量emane_{man}ewomane_{woman}進行減法運算,即emanewoman[2000]e_{man}-e_{woman}\approx\left[ \begin{array}{c} -2\\ 0\\ 0\\ 0\\ \end{array} \right]

類似的,假如用ekinge_{king}減去equeene_{queen},最後也會得到一樣的結果,即ekingequeen[2000]e_{king}-e_{queen}\approx\left[ \begin{array}{c} -2\\ 0\\ 0\\ 0\\ \end{array} \right]

這個結果表示,man和woman主要的差異是gender上的差異,而king和queen之間的主要差異,根據向量的表示,也是gender上的差異,這就是爲什麼emanewomane_{man}-e_{woman}ekingequeene_{king}-e_{queen}結果是相同的。當算法被問及man對woman相當於king對什麼時,算法所做的就是計算emanewomane_{man}-e_{woman},然後找出一個向量也就是找出一個詞,使得emanewomanekinge?e_{man}-e_{woman}\approx e_{king}-e_{?},也就是說,當這個新詞是queen時,式子的左邊會近似地等於右邊。

論文Mikolov et. al., 2013, Linguistic regularities in continuous space word representations,這是詞嵌入領域影響力最爲驚人和顯著的成果之一,這種思想幫助了研究者們對詞嵌入領域建立了更深刻的理解。

在下圖中,詞嵌入向量在一個可能有300維的空間裏,於是單詞man、woman、king、queen分別代表空間中的一個點,事實上,向量man和woman的差值非常接近於向量king和queen之間的差值。爲了得出這樣的類比推理,計算當man對於woman,那麼king對於什麼,找到單詞ww來使得,emanewomanekingewe_{man}-e_{woman}\approx e_{king}-e_{w},即找到單詞ww來最大化ewe_wekingeman+ewomane_{king}-e_{man}+e_{woman}的相似度:Find  word  w:argmaxw  Sim(ew,ekingeman+ewoman)Find\,\,word\,\,w:arg\underset{w}{max}\,\,Sim(e_w,e_{king}-e_{man}+e_{woman})

我們有一些用於測算ewe_wekingeman+ewomane_{king}-e_{man}+e_{woman}之間的相似度的函數,然後通過方程找到一個使得相似度最大的單詞,如果結果理想的話會得到單詞queen. 如果查看一些研究論文就不難發現,通過這種方法來做類比推理準確率大概只有30%~75%,只要算法猜中了單詞,就把該次計算視爲正確,從而計算出準確率,在該例子中,算法選出了單詞queen.

t-SNE算法所做的就是把這些300維的數據用一種非線性的方式映射到2維平面上,可以得知t-SNE中這種映射很複雜。在進行t-SNE映射之後,不能總是期望使等式emanewomanekingewe_{man}-e_{woman}\approx e_{king}-e_{w}成立的關係,會像上邊那樣成一個平行四邊形,儘管在這個例子最初的300維的空間內可以依賴這種平行四邊形的關係來找到使等式成立的一對類比,通過t-SNE算法映射出的圖像可能是正確的。但在大多數情況下,由於t-SNE的非線性映射,就沒法再指望這種平行四邊形了,很多這種平行四邊形的類比關係在t-SNE映射中都會失去原貌

一個最常用的相似度函數叫做餘弦相似度,假如在向量uuvv之間定義相似度:sim(u,v)=uTvu2v2sim(u,v)=\frac{u^Tv}{\lVert u \rVert_2\lVert v \rVert_2}. 分子其實就是uuvv的內積,如果uuvv非常相似,那麼它們的內積將會很大。這個公式實際就是計算兩向量夾角ϕ\phi的餘弦值。夾角爲0度時,餘弦相似度就是1,當夾角是90度角時餘弦相似度就是0,當夾角是180度時,相似度等於-1。或者用距離相似度,用平方距離或者歐氏距離來表示:uv2\lVert u-v \rVert^2. 距離越小,相似性越大。

如果向量uuvv非常相似,它們的餘弦相似度將接近1;如果它們不相似,則餘弦相似度將取較小的值。 兩個向量之間角度的餘弦是衡量它們有多相似的指標,角度越小,兩個向量越相似。

詞嵌入的一個顯著成果就是,可學習的類比關係的一般性。舉個例子,它能學會man對於woman相當於boy對於girl,還能學習Ottawa對於Canada相當於Nairobi對於Kenya,這些都是國家中首都城市名字。它還能學習big對於bigger相當於tall對於taller,還能學習Yen對於Janpan,円(Yen)是日本的貨幣單位,相當於Ruble對於Russia。這些東西都能夠學習,只要在大型的文本語料庫上實現一個詞嵌入學習算法,只要從足夠大的語料庫中進行學習,它就能自主地發現這些模式。

嵌入矩陣
假設詞彙表含有10000個單詞,詞彙表裏有a,aaron,orange,zulu,可能還有一個未知詞標記< UNK >。我們要做的就是學習一個嵌入矩陣EE,它將是一個300×10000的矩陣,如果詞彙表裏有10000個,或者加上未知詞就是10001維。假設orange的單詞編號是6257,代表詞彙表中第6257個單詞,用符號O6527O_{6527}來表示這個one-hot向量,這個向量除了第6527個位置上是1,其餘各處都爲0,顯然它是一個10000維的列向量。

假設這個嵌入矩陣叫做矩陣EE,如果用EE去乘以右邊的one-hot向量O6527O_{6527},那麼就會得到一個300維的向量。最後得到的這個向量的第kk個元素就是orange這一列下的第kk個數字(1k300)(1\leqslant k\leqslant 300). 它等於e6527e_{6527},這個符號是用來表示這個300×1的嵌入向量,它表示的單詞是orange.

更廣泛來說,假如說有某個單詞ww,那麼ewe_w就代表單詞ww的嵌入向量。同樣,EOjEO_jOjO_j就是隻有第jj個位置是1的one-hot向量,得到的結果就是eje_j,它表示的是字典中單詞jj的嵌入向量。EOj=ejEO_j=e_j

我們的目標是學習一個嵌入矩陣EE。在下節視頻中將會隨機地初始化矩陣EE,然後使用梯度下降法來學習這個300×10000的矩陣中的各個參數,EE乘以這個one-hot向量會得到嵌入向量。

當動手實現時,用大量的矩陣和向量相乘來計算它,效率是很低下的,因爲one-hot向量是一個維度非常高的向量,並且幾乎所有元素都是0,所以矩陣向量相乘效率太低,因爲我們要乘以一大堆的0。所以在實踐中會使用一個專門的函數來單獨查找矩陣EE的某列,例如在Keras中就有一個嵌入層(Embedding layer),用這個嵌入層更有效地從嵌入矩陣中提取出需要的列,而不是對矩陣進行很慢很複雜的乘法運算。

學習詞嵌入
在深度學習應用於學習詞嵌入的歷史上,人們一開始使用的算法比較複雜,但隨着時間推移,研究者們不斷髮現他們能用更加簡單的算法來達到一樣好的效果,特別是在數據集很大的情況下。

構建一個語言模型,並且用神經網絡來實現這個模型。於是在訓練過程中,可能想要神經網絡能夠做到輸入:“I want a glass of orange ___.”,然後預測這句話的下一個詞。實踐證明,建立一個語言模型是學習詞嵌入的好方法,參考論文Bengio et. al., 2003, A neural probabilistic language model.

從第一個詞I開始,建立一個one-hot向量表示這個單詞,在第4343個位置是1,它是一個10000維的向量。然後要做的就是生成一個參數矩陣EE,用EE乘以o4343o_{4343},得到嵌入向量e4343e_{4343}. 然後對其他的詞也做相同的操作。

於是現在有許多300維的嵌入向量。把它們全部放進神經網絡中,經過神經網絡以後再通過Softmax層,然後這個Softmax分類器(Softmax層有10000個概率輸出)會在10000個可能的輸出中預測結尾這個單詞。假如說在訓練集中有juice這個詞,訓練過程中Softmax的目標就是預測出單詞juice,就是結尾的這個單詞。這個隱藏層有自己的參數W[1]W^{[1]}b[1]b^{[1]},這個Softmax層也有自己的參數W[2]W^{[2]}b[2]b^{[2]}。如果它們用的是300維大小的嵌入向量,而這裏有6個詞,所以用6×300,所以這個輸入會是一個1800維的向量,這是通過將這6個嵌入向量堆在一起得到的。

實際上更常見的是有一個固定的歷史窗口,舉個例子,想預測給定4個單詞後的下一個單詞,注意這裏的4是算法的超參數。這就是如何適應很長或者很短的句子,方法就是總是隻看前4個單詞。如果一直使用一個4個詞的歷史窗口,則神經網絡會輸入一個1200維的特徵變量到這個層中,然後再通過Softmax來預測輸出。選擇有很多種,用一個固定的歷史窗口就意味着可以處理任意長度的句子,因爲輸入的維度總是固定的。

所以這個模型的參數就是矩陣EE,對所有的單詞用的都是同一個矩陣EE。然後這些權重也都是算法的參數,可以用反向傳播來進行梯度下降來最大化訓練集似然(likelihood),通過序列中給定的4個單詞去重複地預測出語料庫中下一個單詞什麼。對足夠的訓練例句樣本,運用梯度下降算法,迭代優化,最終求出嵌入矩陣EE

在這個算法的激勵下,apple和orange會學到很相似的嵌入,這樣做能夠讓算法更好地擬合訓練集,因爲它有時看到的是orange juice,有時看到的是apple juice。如果只用一個300維的特徵向量來表示所有這些詞,算法會發現要想最好地擬合訓練集,就要使apple、orange、grape和pear等等,還有像durian這種很稀有的水果都擁有相似的特徵向量。

這就是早期最成功的學習詞嵌入,學習這個矩陣EE的算法之一。假設在訓練集中有這樣一個更長的句子:“I want a glass of orange juice to go along with my cereal.”。一般地,我們把輸入叫做context,輸出叫做target。算法預測出了某個單詞juice,把它叫做目標詞,它是通過一些上下文,這前4個詞(Last 4 words)推導出來的。如果目標是學習一個嵌入向量,研究人員已經嘗試過很多不同類型的上下文。如果要建立一個語言模型,那麼一般選取目標詞之前的幾個詞作爲上下文。

比如說,可以提出這樣一個學習問題,它的上下文是左邊和右邊的四個詞(4 words on left & right),可以把目標詞左右各4個詞作爲上下文。算法獲得左邊4個詞,也就是a glass of orange,還有右邊四個詞to go along with,然後要求預測出中間這個詞。這個問題需要將左邊的還有右邊這4個詞的嵌入向量提供給神經網絡,來預測中間的單詞是什麼,這也可以用來學習詞嵌入。

或者想用一個更簡單的上下文,也許只提供目標詞的前一個詞(Last 1 word),比如只給出orange這個詞來預測orange後面是什麼,這將會是不同的學習問題。可以構建一個神經網絡,只把目標詞的前一個詞或者說前一個詞的嵌入向量輸入神經網絡來預測該詞的下一個詞。

還有一個效果非常好的做法就是上下文是附近一個單詞(Nearby 1 word),它可能會告訴你單詞glass是一個鄰近的單詞。或者說看見了單詞glass,然後附近有一個詞和glass位置相近,那麼這個詞會是什麼?這用的是一種Skip-Gram模型的思想。這是一個簡單算法的例子,因爲上下文相當的簡單,比起之前4個詞,現在只有1個,但是這種算法依然能工作得很好。事實證明,不同的context選擇方法都能計算出較準確的EE.

如果真想建立一個語言模型,用目標詞的前幾個單詞作爲上下文是常見做法。但如果目標是學習詞嵌入,那麼就可以用這些其他類型的上下文,它們也能得到很好的詞嵌入。

Word2Vec
這節有點難懂!
假設在訓練集中給定了一個句子:“I want a glass of orange juice to go along with my cereal.”,在Skip-Gram模型中,抽取上下文和目標詞配對,來構造一個監督學習問題。上下文不一定總是目標單詞之前離得最近的四個單詞,或最近的nn個單詞。隨機選一個詞作爲上下文詞,比如選orange這個詞,然後隨機在一定詞距內選另一個詞,比如在上下文詞前後5個詞內或者前後10個詞內,在這個範圍內選擇目標詞。可能正好選到了juice或glass或my作爲目標詞。

參考論文Mikolov et. al., 2013. Efficient Estimation of Word Representations in Vector Space.

於是我們將構造一個監督學習問題,它給定上下文詞,要求預測在這個詞正負10個詞距或者正負5個詞距內隨機選擇的某個目標詞。顯然,這不是個非常簡單的學習問題,因爲在單詞orange的正負10個詞距之間,可能會有很多不同的單詞。但是構造這個監督學習問題的目標並不是想要解決這個監督學習問題本身,而是想要使用這個學習問題來學到一個好的詞嵌入模型

繼續假設使用一個10000詞的詞彙表,有時訓練使用的詞彙表會超過一百萬詞。但我們要解決的基本的監督學習問題是學習一種映射關係,從上下文,記爲c,比如單詞orange,到某個目標詞,記爲t,可能是單詞juice或者glass或者my。在我們的詞彙表中,orange是第6257個單詞,juice是第4834個,映射:xy(orangejuice).x\rightarrow y(orange\rightarrow juice).

爲了表示輸入,比如單詞orange,可以先從one-hot向量開始,將其寫作oco_c,這就是上下文詞的one-hot向量。然後可以拿嵌入矩陣EE乘以向量oco_c,然後得到了輸入的上下文詞的嵌入向量,於是這裏ec=Eoce_c=Eo_c. 在這個神經網絡中,我們將把向量ece_c喂入一個Softmax單元。Softmax單元要做的就是輸出y^\hat{y}. 這是Softmax模型,預測不同目標詞的概率:Softmax:p(tc)=eθtTecj=110000eθjTecSoftmax:p(t|c)=\frac{e^{\theta_t^Te_c}}{\sum_{j=1}^{10000}e^{\theta_j^Te_c}}

這裏θt\theta_t是一個與輸出tt有關的參數,pp即某個詞tt和標籤相符的概率是多少。這裏省略了Softmax中的偏差項,想要加上的話也可以加上。

最終Softmax的損失函數如下,我們用yy表示目標詞,這裏用的yyy^\hat{y}都是用one-hot表示的,y^\hat{y}是一個從Softmax單元輸出的10000維的向量,這個向量是所有可能目標詞的概率:L(y^,y)=i=110000yilogy^i\mathscr{L}(\hat{y},y)=-\sum_{i=1}^{10000}y_ilog\hat{y}_i

總結一下,這大體上就是一個可以找到詞嵌入的簡化模型和神經網絡,其實就是個Softmax單元。矩陣EE將會有很多參數,有對應所有嵌入向量ece_c的參數,Softmax單元也有θt\theta_t的參數。如果優化這個關於所有這些參數的損失函數,就會得到一個較好的嵌入向量集,這個就叫做Skip-Gram模型。它把一個像orange這樣的詞作爲輸入,並預測上下文詞的前面一些或者後面一些是什麼詞。

實際上使用這個算法會遇到一些問題,首要的問題就是計算速度。尤其是在Softmax模型中,每次想要計算這個概率,需要對你詞彙表中的所有10000個詞做求和計算,如果用了一個大小爲100000或1000000的詞彙表,那麼這個分母的求和操作是相當慢的,實際上10000已經是相當慢的了,所以擴大詞彙表就更加困難了。

這裏有一些解決方案,如分級(hierarchical)的Softmax分類器和負採樣。

使用一個分級的Softmax分類器,意思就是說不是一下子就確定到底是屬於10000類中的哪一類。如果有一個分類器,它告訴你目標詞是在詞彙表的前5000箇中還是在詞彙表的後5000個詞中,假如這個二分類器告訴你這個詞在前5000個詞中,然後第二個分類器會告訴你這個詞在詞彙表的前2500個詞中,或者在詞彙表的第二組2500個詞中,諸如此類,直到最終找到一個詞準確所在的分類器,那麼就是這棵樹的一個葉子節點。這好比是猜數字遊戲,數字範圍0~100。我們可以先猜50,如果分類器給出目標數字比50大,則繼續猜75,以此類推,每次從數據區間中部開始。像這樣一個樹形的分類器,意味着樹上內部的每一個節點都可以是一個二分類器,比如邏輯迴歸分類器,所以不需要對詞彙表中所有的10000個詞求和了。實際上用這樣的分類樹,計算成本與詞彙表大小的對數成正比(log|v|),這個就叫做分級Softmax分類器(類似哈夫曼樹)。

在實踐中分級Softmax分類器不會使用一棵完美平衡的分類樹或者說一棵左邊和右邊分支的詞數相同的對稱樹。實際上,分級的Softmax分類器會被構造成常用詞在頂部,然而不常用的詞像durian會在樹的更深處,因爲更常見的詞會更頻繁,所以可能只需要少量檢索就可以獲得常用單詞像the和of。然而更少見到的詞比如durian就更合適在樹的較深處,因爲一般不需要到那樣的深處,所以有不同的經驗法則可以幫助構造分類樹形成分級Softmax分類器。

一旦對上下文c進行採樣,那麼目標詞t就會在上下文c的正負10個詞距內進行採樣。但是要如何選擇上下文c?一種選擇是可以就對語料庫均勻且隨機地採樣,會發現有一些詞,像the、of、a、and、to諸如此類是出現得相當頻繁的,上下文到目標詞的映射會相當頻繁地得到這些種類的詞,但是其他詞,像orange、apple或durian就不會那麼頻繁地出現了。可能不會想要訓練集都是這些出現得很頻繁的詞,因爲這會導致花大部分的力氣來更新這些頻繁出現的單詞的ece_c,但想要的是花時間來更新像durian這些更少出現的詞的嵌入,即eduriane_{durian}。實際上詞p(c)p(c)的分佈並不是單純的在訓練集語料庫上均勻且隨機的採樣得到的,而是採用了不同的分級來平衡更常見的詞和不那麼常見的詞。

這就是Word2Vec的Skip-Gram模型,論文實際上有兩個不同版本的Word2Vec模型,Skip-Gram只是其中的一個,另一個叫做CBOW,即連續詞袋模型(Continuous Bag-Of-Words Model),它獲得中間詞兩邊的上下文,然後用周圍的詞去預測中間的詞,這個模型也很有效,也有一些優點和缺點。

總結下:CBOW是從原始語句推測目標字詞;而Skip-Gram正好相反,是從目標字詞推測出原始語句。CBOW對小型數據庫比較合適,而Skip-Gram在大型語料中表現更好。 (下圖左邊爲CBOW,右邊爲Skip-Gram)

來源:http://www.ai-start.com/dl2017/html/lesson5-week2.html#header-n138

負採樣
這節比較難理解!
負採樣,它能做到與Skip-Gram模型相似的事情,但是用了一個更加有效的學習算法。

論文:Mikolov et. al.,2013. Distributed Representations of Words and Phrases and their Compositionality.

例句:I want a glass of orange juice to go along with my cereal. 構造一個新的監督學習問題,給定一對單詞,比如orange和juice,要去預測這是否是一對上下文詞-目標詞(context-target)。

先抽取一個上下文詞,在一定詞距內比如說正負10個詞距內選一個目標詞,在這個例子中orange和juice就是個正樣本,用1作爲標記。然後爲了生成一個負樣本,用相同的上下文詞,再在字典中隨機選一個詞,在這裏隨機選了單詞king,標記爲0. 然後再拿orange,再隨機從詞彙表中選一個詞,於是orange–book–0. 同樣orange–the–0. 還有orange–of–0,注意of被標記爲0,即使of的確出現在orange詞的前面。

context(x) word(x) target?(y)
orange juice 1
orange king 0
orange book 0
orange the 0
orange of 0

總結一下,生成這些數據的方式是選擇一個上下文詞,再選一個目標詞,表的第一行給了一個正樣本。然後給定kk次,將用相同的上下文詞,再從字典中選取隨機的詞,king、book、the、of等,並標記0,這些就會成爲負樣本。出現以下情況也沒關係,就是如果從字典中隨機選到的詞,正好出現在了詞距內,比如說在上下文詞orange正負10個詞之內。

接下來構造一個監督學習問題,其中學習算法輸入xx,輸入這對詞,要去預測目標的標籤,即預測輸出yy。因此問題就是給定一對詞,像orange和juice,這兩個詞是通過對靠近的兩個詞采樣獲得的還是分別在文本和字典中隨機選取得到的呢?這個算法就是要分辨這兩種不同的採樣方式,這就是如何生成訓練集的方法。

  • 小數據集,kk從5到20比較好;
  • 大數據集,kk從2到5比較好。

那麼在這個例子中,我們就用k=4k=4.

學習從xx映射到yy的監督學習模型,這是之前討論的Softmax模型p(tc)=eθtTecj=110000eθjTecp(t|c)=\frac{e^{\theta_t^Te_c}}{\sum_{j=1}^{10000}e^{\theta_j^Te_c}}. 爲了定義模型,使用記號cc表示上下文詞,記號tt表示可能的目標詞,再用yy表示0和1,表示是否是一對上下文-目標詞。定義一個邏輯迴歸模型,給定輸入的c,tc,t對的條件下,y=1y=1的概率,即:P(y=1c,t)=σ(θtTec)P(y=1|c,t)=\sigma(\theta_t^Te_c)

將一個Sigmoid函數作用於θtTec\theta_t^Te_c,對每一個可能的目標詞有一個參數向量θt\theta_t和另一個向量ece_c,即每一個上下文詞的的嵌入向量,用這個公式估計y=1y=1的概率。如果有kk個樣本,每一個正樣本都有kk個對應的負樣本來訓練一個類似邏輯迴歸的模型。

如果輸入詞是orange,要做的就是輸入one-hot向量,再傳遞給EE,通過兩者相乘獲得嵌入向量e6257e_{6257},就得到了10000個可能的邏輯迴歸分類問題,其中一個將會是用來判斷目標詞是否是juice的分類器,還有其他的詞,比如說可能下面的某個分類器是用來預測king是否是目標詞,諸如此類,預測詞彙表中這些可能的單詞。

把這些看作10000個二分類邏輯迴歸分類器,但並不是每次迭代都訓練全部10000個,只訓練其中的5個,要訓練對應真正目標詞那一個分類器,再訓練4個隨機選取的負樣本,這就是k=4k=4的情況。所以不使用一個巨大的10000維度的Softmax,因爲計算成本很高,而是把它轉變爲10000個二分類問題,每個都很容易計算,每次迭代只是訓練它們其中的5個,一般而言就是k+1k+1個,其中kk個負樣本和1個正樣本。這個算法計算成本更低,因爲只需更新k+1k+1個邏輯單元,k+1k+1個二分類問題,相對而言每次迭代的成本比更新10000維的Softmax分類器成本低。

這個技巧就叫負採樣。有一個正樣本詞orange和juice,然後會特意生成一系列負樣本,所以叫負採樣,即用這4個負樣本訓練,4個額外的二分類器,在每次迭代中選擇4個不同的隨機的負樣本詞去訓練算法。

在選取了上下文詞orange之後,如何對這些詞進行採樣生成負樣本?一個辦法是對中間的這些詞進行採樣,即候選的目標詞,可以根據其在語料中的經驗頻率進行採樣,就是通過詞出現的頻率對其進行採樣。但問題是這會導致在like、the、of、and諸如此類的詞上有很高的頻率。另一個極端就是用1除以詞彙表總詞數,即1v\frac{1}{|v|},均勻且隨機地抽取負樣本,這對於英文單詞的分佈是非常沒有代表性的。

所以論文的作者Mikolov等人根據經驗,他們發現這個經驗值的效果最好,它位於這兩個極端的採樣方法之間,既不用經驗頻率,也就是實際觀察到的英文文本的分佈,也不用均勻分佈,他們採用以下方式:P(wi)=f(wi)34j=110000f(wj)34P(w_i)=\frac{f(w_i)^{\frac{3}{4}}}{\sum_{j=1}^{10000}{f(w_j)^{\frac{3}{4}}}}

進行採樣,所以如果f(wi)f(w_i)是觀測到的在語料庫中的某個英文詞的詞頻,通過34\frac{3}{4}次方的計算,使其處於完全獨立的分佈和訓練集的觀測分佈兩個極端之間(這裏不懂)。很多研究者現在使用這個方法,似乎也效果不錯。

在Softmax分類器中學到詞向量,但是計算成本很高。通過將其轉化爲一系列二分類問題,因此可以非常有效地學習詞向量。如果使用這個算法,將可以學到相當好的詞向量。當然和深度學習的其他領域一樣,有很多開源的實現,當然也有預訓練過的詞向量,就是其他人訓練過的然後授權許可發佈在網上的,所以如果想要在NLP問題上取得進展,去下載其他人的詞向量是很好的方法,在此基礎上改進。

GloVe詞向量
這節十分難!!建議參考文末資料!
一個在NLP社區有着一定勢頭的算法是GloVe算法,這個算法並不如Word2Vec或是Skip-Gram模型用的多,但是也有人熱衷於它,可能是因爲它簡便。

GloVe代表用詞表示的全局變量(global vectors for word representation). 之前我們曾通過挑選語料庫中位置相近的兩個詞,列舉出詞對,即上下文和目標詞,GloVe算法做的就是使其關係開始明確化。假定XijX_{ij}是單詞ii在單詞jj上下文中出現的次數(即iijj同時出現的次數),那麼這裏iijj就和ttcc的功能一樣,所以可以認爲XijX_{ij}等同於XtcX_{tc}.

根據上下文和目標詞的定義,大概會得出Xij=XjiX_{ij}=X_{ji}這個結論。事實上,如果將上下文和目標詞的範圍定義爲出現於左右各10詞以內的話,那麼就會有一種對稱關係。如果對上下文的選擇是,上下文總是目標詞前一個單詞的話,那麼XijX_{ij}XjiX_{ji}就不會像這樣對稱了。對於GloVe算法,可以定義上下文和目標詞爲任意兩個位置相近的單詞,假設是左右各10詞的距離,那麼XijX_{ij}就是一個能夠獲取單詞ii和單詞jj出現位置相近時或是彼此接近的頻率的計數器

GloVe模型做的就是進行優化,將它們之間的差距進行最小化處理:minimizei=110000j=110000f(Xij)(θiTej+bi+bjlogXij)2minimize\sum_{i=1}^{10000}\sum_{j=1}^{10000}{f(X_{ij})(\theta_i^Te_j+b_i+b_j'-logX_{ij})^2}

從上式可以看出,若兩個詞的Embedding Vector越相近,同時出現的次數越多,則對應的Loss(minimize右邊式子)越小。其中θiTej\theta_i^Te_j,想一下iijjttcc的功能一樣,因此這就和θtTec\theta_t^Te_c有些類似了,同時對於這個θtTec\theta_t^Te_c來說,想要知道這兩個單詞之間有多少聯繫,ttcc之間有多緊密,iijj之間聯繫程度如何,換句話說就是它們同時出現的頻率是多少,這是由XijX_{ij}影響的。然後解決參數θ\thetaee的問題,準備用梯度下降來最小化上面的公式,只想要學習一些向量,這樣它們的輸出能夠對這兩個單詞同時出現的頻率進行良好的預測。引入偏移量bi,bjb_i,b_j'.

如果Xij=0X_{ij}=0,那麼log0log0就是未定義的,是負無窮大的,所以想要對Xij=0X_{ij}=0時進行求和,就要添加一個額外的加權項f(Xij)f(X_{ij}). 如果Xij=0X_{ij}=0f(Xij)=0f(X_{ij})=0,同時會用一個約定,即0log0=00log0=0,意思是如果Xij=0X_{ij}=0,不要進行求和,所以這個log0log0項就是不相關項。上面的求和公式表明,這個和僅是一個上下文和目標詞關係裏連續出現至少一次(Xij>0X_{ij}>0)的詞對的和。

f(Xij)f(X_{ij})的另一個作用是,有些詞在英語裏出現十分頻繁,比如說this,is,of,a等等,這叫做停止詞,但是在頻繁詞和不常用詞之間也會有一個連續統(continuum)。不過也有一些不常用的詞,比如durion,還是想將其考慮在內。因此,這個加權因子f(Xij)f(X_{ij})就可以是一個函數,即使是像durion這樣不常用的詞,它也能給予大量有意義的運算,同時也能夠給像this,is,of,a這些在英語裏出現更頻繁的詞更大但不至於過分的權重。因此對加權函數ff的選擇有着啓發性的原則,就是既不給這些頻繁詞過分的權重,也不給這些不常用詞太小的權值。具體可參考資料1。

最後,θ\thetaee現在是完全對稱的,所以θi\theta_ieje_j就是對稱的(why?)。如果只看數學式的話,θi\theta_ieje_j的功能其實很相近,可以將它們顛倒或者將它們進行排序,實際上它們都輸出了最佳結果。因此一種訓練算法的方法是一致地初始化θ\thetaee,然後使用梯度下降來最小化輸出,當每個詞都處理完之後取平均值,所以,給定一個詞ww,使用優化算法得到所有參數之後,就會有ew(final)=ew+θw2e_w^{(final)}=\frac{e_w+\theta_w}{2}. 因爲θ\thetaee在這個特定的公式裏是對稱的,而不像之前我們瞭解的模型,θ\thetaee和功能不一樣,因此也不能取平均。(不懂)

僅僅是最小化,像這樣的一個二次代價函數是怎麼能夠學習有意義的詞嵌入的呢?但是結果證明它確實有效,發明者們發明這個算法的過程中,以歷史上更爲複雜的算法,像是Newer Language模型,以及之後的Word2VecSkip-Gram模型等等爲基礎,同時希望能夠簡化所有之前的算法。

以這個表格作爲例子來開始學習詞向量,第一行的嵌入向量是來表示Gender的,第二行是來表示Royal的,然後是是Age,之後是Food等等。但是當使用GloVe算法來學習一個詞嵌入時,不能保證嵌入向量的獨立組成部分是能夠理解的,爲什麼呢?

(不懂)假設說有個空間,裏面的第一個軸是Gender,第二個軸是Royal,能夠保證的是第一個嵌入向量對應的軸是和這個軸有聯繫的。具體而言,這個學習算法會選擇這個作爲第一維(ew,1e_{w,1})的軸,所以給定一些上下文詞,第一維可能是這個軸,第二維(ew,2e_{w,2})也許是這個,它也可能是第二個非正交軸,它可以是學習到的詞嵌入中的第二部分。當我們看到這個的時候,如果有某個可逆矩陣,那麼這項就可以簡單地替換成(Aθi)T(ATej)(A\theta_i)^T(A^{-T}e_j),因爲將其展開:(Aθi)T(ATej)=θiTATATej=θiTej(A\theta_i)^T(A^{-T}e_j)=\theta_i^TA^TA^{-T}e_j=\theta_i^Te_j

不能保證這些用來表示特徵的軸能夠等同於人類可能簡單理解的軸,具體而言,第一個特徵可能是個Gender、Roya、Age、Food Cost和Size的組合,它也許是名詞或是一個行爲動詞和其他所有特徵的組合,所以很難看出獨立組成部分,即這個嵌入矩陣的單行部分,然後解釋出它的意思。儘管有這種類型的線性變換,這個平行四邊形映射(?)也說明了我們解決了這個問題,當在類比其他問題時,這個方法也是行得通的。因此儘管存在特徵量潛在的任意線性變換,最終還是能學習出解決類似問題的平行四邊形映射。無論使用Skip-Gram模型還是GloVe模型等等,計算得到的Embedding Matrix E的每一個特徵值不一定對應有實際物理意義的特徵值。

參考資料:
(1)原論文:https://www.aclweb.org/anthology/D14-1162.pdf
(2)原項目(網頁很難打開):GloVe: Global Vectors for Word Representation
(3)理解GloVe模型(+總結)
(4)高級詞向量表達(一)——GloVe(理論、相關測評結果、R&python實現、相關應用)
(5)斯坦福CS224n視頻動手學深度學習視頻中科院NLP視頻(2010)

情緒分類
情感分類任務就是看一段文本,然後分辨這個人是否喜歡他們在討論的這個東西,這是NLP中最重要的模塊之一,經常用在許多應用中。情感分類一個最大的挑戰就是可能標記的訓練集沒有那麼多,但是有了詞嵌入,即使只有中等大小的標記的訓練集,也能構建一個不錯的情感分類器。

這是一個情感分類問題的一個例子,輸入xx是一段文本,而輸出yy是要預測的相應情感。比如說下圖是一個餐館評價的星級:

如果能訓練一個從xxyy的映射,基於這樣的標記的數據集,那麼就可以用來蒐集大家對運營的餐館的評價。如果有一個情感分類器,那麼它就可以看一段文本然後分析出這個人對餐館的評論的情感是正面的還是負面的,這樣就可以一直記錄是否存在一些什麼問題,或者餐館是在蒸蒸日上還是每況愈下。

情感分類一個最大的挑戰就是可能標記的訓練集沒有那麼多。對於情感分類任務來說,訓練集大小從10000到100000個單詞都很常見,甚至有時會小於10000個單詞,採用了詞嵌入能夠帶來更好的效果,尤其是隻有很小的訓練集時。

這是一個簡單的情感分類的模型,假設有一個句子"dessert is excellent",然後在詞典裏找這些詞,我們通常用10000個詞的詞彙表。我們要構建一個分類器能夠把它映射成輸出四個星,給定這四個詞,我們取這些詞,找到相應的one-hot向量,所以這裏就是o8928o_{8928},乘以嵌入矩陣EEEE可以從一個很大的文本集裏學習到,比如它可以從一億個詞或者一百億個詞裏學習嵌入,然後用來提取單詞the的嵌入向量e8928e_{8928},對dessert、is、excellent做同樣的步驟。

如果在很大的訓練集上訓練EE,比如一百億的單詞,這樣就會獲得很多知識,甚至從有些不常用的詞中獲取,然後應用到問題上,即使標記數據集裏沒有這些詞。可以這樣構建一個分類器,取這些向量,比如是300維度的向量。然後把它們求和或者求平均。這個單元會得到一個300維的特徵向量,把這個特徵向量送進Softmax分類器,然後輸出y^\hat{y}. 這個Softmax能夠輸出5個可能結果的概率值,從一星到五星,這個就是5個可能輸出的Softmax結果用來預測yy的值。

這裏用的平均值運算單元,這個算法適用於任何長短的評論,因爲即使評論是100個詞長,也可以對這一百個詞的特徵向量求和或者平均它們,然後得到一個表示一個300維的特徵向量表示,然後把它送進Softmax分類器,所以這個平均值運算效果不錯。它實際上會把所有單詞的意思給平均起來,或者把例子中所有單詞的意思加起來就可以用了。

這個算法有一個問題就是沒考慮詞序,尤其是這樣一個負面的評價,“Completely lacking in good taste, good service, and good ambience.”,但是good這個詞出現了很多次,有3個good,如果用的算法跟這個一樣,忽略詞序,僅僅把所有單詞的詞嵌入加起來或者平均下來,最後的特徵向量會有很多good的表示,分類器很可能認爲這是一個好的評論,儘管事實上這是一個差評,只有一星的評價。

我們有一個更加複雜的模型,不是簡單地把所有的詞嵌入都加起來,我們用一個RNN來做情感分類。首先取這條評論,“Completely lacking in good taste, good service, and good ambiance.”,找出每一個one-hot向量,用每一個one-hot向量乘以詞嵌入矩陣EE,得到詞嵌入表達ee,然後把它們送進RNN裏。RNN的工作就是在最後一步計算一個特徵表示,用來預測y^\hat{y},這是一個多對一的網絡結構的例子。有了這樣的算法,考慮詞的順序效果就更好了,它就能意識到"things are lacking in good taste",這是個負面的評價,“not good”也是一個負面的評價。而不像原來的算法一樣,只是把所有的加在一起得到一個大的向量,根本意識不到“not good”和 “good”不是一個意思,"lacking in good taste"也是如此,等等。

如果訓練一個這樣的算法,最後會得到一個很合適的情感分類的算法。由於詞嵌入(Word Embedding)是在一個更大的數據集裏訓練的,這樣效果會更好,更好地泛化一些沒有見過的新的單詞。比如其他人可能會說,“Completely absent of good taste, good service, and good ambiance.”,即使absent這個詞不在標記的訓練集裏,如果是在一億或者一百億單詞集裏訓練詞嵌入,它仍然可以正確判斷,並且泛化的很好,甚至這些詞是在訓練集中用於訓練詞嵌入的,但是可以不在專門用來做情感分類問題的標記的訓練集中。

一旦學習到或者從網上下載詞嵌入,就可以很快構建一個很有效的NLP系統。

詞嵌入除偏
仍然很難!
現在機器學習和人工智能算法正漸漸地被信任用以輔助或是制定極其重要的決策,因此我們想盡可能地確保它們不受非預期形式偏見影響,比如說性別歧視、種族歧視等等。

當使用術語bias時,是指性別、種族、性取向方面的偏見,那是不同的偏見,同時這也通常用於機器學習的學術討論中。不過我們討論的大部分內容是詞嵌入是怎樣學習類比像Man: Woman,就像King: Queen,這篇論文:Bolukbasi et. al.,2016. Man is to Computer Programmer as Woman is to Homemaker? Debiasing Word Embeddings. 發現了一個十分可怕的結果,一個已經完成學習的詞嵌入可能會輸出Man: Computer Programmer,同時輸出Woman: Homemaker,結果看起來是錯的,並且它執行了一個十分不良的性別歧視。如果算法輸出的是Man: Computer Programmer,同時Woman: Computer Programmer這樣子會更合理。同時他們也發現如果Father: Doctor,有些完成學習的詞嵌入會輸出Mother: Nurse(因爲Mother也可以是Doctor)。

因此根據訓練模型所使用的文本,詞嵌入能夠反映出性別、種族、年齡、性取向等其他方面的偏見,這些偏見都和社會經濟狀態相關,每個人不論出身富裕還是貧窮,亦或是二者之間,都應當擁有好的機會,同時因爲機器學習算法正用來制定十分重要的決策,它也影響着世間萬物,從大學錄取到人們找工作的途徑,到貸款申請,不論貸款申請是否會被批准,再到刑事司法系統,甚至是判決標準,學習算法都在作出非常重要的決策,所以我們儘量修改學習算法來儘可能減少或是理想化消除這些非預期類型的偏見是十分重要的。

至於詞嵌入,它們能夠輕易學會用來訓練模型的文本中的偏見內容,所以算法獲取到的偏見內容就可以反映出人們寫作中的偏見。

假設我們已經完成一個詞嵌入的學習,那麼babysitter,doctor,grandmother,grandfather,girl,boy,she,he的位置如下圖所示(圖中grandmather應爲grandmother),所以首先要辨別出想要減少或想要消除的特定偏見的趨勢(direction)。爲了便於說明,集中討論性別歧視,不過這些想法對於其他類型的偏見都是通用的。辨別出與這個偏見相似的趨勢,主要有以下三個步驟:

一、確定偏見bias的方向(趨勢)。方法是對所有性別對立的單詞求差值,再平均。對於性別歧視這種情況來說,我們能做的是eheeshee_{he}-e_{she},因爲它們的性別不同,然後將emaleefemalee_{male}-e_{female},這些值取平均,將這些差簡單地求平均。這個(橫軸)趨勢看起來就是性別趨勢或說是偏見趨勢,然後這個(豎軸)趨勢與我們想要嘗試處理的特定偏見並不相關,因此這就是個無偏見趨勢。在這種情況下,偏見趨勢可以將它看做1D子空間,所以這個無偏見趨勢就會是299D的子空間。原文章中的描述這個偏見趨勢可以比1維更高,同時相比於取平均值,實際上它會用一個更加複雜的算法叫做SVD,也就是奇異值分解,如果對主成分分析很熟悉的話,奇異值分解這個算法的一些方法和主成分分析(PCA)其實很類似。biasdirection=1N((eheeshe)+(emaleefemale)+)bias\,direction=\frac{1}{N}((e_{he}-e_{she})+(e_{male}-e_{female})+\cdots)

二、單詞中立(中和)化,將需要消除性別偏見的單詞投影到non-bias direction上去,消除bias維度,例如babysitter,doctor等。有些詞本質上就和性別有關,像grandmother、grandfather、girl、boy、she、he,它們的定義中本就含有性別的內容,不過也有一些詞像doctor和babysitter我們想使之在性別方面是中立的。同時在更通常的情況下,可能會希望像doctor或babysitter這些詞成爲種族中立的,或是性取向中立的等等,不過這裏仍然只用性別來舉例說明。所以對於像doctor和babysitter這種單詞我們就可以將它們在這個軸(豎軸)上進行處理,來減少或是消除它們的性別歧視趨勢的成分,也就是說減少它們在這個水平方向上的距離。

三、均衡對,讓性別對立單詞與上面的中立詞距離相等,具有同樣的相似度。例如讓grandmother和grandfather與babysitter的距離同一化。意思是說可能會有這樣的詞對,grandmother和grandfather,或者是girl和boy,對於這些詞嵌入,只希望性別是其區別。babysitter和grandmother之間的距離或者說是相似度實際上是小於babysitter和grandfather之間的,因此這可能會加重不良狀態,或者可能是非預期的偏見,也就是說grandmother相比於grandfather最終更有可能輸出babysitter. 所以在最後的均衡步中,我們想要確保的是像grandmother和grandfather這樣的詞都能夠有一致的相似度,或者說是相等的距離,和babysitter或是doctor這樣性別中立的詞一樣。它主要做的就是將grandmother和grandfather移至與中間軸線等距的一對點上,現在性別歧視的影響也就是這兩個詞與babysitter的距離就完全相同了。所以總體來說,會有許多對像grandmother-grandfather,boy-girl,sorority-fraternity,girlhood-boyhood,sister-brother,niece-nephew,daughter-son這樣的詞對,可能想要通過均衡步來解決它們。

最後一個細節是怎樣才能夠決定哪個詞是中立的呢?對於這個例子來說doctor看起來像是一個應該對其中立的單詞來使之性別不確定或是種族不確定。相反地,grandmother和grandfather就不應是性別不確定的詞。也會有一些像是beard詞,一個統計學上的事實是男性相比於比女性更有可能擁有鬍子,因此也許beard應該比female更靠近male一些。一般來說,大部分英文單詞,例如職業、身份等都需要中立化,消除Embedding Vector中性別這一維度的影響。

因此論文作者做的就是訓練一個分類器來嘗試解決哪些詞是有明確定義的,哪些詞是性別確定的,哪些詞不是。結果表明英語裏大部分詞在性別方面上是沒有明確定義的,只有一小部分詞像是grandmother-grandfather,girl-boy,sorority-fraternity等等,不是性別中立的。因此一個線性分類器能夠告訴你哪些詞能夠通過中和步來預測這個偏見趨勢,或將其與這個本質是299D的子空間進行處理。

最後,需要平衡的詞對的數實際上是很小的,至少對於性別歧視這個例子來說,用手都能夠數出來需要平衡的大部分詞對。完整的算法會比在這裏展示的更復雜一些,可以去看一下原論文了解詳細內容。總結一下,減少或者是消除學習算法中的偏見問題是個十分重要的問題,因爲這些算法會用來輔助制定越來越多的社會中的重要決策。

參考資料:針對性別特定詞彙的均衡算法
如何對兩個單詞除偏,比如:“actress"和"actor”. 均衡算法適用於您可能希望僅通過性別屬性不同的單詞對。 舉一個具體的例子,假設"actress“比"actor"更接近"保姆"。通過將中和應用於"babysit",我們可以減少與保姆相關的性別刻板印象。但是這仍然不能保證"actress"和"actor"與"babysit"等距。均衡算法可以解決這個問題。
均衡背後的關鍵思想是確保一對特定的單詞與49維gg_{\bot}距離相等 。均衡步驟還可以確保兩個均衡步驟現在與ereceptionistdebiasede^{debiased}_{receptionist}距離相同,或者用其他方法進行均衡。下圖演示了均衡算法的工作原理:

主要步驟如下:(公式的推導有點複雜(參考原論文))

出處:http://www.ai-start.com/dl2017/html/lesson5-week2.html#header-n138

序列模型和注意力機制

基礎模型
想通過輸入一個法語句子,比如這句 “Jane visite I’Afrique en septembre.”,將它翻譯成一個英語句子,“Jane is visiting Africa in September.”。我們用x<1>x^{<1>}一直到x<5>x^{<5>}來表示輸入的句子的單詞,然後用y<1>y^{<1>}y<6>y^{<6>}來表示輸出的句子的單詞,那麼,如何訓練出一個新的網絡來輸入序列xx和輸出序列yy呢?

論文:(1)Sutskever et al., 2014. Sequence to sequence learning with neural networks.
(2)Cho et al., 2014. Learning phrase representations using RNN encoder-decoder for statistical machine translation.

首先,我們先建立一個網絡,這個網絡叫做編碼網絡(encoder network),它是一個RNN的結構,RNN的單元可以是GRU或LSTM。每次只向該網絡中輸入一個法語單詞,將輸入序列接收完畢後,這個RNN網絡會輸出一個向量來代表這個輸入序列。之後可以建立一個解碼網絡(decoder network),它以編碼網絡的輸出作爲輸入,之後它可以被訓練爲每次輸出一個翻譯後的單詞,一直到它輸出序列的結尾或者句子結尾標記,這個解碼網絡的工作就結束了。和往常一樣我們把每次生成的標記都傳遞到下一個單元中來進行預測,就像之前用語言模型合成文本時一樣。

深度學習在近期最卓越的成果之一就是這個模型確實有效,在給出足夠的法語和英語文本的情況下,如果訓練這個模型,通過輸入一個法語句子來輸出對應的英語翻譯,這個模型將會非常有效。這個模型簡單地用一個編碼網絡來對輸入的法語句子進行編碼,然後用一個解碼網絡來生成對應的英語翻譯。

圖像描述(圖像捕捉),給出一張圖片,比如這張貓的圖片,它能自動地輸出該圖片的描述,一隻貓坐在椅子上。將圖片輸入到卷積神經網絡中,比如一個預訓練的AlexNet結構,然後讓其學習圖片的編碼,或者學習圖片的一系列特徵。我們去掉最後的Softmax單元,這個預訓練的AlexNet結構會得到一個4096維的特徵向量(編碼向量),向量表示的就是這隻貓的圖片,所以這個預訓練網絡可以是圖像的編碼網絡。接着可以把這個4096維向量輸入到RNN(decoder network)中,RNN要做的就是生成圖像的描述,每次生成一個單詞。現在輸入一個描述貓的特徵向量,然後讓網絡生成一個輸出序列,或者說一個一個地輸出單詞序列。

事實證明在圖像描述領域,這種方法相當有效,特別是當想生成的描述不是特別長時。

論文:(1)Mao et. al., 2014. Deep captioning with multimodal recurrent neural networks.
(2)Vinyals et. al., 2014. Show and tell: Neural image caption generator.
(3)Karpathy and Fei Fei, 2015. Deep visual-semantic alignments for generating image descriptions.

以上就是seq2seq模型以及image2seq模型或者說圖像描述模型,這兩個模型運作方式有一些不同,主要體現在如何用語言模型合成新的文本,並生成對應序列的方面。一個主要的區別就是不會想得到一個隨機選取的翻譯,想要的是最準確的翻譯,或者說可能不想要一個隨機選取的描述,想要的是最好的最貼切的描述。

選擇最可能的句子
語言模型是自動生成一條完整語句,語句是隨機的。機器翻譯模型,用綠色表示encoder網絡,用紫色表示decoder網絡。會發現decoder網絡看起來和之前講的語言模型(1.6節)幾乎一模一樣,不同在於語言模型總是以零向量(a<0>a^{<0>})開始,而encoder網絡會計算出一系列向量來表示輸入的句子。decoder網絡就可以從這個句子開始,所以把它叫做條件語言模型(conditional language model)。相比語言模型,輸出任意句子的概率,翻譯模型會輸出句子的英文翻譯,將估計一個英文翻譯的概率,“Jane is visiting Africa in September.”,這句翻譯是取決於法語句子,“Jane visite I’Afrique en septembre.”,這就是英語句子相對於輸入的法語句子的可能性,所以它是一個條件語言模型。P(y<1>,,y<Ty>x<1>,,x<Tx>)P(y^{<1>},\cdots,y^{<T_y>}|x^{<1>},\cdots,x^{<T_x>})

現在,假如想真正地通過模型將法語翻譯成英文,通過輸入的法語句子模型將會告訴你各種英文翻譯所對應的可能性。xx在這裏是法語句子。顯然不想讓它隨機地進行輸出,如果從這個分佈中進行取樣得到P(yx)P(y|x),可能取樣一次就能得到很好的翻譯,“Jane is visiting Africa in September.”。但是可能也會得到一個截然不同的翻譯,“Jane is going to be visiting Africa in September.”,這句話聽起來有些笨拙,但它不是一個糟糕的翻譯,只是不是最好的而已。有時也會得到這樣的翻譯,“In September, Jane will visit Africa.”,或者會得到一個很糟糕的翻譯,“Her African friend welcomed Jane in September.”。所以當使用這個模型來進行機器翻譯時,並不是從得到的分佈中進行隨機取樣,而是要找到一個英語句子yy,使得條件概率最大化。所以在開發機器翻譯系統時,需要做的一件事就是想出一個算法,用來找出合適的yy值,使得條件概率最大化,而解決這種問題最通用的算法就是束搜索(Beam Search)。argmaxyP(y^<1>,,y^<Ty>x)arg\underset{y}{max}P(\hat{y}^{<1>},\cdots,\hat{y}^{<T_y>}|x)

爲什麼不用貪心搜索(Greedy Search)呢?貪心搜索是一種來自計算機科學的算法,生成第一個詞的分佈以後,它將會根據條件語言模型挑選出最有可能的第一個詞進入機器翻譯模型中,在挑選出第一個詞之後它將會繼續挑選出最有可能的第二個詞,然後繼續挑選第三個最有可能的詞,這種算法就叫做貪心搜索,但是真正需要的是一次性挑選出整個單詞序列,從y<1>y^{<1>}y<2>y^{<2>}y<Ty>y^{<T_y>}來使得整體的概率最大化。所以這種貪心算法先挑出最好的第一個詞,在這之後再挑最好的第二詞,然後再挑第三個,這種方法其實並不管用,爲了證明這個觀點,我們來考慮下面兩種翻譯。

  1. Jane is visiting Africa in September.
  2. Jane is going to be visiting Africa in September.

第一串翻譯明顯比第二個好,所以我們希望機器翻譯模型會說第一個句子的P(yx)P(y|x)比第二個句子要高,第一個句子對於法語原文來說更好更簡潔,雖然第二個也不錯,但是有些囉嗦,裏面有很多不重要的詞。但如果貪心算法挑選出了"Jane is"作爲前兩個詞,因爲在英語中going更加常見,於是對於法語句子來說"Jane is going"相比"Jane is visiting"會有更高的概率作爲法語的翻譯(P(Jane is going|x)>P(Jane is visiting|x)),所以很有可能如果僅僅根據前兩個詞來估計第三個詞的可能性,得到的就是going,最終會得到一個欠佳的句子,在P(yx)P(y|x)模型中這不是一個最好的選擇。Greedy Search大大增加了運算成本,降低運算速度。

當想得到單詞序列y<1>y^{<1>}y<2>y^{<2>}一直到最後一個詞總體的概率時,一次僅僅挑選一個詞並不是最佳的選擇。當然,在英語中各種詞彙的組合數量還有很多很多,如果字典中有10000個單詞,並且翻譯可能有10個詞那麼長,那麼可能的組合就有100001010000^{10}這麼多,所以可能的句子數量非常巨大,不可能去計算每一種組合的可能性。所以這時最常用的辦法就是用一個近似的搜索算法(近似最優),它會盡力地,儘管不一定總會成功,但它將挑選出句子yy使得條件概率最大化,儘管它不能保證找到的yy值一定可以使概率最大化,但這已經足夠了。

條件語言模型和之前的語言模型一個主要的區別就是,相比之前的模型隨機地生成句子,在該模型中要找到最有可能的英語句子,最可能的英語翻譯,但是可能的句子組合數量過於巨大,無法一一列舉,所以需要一種合適的搜索算法:集束搜索

集束搜索
對於機器翻譯來說,給定輸入,比如法語句子,不會想要輸出一個隨機的英語翻譯結果,想要一個最好的,最可能的英語翻譯結果。對於語音識別也一樣,給定一個輸入的語音片段,不會想要一個隨機的文本翻譯結果,想要最好的,最接近原意的翻譯結果,集束搜索就是解決這個最常用的算法。

“Jane visite l’Afrique en Septembre.”,我們希望翻譯成英語,“Jane is visiting Africa in September”.,集束搜索算法首先做的就是挑選要輸出的英語翻譯中的第一個單詞。這裏列出了10000個詞的詞彙表,所有的單詞都以小寫列出來。在集束搜索的第一步中用這個網絡,綠色是編碼部分,紫色是解碼部分,來評估第一個單詞的概率值,給定輸入序列xx,即法語作爲輸入,第一個輸出yy的概率值是P(y^<1>x)P(\hat{y}^{<1>}|x).

貪婪算法只會挑出最可能的那一個單詞,然後繼續。而集束搜索則會考慮多個選擇,集束搜索算法會有一個參數B,叫做集束寬(beam width)。在這個例子中把這個集束寬設成3,這樣集束搜索一次考慮3個可能結果,比如對第一個單詞有不同選擇的可能性,最後找到in、jane、september,是英語輸出的第一個單詞的最可能的三個選項,然後集束搜索算法會把結果存到計算機內存裏以便後面嘗試用這三個詞。如果集束寬是10,那麼跟蹤的不僅僅3個,而是10個第一個單詞的最可能的選擇。所以爲了執行集束搜索的第一步,需要輸入法語句子到編碼網絡,然後會解碼這個網絡,這個Softmax層會輸出10000個概率值,得到這10000個輸出的概率值,取前三個存起來。

集束搜索算法的第二步,已經選出了in、jane、september作爲第一個單詞三個最可能的選擇,集束算法接下來會針對每個第一個單詞考慮第二個單詞是什麼,單詞in後面的第二個單詞可能是a或者是aaron,從詞彙表裏把這些詞列了出來,或者是september,可能是列表裏的visit,一直到最後一個單詞zulu.

爲了評估第二個詞的概率值,用這個神經網絡,對於解碼部分,當決定單詞in後面是什麼,別忘了解碼器的第一個輸出y^<1>\hat{y}^{<1>},把y^<1>\hat{y}^{<1>}設爲單詞in,然後把它喂回來,因爲它的目的是努力找出第一個單詞是in的情況下,第二個單詞是什麼。這個輸出就是y^<2>\hat{y}^{<2>},有了這個連接,就是這裏的第一個單詞in作爲輸入,這樣這個網絡就可以在給定法語句子和翻譯結果的第一個單詞in的情況下評估第二個單詞的概率:P(y^<2>x,in)P(\hat{y}^{<2>}|x,“in”).

注意,在第二步裏我們更關心的是要找到最可能的第一個和第二個單詞對,所以不僅僅是第二個單詞有最大的概率,而是第一個、第二個單詞對有最大的概率。按照條件概率的準則,這個可以表示成第一個單詞的概率乘以第二個單詞的概率(利用條件概率、事件交換和乘法公式):P(y^<1>,y^<2>x)=P(y^<1>x)P(y^<2>x,y^<1>)P(\hat{y}^{<1>},\hat{y}^{<2>}|x)=P(\hat{y}^{<1>}|x)P(\hat{y}^{<2>}|x,\hat{y}^{<1>})

這個P(y^<2>x,y^<1>)P(\hat{y}^{<2>}|x,\hat{y}^{<1>})可以從網絡部分裏得到,對於已經選擇的in、jane、september這三個單詞,可以先保存這個P(y^<1>x)P(\hat{y}^{<1>}|x)概率值,然後再乘以第二個概率值就得到了第一個和第二個單詞對的概率。

現在第一個單詞是jane,句子可能是"jane a"、“jane aaron”,到"jane is"、"jane visits"等等。這個網絡部分就可以告訴你給定輸入xx和第一個詞是jane的情況下,第二個單詞的概率了,和上面一樣,可以乘以P(y^<1>x)P(\hat{y}^{<1>}|x)得到P(y^<1>,y^<2>x)P(\hat{y}^{<1>},\hat{y}^{<2>}|x).

針對第二個單詞所有10000個不同的選擇,最後對於單詞september也一樣,從單詞a到單詞zulu,用這個網絡部分,來看看如果第一個單詞是september,第二個單詞最可能是什麼。所以對於集束搜索的第二步,由於我們一直用的集束寬爲3,並且詞彙表裏有10000個單詞,那麼最終會有30000個可能的結果,就是集束寬乘以詞彙表大小,要做的就是評估這30000個選擇。按照第一個詞和第二個詞的概率,然後選出前三個,這樣又減少了這30000個可能性,又變成了3個,減少到集束寬的大小。假如這30000個選擇裏最可能的是“in september”和“jane is”,以及“jane visits”,這就是這30000個選擇裏最可能的三個結果,集束搜索算法會保存這些結果,然後用於下一次集束搜索。

因爲集束寬等於3,每一步我們都複製3個同樣的這種網絡來評估部分句子和最後的結果,我們有三個網絡副本,每個網絡的第一個單詞不同,而這三個網絡可以高效地評估第二個單詞所有的30000個選擇。所以不需要初始化30000個網絡副本,只需要使用3個網絡的副本就可以快速地評估Softmax的輸出,即y^<2>\hat{y}^{<2>}的10000個結果。

集束搜索的第三步,前兩個單詞最可能的選擇是“in September”和“jane is”以及“jane visits”,對於每一對單詞我們應該保存起來,給定輸入xx,即法語句子作爲xx的情況下,y^<1>\hat{y}^{<1>}y^<2>\hat{y}^{<2>}的概率值和前面一樣,現在考慮第三個單詞是什麼,可以是“in september a”,“in september aaron”,一直到“in september zulu”。爲了評估第三個單詞可能的選擇,我們用這個網絡部分,第一單詞是in,第二個單詞是september,所以這個網絡部分可以用來評估在給定輸入的法語句子xx和給定的英語輸出的前兩個單詞“in September”情況下第三個單詞的概率P(y^<3>x,y^<1>,y^<2>)P(\hat{y}^{<3>}|x,\hat{y}^{<1>},\hat{y}^{<2>}). 此時得到的前三個單詞的3種情況的概率爲:P(y^<1>,y^<2>,y^<3>x)=P(y^<1>x)P(y^<2>x,y^<1>)P(y^<3>x,y^<1>,y^<2>)P(\hat{y}^{<1>},\hat{y}^{<2>},\hat{y}^{<3>}|x)=P(\hat{y}^{<1>}|x)P(\hat{y}^{<2>}|x,\hat{y}^{<1>})P(\hat{y}^{<3>}|x,\hat{y}^{<1>},\hat{y}^{<2>})

對於“jane is”和“jane visits”也一樣,然後集束搜索還是會挑選出針對前三個詞的三個最可能的選擇,可能是“in september jane”,“jane is visiting”,“jane visits Africa”.

然後繼續,接着進行集束搜索的第四步,再加一個單詞繼續,最終這個過程的輸出一次增加一個單詞,集束搜索最終會找到“jane visits africa in september”這個句子,終止在句尾符號< EOS >,用這種符號的系統非常常見,它們會發現這是最有可能輸出的一個英語句子。在集束寬爲3時,集束搜索一次只考慮3個可能結果。注意如果集束寬等於1,只考慮1種可能結果,這實際上就變成了貪婪搜索算法。但是如果同時考慮多個,可能的結果比如3個,10個或者其他的個數,集束搜索通常會找到比貪婪搜索更好的輸出結果。

改進集束搜索
長度歸一化(Length normalization)就是對束搜索算法稍作調整的一種方式,幫助得到更好的結果。束搜索就是最大化這個概率:argmaxyt=1TyP(y<t>x,y<1>,,y<t1>)=argmaxyP(y<1>,,y<Ty>x)arg\underset{y}{max}\prod_{t=1}^{T_y}{P(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>})}\\=arg\underset{y}{max}P(y^{<1>},\cdots,y^{<T_y>}|x)

這些概率值都是小於1的,通常遠小於1。很多小於1的數乘起來,會得到很小很小的數字,會造成數值下溢。數值太小了,導致電腦的浮點表示不能精確地儲存。因此在實踐中,我們不會最大化這個乘積,而是取log值:argmaxyt=1TylogP(y<t>x,y<1>,,y<t1>)arg\underset{y}{max}\sum_{t=1}^{T_y}{logP(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>})}

在這加上一個log,最大化這個log求和的概率值,在選擇最可能的句子yy時,會得到同樣的結果。所以通過取log,會得到一個數值上更穩定的算法,不容易出現四捨五入的誤差,數值的舍入誤差或者說數值下溢。因爲log函數是嚴格單調遞增的函數,所以最大化logP(yx)logP(y|x)和最大化P(yx)P(y|x)結果一樣。

如果參照原來的目標函數,如果有一個很長的句子,那麼這個句子的概率會很低,因爲乘了很多項小於1的數字來估計句子的概率。所以這個目標函數有一個缺點,它可能不自然地傾向於簡短的翻譯結果,它更偏向短的輸出,因爲短句子的概率是由更少數量的小於1的數字乘積得到的,所以這個乘積不會那麼小。這裏也有同樣的問題,概率值通常小於等於1,實際上在log的這個範圍內,加起來的項越多,得到的結果越負。所以對這個算法另一個改變就是我們不再最大化這個目標函數了,可以把它歸一化,通過除以翻譯結果的單詞數量。這樣就是取每個單詞的概率對數值的平均了,這樣很明顯地減少了對輸出長的結果的懲罰。1Tyαt=1TylogP(y<t>x,y<1>,,y<t1>)\frac{1}{T_y^{\alpha}}\sum_{t=1}^{T_y}{logP(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>})}

在實踐中,有個探索性的方法,相比於直接除以TyT_y,有時會用一個更柔和的方法,在TyT_y上加上指數α\alphaα\alpha可以等於0.7。如果α=1\alpha=1,就相當於完全用長度來歸一化,如果α\alpha等於0,TyT_y的0次冪就是1,就相當於完全沒有歸一化。α\alpha就是算法另一個超參數,需要調整大小來得到最好的結果。不得不承認,這樣用α\alpha實際上是試探性的,它並沒有理論驗證。但是大家都發現實踐中效果不錯,所以很多人都會這麼做。

當運行束搜索時,會看到很多長度等於1、2、3的句子。可能運行束搜索30步,考慮輸出的句子可能達到長度30。因爲束寬爲3,會記錄所有這些可能的句子長度,長度爲1、2、 3、 4 ……30的三個最可能的選擇。然後針對這些所有的可能的輸出句子,用1Tyαt=1TylogP(y<t>x,y<1>,,y<t1>)\frac{1}{T_y^{\alpha}}\sum_{t=1}^{T_y}{logP(y^{<t>}|x,y^{<1>},\cdots,y^{<t-1>})}給它們打分,取概率最大的幾個句子,然後對這些束搜索得到的句子,計算這個目標函數(上式)。最後從經過評估的這些句子中,挑選出在歸一化的loglog概率目標函數上得分最高的一個,有時這個也叫作歸一化的對數似然目標函數

如果束寬很大,會考慮很多的可能,會得到一個更好的結果,但是算法會運行的慢一些,內存佔用也會增大,計算起來會慢一點。而如果用小的束寬,結果會沒那麼好,因爲在算法運行中,保存的選擇更少,但是算法運行的更快,內存佔用也小。

我們例子中用了束寬(B)爲3,所以會保存3個可能選擇,在實踐中這個值有點偏小。在產品中,經常可以看到把束寬設到10,束寬爲100對於產品系統來說有點大了,這也取決於不同應用。但是對科研而言,人們想壓榨出全部性能,這樣有個最好的結果用來發論文,也經常看到大家用束寬爲1000或者3000,這也是取決於特定的應用和特定的領域。當B很大的時候,性能提高會越來越少。對於很多應用來說,從束寬1,也就是貪心算法,到束寬爲3、10,會看到一個很大的改善,但是當束寬從1000增加到3000時,效果就沒那麼明顯了。

計算機科學裏的搜索算法,比如廣度優先搜索(BFS),或者深度優先搜索(DFS),束搜索不像其他在計算機科學算法課程中學到的算法一樣。廣度優先搜索和深度優先搜索,這些都是精確的搜索算法,不同於這些算法,束搜索運行的更快,但是不能保證一定能找到argmax的準確的最大值,並不保證一定能找到正確的翻譯語句。

集束搜索的誤差分析
誤差分析能夠幫助你集中時間做項目中最有用的工作,束搜索算法是一種近似搜索算法,也被稱作啓發式搜索算法(a heuristic search algorithm),它不總是輸出可能性最大的句子,它僅記錄着B爲3或10或100種可能。

用這個例子說明: “Jane visite l’Afrique en septembre”。假如說,在機器翻譯的dev集中,人工是這樣翻譯的:Jane visits Africa in September,將這個標記爲yy^{*}. 這是一個十分不錯的人工翻譯結果。當你在已經完成學習的RNN模型,也就是已完成學習的翻譯模型中運行束搜索算法時,它輸出了這個翻譯結果:Jane visited Africa last September,將它標記爲y^\hat{y}. 這是一個十分糟糕的翻譯,它實際上改變了句子的原意。

模型有兩個主要部分,一個是神經網絡模型,或者說是seq2seq模型,將這個稱作是RNN模型,它實際上是個編碼器和解碼器。另一部分是束搜索算法,以某個集束寬度B運行。我們希望找出造成這個錯誤,這個不太好的翻譯的原因,是兩個部分中的哪一個。大家很容易想到去收集更多的訓練數據,這總歸沒什麼壞處。所以同樣的,大家也會覺得不行就增大束寬,也是不會錯的,或者說是很大可能是沒有危害的。但是單純增大束寬也可能得不到想要的結果。

RNN實際上是個編碼器和解碼器,它會計算P(yx)P(y|x). 舉個例子,對於這個句子:Jane visits Africa in September,將Jane visits Africa填入這裏,然後這個就會計算P(yx)P(y|x). 結果表明,此時能做的最有效的事就是用這個模型來計算P(yx)P(y^{*}|x),同時也用RNN模型來計算P(y^x)P(\hat{y}|x),然後比較一下這兩個值哪個更大。取決於實際是哪種情況,就能夠更清楚地將這個特定的錯誤歸咎於RNN或是束搜索算法,或說是哪個負有更大的責任。

第一種情況,RNN模型的輸出結果P(yx)>P(y^x)P(y^{*}|x)>P(\hat{y}|x),束搜索算法選擇了y^\hat{y},得到y^\hat{y}的方式是,用一個RNN模型來計算P(yx)P(y|x),然後束搜索算法做的就是嘗試尋找使P(yx)P(y|x)最大的yy,不過在這種情況下,yy^{*}P(yx)P(y|x)值更大,因此束搜索算法實際上不能夠給出一個能使P(yx)P(y|x)最大化的yy值。因此這種情況下束搜索算法出錯了。

第二種情況是P(yx)P(y^x)P(y^{*}|x)\leqslant P(\hat{y}|x)yy^{*}是比y^\hat{y}更好的翻譯結果,不過根據RNN模型的結果, P(y)P(y^{*})是小於P(y^)P(\hat{y})的,也就是說,相比於y^\hat{y}yy^{*}成爲輸出的可能更小。因此在這種情況下,看來是RNN模型出了問題,可能值得在RNN模型上花更多時間。這裏略過了有關長度歸一化的細節,如果用了某種長度歸一化,那麼要比較長度歸一化後的最優化目標函數值。

先遍歷開發集,然後在其中找出算法產生的錯誤(即算法輸出了比人工翻譯更差的結果的情況),第一個例子中,假如說P(yx)P(y^{*}|x)的值爲2×10102\times10^{-10},而P(y^x)P(\hat{y}|x)的值爲1×10101\times10^{-10},這種情況下束搜索算法出錯了。接着繼續遍歷第二個錯誤,也許是生成目標函數(束搜索算法使之最大化)的RNN模型出現了問題。再接着遍歷了更多的例子。通過這個過程,就能夠執行誤差分析,得出束搜索算法和RNN模型出錯的比例是多少。

當發現是束搜索算法造成了大部分錯誤時,才值得花費努力增大集束寬度。相反地,如果發現是RNN模型出了更多錯,那麼可以進行更深層次的分析,來決定是需要增加正則化還是獲取更多的訓練數據,抑或是嘗試一個不同的網絡結構,或是其他方案。在第三門課(結構化機器學習項目)中,瞭解到各種技巧都能夠應用在這裏。

這就是束搜索算法中的誤差分析,這個特定的誤差分析過程是十分有用的,它可以用於分析近似最佳算法(如束搜索算法),這些算法被用來優化學習算法(例如seq2seq模型/RNN)輸出的目標函數。

Bleu得分
機器翻譯的一大難題是一個法語句子可以有多種英文翻譯而且都同樣好,所以當有多個同樣好的答案時,怎樣評估一個機器翻譯系統呢?常見的解決辦法是,通過一個叫做BLEU得分的東西來解決。

一個法語句子:Le chat est sur le tapis,然後給你一個這個句子的人工翻譯作參考:The cat is on the mat。不過有多種相當不錯的翻譯,也許會將其翻譯爲:There is a cat on the mat,實際上這兩個都是很好的,都準確地翻譯了這個法語句子。BLEU得分做的就是,給定一個機器生成的翻譯,它能夠自動地計算一個分數來衡量機器翻譯的好壞。直覺告訴我們,只要這個機器生成的翻譯與任何一個人工翻譯的結果足夠接近,那麼它就會得到一個高的BLEU分數。

BLEU代表bilingual evaluation understudy(雙語評估替補)。在戲劇界,侯補演員(understudy)學習資深的演員的角色,這樣在必要的時候,他們就能夠接替這些資深演員。而BLEU的初衷是相對於請評估員,人工評估機器翻譯系統,BLEU得分就相當於一個侯補者,它可以代替人類來評估機器翻譯的每一個輸出結果。

參考論文Papineni et. al., 2002. BLEU: A Method for Automatic Evaluation of Machine Translation. 這篇論文十分有影響力並且實際上也是一篇很好讀的文章。BLEU得分背後的理念是觀察機器生成的翻譯,然後看生成的詞是否出現在至少一個人工翻譯參考之中。因此這些人工翻譯的參考會包含在開發集或是測試集中。

  • 參考1:The cat is on the mat.
  • 參考2:There is a cat on the mat.
  • MT輸出:the the the the the the the.

假設機器翻譯系統縮寫爲MT。機器翻譯(MT)的輸出是:the the the the the the the. 這顯然是一個十分糟糕的翻譯。衡量機器翻譯輸出質量的方法之一是觀察輸出結果的每一個詞看其是否出現在參考中,這叫做機器翻譯的精確度(precision)。這個情況下,機器翻譯輸出了七個單詞並且這七個詞中的每一個都出現在了參考1或是參考2。單詞the在兩個參考中都出現了,所以看上去每個詞都是很合理的。因此這個輸出的精確度就是77\frac{7}{7},看起來是一個極好的精確度。把出現在參考中的詞在MT輸出的所有詞中所佔的比例作爲精確度評估標準並不是很有用,因爲它似乎意味着,例子中MT輸出的翻譯有很高的精確度。

我們要用的這個改良後的精確度評估方法,把每一個單詞的記分上限定爲它在參考句子中出現的最多次數。在參考1中,單詞the出現了2次,在參考2中,單詞the只出現了1次。而2比1大,所以單詞the的得分上限爲2。有了這個改良後的精確度,這個輸出句子的得分爲27\frac{2}{7},因爲在7個詞中,我們最多隻能給它2分。所以這裏分母就是7個詞中單詞the總共出現的次數,而分子就是單詞the出現的計數。我們在達到上限時截斷計數,這就是改良(modified)後的精確度評估。

  • 參考1:The cat is on the mat.
  • 參考2:There is a cat on the mat.
  • MT輸出:The cat the cat on the mat.

在BLEU得分中,也許想考慮成對的單詞,我們定義一下二元詞組(bigrams)的BLEU得分。bigram的意思就是相鄰的兩個單詞。這僅僅只是最終的BLEU得分的一部分,我們會考慮一元詞組(unigrams)以及二元詞組,同時也許會有更長的單詞序列,比如說三元詞組(trigrams)。現在我們假定機器翻譯輸出了稍微好一點的翻譯:The cat the cat on the mat。這裏可能的二元詞組有the cat ,cat the,cat on,on the,the mat,這些就是機器翻譯中的二元詞組。the cat出現了兩次 ,cat the出現了一次,剩下的都只出現了一次。最後定義一下截取計數(the clipped count),也就是Countclip. 給算法設置得分上限,上限值爲二元詞組出現在參考1或2中的最大次數。the cat在兩個參考中最多出現一次,將截取它的計數爲1。cat the它並沒有出現在參考1和參考2中,將它截取爲0。cat on出現了一次,我們就記1分。on the出現一次就記1分,the mat出現了一次,所以這些就是截取完的計數。最後,修改後的二元詞組的精確度就是Count_clip之和,就是4除以二元詞組的總個數6,因此46=23\frac{4}{6}=\frac{2}{3}爲二元詞組改良後的精確度。

Count Countclip
the cat 2 1
cat the 1 0
cat on 1 1
on the 1 1
the mat 1 1

現在將它公式化。將改良後的一元詞組精確度定義爲p1p_1pp代表的是精確度。不過它定義爲一元詞組之和,也就是對機器翻譯結果中所有單詞求和,MT輸出就是y^\hat{y}p1=unigramy^Countclip(unigram)unigramy^Count(unigram)p_1=\frac{\sum_{unigram\in\hat{y}}Count_{clip}(unigram)}{\sum_{unigram\in\hat{y}}Count(unigram)}

也可以定義pnp_nnn元詞組精確度,用n-gram表示nn元詞組。這個方法都能夠衡量機器翻譯輸出中與參考相似重複的程度。另外,如果機器翻譯輸出與參考1或是參考2完全一致的話,那麼所有的這些p1p_1p2p_2等等的值,都會等於1.0。爲了得到改良後的1.0的精確度,只要輸出與參考之一完全相同就能滿足,不過有時即使輸出結果並不完全與參考相同,這也是有可能實現的。pn=ngramy^Countclip(ngram)ngramy^Count(ngram)p_n=\frac{\sum_{n-gram\in\hat{y}}Count_{clip}(n-gram)}{\sum_{n-gram\in\hat{y}}Count(n-gram)}

最後,我們將這些組合一下來構成最終的BLEU得分。按照慣例BLEU得分被定義爲p=exp(1ni=1npi)p=exp(\frac{1}{n}\sum_{i=1}^n{p_i}),我們實際上會用額外的一個叫做BP的懲罰因子來調整這項,BP的意思是“簡短懲罰”(brevity penalty)p=BPexp(1ni=1npi)p=BP\cdot exp(\frac{1}{n}\sum_{i=1}^n{p_i})

事實表明,如果輸出了一個非常短的翻譯,那麼它會更容易得到一個高精確度。因爲輸出的大部分詞可能都出現在參考之中,不過我們並不想要特別短的翻譯結果。因此簡短懲罰(BP)就是一個調整因子,它能夠懲罰輸出了太短翻譯結果的翻譯系統。BP的公式如下:

在之前瞭解了擁有單一實數評估指標的重要性,因爲它能夠讓你嘗試兩種想法,然後看一下哪個得分更高,儘量選擇得分更高的那個。BLEU得分對於機器翻譯來說,具有革命性的原因是因爲它有一個相當不錯的雖然不是完美的單一實數評估指標,因此它加快了整個機器翻譯領域的進程。

實踐中,很少人會從零實現一個BLEU得分,有很多開源的實現結果,可以下載下來然後直接用來評估系統。BLEU得分被用來評估許多生成文本的系統,比如說機器翻譯系統,也有圖像描述系統,也就是說會用神經網絡來生成圖像描述,然後使用BLEU得分來看一下,結果在多大程度上與多個人工完成的參考描述內容相符。

BLEU得分是一個有用的單一實數評估指標,用於評估生成文本的算法,判斷輸出的結果是否與人工寫出的參考文本的含義相似。不過它並沒有用於語音識別。因爲在語音識別當中,通常只有一個答案,可以用其他的評估方法,來看一下語音識別結果,是否十分相近或是字字正確。

注意力模型直觀理解
這節不容易!
當使用RNN讀一個句子,於是另一個會輸出一個句子。我們要對其做一些改變,稱爲注意力模型,並且這會使它工作得更好。注意力模型或者說注意力這種思想已經是深度學習中最重要的思想之一。

像這樣給定一個很長的法語句子,在神經網絡中,這個綠色的編碼器要做的就是讀整個句子,然後記憶整個句子,再在感知機中傳遞。而對於這個紫色的神經網絡,即解碼網絡將生成英文翻譯。而人工翻譯,首先會做的可能是先翻譯出句子的部分,再看下一部分,並翻譯這一部分。看一部分,翻譯一部分,一直這樣下去。因爲記憶整個長句子是非常困難的。在這個編碼解碼結構中,會看到它對於短句子效果非常好,於是它會有一個相對高的Bleu分,但是對於長句子而言,它的表現就會變差。有了注意力模型,機器翻譯系統的表現會像這個一樣(平滑直線),因爲翻譯只會翻譯句子的一部分,不會看到這個有一個巨大的下傾(huge dip),這個下傾實際上衡量了神經網絡記憶一個長句子的能力。對待長語句,正確的翻譯方法是將長語句分段,每次只對長語句的一部分進行翻譯。

論文Bahdanau et. al., 2014. Neural Machine Translation by Jointly Learning to Align and Translate. 雖然這個模型源於機器翻譯,但它也推廣到了其他應用領域。在深度學習領域,這個是非常有影響力的,非常具有開創性的論文。

我們有一個法語句子:Jane visite l’Afrique en Septembre. 假定我們將使用一個雙向的RNN,爲了計算每個輸入單詞的的特徵集,必須要理解輸出y^<1>\hat{y}^{<1>}y^<2>\hat{y}^{<2>}一直到y^<5>\hat{y}^{<5>}的雙向RNN。但是我們並不是只翻譯一個單詞,先去掉上面的y^\hat{y}. 使用雙向RNN,對於句子裏的五個單詞,計算一個句子中單詞的特徵集,也有可能是周圍的詞。我們將使用另一個RNN生成英文翻譯,用SS來表示RNN的隱藏狀態。我們希望在這個模型裏第一個生成的單詞將會是Jane,最後生成Jane visits Africa in September. 於是問題就是,當嘗試生成第一個詞,即輸出Jane,那麼應該看輸入的法語句子的哪個部分?似乎應該先看第一個單詞,或者它附近的詞。但是別看太遠了,比如說看到句尾去了。

所以注意力模型就會計算注意力權重,用α<1,1>\alpha^{<1,1>}來表示當生成第一個詞時應該放多少注意力在這個第一塊信息處。然後我們算第二個,α<1,2>\alpha^{<1,2>}告訴我們當嘗試去計算第一個詞Jane時,我們應該花多少注意力在輸入的第二個詞上面。同理這裏是α<1,3>\alpha^{<1,3>},等等。這些將會告訴我們,我們應該花多少注意力在記號爲cc的內容上。這就是RNN的一個單元(嘗試生成第一個詞)。

對於RNN的第二步,我們將有一個新的隱藏狀態S<2>S^{<2>},用一個新的注意力權值集,用α<2,1>\alpha^{<2,1>}來告訴我們當生成第二個詞visits,我們應該花多少注意力在輸入的第一個法語詞上。然後同理α<2,2>\alpha^{<2,2>},我們應該花多少注意力在visite詞上,α<2,3>\alpha^{<2,3>}我們應該花多少注意在詞l’Afique上面。當然我們第一個生成的詞Jane也會輸入到這裏(見上圖),於是我們就有了需要花注意力的上下文cc,這也是個輸入,然後會一起生成第二個詞。

接着來到第三步S<3>S^{<3>},這(visits)是輸入,我們再有上下文cc,它取決於在不同的時間集上面的α<3,t>\alpha^{<3,t'>}. 當嘗試去生成第三個詞,應該是Africa,這個RNN步驟應該要花注意力α<3,t>\alpha^{<3,t'>}tt'時的法語詞上,而α<3,t>\alpha^{<3,t'>}取決於在tt'時的雙向RNN的激活值a<t>=(a<t>,a<t>)a^{<t'>}=(\overrightarrow{a}^{<t'>},\overleftarrow{a}^{<t'>})和上一步的狀態S<2>S^{<2>},然後這些一起影響應該花多少注意在輸入的法語句子的某個詞上面。注意力權重,即α<t,t>\alpha^{<t,t'>}告訴你,當嘗試生成第tt個英文詞,它應該花多少注意力在第tt'個法語詞上面。當生成一個特定的英文詞時,這允許它在每個時間步去看周圍詞距內的法語詞要花多少注意力。更多細節見下一節。

注意力模型
這節有點難!
注意力模型讓一個神經網絡只注意到一部分的輸入句子。當它在生成句子的時候,更像人類翻譯。

我們先假定有一個輸入句子,並使用雙向的RNN,或雙向的GRU或LSTM,去計算每個詞的特徵。對於前向傳播,有第一個時間步的前向傳播的激活值,第一個時間步後向傳播的激活值,以此類推。它們一共向前了五個時間步,也向後了五個時間步,技術上我們把a<0>\overrightarrow{a}^{<0>}設置爲0。也可以後向傳播6次,設一個(a<6>\overleftarrow{a}^{<6>})都是0的因子。

a<t>=(a<t>,a<t>)a^{<t'>}=(\overrightarrow{a}^{<t'>},\overleftarrow{a}^{<t'>})來表示時間步tt'上的特徵向量,這裏用tt'來索引法語句子裏面的詞。接下來只進行前向計算,這是個單向的RNN,用狀態SS表示生成翻譯。所以第一個時間步應該生成y^<1>\hat{y}^{<1>},當輸入上下文cc的時候就會這樣,如果想用時間來索引它,可以寫c<1>c^{<1>},這個取決於注意力參數,即α<1,1>\alpha^{<1,1>}α<1,2>\alpha^{<1,2>}\cdots,告訴我們應該花多少注意力在從不同時間步中得到的特徵或者激活值上。注意力權重將會滿足非負的條件,它們加起來等於1:tα<1,t>=1c<1>=tα<1,t>a<t>\sum_{t'}\alpha^{<1,t'>}=1\\c^{<1>}=\sum_{t'}\alpha^{<1,t'>}a^{<t'>}

t=1t=1時的上下文c<1>c^{<1>},這就變成對tt'的求和。於是α<t,t>\alpha^{<t,t'>}就是y^<t>\hat{y}^{<t>}應該在tt'時花在a<t>a^{<t'>}上注意力的數量。換句話來說,當在tt處生成輸出詞,應該花多少注意力在第tt'個輸入詞上面,這是生成輸出的其中一步。

然後下一個時間步,會生成第二個輸出。類似的,現在有了一個新的注意力權重集,再用上面的方式將它們相加,這就產生了一個新的上下文c<2>c^{<2>},這個也是輸入,且允許生成第二個詞。這裏的神經網絡看起來很像相當標準的RNN序列,這裏有着上下文向量作爲輸入,我們可以一次一個詞地生成翻譯。

α<t,t>\alpha^{<t,t'>}是應該花費在a<t>a^{<t'>}上的注意力的數量,當嘗試去生成第tt個輸出的翻譯詞,下面這個式子可以用來計算α<t,t>\alpha^{<t,t'>},在此之前我們要先計算e<t,t>e^{<t,t'>},關鍵要用Softmax,來確保這些權重加起來等於1。如果對tt'求和,固定tt,這些加起來等於1.α<t,t>=exp(e<t,t>)t=1Txexp(e<t,t>)\alpha^{<t,t'>}=\frac{exp(e^{<t,t'>})}{\sum_{t'=1}^{T_x}exp(e^{<t,t'>})}

一種可以用來計算e<t,t>e^{<t,t'>}的方式是用下面這樣的小的神經網絡,於是S<t1>S^{<t-1>}就是神經網絡在上一個時間步的隱藏狀態。S<t1>S^{<t-1>}是給小神經網絡的其中一個輸入,這是神經網絡中的一個隱藏層,因爲需要經常計算它們,然後a<t>a^{<t'>},即tt'時間步的的特徵是另一個輸入。直觀來想就是,如果想要決定要花多少注意力在tt'的激活值上,似乎它會很大程度上取決於上一個時間步的的隱藏狀態的激活值。

還沒有當前狀態的激活值S<t>S^{<t>},因爲上下文cc會輸入到這裏,所以還沒計算出來,但是看看生成上一個翻譯的RNN的隱藏狀態,然後對於每一個位置,每一個詞都看向它們的特徵值aa. 但是不知道具體函數是什麼,所以可以訓練一個很小的神經網絡,利用反向傳播算法和梯度下降算法學到一個正確的函數。這個小型的神經網絡告訴你y^<t>\hat{y}^{<t>}應該花多少注意力在a<t>a^{<t'>}上面,然後α<t,t>\alpha^{<t,t'>}這個式子確保注意力權重加起來等於1,於是當持續地一次生成一個詞,下面這個神經網絡實際上會花注意力在輸入句子的右邊(right parts)上,它會完全自動地通過梯度下降來學習。

這個算法的一個缺點就是它要花費三次方的時間,就是說這個算法的複雜度是O(n3)O(n^3)的,如果有TxT_x個輸入單詞和TyT_y個輸出單詞,注意力參數的總數就會是Tx×TyT_x\times T_y,所以這個算法有着三次方的消耗。但是在機器翻譯的應用上,輸入和輸出的句子一般不會太長,三次方的消耗是可以接受,但也有很多研究工作,嘗試去減少這樣的消耗。這個想法也被應用到了其他的很多問題中去,比如圖片加標題,就是看一張圖,寫下這張圖的標題。論文Xu et. al., 2015. Show, attend and tell: Neural image caption generation with visual attention.,他們也顯示了可以有一個很相似的結構看圖片,然後當在寫圖片標題的時候,一次只花注意力在一部分的圖片上面。

因爲機器翻譯是一個非常複雜的問題。在編程練習中應用了注意力,在日期標準化的問題上面。問題輸入了一個日期July 20th 1969,把它轉化爲標準的形式:1969-07-20,或者這樣的日期:23 April, 1564,用一個序列的神經網絡,即序列模型去標準化到這樣的形式:1564-04-23. 可以訓練一個神經網絡,輸入任何形式的日期,生成標準化的日期形式。

下圖將注意力權重α<t,t>\alpha^{<t,t'>}可視化:這是一個機器翻譯的例子,這裏被畫上了不同的顏色,顏色越白表示注意力權重越大,顏色越深表示權重越小。可以發現對應的輸入輸出詞,注意力權重會變高,因此這顯示了當它生成特定的輸出詞時通常會花注意力在輸入的正確的詞上面。

語音識別
seq2seq模型在語音識別方面準確性有了很大的提升。現在有一個音頻片段xx,任務是自動地生成文本yy。音頻片段圖的橫軸是時間,一個麥克風的作用是測量出微小的氣壓變化,像圖上這樣的音頻片段,氣壓隨着時間而變化。假如這個的音頻片段的內容是:“the quick brown fox”,這時我們希望一個語音識別算法,通過輸入這段音頻,然後輸出音頻的文本內容。考慮到人的耳朵並不會處理聲音的原始波形,而是通過一種特殊的物理結構來測量這些,不同頻率和強度的聲波。音頻數據的常見預處理步驟,就是運行這個原始的音頻片段,可以把信號轉化爲頻域信號,然後生成一個聲譜圖(a spectrogram)。橫軸是時間,縱軸是聲音的頻率,而圖中不同的顏色,顯示了聲波能量的大小(the amount of energy),也就是在不同的時間和頻率上這些聲音有多大。通過這樣的聲譜圖,僞空白輸出(the false blank outputs)也經常應用於預處理步驟,也就是在音頻被輸入到學習算法之前,而人耳所做的計算和這個預處理過程非常相似。

曾經有一段時間,語音識別系統是用音位(phonemes)來構建的,也就是人工設計的基本單元。如果用音位來表示"the quick brown fox",“the"含有"th"和"e"的音,而"quick"有"k” “w” “i” "k"的音,語音學家過去把這些音作爲聲音的基本單元寫下來,把這些語音分解成這些基本的聲音單元,而"brown"不是一個很正式的音位,因爲它的音寫起來比較複雜,不過語音學家們認爲用這些基本的音位單元來表示音頻,是做語音識別最好的辦法。不過在end-to-end模型中,我們發現這種音位表示法已經不再必要了,而是可以構建一個系統,通過向系統中輸入音頻片段,然後直接輸出音頻的文本,而不需要使用這種人工設計的表示方法。

使這種方法成爲可能的一件事就是用一個很大的數據集,所以語音識別的研究數據集可能長達300個小時,在學術界,甚至3000小時的文本音頻數據集,都被認爲是合理的大小。大量的研究,大量的論文所使用的數據集中,有幾千種不同的聲音,而且,最好的商業系統現在已經訓練了超過1萬個小時的數據,甚至10萬個小時,並且它還會繼續變得更大。在文本音頻數據集中同時包含xxyy,通過深度學習算法大大推進了語音識別的進程。

在橫軸上,也就是在輸入音頻的不同時間幀上,可以用一個注意力模型,來輸出文本描述,如"the quick brown fox",或者其他語音內容。

還有一種效果也不錯的方法,就是用CTC損失函數來做語音識別。CTC就是Connectionist Temporal Classification,論文:Graves et al., 2006. Connectionist Temporal Classification: Labelling unsegmented sequence data with recurrent neural networks.

算法思想如下:假設語音片段內容是某人說:“the quick brown fox”,這時我們使用一個新的網絡,這裏輸入xx和輸出yy的數量都是一樣的,在實際中,它有可能是雙向的LSTM結構,或者雙向的GRU結構,並且通常是很深的模型。但注意一下這裏時間步的數量,它非常地。在語音識別中,通常輸入的時間步數量要比輸出的時間步的數量多出很多。舉個例子,比如有一段10秒的音頻,並且特徵是100赫茲的,即每秒有100個樣本,於是這段10秒的音頻片段就會有1000個輸入,就是簡單地用100赫茲乘上10秒。所以有1000個輸入,但可能輸出就沒有1000個字母了,或者說沒有1000個字符。ttt_h_eee______qqq__ttt\_h\_eee\_\_\_\sqcup \_\_\_qqq\_\_

CTC損失函數允許RNN生成上面的輸出,這裏的下劃線是一個特殊的字符,叫做空白符,然後這裏可能有個空格,我們用\sqcup來表示空格,這樣的輸出也被看做是正確的輸出,這段輸出對應的是"the q"。CTC損失函數的一個基本規則是將空白符之間的重複的字符摺疊起來。the和quick之間有一個空格符,所以要輸出一個空格,通過把用空白符所分割的重複的字符摺疊起來,然後就可以把這段序列摺疊成"the q"。最後我們得到的文本會短很多。於是這句"the quick brown fox"包括空格一共有19個字符,在這樣的情況下,通過允許神經網絡有重複的字符和插入空白符使得它能強制輸出1000個字符

觸發字檢測
隨着語音識別的發展,越來越多的設備可以通過聲音來喚醒,這有時被叫做觸發字檢測(detection)系統。

觸發字系統的例子包括Amazon echo,它通過單詞Alexa喚醒;還有百度DuerOS設備,通過"小度你好"來喚醒;蘋果的Siri用Hey Siri來喚醒;Google Home使用Okay Google來喚醒,這就是觸發字檢測系統。如果能建立一個觸發字檢測系統,也許就能讓電腦通過聲音來執行某些事。

關於觸發字檢測系統的文獻,還處於發展階段。對於觸發字檢測,最好的算法是什麼,目前還沒有一個廣泛的定論。這裏就簡單介紹一個能夠使用的算法。現在有一個這樣的RNN結構,我們要做的就是把一個音頻片段計算出它的聲譜圖特徵得到特徵向量x<1>x^{<1>}x<1>x^{<1>}x<3>x^{<3>}\cdots,然後把它放到RNN中,最後要做的就是定義目標標籤yy。假如音頻片段中的這一點是某人剛剛說完一個觸發字,比如"Alexa",那麼在這一點之前,就可以在訓練集中把目標標籤都設爲0,然後在這個點之後把目標標籤設爲1。假如在一段時間之後,觸發字又被說了一次,比如是在這個點說的,那麼就可以再次在這個點之後把目標標籤設爲1。這樣的標籤方案對於RNN來說是可行的,並且確實運行得非常不錯。不過該算法一個明顯的缺點就是它構建了一個很不平衡的訓練集,0的數量比1多太多了,即正負樣本分佈不均。

一種解決辦法是在出現一個觸發字時,將其附近的RNN都輸出1。這樣就簡單粗暴地增加了正樣本。比起只在一個時間步上去輸出1,其實可以在輸出變回0之前,多次輸出1,或說在固定的一段時間內輸出多個1。這樣的話,就稍微提高了1與0的比例,這確實有些簡單粗暴。在音頻片段中,觸發字剛被說完之後,就把多個目標標籤設爲1,這裏觸發字又被說了一次。說完以後,又讓RNN去輸出1。

結論和致謝
深度學習是一種超能力,通過深度學習算法,可以讓計算機擁有"視覺",可以讓計算機自己合成小說,或者合成音樂,可以讓計算機將一種語言翻譯成另一種,或者對放射影像進行定位然後進行醫療診斷,或者構建自動駕駛系統。

序列模型編程作業

  • 搭建循環神經網絡及其應用
  • 詞向量的運算與Emoji生成器
  • 機器翻譯與觸發詞檢測
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章