ELMo模型解讀

 

 

 

 

  在反覆的看了ELMo源碼和參考網上各路大神的經驗之後,終於對ELMo的架構有了比較清楚的認識。總結一下自己對ELMo的理解,其實還有很多細節沒有搞清楚。

一.模型架構

下面是我畫的一個架構簡圖,對於ELMo不管你輸入的是詞還是字符,它都會以字符的單位進行後續的字符卷積,對與詞的索引是根據詞典序號索引的,而字符論文說英文的字符加上一些特殊的標記字符總共不會超過262個,所以對字符的索引是通過utf-8編碼來索引的,比如單詞「word"的utf-8編碼是{119,111,114,100},通過這些編碼就可以找到某一個固定的字符。對於整個模型的輸入(這裏只考慮最小單位的輸入,也就是模型的每一個樣本)的話由於是語言模型,而作者在train_elmo文件中定義的時間步(unroll_steps)是20,也就是一個樣本就是X=20個詞,對應的Y=20個對應的下一個詞。這就是我們之前常說的一個樣本X:Y。

     在得到我們的樣本之後,初始化一個262*16的字符嵌入矩陣(或者二維數組),這個16就是每一個字符向量的緯度可以自己定義,通過索引可以找到每個詞對應字符的向量,然後進行字符卷積,這其中包括最池化,然後在經過2個highway layers。進入BiLSTM。這一層總共有20個LSTMCell,分別對應20個詞的向量的輸入,論文中的4096也就是源碼中的lstm_dim其實就是隱藏層最原始的h,c的緯度。或者說就是單個LSTMCell裏面單個門的sigmoid單元的個數,這個參數其實就是tensorflow中tf.nn.rnn_cell.LSTMCell(num_units)中的第一個參數。注意不同地方的命名不一樣,非常容易混淆。源碼中的名字也換了好幾次。

    兩層LSTM之間還有一個殘差連接,其實就是把第一層LSTM的輸入加到LSTM的輸出上。

   最後就是一個softmax,但是這個用的是Sampled_softmax,這個我開始讀源碼的時候並沒有注意到,知道看到n_negtive_samples_batch這個參數時纔開始注意它。在網上找了很多資料感覺都是把論文的東西重複了一遍,並沒有講的很清楚,這裏感覺是用了負採樣的思想,在計算softmax的時候,只需要找一些負樣本,並不需要計算所有的可能的詞。但是這只是訓練 的時候纔會用到,而真正在預測的時候還是要遍歷詞典,計算softmax分母的和,就是那個當詞典很大的時候非常耗費時間的那部分。

 

注意:

我上面所說的中間有20個詞輸入是源碼中的bilm.train中的實現方式,是通過tf.nn.static_rnn()實現的。就是這個BiLSTM的長度是固定的,不管你的句子長度有多少,他都會湊成20個詞。這裏只是在訓練權重的時候時間步設置爲20,這裏訓練就是訓練模型的共享參數,因爲rnn的參數在時間步上是共享的,這一點要注意。

但是在bilm.model中也是最初生成詞向量的結構中,rnn是用tf.nn.dynamic_rnn()實現的,感覺這裏纔是真正的語言模型,在訓練好參數之後,這裏生成文件中每一行句子的詞向量,這裏的動態rnn,每一詞輸入的個數就是每一行句子的長度,而每一個批量的時間步就是這個批量中最大句子的長度,這個批量中長度不夠的,用‘0’來進行填補。

下面是我用Notepad++打開的作者使用的訓練文件部分截圖,作者使用的訓練文件是已經分詞好了的,將單詞和標點符號以及特殊的’s都用空格分開。難怪源碼中沒有進行分詞處理,只是通過空格分開了每一個詞。

 

爲了更加清楚的認識輸入和輸出,我打印了2個訓練樣本{X:Y}如下圖所示。

 

 

但是在bilm.model中確用了另一種實現方式,tf.nn.dynamic_rnn(),這種方式的BiLSTM的長度不是固定的,而且每一個批量的都不一定一樣,它是根據輸入的句子長度來進行調整的,我不知大這個bilm.model 是用來幹嘛的,他後面並沒有調用。如果知道的可以討論一下。

模型搞清楚之後,就是跑代碼了,由於這個模型對設備要求較高,我只用了一個小型的數據train文件和詞典文件跑了一下。得到了3個ckpt文件和一個權重文件,這個權重文件包含了ELMo的所有模型參數,用HDF explorer打開後可以查看參數的結構。

有了模型結構ckpt文件和模型參數weights.h5文件我就可以算出特定句子中詞的3層組合向量,也就是作者所說的與上下文相關的詞向量。

 

二.動態詞向量(context dependent)詞向量的是生成

      其實ELMo模型最大的創新之處就是這個,生成與上下文相關的詞向量。作者先用20個時間步的模型訓練共享參數,這個上面講過使用靜態rnn實現的,然後在計算動態詞向量的時候用了動態rnn,這個時候給定任意長度的句子,用之前預訓練好的模型,可以算出這個句子的每個詞的3層向量,這3層向量由一層是與上下文無關的向量,這個也是模型中BiLSTM的輸入,另外兩層的向量是lstm的輸出,當然模型最後的輸出是經過投影之後的。生成動態詞向量的函數是dump_bilm_embeddings(),這個函數把訓練文件中每個字的詞向量保存爲h5格式的文件。3層動態詞向量就是代碼中的lm_embeddings這個4階張量。當我們獲取這個3層動態詞向量之後,爲了檢驗詞向量的質量,必須搭建下游模型比如文本分類等。輸入下游模型的詞向量可以是這3層詞向量的任意組合,也可以是單個層的輸入到下游模型中。當然這個組合權重論文中也提到,也可以加入每層的組合權重來體現每層的向量在不同任務時所發揮的比重。代碼中的dump_token_embeddings()保存的其實是與上下文無關的詞向量。很明顯他是用詞典文件生成的,每一個詞的詞向量是唯一的,dump_bilm_embeddings()纔是真正的動態的詞向量