BERT模型的OneFlow实现
BERT模型的OneFlow實現
模型概述
BERT(Bidirectional Encoder Representations from Transformers)是NLP領域的一種預訓練模型。本案例中,基于論文BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding實現了BERT模型的OneFlow版本。
模型架構
BERT 在實際應用中往往分為兩步:
? 首先,預訓練得到 BERT 語言模型;
? 然后,為滿足下游應用,在得到的 BERT 語言模型的基礎上,多加一層網絡,并進行微調,得到下游應用。
快速開始
獲取相關數據集
提供了完成 BERT 預訓練及 SQuAD 微調的 OFRecord 數據集及相關數據文件,可以通過以下命令下載并解壓:
wget https://oneflow-static.oss-cn-beijing.aliyuncs.com/oneflow-tutorial-attachments/bert_squad_dataset.zip
unzip bert_squad_dataset.zip
解壓后的文件目錄清單如下:
? bert_config.json、vocab.txt:制作 prediction json 文件需要的文件,來自google bert
? dev-v1.1/、dev-v1.1.json:SQuAD 檢驗集,用于打分
? part-0:預訓練集樣本(40個樣本)
? train-v1.1:SQuAD 訓練集,已經轉為 ofrecord 數據集格式
以上各個文件將在下文的預訓練任務、SQuAD 微調中使用到。
訓練 BERT 模型
首先,克隆 OneFlow-Benchmark 倉庫。
git clone https://github.com/Oneflow-Inc/OneFlow-Benchmark.git
cd OneFlow-Benchmark/LanguageModeling/BERT/
然后,通過以下命令,使用預訓練好的 pretrain 模型以及小型樣本集合,開始 BERT 預訓練查看效果:
python ./run_pretraining.py
–gpu_num_per_node=1
–learning_rate=3e-5
–batch_size_per_device=1
–iter_num=3
–loss_print_every_n_iter=50
–seq_length=128
–max_predictions_per_seq=20
–num_hidden_layers=12
–num_attention_heads=12
–max_position_embeddings=512
–type_vocab_size=2
–vocab_size=30522
–attention_probs_dropout_prob=0.0
–hidden_dropout_prob=0.0
–hidden_size_per_head=64
–use_boxing_v2=True
–data_dir=./dataset/
–data_part_num=1
–log_dir=./bert_regresssioin_test/of
–loss_print_every_n_iter=5
–model_save_dir=./bert_regresssioin_test/of
–warmup_batches 831
–save_last_snapshot True
將獲得類似以下輸出:
Running bert: num_gpu_per_node = 1, num_nodes = 1.
gpu_num_per_node = 1
node_num = 1
node_list = None
learning_rate = 3e-05
weight_decay_rate = 0.01
batch_size_per_device = 1
iter_num = 20
warmup_batches = 831
log_every_n_iter = 1
data_dir = ./dataset/
data_part_num = 1
use_fp16 = None
use_boxing_v2 = True
loss_print_every_n_iter = 5
model_save_every_n_iter = 10000
model_save_dir = ./bert_regresssioin_test/of
save_last_snapshot = True
model_load_dir = None
log_dir = ./bert_regresssioin_test/of
seq_length = 128
max_predictions_per_seq = 20
num_hidden_layers = 12
num_attention_heads = 12
max_position_embeddings = 512
type_vocab_size = 2
vocab_size = 30522
attention_probs_dropout_prob = 0.0
hidden_dropout_prob = 0.0
hidden_size_per_head = 64
Time stamp: 2020-07-06-19:09:29
I0706 19:09:29.605840639 34801 ev_epoll_linux.c:82] Use of signals is disabled. Epoll engine will not be used
Init model on demand
iter 4, total_loss: 11.032, mlm_loss: 10.281, nsp_loss: 0.751, speed: 33.086(sec/batch), 0.151(sentences/sec)
iter 9, total_loss: 11.548, mlm_loss: 10.584, nsp_loss: 0.965, speed: 0.861(sec/batch), 5.806(sentences/sec)
iter 14, total_loss: 10.697, mlm_loss: 10.249, nsp_loss: 0.448, speed: 0.915(sec/batch), 5.463(sentences/sec)
iter 19, total_loss: 10.685, mlm_loss: 10.266, nsp_loss: 0.419, speed: 1.087(sec/batch), 4.602(sentences/sec)
Saving model to ./bert_regresssioin_test/of/last_snapshot.
average speed: 0.556(sentences/sec)
詳細說明
腳本說明
腳本參數
run_pretraining.py通過命令行參數配置包括超參在內的訓練環境,可以通過 run_pretraining.py --help查看,以下是這些參數作用的具體說明:
? gpu_num_per_node: 每個節點上 GPU 的數目,OneFlow 要求每個節點的 GPU 數目必須一致
? node_num: 節點數目,即分布式訓練時的主機數目
? node_list: 節點列表,如果節點數大于1,則需要通過 node_list 指定節點列表,節點列表為字符串形式,采用逗號分隔,如–node_num=2 --node_list=“192.168.1.12,192.168.1.14”
? learning_rate: Learning rate
? weight_decay_rate:設置權重衰減率
? batch_size_per_device: 分布式訓練時每個設備上的batch大小
? iter_num ITER_NUM: 訓練的總輪數
? warmup_batches: 預熱輪數,默認值為10000
? data_dir: OFRecord數據集的路徑
? data_part_num:OFRecord數據集目錄下的數據文件數目
? use_fp16: 是否使用fp16
? use_boxing_v2: 是否使用boxing v2
? loss_print_every_n_iter:訓練中每隔多少輪打印一次訓練信息(loss信息)
? model_save_every_n_iter: 訓練中每隔多少輪保存一次模型
? model_save_dir: 模型存儲路徑
? save_last_snapshot:指定最后一輪訓練完成后,模型保存路徑
? model_load_dir:指定模型加載路徑
? log_dir LOG_DIR:指定日志路徑
? seq_length: 指定BERT句子長度,默認值為512
? max_predictions_per_seq: 默認值為80
? num_hidden_layers:隱藏層數目,默認值為24
? num_attention_heads: Attention頭數目,默認值為16
使用完整的 Wikipedia + BookCorpus 數據集
如果需要從無到有進行 BERT 的 pretrain 訓練,則需要使用較大的訓練集。
如果感興趣,可以通過 google-research BERT 的頁面,下載 tfrecord 格式的數據集。再根據加載與準備OFRecord數據集中的方法,將 TFRecord 數據轉為 OFRecord 數據集使用。
將 Tensorflow 的 BERT 模型轉為 OneFlow 模型格式
如果想直接使用已經訓練好的 pretrained 模型做 fine-tune 任務(如以下將展示的SQuAD),可以考慮直接從 google-research BERT 頁面下載已經訓練好的 BERT 模型。
再利用提供的 convert_tf_ckpt_to_of.py 腳本,將其轉為 OneFlow 模型格式。轉換過程如下:
首先,下載并解壓某個版本的 BERT 模型,如 uncased_L-12_H-768_A-12。
wget https://storage.googleapis.com/bert_models/2020_02_20/uncased_L-12_H-768_A-12.zip
unzip uncased_L-12_H-768_A-12.zip -d uncased_L-12_H-768_A-12
然后,運行以下命令:
cd uncased_L-12_H-768_A-12/
cat > checkpoint <<ONEFLOW
model_checkpoint_path: “bert_model.ckpt”
all_model_checkpoint_paths: “bert_model.ckpt”
ONEFLOW
該命令將在解壓目錄下創建一個 checkpoint 文件,并寫入以下內容:
model_checkpoint_path: “bert_model.ckpt”
all_model_checkpoint_paths: “bert_model.ckpt”
此時,已經準備好待轉化的 TensorFlow 模型目錄,整個模型目錄的結構如下:
uncased_L-12_H-768_A-12
├── bert_config.json
├── bert_model.ckpt.data-00000-of-00001
├── bert_model.ckpt.index
├── checkpoint
└── vocab.txt
接著使用 convert_tf_ckpt_to_of.py 將 TensorFlow 模型轉為 OneFlow 模型:
python convert_tf_ckpt_to_of.py
–tf_checkpoint_path ./uncased_L-12_H-768_A-12
–of_dump_path ./uncased_L-12_H-768_A-12-oneflow
以上命令,將轉化好的 OneFlow 格式的模型保存在 ./uncased_L-12_H-768_A-12-oneflow 目錄下,供后續微調訓練(如:SQuAD)使用。
微調:SQuAD 問答任務
將 pretrained 模型修改為 SQuAD 模型
只需要在 BERT 的 backbone 基礎上,加上一層 output 層,并修改 loss 的表達式即可,完整的代碼可以查看 squad.py 腳本,以下是幾處關鍵修改:
def SQuADTrain():
#…
backbone = bert_util.BertBackbone()
#在BERT的基礎上加上一個全連接層
with flow.name_scope("cls-squad"):final_hidden = backbone.sequence_output()final_hidden_matrix = flow.reshape(final_hidden, [-1, hidden_size])logits = bert_util._FullyConnected(final_hidden_matrix,hidden_size,units=2,weight_initializer=bert_util.CreateInitializer(initializer_range),name='output')logits = flow.reshape(logits, [-1, seq_length, 2])start_logits = flow.slice(logits, [None, None, 0], [None, None, 1])end_logits = flow.slice(logits, [None, None, 1], [None, None, 1])#重新定義SQuAD任務的lossstart_loss = _ComputeLoss(start_logits, start_positions_blob, seq_length)end_loss = _ComputeLoss(end_logits, end_positions_blob, seq_length)total_loss = 0.5*(start_loss + end_loss)return total_loss
為了得到一個初始化的 squad 模型,通過以下腳本啟動 squad 訓練,并保存模型。
python ./run_squad.py
–gpu_num_per_node=1
–learning_rate=3e-5
–batch_size_per_device=2
–iter_num=50
–loss_print_every_n_iter=50
–seq_length=384
–max_predictions_per_seq=20
–num_hidden_layers=12
–num_attention_heads=12
–max_position_embeddings=512
–type_vocab_size=2
–vocab_size=30522
–attention_probs_dropout_prob=0.0
–hidden_dropout_prob=0.0
–hidden_size_per_head=64
–use_boxing_v2=True
–data_dir=./dataset/train-v1.1
–data_part_num=1
–log_dir=./bert_regresssioin_test/of
–model_save_dir=./bert_regresssioin_test/of
–warmup_batches 831
–save_last_snapshot True
完成訓練后,在 ./bert_regresssioin_test/of/last_snapshot 中保存有初始化的 SQuAD 模型,將其與訓練好的 BERT 合并后,進行微調(fine-tune)訓練。
合并 pretrained 模型為 SQuAD 模型
SQuAD 模型是在 pretrained 模型基礎上的擴充,需要參照模型的加載與保存中的“模型部分初始化和部分導入”方法,將訓練好的 BERT pretrained 模型與初始化的 SQuAD 模型合并。
cp -R ./bert_regresssioin_test/of/last_snapshot ./squadModel
cp -R --remove-destination ./dataset/uncased_L-12_H-768_A-12_oneflow/* ./squadModel/
OneFlow 預訓練模型的訓練次數問題
OneFlow 生成的模型目錄中,會有一個名為 System-Train-TrainStep-xxx 的子目錄(xxx為作業函數的函數名),該子目錄下的 out 文件中,保存有訓練總迭代數,并且這個迭代數會用于動態調節訓練過程的learning rate。
為了防止保存的迭代數影響到微調的訓練,應該將out文件中的二進制數據清零:
cd System-Train-TrainStep-xxx
xxd -r > out <<ONEFLOW
00000000: 0000 0000 0000 0000
ONEFLOW
如果使用的是由 TensorFlow 轉過來的預訓練模型,則可以省去這個步驟。
開始 SQuAD 訓練
通過 run_suqad.py 腳本,開始訓練 SQuAD 模型,主要配置如下:
? 使用以上合并得到的 SQuAD 模型 ./squadModel
? 采用 SQuAD v1.1 作為訓練集
? epoch = 3 (iternum = 886413/(48) = 8310)
? learning rate = 3e-5
python ./run_squad.py
–gpu_num_per_node=4
–learning_rate=3e-5
–batch_size_per_device=8
–iter_num=8310
–loss_print_every_n_iter=50
–seq_length=384
–max_predictions_per_seq=20
–num_hidden_layers=12
–num_attention_heads=12
–max_position_embeddings=512
–type_vocab_size=2
–vocab_size=30522
–attention_probs_dropout_prob=0.0
–hidden_dropout_prob=0.0
–hidden_size_per_head=64
–use_boxing_v2=True
–data_dir=./dataset/train-v1.1
–data_part_num=8
–log_dir=./bert_regresssioin_test/of
–model_save_dir=./bert_regresssioin_test/of
–warmup_batches 831
–save_last_snapshot True
–model_load_dir=./squadModel
預測及打分
生成為了生成 Preidiction File 格式的 json 文件,先將預測結果保存為 npy 文件,再使用 google BERT的run_squad.py 中的 write_predictions 函數,轉化為 json 格式。
利用 run_squad_predict.py 生成 all_results.npy 文件:
python run_squad_predict.py
–gpu_num_per_node=1
–batch_size_per_device=4
–iter_num=2709
–seq_length=384
–max_predictions_per_seq=20
–num_hidden_layers=12
–num_attention_heads=12
–max_position_embeddings=512
–type_vocab_size=2
–vocab_size=30522
–attention_probs_dropout_prob=0.0
–hidden_dropout_prob=0.0
–hidden_size_per_head=64
–use_boxing_v2=True
–data_part_num=1
–data_dir=./dataset/dev-v1.1
–log_dir=./bert_regresssioin_test/of
–model_load_dir=path/to/squadModel
–warmup_batches 831
注意將以上 model_load_dir 修改為 訓練好的 squadModel。
得到 all_results.npy 文件后,在google bert倉庫目錄下(注意該倉庫的 tensorflow 版本為 tensorflow v1 ),運行提供的 npy2json.py (由 google bert 中的 run_squand.py 修改得來):
python npy2json.py
–vocab_file=./dataset/vocab.txt
–bert_config_file=./dataset/bert_config.json
–do_train=False
–do_predict=True
–all_results_file=./all_results.npy
–predict_file=./dataset/dev-v1.1.json
–max_seq_length=384
–doc_stride=128
–output_dir=./squad_base/
注意將 all_results_file 修改為上一步得到的 all_results.npy 的路徑。
最終,得到 predictions.json 文件,可以使用 evaluate-v1.1.py 進行打分。
python evaluate-v1.1.py
./dataset/dev-v1.1.json
path/to/squad_base/predictions.json
分布式訓練
如之前介紹腳本參數時描述:進行分布式訓練,只需要在啟動訓練腳本式加入 node_num 選項指定主機數目及 node_list 選項即可:
python run_squad_predict.py
–gpu_num_per_node=1
–batch_size_per_device=4
–iter_num=2709
–seq_length=384
–max_predictions_per_seq=20
–num_hidden_layers=12
–num_attention_heads=12
–max_position_embeddings=512
–type_vocab_size=2
–vocab_size=30522
–attention_probs_dropout_prob=0.0
–hidden_dropout_prob=0.0
–hidden_size_per_head=64
–use_boxing_v2=True
–data_part_num=1
–data_dir=./dataset/dev-v1.1
–log_dir=./bert_regresssioin_test/of
–model_load_dir=path/to/squadModel
–warmup_batches 831
–node_num=2
–node_list=“192.168.1.12,192.168.1.14”
總結
以上是生活随笔為你收集整理的BERT模型的OneFlow实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Yolov3 的 OneFlow 实现
- 下一篇: Wide Deep的OneFlow网络