[[TOC好像還不支持]]
簡介
bert官方代碼google-research/bert中有很多很強的設定,稍有改動就會導致結果很差。此文檔簡單總結如下浸剩。
google-research/bert版本:d66a146
該文檔撰寫時間:2019年4月11日
主要基于任務:run_classifier.py
一糠馆、學習率相關
代碼使用了帶warmup和decay的Adam(AdamWeightDecayOptimizer),這兩個參數(shù)策略是動態(tài)學習率常用的工腋,問題在于,官方代碼中此兩參數(shù)跟命令行參數(shù)num_train_epochs-訓練輪數(shù)強關聯(lián)畅卓,如果盲目修改代碼而不恰當?shù)卦O置num_train_epochs參數(shù)就會很糟糕擅腰。
一個重要、多次用的參數(shù):訓練總步數(shù)num_train_steps=num_train_steps = int(len(train_examples) / FLAGS.train_batch_size * FLAGS.num_train_epochs)是由樣本數(shù)翁潘、batch_size和命令行參數(shù)num_train_epochs輪數(shù)計算出來的趁冈。【很重要拜马,一定注意】
1.1 warmup
在訓練的開始階段使用較小的學習率再逐漸調整到預設初始學習率對訓練有一定的幫助(某共識渗勘,出處未尋)。代碼中使用了線性的調整俩莽,計算公式(github)如下:
# Implements linear warmup. I.e., if global_step < num_warmup_steps, the
# learning rate will be `global_step/num_warmup_steps * init_lr`.
if num_warmup_steps:
global_steps_int = tf.cast(global_step, tf.int32)
warmup_steps_int = tf.constant(num_warmup_steps, dtype=tf.int32)
global_steps_float = tf.cast(global_steps_int, tf.float32)
warmup_steps_float = tf.cast(warmup_steps_int, tf.float32)
warmup_percent_done = global_steps_float / warmup_steps_float
warmup_learning_rate = init_lr * warmup_percent_done
is_warmup = tf.cast(global_steps_int < warmup_steps_int, tf.float32)
learning_rate = (
(1.0 - is_warmup) * learning_rate + is_warmup * warmup_learning_rate)
當訓練的步數(shù)global_steps小于warmup_steps時旺坠,將學習率乘以global_steps/warmup_steps這樣一個線性增長的系數(shù)。warmup_steps的計算公式(github)是num_warmup_steps = int(num_train_steps * FLAGS.warmup_proportion),其中warmup_proportion是一個命令行參數(shù)扮超,默認0.1取刃,num_train_steps是總共訓練的步數(shù),由num_train_epochs輪數(shù)計算出瞒津,如果這個數(shù)設的太大(尤其當訓練樣本超大時)前期學習率會很低基本不優(yōu)化蝉衣。
1.2 lr decay
代碼中使用了線性學習率衰減,使用了tf.train.polynomial_decay多項式衰減巷蚪,參數(shù)如下:
# Implements linear decay of the learning rate.
learning_rate = tf.train.polynomial_decay(
learning_rate,
global_step,
num_train_steps,
end_learning_rate=0.0,
power=1.0,
cycle=False)
經(jīng)過num_train_steps后將學習率衰減到end_learning_rate=0.0病毡,power=1.0表示是個線性衰減。num_train_steps是根據(jù)命令行參數(shù)FLAGS.num_train_epochs-輪數(shù)計算出的屁柏,F(xiàn)LAGS.num_train_epochs默認是3啦膜,即如果未設置該參數(shù),3個epoch后學習率將是0L视鳌僧家!。
附tf.train.polynomial_decay介紹:
global_step = min(global_step, decay_steps)
decayed_learning_rate = (learning_rate - end_learning_rate) *
(1 - global_step / decay_steps) ^ (power) +
end_learning_rate
【注意】:
- CASE1:訓練時間后裸删,調大batch_size八拱,會導致訓練總步數(shù)num_train_steps變小,甚至小于當前全局步數(shù)global_steps,此時學習率會變成0肌稻,就沒必要訓練了清蚀。可以對應比例調整num_train_epochs訓練輪數(shù)解決爹谭。
1.3 二次訓練
訓練一次后發(fā)現(xiàn)沒有完全收斂秽誊,要再接著訓練一次椎咧,但最新的checkpoints中學習率已經(jīng)變成0了鹰服,修改num_train_epochs直接訓練會導致沒有任何效果议惰。可以做如下修改:
- 指定新的output_dir, FLAGS.init_checkpoint指向上一次訓練的output_dir腹泌。
- model_fn函數(shù)初始化模型時不要restore優(yōu)化器相關的權重嘶卧,尤其learning_rate。
二真屯、shuffle-訓練時的數(shù)據(jù)打亂
如果訓練樣本順序輸入脸候,且同類樣本較多時,代碼中的shuffle可能起不到作用绑蔫。d = d.shuffle(buffer_size=100)使用了Dataset的shuffle,其中buffer_size指緩沖區(qū)大小泵额。訓練時每次從緩沖區(qū)中按batch取樣本配深,但緩沖區(qū)的補充是順序取后續(xù)樣本,如果buffer_size=1那就是沒有打亂嫁盲,順序地取樣本篓叶,如果buffer_size等于樣本總數(shù)就相當于全局shuflle,但可能內(nèi)存不夠羞秤。
【建議】:數(shù)據(jù)處理時提前打亂缸托;或buffer_size調大些,比如調成最大類下樣本數(shù)瘾蛋。
三俐镐、流程相關-Estimator
Estimator時tensorflow高級api,旨在將訓練哺哼、驗證佩抹、預測、部署統(tǒng)一取董,但會帶來很大的不靈活棍苹。常用流程一個epoch后eval一次,可以通過多次/循環(huán)調用estimator.train茵汰、estimator.evaluate枢里。但是:
目前代碼版本,每次train/evaluate都會重新構建model、加載與訓練模型栏豺、回復最新的checkpoint梭灿,目前有個PR提出通過RunHook避免,相見google-research/bert/pull/450
一定不要忽略FLAGS.num_train_epochs和num_train_epochs
后記
相比pytorch冰悠,tf學習成本要高一些堡妒,尤其一些高級api。預研型實驗還是pytorch簡單高效溉卓。