作業內容翻譯:@胡楊([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分)
在本題中,我們會執行一個線性分類器,並會用到下面的交叉熵損失函數:
(請注意,這裏行
a.(4分)
在腳本q1_softmax.py
中,通過TensorFlow來構造softmax 函數。softmax的公式如下:
請注意,你不能使用Tensorflow內建的tf.nn.softmax
函數或者其他相關內建函數。這道題目做好的標準是你能夠通過運行python q1_softmax.py
命令將這個函數跑起來(需要跑通一個測試用例。當然,不要求完全詳盡的測試。)
b. (4分)
在腳本q1_softmax.py
中,通過TensorFlow來構造交叉熵損失函數(Cross Entropy Loss)。交叉熵損失函數的公式長這個樣子:
在這裏 cross-entropy
函數或者其他相關內建函數。這道題目做好的標準是你能夠通過運行Python q1_softmax.py
腳本將這個函數跑起來(需要寫一個測試用例。當然,這同樣不要求完全詳盡的測試。)
c. (4分)
請仔細學習model.py
腳本中的model
類。並簡要解釋一下其中佔位符變量 (place holder vaiables)和填充字典(feed dictionaries)函數的目的. 在q1_classifier.py
中填充好add_palceholders
和creat_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維度的行向量:
這裏的
之後通過交叉熵損失函數來評估誤差:
爲了計算整個訓練集的損失,我們在對每個訓練樣本計算
對於本題,令
(part a)
(5分)請計算損失函數
,這裏矩陣的維度分別是
提示:爲了能夠清晰的表示反向傳播的過程,我們給您推薦兩個小trick:
- 使用激活函數的值來代替激活函數的微分。就像作業1中的
sigmoid 函數一樣,下面這個函數也可以起到相同的作用。
tanh(z)=2sigmoid(2z)−1 - 使用“殘差向量”的形式來表示某一層的梯度。這意味着我們的鏈式法則,現在可以寫成用括號括起來的向量(矩陣)乘法的形式,這將使您的分析過程大大簡化。
值得注意的是,損失函數在模型各參數上的梯度應該化簡到可以使用矩陣運算的形式。(通過作業1,相信您已經掌握的很熟練了:))
答案:
由提示公式可得:
可得偏微分如下:
我們令
(part b)(5分)
神經網絡的訓練過程往往不是一帆風順的,許多小問題都會影響模型的性能,例如神經網絡中參數過多會導致訓練太慢,神經元之間相關性太強會導致模型陷入局部最優。爲了避免上述兩種情況的發生,我們在(part a)損失函數的基礎上增加一個高斯先驗。可別小看這個高斯先驗,它能使我們的模型參數/權重不會有那麼誇張的幅度(更向0值靠攏),同時又能保證大部分參數都有取值(不會對起作用的特徵產生太大影響)
,從而保證了模型在實際場景中的可用性(泛化能力強一些)
。混合後的損失函數就長這樣。
有了混合損失函數,下面就可以開始將似然函數求最大化了(就像上面我們對交叉熵求最大化那樣),高斯先驗在這個過程中就會變成一個平方項的形式(
要對似然函數求極大,我們所採用的方法是梯度上升,所以本題的任務還是請您來求一下梯度。與(part a)中不同的是,我們這裏的損失函數多了一個由高斯先驗得來的正則化項。所以,聰明的你,給我個答案吧。
答案:
將
(part c)(5分)
在part b中我們瞭解到,如果神經元之間的相關性太強,模型就容易陷入局部最優,從而降低了模型的泛化能力。對此,我們的解決方法是使用L2正則化項對模型參數加以限制。在本題中,我們提供另外一種方法,叫做“參數隨機初始化”。在衆多參數隨機初始化的方法中,我們使用最多的是Xavier方法。
Xavier方法的原理是這樣的:給定一個
好了,根據算法原理,請你在
q2_initialization.py
的xavier_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_data
中debug
參數設爲True
。 - 提示2:請注意程序的時間複雜度,保證在GPU上不超過15分鐘,CPU上不超過1小時。
3.遞歸神經網絡:語言建模(45分)
在這一節,你將首次實現一個用於建立語言模型的遞歸神經網絡。
語言模型的建立是NLP技術的核心部分,語言模型被廣泛使用於語音識別,機器翻譯以及其他各類系統。給出一串連續的詞
其中,
你的任務是實現一個遞歸神經網絡,此網絡利用隱層中的反饋信息對“歷史記錄”
其中,
其中,
輸出向量
其中,
(a) (5分)
通常地,我們使用困惑度來評估語言模型的性能,其定義形式如下:
即通過模型分佈
對於一個詞庫
解答:使用的樣例
因此,上式可以被聯立爲如下形狀:
這條公式展示了交叉熵在數學意義上的最小化目標,同時表達了困惑度在幾何意義上的最小值。當模型的預測值完全隨機化時,會有
(b) (5分)
正如註釋[2]所描述的操作,在時刻
其中,
此外,還要計算代表前趨隱層權值的導數:
解答:調用函數
(c) (5分)
下面爲一步迭代的網絡框圖:
繪製下面三次迭代所“展開”的網絡,並且計算迭代時後向傳播的梯度:
這裏
最好參考講義[5]所描述的後向傳播原理去將這些梯度表達成殘差的形式:
(同樣的表達方式在迭代時刻
注意一個訓練樣本的實際梯度是需要我們將直到
解答:
還是延續前一節的條件
\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)
\frac{\partial{J^{(t)}}}{\partial{L_{x^{(t-1)}}}}=\left.\frac{\partial{J^{(t)}}}{\partial{b_1}}\right|_{(t-1)}I^T
\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)}
(d) (3分)
對於給定的
回憶各個參數的設置:
(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模型是一個生成式模型,因爲它建模了數據序列
訓練完成後,在代碼q3 RNNLM.py
中實現函數generate_text()
。該函數首先運行RNN前向算法,在起始標記<eos>
處建立索引,然後從每輪迭代的</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
[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