深度學習與自然語言處理(5)_斯坦福cs224d 大作業測驗2與解答

作業內容翻譯:@胡楊([email protected]) && @麪包君 && Fantzy同學
校正與調整:寒小陽 && 龍心塵
時間:2016年7月
出處:http://blog.csdn.net/han_xiaoyang/article/details/51815683
http://blog.csdn.net/longxinchen_ml/article/details/51814343

說明:本文爲斯坦福大學CS224d課程的中文版內容筆記,已得到斯坦福大學課程@Richard Socher教授的授權翻譯

0. 前言

原本打算把作業和答案做個編排,一起發出來給大家看,無奈代碼量有點大,貼上來以後文章篇幅過長,於是乎題目的代碼解答放到了百度雲盤,歡迎自行下載和運行或者調整。

1. Tensorflow 與 softmax(20分)

在本題中,我們會執行一個線性分類器,並會用到下面的交叉熵損失函數:

JsoftmaxCE(W)=CE(y,softmax(xW))

(請注意,這裏行x 的是行特徵向量)。我們會通過Tensorflow 的自動微分功能來訓練數據。

a.(4分)
在腳本q1_softmax.py中,通過TensorFlow來構造softmax 函數。softmax的公式如下:

softmax(x)i=exijexj

請注意,你不能使用Tensorflow內建的tf.nn.softmax函數或者其他相關內建函數。這道題目做好的標準是你能夠通過運行python q1_softmax.py命令將這個函數跑起來(需要跑通一個測試用例。當然,不要求完全詳盡的測試。)

b. (4分)
在腳本q1_softmax.py中,通過TensorFlow來構造交叉熵損失函數(Cross Entropy Loss)。交叉熵損失函數的公式長這個樣子:

CE(y,ŷ )=i=1Ncyilog(ŷ i)

在這裏 y5 是一個one-hot標籤向量,Nc 是所有類目的數量。請注意,你不能使用Tensorflow內建的cross-entropy函數或者其他相關內建函數。這道題目做好的標準是你能夠通過運行Python q1_softmax.py腳本將這個函數跑起來(需要寫一個測試用例。當然,這同樣不要求完全詳盡的測試。)

c. (4分)
請仔細學習model.py腳本中的model類。並簡要解釋一下其中佔位符變量 (place holder vaiables)和填充字典(feed dictionaries)函數的目的. 在q1_classifier.py中填充好add_palceholderscreat_feed_dict這兩個函數.
提示: 請注意配置變量(configuration variables)在config類中已經儲存好了,而在代碼中你會用到它們。
答案:在Tensorflow 計算圖中,佔位符變量是作爲其輸入節點而存在的。而填充字典函數說明了在計算的時候怎麼給這些佔位符(或者其他)變量賦值。

d. (4分)
在腳本ql_classifier.py中的add_model函數裏完成一個用softmax進行分類的基本流程。並在同一個腳本中的函數add_loss_op中補充好交叉熵損失函數。 請注意,使用本題之前提供的函數,而不是 Tensorflow 內建函數。

e. (4分)
在腳本ql_classifier.py中填充完成add_training_op 函數。 並解釋爲什麼Tensorflow 的自動微分功能能省去我們直接求梯度的工作。你可以在我們提供的數據上順利運行python ql_classifier.py命令,並且確保能夠跑通測試用例。這樣你才能保證你的模型是合適的。
提示:請一定要使用在config類中定義的學習率.
答案:只要正確定義好了計算圖,Tensorflow就能自動應用反向傳播算法來計算梯度。

2. 神經網絡在命名實體識別中的應用(35分)

這一節中,我們會練習反向傳播算法和訓練深度神經網絡,通過它們來實現命名實體識別。命名實體識別是指識別文本中具有特定意義的實體,主要包括人名、地名、機構名、專有名詞等。
這其實就是一個5類分類問題(5選1的單選題)。這5類包括上面這4類和1個非實體類——也就是不代表任何實體的類(大部詞語都屬於這類)。
這個模型是一個單隱藏層神經網絡,它有一個類似我們在word2vec中看到的表示層。 這裏我們不需要求平均值或者抽樣,而是明確地將上下文定義爲一個“窗口”,這個“窗口”包含目標詞和它左右緊鄰的詞,是一個3d維度的行向量:

x(t)=[xt1L,xtL,xt+1L]R3d

這裏的xt1,xt,xt+1 是one-hot行向量(|V|維),而L|V|×d 是嵌入矩陣,它的每一行Li 其實就代表一個特定的詞i 。我們然後做如下預測:
h=tanh(x(t)W+b1)

ŷ =softmax(hU+b2)

之後通過交叉熵損失函數來評估誤差:
J(θ)=CE(y,ŷ )=i=15yilog(ŷ i)

爲了計算整個訓練集的損失,我們在對每個訓練樣本計算J(θ) 之後將其求和(或者求平均值)。
對於本題,令d=50 作爲詞向量的長度。3個向量組成了一個寬度爲3×50=150 的窗口。隱藏層有100個神經元,輸出層有5個神經元.

(part a)
(5分)請計算損失函數J(θ) 在下列模型各個參數上的梯度

JU Jb2 JW Jb1 JLi

,這裏矩陣的維度分別是

UR100×5 b2R5 WR150×100 b1R100 LiR50

提示:爲了能夠清晰的表示反向傳播的過程,我們給您推薦兩個小trick:

  • 使用激活函數的值來代替激活函數的微分。就像作業1中的sigmoid 函數一樣,下面這個函數也可以起到相同的作用。
    tanh(z)=2sigmoid(2z)1
  • 使用“殘差向量”的形式來表示某一層的梯度。這意味着我們的鏈式法則,現在可以寫成用括號括起來的向量(矩陣)乘法的形式,這將使您的分析過程大大簡化。

值得注意的是,損失函數在模型各參數上的梯度應該化簡到可以使用矩陣運算的形式。(通過作業1,相信您已經掌握的很熟練了:))

答案:
由提示公式可得:

tanh(z)=4sigmoid(2z)(1sigmoid(2z))

可得偏微分如下:
JU=hT(yŷ )

Jb2=yŷ 

Jh=(yŷ )UT

JW=(x(t))T(Jhtanh(2(x(t)W+b1)))

Jb1=(Jhtanh(2(x(t)W+b1)))

Jx(t)=(Jhtanh(2(x(t)W+b1)))WT

我們令i=xt,j=xt1,k=xt+1 ,可得:
Jx(t)=[JLi,JLj,JLk]

(part b)(5分)
神經網絡的訓練過程往往不是一帆風順的,許多小問題都會影響模型的性能,例如神經網絡中參數過多會導致訓練太慢,神經元之間相關性太強會導致模型陷入局部最優。爲了避免上述兩種情況的發生,我們在(part a)損失函數的基礎上增加一個高斯先驗。可別小看這個高斯先驗,它能使我們的模型參數/權重不會有那麼誇張的幅度(更向0值靠攏),同時又能保證大部分參數都有取值(不會對起作用的特徵產生太大影響),從而保證了模型在實際場景中的可用性(泛化能力強一些)。混合後的損失函數就長這樣。

Jfull(θ)=J(θ)+Jreg(θ)

有了混合損失函數,下面就可以開始將似然函數求最大化了(就像上面我們對交叉熵求最大化那樣),高斯先驗在這個過程中就會變成一個平方項的形式(L2 正則化項)。

Jreg(θ)=λ2i,jW2ij+i,jW2ij

要對似然函數求極大,我們所採用的方法是梯度上升,所以本題的任務還是請您來求一下梯度。與(part a)中不同的是,我們這裏的損失函數多了一個由高斯先驗得來的正則化項。所以,聰明的你,給我個答案吧。
答案:

Jfull=J+Jreg

JregW=λW

JregU=λU

WV 的梯度代入即可。

(part c)(5分)
在part b中我們瞭解到,如果神經元之間的相關性太強,模型就容易陷入局部最優,從而降低了模型的泛化能力。對此,我們的解決方法是使用L2正則化項對模型參數加以限制。在本題中,我們提供另外一種方法,叫做“參數隨機初始化”。在衆多參數隨機初始化的方法中,我們使用最多的是Xavier方法。
Xavier方法的原理是這樣的:給定一個m×n 的矩陣A 和一個區間[ϵ,ϵ ],從該範圍中進行均勻採樣作爲Aij ,其中

ϵ=6m+n

好了,根據算法原理,請你在q2_initialization.pyxavier_weight_init中,用代碼來實現一下吧。

(part d)(20分)
在q2_NER.py中,我們實現了一個命名實體窗口(NER Window)模型。模型的輸入是sections(小編注:sections在特定情況下可以看作是一句查詢,每個section由一些tokens組成,即分詞),輸出就是命名實體的標籤。您可以看一下代碼,您剛剛推導的反向傳播過程在代碼中已經被實現了,是不是很神奇!?
以下工作需要您來完成:

  • 您需要在q2_NER.py中實現命名實體窗口模型部分的代碼,我們會根據算法正確性和程序是否可執行來進行打分。
  • 您需要說明模型最佳的參數的值,主要有正則化項、特徵維度、學習速率和SGD批量的大小等。您還需要說明模型在交叉驗證集上的誤差,本課程要求模型在交叉驗證集上的誤差不高於0.2。
  • 您需要將測試集的預測結果保存在q2_test.predicted文件中,格式爲一行一個label,我們會根據真實結果來評估您模型的泛化能力。
  • 提示1:在debug模式中,請將參數max_epchs設爲1,並且將load_datadebug參數設爲True
  • 提示2:請注意程序的時間複雜度,保證在GPU上不超過15分鐘,CPU上不超過1小時。

3.遞歸神經網絡:語言建模(45分)

在這一節,你將首次實現一個用於建立語言模型的遞歸神經網絡。

語言模型的建立是NLP技術的核心部分,語言模型被廣泛使用於語音識別,機器翻譯以及其他各類系統。給出一串連續的詞x1,,xt ,關於預測其後面緊跟的詞xt+1 的建模方式是:

P(xt+1=υj|xt,,x1)

其中,υj 是詞庫中的某個詞。

你的任務是實現一個遞歸神經網絡,此網絡利用隱層中的反饋信息對“歷史記錄”xt,xt1,,x1 進行建模。關於t=1,,n1 形式化的表示如文獻[3]所描述:

e(t)=x(t)L(10)

h(t)=sigmoid(h(t1)H+etI+b1)(11)

ŷ (t)=sigmoid(h(t)U+b2)(12)

P¯(xt+1=υj|xt,,x1)=yj^(t)(13)

其中,h(0)=h0Dh 是隱藏層的初始化向量,x(t)L 是以x(t) 爲one-hot行向量與嵌入矩陣L 的乘積,這個one-hot行向量就是當前處理詞彙的索引。具體的一些參數設置如下:
L|V|×dHDh×DhId×Dhb1DhUDh×|V|b2|V|(14)

其中,L 是詞嵌入矩陣,I 是輸入詞表徵矩陣,H 是隱藏轉化矩陣,還有U 是輸出詞表徵矩陣。b1,b2 是偏置值。d 是詞嵌入的維數,|V| 代表詞庫的規模,然後Dh 是隱層的維數。

輸出向量ŷ (t)|V| 是面向整個詞庫的概率分佈,我們需要最優化交叉熵(非正則化的)的損失率:

J(t)(θ)=CE(y(t),ŷ (t))=i=1|V|y(t)ilogŷ (t)(15)

其中,y(t) 是與目標詞(這裏表示爲xt+1 )對應的one-hot向量。正如註釋[2]所描述的,它是一個對於單個樣本點的損失率,然後我們對序列中全部樣例的交叉熵損失值做求和(或求平均)計算,對數據集中的全部序列[4]均採取上述操作以衡量模型的性能。

(a) (5分)
通常地,我們使用困惑度來評估語言模型的性能,其定義形式如下:

PP(t)(y(t),ŷ (t))=1P¯(xpredt+1=xt+1|xt,,x1)=1|V|j=1y(t)jŷ (t)(16)

即通過模型分佈P¯ 得到詞預測正確的逆概率。給出你是如何從交叉熵的損失率中得到困惑度的(提示:不要忘了向量y(t) 是one-hot形式的!),因此如果要進行最小化操作(從算術上)意味着(從幾何上講)交叉熵損失率將被最小化,就得到了在訓練集上的困惑度。在這一部分要處理的是一個非常短小的問題-所以並不需要過大的困惑度!
對於一個詞庫|V| 中的詞彙,如果你的模型做出的預測是完全隨機的,你將如何預測你的困惑度呢?計算詞庫規模爲|V|=2000|V|=10000 時對應的交叉熵損失率,並且將其作爲基準牢記於心。

解答:使用的樣例y(t) 爲one-hot模型,且假定y(t)iy(t) 中唯一的非零元素。於是,記:

CE(y(t),ŷ (t))=logŷ (t)i=log1ŷ (t)

PP(t)(y(t),ŷ (t))=1ŷ (t)i

因此,上式可以被聯立爲如下形狀:
CE(y(t),ŷ (t))=logPP(t)(y(t),ŷ (t))

這條公式展示了交叉熵在數學意義上的最小化目標,同時表達了困惑度在幾何意義上的最小值。當模型的預測值完全隨機化時,會有E[ŷ (t)i]=1|V| 。所給的向量y(t) 是one-hot模型,所對應困惑度的期望值是|V| 。由於困惑度的對數即爲交叉熵,那麼交叉熵的期望值在面對上述兩個詞庫規模時分別應爲log20007.6log100009.21

(b) (5分)
正如註釋[2]所描述的操作,在時刻t 關於單點全部模型參數的梯度計算如下:

J(t)UJ(t)b2J(t)Lx(t)J(t)I(t)J(t)H(t)J(t)b1(t)

其中,Lx(t) 是詞嵌入矩陣L 中對應到當前處理詞彙x(t) 的列,符號|(t) 表示時刻t 該參數的顯式梯度。(同樣地,h(t1) 的取值是固定的,而且你現在也不需要在早先的迭代時刻中實現反向傳播算法——這是c小節的任務)。

此外,還要計算代表前趨隱層權值的導數:

J(t)h(t1)

解答:調用函數ddzsigmoid(z)=sigmoid(z)(1sigmoid(z))

J(t)U=(h(t))T(yŷ )

J(t)h(t)=(yŷ )UT

J(t)b2=(yŷ )

J(t)b1(t)=(J(t)h(t)sigmoid(h(t1)H+e(t)I+b1))

J(t)Lx(t)=J(t)e(t)=J(t)b1(t)IT

J(t)I(t)=(e(t))TJ(t)b1(t)

J(t)H(t)=(h(t1))TJ(t)b1(t)

J(t)h(t1)=HTJ(t)b1(t)

(c) (5分)
下面爲一步迭代的網絡框圖:

圖一

繪製下面三次迭代所“展開”的網絡,並且計算迭代時後向傳播的梯度:

J(t)Lx(t1)J(t)H(t1)J(t)I(t1)J(t)b1(t1)

這裏|(t1) 代表模型參數在(t1) 時刻的顯式梯度。由於這些參數在前饋計算中要被多次使用,我們需要在每次迭代時都計算一下它們的梯度。

最好參考講義[5]所描述的後向傳播原理去將這些梯度表達成殘差的形式:

δ(t1)=J(t)h(t1)

(同樣的表達方式在迭代時刻t2,t3 以及更多的時候都同樣適用)。

注意一個訓練樣本的實際梯度是需要我們將直到t=0 時刻的整條後向路徑都使用後向傳播算法迭代完成後才能得到的。在練習中,我們通常只需截取固定數值\tau\approx3-5τ35 的後向傳播步驟即可。

解答:

圖二

還是延續前一節的條件
\left.\frac{\partial{J^{(t)}}}{\partial{b_1}}\right|_{(t-1)}=\delta^{(t-1)}\odot\mathrm{sigmoid}^{'}(h^{(t-2)}H+e^{(t-1)}I+b_1)

J(t)b1(t1)=δ(t1)sigmoid(h(t2)H+e(t1)I+b1)

\frac{\partial{J^{(t)}}}{\partial{L_{x^{(t-1)}}}}=\left.\frac{\partial{J^{(t)}}}{\partial{b_1}}\right|_{(t-1)}I^T
J(t)Lx(t1)=J(t)b1(t1)IT

\left.\frac{\partial{J^{(t)}}}{\partial{I}}\right|_{(t-1)}=(e^{(t-1)})^T\left.\frac{\partial{J^{(t)}}}{\partial{b_1}}\right|_{(t-1)}
J(t)I(t1)=(e(t1))TJ(t)b1(t1)

J(t)H(t1)=(h(t2))TJ(t)b1(t1)

(d) (3分)
對於給定的h(t1) ,執行一輪前向傳播計算J(t)(θ) 需要多少操作?執行一輪後向傳播又會如何呢?執行τ 輪呢?對於維度參數組d,Dh|V| ,使用符號“大寫-O”來表示你的答案(如公式14)。是否該慎重考慮這一步?

回憶各個參數的設置:h(t1) 大小爲Dhe(t) 大小爲d ,還有ŷ (t) 的大小爲|V| 。計算e(t) 花費的時間O(d) 視矩陣L 而定。計算h(t) 花費的時間O(D2h+dDh) ,計算ŷ (t) 花費時間O(Dh|V|) 。後向傳播和前向傳播算法具有相同的時間複雜度,於是,計算τ 輪的前向或後向傳播複雜度僅通過τ 與先前得到的複雜度值相乘即可。由於|V| 比較大的原因,計算ŷ (t) 的“較慢的一步”計算需花費時長O(Dh|V|)

(e) (20分)
在代碼q3_RNNLM.py中實現以上的模型。其中已經實現了數據加載器和其它的初始化功能代碼。順着已有代碼的指引來補充缺失的代碼。執行命令行python q3_RNNLM.py將運行該模型。注意,在運行過程中你不能用到tensorflow庫的內置函數,如rnn_cell模塊。

ptb-train數據集中訓練該模型,其中包含Penn Treebank中WSJ語料集的前20節語料。正如在Q 2部分中所說的,你應該最大限度地提高該模型在實際生產中數據集上的泛化性能(即最小化交叉熵損失率)。關於模型的性能,我們將通過未知但類似的句子集來進行評估。

撰寫實驗報告的一些要求:

  • 在你實驗報告中,應包括最優超參數(如訓練框架、迭代次數、學習率、反饋頻率)以及在ptb-dev數據集上的困惑度。正常情況應當保持困惑度值低於175。
  • 在報告中還應當包含最優模型的相關參數;方便我們測試你的模型。
  • 提示:在調試過程中把參數max_epochs的值設爲1。在_init_方法中通過設置參數關鍵字debug=True來開啓對load_data方法的調用。
  • 提示:該模型代碼在GPU上運行很快(低於30分鐘),而在CPU上將耗時多達4小時。

(f) (7分)
作業1和該作業中的q2部分所示的神經網絡是判別模型:他們接收數據之後,對其做出預測。前面你實現的RNNLM模型是一個生成式模型,因爲它建模了數據序列x1,,xn 的分佈狀況。這就意味着我們不僅能用它去評估句子生成概率,而且還能通過它生成概率最優的語句!

訓練完成後,在代碼q3 RNNLM.py 中實現函數generate_text()。該函數首先運行RNN前向算法,在起始標記<eos>處建立索引,然後從每輪迭代的ŷ (t) 分佈對新詞xt+1 進行採樣。然後把該詞作爲下一步的輸入,重複該操作直至該模型遇到一個結束標誌(結束標誌記爲</eos>)。

撰寫實驗報告的一些要求:

  • 在你的實驗報告中至少包含2-3條的生成語句。看看你是否能產生一些妙趣橫生的東西!

一些參考建議:
如果你想用語言模型繼續實驗,非常歡迎你上傳自己的文檔和對其訓練成果——有時會是一個令人驚喜的實驗結果!(可從網站http://kingjamesprogramming.tumblr.com/ 上獲取以紀念版《聖經》和書籍《計算機程序設計與實現》爲混合語料進行訓練的偉大成果。)
[1]Optional (not graded): The interested reader should prove that this is indeed the maximum-likelihood objective when we let WijN(0,1/λ) for all i,j .
[2]This is also referred to as Glorot initialization and was initially described in http://jmlr.org/proceedings/papers/v9/glorot10a/glorot10a.pdf
[3]這個模型可以參考Toma Mikolov的論文, 發表於2010年: http://www.fit.vutbr.cz/research/groups/speech/publi/2010/mikolov_interspeech2010_IS100722.pdf
[4]我們使用Tensorflow庫函數計算此處的損失率。
[5]http://cs224d.stanford.edu/lectures/CS224d-Lecture5.pdf

發佈了121 篇原創文章 · 獲贊 2015 · 訪問量 299萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章