漫談ELMo

詞嵌入在很長一段時間內對NLP領域有很大的推動的作用,以至於embedding已經成爲NLP任務中的一個標準操作。而研究人員卻發現,詞嵌入雖好,但弊端也明顯,於是乎,語言模型就上位了……如果說Word2Vec是NLP領域裏面的一個重要里程碑,那麼ELMo的誕生應該可以算得上另外一個里程碑了吧!最近我正是在學習ELMo,因此想整理一下跟大家探討。

詞嵌入到底出了什麼問題?

詞嵌入(如想詳細瞭解可戳這裏)操作在NLP中已經成爲常規操作,它也使得詞義可以通過稠密向量來表達。但人們開始發現這種操作雖然好,但是仍然有一些問題:

  1. 集外詞怎麼辦?統一使用UNK作爲oov的token並不能區分各個集外詞的詞義。
  2. 多義詞怎麼辦?每個詞只對應一個向量,對於多語義或者有多層意思的詞語而言這樣是不夠的。

第一個問題中,人們嘗試使用字符級的嵌入操作來代替,畢竟字符是可以有限數量的。但第二個問題就是詞嵌入的天然硬傷了,因爲詞語無論出現在什麼場景,都只有一個向量作爲表示,舉個例子說,spark這個詞出現在日常對話裏面一般都是表示“星火”,但如果出現在一些數據科學技術文章中則可能表示spark這個並行計算平臺。

這個問題歸根結底就是,詞向量只在訓練過程考慮了訓練語料上下文賦予其語義,而在使用過程沒有考慮它在特定的語義環境中到底表達何種意思,所以預訓練的詞向量是“上下文獨立”(context-independent)的。

那麼我們在使用詞向量時進行fine-tune可以解決這個問題嗎?很遺憾,也是不行的,人類語言實在太複雜了,有時不僅僅像上面舉例的spark這種領域區別問題,更有可能是表達程度等方面的問題。

所以,我們必須賦予詞嵌入一些更大的彈性,即讓每個詞在特定的詞義環境中表達出它該有的意思,也即我們追求的是獲得“特定上下文”(context-specific)的表徵。

ELMo誕生的前夕

等等,還記得語言模型嗎?語言模型每個時間步輸出的表徵ht=f(xt1,ht1)h_t = f(x_{t-1},h_{t-1})不就正是綜合上下文的表徵嗎?!於是乎,有人就拍案而起:就是你了!

TagLM大概是第一個使用這種想法的模型,咱們直接上圖

圖一 TagLM模型

咱們先看圖的右手邊。右手邊是一個bi-RNN實現的語言模型(我又要打廣告了!如果不瞭解語言模型請戳這裏,不瞭解RNN請戳這裏),即對前向RNN是用前文[x1,x2,...,xt1][x_1,x_2,...,x_{t-1}]預測xtx_{t},而後向RNN用後文[xt+1,xt+2,...,xT][x_{t+1},x_{t+2},...,x_T]預測xtx_{t},每個詞xtx_t進入這個LM都會得到一個hidden state,咱們把它標記爲htLM=[htforward;htbackward]h_{t}^{LM} = [h_{t}^{forward};h_{t}^{backward}]。這一部分是預訓練的,跟咱們使用Word2Vec預訓練詞向量一個道理。

再看左圖下方。每個輸入詞都會同時經過兩個嵌入操作,分別是char-level和word-level的嵌入,兩個嵌入操作得到的向量會拼接起來,作爲網絡主體bi-RNN的input。

然後看左圖中部。詞向量通過一個L層的bi-RNN得到每個time step的hidden state,咱們標記爲ht,lh_{t,l}l表示所在的層標,t則表示輸入序列的時間步。以兩層的bi-RNN爲例,第一層輸出的hidden state會拼接上預訓練的bi-LM得到的雙向隱藏特徵,再進入下一層,最後用以NER等序列標註任務。

簡單來說,tagLM對之前單純使用預訓練詞向量的NLP任務做了簡單的優化,就是加上LM embedding,因此同時考慮了上下文獨立特徵特定上下文特徵

ELMo的降臨

受到TagLM的啓發,ELMo就應運而生,它的降臨將徹底改變之前的局面:詞嵌入不再是一個向量查找表,而是一個function!換句話說,以前咱們可以根據單個詞的index查找出對應的詞向量,但現在不一樣了,我們需要把整句話放進這個函數裏面,我們才能獲得每個詞的表徵向量。而且以前同一個index查找出來的詞向量肯定是相同的,但現在每個詞在任何不同語境下都會有不同的表徵。

老規矩先上圖

圖二 ELMo模型

這個大概是我能找到的最棒的動圖了。ELMo是在TagLM的基礎上進行了若干的優化。

  1. 與TagLM只採用頂層輸出不同,ELMo的representation使用了bi-LSTM所有層的hidden state的以及token的表徵。以L=2爲例,bi-LM輸出的表徵爲
    Rk={xkLM,hk,jforward,hk,jbackwardj=1,2}={hk,jLMj=0,1,2} \begin{aligned} R_k &=\{x_k^{LM},h_{k,j}^{forward},h_{k,j}^{backward}|j=1,2\}\\ &=\{h_{k,j}^{LM}|j=0,1,2\} \end{aligned}
    當j=0時表示輸入token的表徵,這一部分大體跟TagLM是一樣的。原論文作者指出,低層表徵容易捕獲語法信息,語法信息在序列標註如POS、NER等任務中作用比較大,而高層表徵卻更好地捕獲語義信息,在機器翻譯、智能問答等任務裏面比較有用。爲了綜合語義和語法信息,最好的辦法就是將所有信息彙總起來。

  2. 各表徵的彙總方式是
    ELMoktask=γtaskj=0Lsjtaskhk,jLM ELMo_{k}^{task} = {\gamma}^{task}\sum_{j=0}^{L}s_{j}^{task}h_{k,j}^{LM}
    其中γtask\gamma^{task}是一個可訓練單值參數,用於對特定任務場景下對錶徵向量的調整;sjtasks_{j}^{task}是softmax正則化的參數。(原文沒有細說,經過查證,這個應該是一個用作加權平均各層表徵的向量,即sR1×Ls{\in}R^{1{\times}L},只是各層權重歸一化的方法是使用softmax)

    累加號要成立的話,那麼意味着各層的output都必須是相同維度的,對LSTM來說這個還是比較好操作的東西,畢竟輸出維度(即units)多少都是可以設定的,但是要求輸入token表徵也是一樣的維度。原論文說每層units都是4096,但都會映射到512進行skip-connection(按原論文的描述,第一層與第二層的映射output應該相加,這個結構在上圖中沒有畫出來)

  3. 最後一點,ELMo只採用char-level的特徵,就是每個詞采用多個kernel size一共2048個卷積核/層的CNN獲得字符序列特徵,默認設置是兩層,這個CNN也使用了skip-connection,並同樣映射到512維,而且模型參數在每個時間步是共享的。

至此,ELMo的細節應該都摳完了。

ELMo咋用?

ELMo其實就是多層bi-LM,所以它需要在大規模unlabel語料中進行預訓練。在具體的NLP任務中,預訓練好的ELMo相當於一個函數,將輸入句子中每個詞映射成對應的實數向量,然後再將其feed到後續的模型裏面。

在具體NLP任務中,最簡單的做法是將ELMo的模型參數固定住,這樣梯度信號並不會backprop過來,整個ELMo唯一受影響的就是γ\gamma參數和ss參數。但更通行的,也是效果更好的做法,就是在具體的NLP任務中做transfer learning,即對預訓練模型做fine-tune,就像在各種CV任務中一樣。

所以說ELMo開啓了NLP的新曆程,因爲從這之後,NLP任務也可以像CV任務一樣,選擇一個合適的back bone,再針對自己需要解決任務去設計模型。

參考資料

  • https://www.analyticsvidhya.com/blog/2019/03/learn-to-use-elmo-to-extract-features-from-text/
  • https://arxiv.org/pdf/1802.05365.pdf
  • https://blog.csdn.net/sinat_29819401/article/details/90669304
  • https://blog.csdn.net/sinat_29819401/article/details/94015798
  • https://blog.csdn.net/sinat_29819401/article/details/94176245
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章