BERTリポジトリのコードリーディング①(概要を掴む)|言語処理へのDeepLearningの導入の研究トレンドを俯瞰する #7

f:id:lib-arts:20190501180404p:plain

近年猛威を振るっているDeepLearningの言語処理への応用についてまとめていければと思います。
#4,#5ではBERTで用いられているモジュールであるTransformerに関してまとめました。

#6以降ではBERTのリポジトリのサンプル実行と実装の確認について行っていければと思います。

GitHub - google-research/bert: TensorFlow code and pre-trained models for BERT
#6ではまずサンプル実行までまとめました。

#7では簡単なコードリーディングを行なっていければと思います。high-level TensorFlow APIの実装サンプルとして手頃なものを知らなかったので、題材としてもちょうど良さそうです。
以下目次になります。

1. run_classifier.pyの実装概要(main関数まで)
2. BERTのモデル構造の実装概要(create_modelとmodelling.BertModelなどについて)
3. 前処理(file_based_input_fn_builderなど)の実装概要
4. まとめ

 

1. run_classifier.pyの実装概要(main関数まで)
#6ではrun_classifier.pyの実行まで行いました。

bert/run_classifier.py at master · google-research/bert · GitHub
まず、実行ファイルにメイン関数があるのでそちらを確認します。(上下関係がわかるように行数も載せます。写真は投稿時のリポジトリの写真のため詳細の行数の変更の可能性はありますが、大まかな設計はそこまで急には変わらないと思うのでしばらくは参考になるのではと思います)

f:id:lib-arts:20190509130705p:plain
tf.app.run()は基本的に同一ファイル内のmain関数を実行すると考えておくと良いです。遡ると下記のようにmain関数を見つけることができます。

f:id:lib-arts:20190509130849p:plain
以下上記のmain関数を読み解いていきますが、TensorFlow実装は長くなりがちでこちらの実装もそれなりに長いので、読み解く目的を明確にします。

====
1. Bertのモデル構造に関しての実装の把握を行う
2. Bertにデータを与える前の前処理関連の実装の把握を行う

今回は上記の二点に対し、どういう構成で実装されているのかについての概要まで把握できたらと思います。最初から詳しく見過ぎると大変なので、一旦ここまでの把握を通して実装の全体像のイメージを掴めればと思います。以下2節でBertのモデル構造に関して、3節では前処理部分について確認を行なっていきます。


2. BERTのモデル構造の実装概要(create_modelとmodelling.BertModelなどについて)
TensorFlowのhigh-level APIではモデルの実装はEstimatorの引数としてmodel functionに実装されます。したがって、その辺にあたりをつけて探すと良さそうです。

f:id:lib-arts:20190509154357p:plain
探すと上記でmodel_fnのインスタンス生成からEstimatorの引数に渡すところまでが実装されていることが確認できます。したがってモデルの実装に関してはmodel_fn_builderを確認していけば良いということがわかります。

f:id:lib-arts:20190509154455p:plain

(中略)

f:id:lib-arts:20190509154517p:plain
上記よりわかるようにmodel_fn_builderではmodel_fnという関数を実装し、それを引数として返します。

f:id:lib-arts:20190509155655p:plain
model_fnの中身をみると、上記のようにcreate_model関数があり、引数にbert_configなどを与えることによってアウトプットとして
(total_loss, per_example_loss, logits, probabilities)
が計算されているので、このcreate_modelでBERTのモデルが組まれているであろうことが推測できます(思考プロセスを再現するために推測と書きましたが、実際にこのcreate_modelでモデルが実装されています)。ちなみにTensorFlowのコードではモデルの構造を計算グラフで記述するのですが、このモデルの構築自体は別のモジュールに投げることが多く、create_modelやbuild_modelなど名称が関数に名付けられることが多い印象です。

f:id:lib-arts:20190509155713p:plain
これまでの話を受けてcreate_modelを確認すると、上記のようにmodeling.BertModelでmodelを構築できることが確認できるので、モデルの実装自体は同じディレクトリ内のmodeling.pyにあるのではないかというのがわかります(import文と同じディレクトリ内を確認すると実際にmodeling.pyがあり推測が正しいことが確認できます)。
次にmodeling.pyの中に実装されているBertModelの__init__関数を確認していきます。

f:id:lib-arts:20190509160653p:plain
上記のように__init__関数があるのですが、関数の中に下記のようなtransformer_modelという関数を見つけることができます。

f:id:lib-arts:20190509160712p:plain
次にtransformer_modelの中身を確認していくと、下記のようなnum_hidden_layerの数に対しての繰り返し文の中にattention_layer関数が実装されていることがわかります。

f:id:lib-arts:20190509160738p:plain
ここまでの話で『create_model->BertModel->transformer_model->attention_layer』という実装の流れがわかり、BERTのモデル構造の大体の実装の概要を掴むことができました。最初から踏み込み過ぎると大変なので一旦ここまでとし、3節では前処理について追っていければと思います。


3. 前処理(file_based_input_fn_builderなど)の実装概要
2節ではモデル構造に関する実装の概要について抑えたので、3節では前処理の実装概要について抑えて行きます。

f:id:lib-arts:20190509163105p:plain
前処理に関しては上記のような実行した際に出力されるログから読み解いていけそうです。上記の"***** Running training *****"部分ですが実装においては下記のtf.logging.info("***** Running training *****")部分にあたります。(学習が終わったのちの実行のため、学習の処理自体は回っていません)

f:id:lib-arts:20190509163746p:plain
前後にprint文を入れて解析すると、file_based_convert_examples_to_features関数で"INFO:tensorflow:guid: train-5"部分の実行を行なっていたことが確認できました。出力されているログを見ると単語にIDを割り振る前処理を行なっていることが明らかなので、file_based_convert_examples_to_featuresの中身でログの内容が出力される前後を見ることで前処理について把握できることがわかります。

f:id:lib-arts:20190509163912p:plain
file_based_convert_examples_to_featuresの実装は上記です。featureの中に様々な情報を保持していそうな雰囲気のため、それを生成した関数であるconvert_single_exampleを着目すれば良さそうなことがわかります。

f:id:lib-arts:20190509164650p:plain
(中略)

f:id:lib-arts:20190509164707p:plain
convert_single_exampleを確認すると上記のようなtf.logging.info("guid: %s" % (example.guid))などが見つかるので、ここでログのサンプルが出力されていることがわかります。従って、前処理について把握したい際はこの関数の中身を確認していくと良さそうです。
ここまでの話で『file_based_convert_examples_to_features->convert_single_example』という実装の流れがわかり、前処理の大体の実装の概要を掴むことができました。2節同様に最初から踏み込み過ぎると大変なので一旦ここまでとします。

 

4. まとめ
#7ではBERTの実装に関してモデルの実装概要と前処理の実装概要について簡単に掴みました。
大枠については把握できたので、#8以降では論文を読んでわかりにくかったところと実装を対応づけながら理解していければと思います。