SQUAD的rnet復現踩坑記

在港科大rnethttps://github.com/HKUST-KnowComp/R-Net) 實現的基礎上作了復現 python

採用melt框架訓練,緣由是港科大實如今工程上不是很完美,包括固定了batch size,固定了context 長度爲400,過濾了超過長度的context,每一個batch長度都固定到400git

melt框架支持dynamic batch size dynamic batch length,多gpu,修復了bucket的問題,目前採用長度<=400 batch size 64 長度> 400 batch size 32 github

   

Dynamic batch能夠比較準確的eval 由於樣本數目不必定是64的倍數, dynamic batch length 能夠必定程度提速 而且結合buket和不一樣的batch size來跑較長的文本,港科大去掉了部分長度>400的訓練數據 api

   

   

港科大版README展現的效果(word+char) 安全

   

   

下載港科大代碼修改hiden size 75->76 實際跑沒有達到上面這麼高的dev效果 實際效果以下 app

   

Word + char 框架

Exact Match: 70.39735099337749, F1: 79.38397223161256 dom

   

Word only ide

Exact Match: 69.59318826868495, F1: 78.64400408874889 函數

   

港科大word only

   

港科大 word + char hidden 76

   

Word + char hidden 75 (原始代碼不作任何改動, python3 tensorflow_version: 1.8.0-dev20180330) 彷佛仍是沒達到readme 給出的那麼高效果 em 差不到0.6個點

   

   

   

訓練速度以下圖 採用單gpu gtx1080ti訓練 硬盤ssd,參考港科大我提的issue 改動港科大默認hidden size 75 -> 76 4的倍數)不然速度比較慢, 不使用bucket

   

   

   

buckets按照 40,361 加速一點點

   

兩個gpu加速效果甚小

四個gpu

   

gpu word + char 訓練 char embedding和港科大同樣 設置爲8

   

對應大概0.35s/it

   

在這個數據集 其它一些實驗結果

adadelta 0.5 learning rate效果最好 > adam 0.001 > adagrad 0.01

按照dev loss動態減少learning rate有效 和港科大同樣採用decay patience 3 decay factor 0.5 後面也能夠考慮對比採用監控dev em或者f1來作lr decay

Glove word embedding 若是finetune 效果提高一點

contextquestion採用不一樣rnn encoder 效果變差。

4gpu 64*4 batch size訓練 因爲batch size的增大 收斂加快,word only模型f1 能達到0.783因爲 gpu batch size 64,可是比較奇怪是dev loss30 epoch以後就呈現增加過擬合趨勢 儘管dev em f1指標沒有過擬合傾向

   

   

後來我發現 始終和HKUST版本效果有差距 港科大代碼實測 雖然em 70+ f1 78+ 若是隻使用word em69.6 f1 78+, 差在哪裏呢 只能說理解不深。。。 通過屢次實驗定位。。。

這裏發現開始階段表現更好的模型 通常來講最後效果也會好一些。

   

緣由在於dropout。。

我改爲了這個樣子

   

港科大代碼是dropout放在了類的初始化函數中 每一個layer一個dropout

   

A Theoretically Grounded Application of Dropout in

Recurrent Neural Networks

   

The variant Gal and Ghahramani [6] proposed is to use the same dropout mask at each time step for both inputs, outputs, and recurrent layers.

   

來自 <https://becominghuman.ai/learning-note-dropout-in-recurrent-networks-part-1-57a9c19a2307>

   

  • variational_recurrent: Python bool. If True, then the same dropout pattern is applied across all time steps per run call. If this parameter is set, input_size must be provided.

       

    來自 <https://www.tensorflow.org/api_docs/python/tf/contrib/rnn/DropoutWrapper>

       

    這裏港科大采用的是variational_recurrent 也就是個人修改也沒有改動這個事實 港科大爲啥要放在init裏面?

       

  • noise_shape: 1D tensor of type int32 representing the shape of the binary dropout mask that will be multiplied with the input.
  • For instance, if your inputs have shape (batch_size, timesteps, features), and you want the dropout mask to be the same for all timesteps, you can use noise_shape=[batch_size, 1, features].

       

    來自 <https://www.tensorflow.org/api_docs/python/tf/layers/dropout>

       

       

       

    我理解主要目的是爲了context question在一次訓練batch 同一個contextquestion共享 相同的droput

       

    不過港科大 處理char的時候 context question embedding用了不一樣droput。。 彷佛用cudnn gru 也不太好share。。。由於是3緯度的 展開以後 batch size 不同 對應不上去了 固然也能夠實現share(title + reshape)

    爲啥港科大對單層 char 沒有用cudnn gru 仍是用普通gru 我理解同樣的 我這裏仍是使用cudnn gruchar建模

       

       

    Ok 最後是運行效果

       

    對應squad 這個 HKUST只選了glove有預訓練的詞 而且不finetune,實測是否finetune word embedding針對這個應用效果差很少

    特別須要注意是context question須要共享相同的dropout不然效果變差

       

    無一例外 不共享的.nsd效果差

       

    實驗了 模仿HKUSTgloveonly,選取訓練集合中min_count>=10的做爲vocab增長少許不在glove中的詞(91590->91651不到100),選取glove+train全量

       

       

    彷佛min count10 效果好一點點

    Word only採用了contextquestion共享droput以後 效果追上HKUST效果

       

    比較明顯的 貌似 gloveonly 仍是 finetune效果比較好 說明基本都是訓練中出現充分的詞彙finetune效果會好一些

    可是rnet.fixebm效果好於rnet說明 可能 部分詞彙finetune 部分沒訓練到 用的初始隨機變量 效果會稍微差一些?

    Word only版本 min10.fixemb效果好 說明若是詞彙出現足夠 訓練過程當中參與rnn調整 對效果有必定提高 儘管隨機的詞向量不變化

       

    dev指標提高一點 訓練數據指標提高明顯 包括loss 可是看dev loss已經有明顯過擬合傾向 雖然dev指標仍是稍好 安全起見squad能夠不finetune word embedding char embedding能夠補充不常見詞的知識。

    TODO 能夠嘗試 ngram embedding 而且嘗試不一樣組合方式 以及添加highway network layer normalization.

       

    下圖是我對char 也採用了 droput share機制 相似word對應.chsd 不過看起來相比worddroput share 對結果影響不大。。 因爲模型自己存在隨機性 幾個對比效果以下

       

       

       

       

    不過感受效果仍是比HKUST的實現差一丟丟,儘快已經不多了,可是特別eval loss 仍是明顯比HKUST的高 word only 2.9+ word+char 2.88 大概 HKUST word only 2.88 word+char 2.85大概

    又仔細看了下HKUST的預處理髮現一個diff 我對於 unk的處理和HKUST不同 HKUST使用的是全0向量做爲unk向量 而且訓練過程當中再也不變化。。

    儘管直覺感受這樣很差。。 不過我仍是實驗了一下 設置 specail_as_zero == True(對應模型.hkust

    可是這組實驗仍是dev loss 不如HKUST。。這不是關鍵性的影響

       

    最後考慮是不是bucket 400 影響了效果 儘管>400的文本很少 是個人的tf bucket代碼使用 或是 tf bucket自己影響了隨機性?

    HKUST注意是去掉了全部>400長度的文章,這裏我仍是使用全部文章 可是不使用bucket(nobucket) bucket貌似有一點點影響 不大

    另外以前我使用了手工設定的按照特定epoch修改learning rate的方式,這裏也改成使用HKUSTpatience 3,decay 0.5的方式。

    實驗結果wordonly基本打平了HKUST的原版效果,儘管彷佛dev loss後期不是如HKUST原版穩定可是數值基本一致,

    這說明decay是很是重要的。。。 可能由於訓練數據集合比較小,針對dev監控的動態學習率調整比較關鍵。 bucket若是隻是400這個影響不大。

    另外語料不是太大 隨機性也起到很大效果 可能陷入局部最優 不能找到全局最優

       

       

       

       

       

    HKUST word only 最佳

    Em 69.69 F1 78.69 dev loss 2.87

       

       

       

       

    可是word_char效果仍是明顯比HKUST的差 , 定位問題應該出在char的處理上,注意HKUSTcharencodingcontextqustion 先走的recurrent droput 也就是說

    noise_shape=[shape[0], 1, shape[-1]]狀況下的droputout,因而改原來wordchar都用cudnn gru一樣encoding方式爲HKUST原版的處理方式。

    不過這裏實際用cudnn效果差的緣由是last state不對,由於cudnn不支持動態長度 因此padding 0也參與計算了 最後last state極可能是padding 0對應的 因此不許確。

    實現效果 dev loss效果確實更貼近了2.85 可是f1值仍是有差距79.1

       

       

    HKUST word+char 最佳

    Em 70.37 F1 79.34 dev loss 2.85 效果彷佛還有一點點上升趨勢

       

       

       

       

    不過按照HKUSTissue

    2.82 in dev, 1.32 in train

       

    來自 <https://github.com/HKUST-KnowComp/R-Net/issues/6>

       

       

    另一個遺留的問題 cudnn gru HKUST的實現 我搬動到其它地方使用save模型 只有 cudnn_gru/opaque_kernel (DT_FLOAT) [87096]

    可是原版代碼 save以後都是帶有

       

    不知道是哪裏影響的 我猜想這裏不影響模型效果 opaque_kernel 應該存儲了全部信息,下面的只是不知道什麼開關影響的 存儲了轉換後的權重數據。

       

    Finally 總結

       

    • Word Share dropout 是最關鍵的 對應 一個context question pair 須要相同的droput
    • Char dropout也是很重要的
    • Adadelta optimizer的默認參數 按照keras 設置 很關鍵 已修改
    • 按照dev losslearning rate decay 很重要
    • Finetune emb train擬合的更好 可是dev彷佛容易過擬合 不要finetune emb 或者開始bufinetune 後期finetune可能有一點點提高效果 目前(70.53,79.12是這樣 不過finetune帶來的提高不多)
    • Embedding 初始化影響一點效果 我以前都是 random_uniform HKUST使用random_normal 0.01 google transformer使用 random_normal dim ** -0.5 實驗都還ok 影響不大
    • 最後仍是eval loss差一點 f1效果仍是差 em 達到了, 可是HKUST訓練過程當中的dev loss計算也是過濾了長度>=400 我這邊實驗了raindev都按照HKUST的作法 去掉長度>=400的(不過每一個batch 長度仍是按dynamic)

      測試集合長度偏短 顯然位置的softmax loss會低一點了 不過彷佛去掉長文本以後效果還穩定了一些 並且提高了f1值。。 那麼說明loss須要考慮下文本長度嗎? TODO

         

      Train 87599

      Dev 10570

         

      Filter context > 400 or qeustion > 100

         

      Train 87391 99.7%

      Dev 10483 99.1%

         

         

      HKUST 60k step

      Exact Match: 70.39735099337749, F1: 79.3839722316126

         

    gezi@gezi:~/mine/hasky/deepiu/squad$ c0 sh ./train/evaulate.sh ./mount/temp/squad/model/rnet.hkust/epoch/model.ckpt-37.00-50505

    Mine:

    [('metric/valid/loss', 2.892608779740621), ('metric/valid/em', 0.7044465468306528), ('metric/valid/f1', 0.7919028182076531)]

       

    epoch:37.3626/60 valid_step:51000 valid_metrics:['metric/valid/loss:2.87955', 'metric/valid/em:0.70466', 'metric/valid/f1:0.79236']

       

    [('metric/valid/loss', 2.9003277952412523), ('metric/valid/em', 0.7048249763481551), ('metric/valid/f1', 0.7928490855150692)]

       

    最高

    ./mount/temp/squad/model/rnet.hkust/epoch/model.ckpt-39.00-53235

    [('metric/valid/loss', 2.890633753983371), ('metric/valid/em', 0.7052980132450332), ('metric/valid/f1', 0.7933954605863096)]

       

    60k

    [('metric/valid/loss', 2.887699271541044), ('metric/valid/em', 0.7054872280037843), ('metric/valid/f1', 0.7925004189917955)]

       

       

       

    仍是f1差一點點 那麼 發現HKUSTinit state是訓練出來的 而不是使用它zero默認初始化的方式 修改按照HKUST的方式

    Model增長變量。。

    rnet/main/encoding/cudnn_gru/bw_0/init_state (DT_FLOAT) [1,1,76]

       

    效果。。 就差在這個地方了 至少對應squad的應用 init state是很是影響效果的。。。 貌似以前各類應用場景都是用的zero做爲init state 能夠試一下對init state訓練。

       

    Rnet.hkust 表示char使用random uniform

    rnet.hkust2 表示char使用random normal stddev 0.01

    Rnet.hkust3 表示char使用相似google transoferrandom normal , stddev dim ** -0.5

    Rnet.hkust.initstaternet.hkust可是 使用了 訓練 init state

       

    貌似hkust3的初始化方式好一點 dev loss

       

       

       

       

       

       

    因爲測試數據少了一小部分 最終測試結果

       

    [('metric/valid/loss', 2.871750499110624), ('metric/valid/em', 0.7094607379375591), ('metric/valid/f1', 0.7962988473545626)]

    gezi@gezi:~/mine/hasky/deepiu/squad$ c3 sh ./train/evaulate.sh ~/temp/squad/model/rnet.hkust.initstate/epoch/model.ckpt-40.00-54600

       

    HKUST 60k step

    Exact Match: 70.39735099337749, F1: 79.3839722316126

       

    基本完整復現了 HKUST RNET固然是在幾乎徹底copy的基礎上 不過作了不少工程改進,有了這個baseline, 更加方便後續實驗對比,有了這個baseline,後面能夠嘗試快速的conv seq2seq, transformer, qanet中的encoder用在文本分類和圖文關係的效果。

    最終定位了 影響最終效果的幾個核心地方 感謝HKUST無私的代碼開源! cudnn的使用 droput的使用 init state參與訓練,太讚了

       

       

    TODO:

       

    Train init state的效果和 <S> </S> 相比呢 是否添加 <S>能夠起到一樣效果?

       

    Random normal google transform效果不錯的 特別是dev loss相對較低 可是隨機性起到了更大的做用 也就是說相同配置 不一樣運行 結果可能會diff很大 特別是因爲動態learning rate調整的不肯定性形成 最終結果不肯定性很大

       

    最終目前跑出來的最佳結果 對應

    使用random uniform embedding char dim 8, glove only vocab, not finetune word embedding, char use share dropuout using cudnn gru with outpout max pooling, using all trainging data for train,dev data for evaluate, buckets [400] batchs size[64,32]

    75k step EM 70.96 F1 79.86

相關文章
相關標籤/搜索