Laboro.AI

エンジニアコラム

広い技術領域をカバーする当社の機械学習エンジニアが、アカデミア発のAI&機械学習技術を紹介&解説いたします。

To Get the Best Out of a BERT Model

− BERTモデルを最大限に活用する −

2021.9.27
Machine Learning Engineer Zhao Xinyi


(※このコラムでは、当社が開発した機械翻訳モデルによる日本語訳を各セクションに掲載しています。翻訳文は、その性能を実感いただくことを目的に、いくつかの用語を置き換える以外は人手による修正は行なっておりません。そのため、一部文章に不自然な箇所も含みますことをご了承ください。)

INTRODUCTION

The pre-training of a BERT model was finally done, having taken quite a long time and plenty of computing resources. You can finally take a break and relax. But wait, can you? To adapt a BERT model to a down-stream NLP task, i.e. to put it into practical use, you definitely want to take full advantage of the BERT model and optimize its performance. Therefore, there are still several things to consider during fine-tuning. But the good thing is that you’ve found this article that can save you time fine-tuning the BERT models. For the scripts and detailed implementation instruction, please refer to our GitHub document.

This article starts with comparing 10 Japanese BERT models. These models are pre-trained using various pre-training hyper-parameters, implementations, and even different training corpora. We evaluate all of them on three tasks and compare the best performance we obtained. Then we will introduce the fine-tuning strategy we applied when dealing with Japanese BERT models. The strategy, although includes miscellaneous tips on implementation, mainly focuses on how to correctly function tokenizers and how to efficiently tune hyper-parameters. To demonstrate how our strategy influences the fine-tuning results, more comparisons will be made with only one factor being changed.

BERTモデルの事前学習は、かなりの時間と多くのコンピューティングリソースを費やして、最終的に完了しました。やっと休憩してリラックスできるわよ。しかし、待って、できるか。BERTモデルを下流のNLPタスクに適応させるには、すなわち、それを実用化するために、BERTモデルを最大限に活用し、その性能を最適化したい。したがって、ファインチューニング中に考慮すべき点がまだいくつかあります。しかしよい事はBERTモデルをファインチューニングする時間を節約できるこの記事を見つけたことである。スクリプトと詳細な実装手順については、GitHubのドキュメントを参照してください。

この記事では日本のBERTモデルを10種類比較から始めます。これらのモデルは、さまざまな事前学習ハイパーパラメータ、実装、さらにはさまざまなトレーニングコーパスを使用して事前学習されています。これらすべてを3つのタスクで評価し、得られた最適なパフォーマンスを比較します。次に、日本のBERTモデルを扱う際に適用したファインチューニング戦略について紹介します。この戦略には、実装に関するさまざまなヒントが含まれていますが、主にトークナイザーを正しく機能させる方法とハイパーパラメータを効率的にチューニングする方法に焦点を当てます。当社の戦略がファインチューニング結果にどのように影響するかを示すために、1つの要因のみを変更してより多くの比較が行われます。

Contents

Our Experiment Setup
Performance
Tokenizer
 ・Segmentation Algorithms
 ・Correctly Using the Tokenizer
Fine-Tuning Hyper-parameters
 ・Test Every Possibility
 ・Fit Your Model
 ・Unanswerable Questions
Final Thoughts

Our Experiment Setup

Japanese BERT models involved in this article are listed as follows.

NAMESIZETOKENIZERTRAINED BYLINK
Laboro-large-unigramlargeunigramLaboro.AI Inc.link
Laboro-large-BPElargeMecab(Jumandict) + BPELaboro.AI Inc.link
Laboro-base-unigrambaseunigramLaboro.AI Inc.link
Laboro-base-BPEbaseMecab(Jumandict) + BPELaboro.AI Inc.link
UKyoto-largelargeMecab(Jumanpp) + BPEKurohashi-Chu-Murawaki Lablink
UKyoto-basebaseMecab(Jumanpp) + BPEKurohashi-Chu-Murawaki Lablink
NICT-BPEMecab(Jumandict) + BPENICTlink
NICT-noBPEbaseMecab(Jumandict)NICTlink
UTohoku-32k-WWMbaseMecab(Neologd) + BPEInui Lablink
bert-wiki-jabaseunigramYohei Kikutalink

As for the implementation, two main-stream ways include

● using the original code provided by Google based on TensorFlow
● using Transformers developed by Hugging Face based on PyTorch

Besides the fact that the deep learning frameworks are different, another main difference is that the original Google code has better support for pre-training using TPU, which can vastly speedup the pre-training compared to GPU. On the other hand, with all the built-in modules, Transformers comes in very handy with fine-tuning and evaluation. Fortunately, you can easily convert a Tensorflow checkpoint to a PyTorch checkpoint, so it’s possible to take advantage of both. In our case, all the models were pre-trained with TPU based on Google’s source code, and then they were converted into PyTorch checkpoints and evaluated using Transformers.

All models are tested on 3 tasks, two of which were already introduced in our previous articles in Japanese and in English. You should take a look if you want more details, since we’ll only briefly introduce them here. LDCC is a news classification task. DDQA is a QA task in SQuAD v1.1 format, which means all the questions are answerable. In contrast, RCQA is a QA task in SQuAD v2.0 format. Comparing to SQuAD v1.1, in order to better simulate the actual situation that the articles might not include the what you’re looking for, SQuAD v2.0 added questions that cannot be answered.

私たちの実験セットアップ

この記事に登場する日本のBERTモデルは、以下の通りです。

実装に関しては、主に2つの方法があります。

●TensorFlowをベースにGoogleが提供しているオリジナルコードを使用
●PyTorchをベースにHugging Faceで開発したTransformersを使用

ディープラーニングフレームワークが異なるという事実に加えて、もう一つの大きな違いは、オリジナルのGoogleコードはTPUを使用した事前学習のサポートが優れていることです。GPUと比較して、事前学習を大幅に高速化できます。一方、すべてのビルトインモジュールを使用すると、Transformersはファインチューニングと評価に非常に便利です。幸いなことに、TensorflowチェックポイントをPyTorchチェックポイントに簡単に変換できるので、両方を利用することができます。今回のケースでは、Googleのソースコードに基づいてすべてのモデルをTPUで事前学習し、PyTorchチェックポイントに変換してTransformersで評価しました。

すべてのモデルは3つのタスクでテストされ、そのうちの2つは以前の記事で日本語英語で紹介されています。ここでは簡単に紹介しますので、詳しく知りたい方はそちらをご覧ください。LDCCはニュース分類タスクです。DDQAはSQuAD v1.1形式のQAタスクです。一方、RCQAはSQuAD v2.0形式のQAタスクです。SQuAD v1.1と比較して、記事に探しているものが含まれていない実際の状況をよりよくシミュレートするため、SQuAD v2.0では回答できない質問が追加されました。

Performance

Before talking about the fine-tuning strategy, let me list the best performance we obtained for each model. We tuned the hyper-parameters and then selected the best result for each model on each task. Take note that the two models pre-trained by Kurohashi-Chu-Murawaki Lab of Kyoto University have a max sequence length of 128, while all the other models use 512. Accordingly, the max_seq_len in fine-tuning is set the same as the length used in pre-training.

graph2

● For LDCC task, LaboroBERT-base-BPE model gives the best performance, followed by the other three LaboroBERT models, and NICT-BPE model is barely as good as LaboroBERT-base-unigram model.
● While for the other two QA tasks, NICT-BPE model has the highest score, followed by the NICT-noBPE and UKyoto-large model. Laboro-large-BPE model also performs well on DDQA task.

パフォーマンス

ファインチューニング戦略について話す前に、各モデルで得られた最高のパフォーマンスをリストアップします。ハイパーパラメータを調整し、各タスクで各モデルに最適な結果を選択します。なお、京大黒橋・褚・村脇研究室で事前学習を受けた2つのモデルは最大配列長128本、その他のモデルは最大配列長512本です。したがって、ファインチューニングにおけるmax_seq_lenは、事前学習で使用する長さと同じに設定されます。

●LDCC タスクでは、LaboroBERT-base-BPE モデルが最高のパフォーマンスを発揮し、その後に他の3つのLaboroBERTモデルが続きます。NICT-BPEモデルはLaboroBERT-base-unigramモデルとほとんど変わらない。
●その他2つのQAについてはNICT-BPEモデルが最高得点、NICT-noBPEモデル、UKyoto-largeモデルが続きます。 DDQAタスクでも、Laboro-large-BPEモデルは優れたパフォーマンスを発揮します。

Tokenizer

Segmentation Algorithms

While most English BERT models adopt WordPiece, i.e. BPE tokenizer, Japanese models require more efforts on tokenization. It doesn’t have any whitespace to imply the word boundary due to the nature of the language. There are mainly two segmentation algorithms used in Japanese BERT models,

● unigram
● MeCab + BPE

In the second situation, MeCab serves as the pre-tokenizer, and BPE serves as the subword tokenizer.

We pre-trained two BERT models using the same corpus and hyper-parameters but different tokenizers. As for the implementation, sentencepiece is applied on both of our models, because it supports unigram and BPE at the same time.

graph4

We are curious to see which model is better on which task, so we put their performance together in the graph above and made a comparison.

● The model using unigram tokenizer performs better on sentence classification (LDCC) task
● The model using BPE tokenizer performs better on DDQA task. This result makes sense. With both word and subword tokenization, BPE tokenizer tends to split a sentence into finer pieces. By doing this, the searching for starting and ending indices for the answer can be more accurate.
● For QA task with unanswerable questions as in SQuAD v2.0, unigram and BPE give very similar performance. This is probably because SQuAD v2.0 is a combination of classification and QA tasks instead of a simple QA task.
● For other models based on Japanese Wikipedia data, although they are not pre-trained with the exact same hyper-parameters, the same conclusions can be made when comparing bert-wiki-ja model using unigram tokenizer to other models using BPE tokenizers.

トークナイザー

セグメンテーションアルゴリズム


ほとんどの英語のBERTモデルはWordPiece、すなわちBPEトークナイザーを採用していますが、日本語のモデルはトークン化により多くの努力が必要です。言語の性質上、単語境界を暗示する空白はありません。日本語のBERTモデルには主に2つのセグメンテーションアルゴリズムがあり、

● unigram
● MeCab + BPE

2番目の状況では、MeCabがプリトークナイザー(pre-tokenizer)として、BPEがサブワードトークナイザー(subword tokenizer)として機能します。

同じコーパスとハイパーパラメータを使って2つのBERTモデルを事前学習しましたが、トークナイザーは異なります。実装に関しては、unigramとBPEを同時にサポートしているため、どちらのモデルにもsentencepieceが適用される。

どのモデルがどのタスクに優れているのか知りたいので、それらを上記のグラフにまとめて比較しました。

● unigramトークナイザーを使ったモデルは、文分類(LDCC)タスクよりもパフォーマンスが優れています。
● BPEトークナイザーを使用したモデルは、DDQAタスクでのパフォーマンスが向上します。この結果は理にかなっています。単語とサブワードの両方のトークナイザーで、BPEトークナイザーは文章をより細かく分割する傾向があります。これを行うことで、答えの開始インデックスと終了インデックスの検索がより正確になります。
● SQuAD v2.0のような答えの出ない質問をするQAタスクでは、unigramとBPEは非常によく似たパフォーマンスを発揮します。 SQuAD v2.0は単純なQAタスクではなく、分類タスクとQAタスクの組み合わせであるからです。
● 日本語Wikipediaのデータに基づく他のモデルでは、全く同じハイパーパラメータで事前学習はされていませんが、ユニグラムトークナイザーを使ったbert-wiki-jaモデルとBPEトークナイザーを使った他のモデルを比較すると、同じ結論を出すことができます。

Correctly Using the Tokenizer

To get the best out of a BERT model, you’ve got to use the tokenizer correctly. This seems needless to emphasize, but in fact, there are several points that are easily overlooked.The evaluation scripts are originally designed for English models, so be careful when adapting them for Japanese.

In the tokenizer configuration, use_lower_case should always be set as False. If not, the Dakuten and Handakuten will disappear. Also, tokenize_chinese_chars should be set as False. The tokenize_chinese_chars function purposely adds whitespace around any CJK character, which might be convenient for other languages but not for Japanese. The CJK Unicode block only includes Chinese characters, so turning on the function would mess up the Japanese text.

In order to make sure all the parameters are well set, I would suggest dumping everything in a config_tokenizer.json file. And don’t forget to doublecheck if the config_tokenizer.json is included when you download a model from online sources.

Another thing that can be confusing is which tokenizer to use in Transformers, BertTokenizer or BertJapaneseTokenizer. The BertTokenizer consists of two steps,

● basic tokenizer, in which the default is tokenize_chinese_chars=true
● wordpiece tokenizer

while the BertJapaneseTokenizer consists of two steps different from BertTokenizer,

● word tokenizer with 2 options
  ○ basic tokenizer, in which the default is tokenize_chinese_chars=false
  ○ mecab tokenizer
● subword tokenizer with 2 options
  ○ wordpiece tokenizer same as the one in BertTokenizer
  ○ character tokenizer

When tokenize_chinese_chars=false, using the combination of basic tokenizer and wordpiece tokenizer from either BertTokenizer or BertJapaneseTokenizer stays exactly the same.

Let’s see how much harm it can do to mistakenly configure the tokenizer. The graph below demonstrates the drop of the performance when setting the tokenize_chinese_chars wrongly as True (TCC=True), comparing to correctly setting it as False (TCC=False). In all of our experiments, the drop always happens no matter which model is evaluated on which task. Correctly configuring the tokenizer is the very crucial first step in fine-tuning. Any tiny mistakes can cause the performance to be worse than it’s supposed to be. So make sure you’re not missing anything in this step!

graph5

トークナイザーを正しく使う

BERTモデルを最大限に活用するには、トークナイザーを正しく使用する必要があります。これは強調する必要はないように思えますが、実際は見落とされやすい点がいくつかあります。評価スクリプトはもともと英語のモデル用に作られていますので、日本語に適応する際には注意が必要です。

トークナイザーの設定では、use_lower_caseは常にFalse に設定する必要があります。そうでない場合は、濁点と半濁点は消えます。また、tokenize_chinese_charsはFalseに設定する必要があります。関数tokenize_chinese_charsはCJK文字の周囲に意図的に空白を追加します。CJK Unicodeブロックには漢字しか含まれていないため、この機能をオンにすると、日本語テキストが散らかってしまいます。

すべてのパラメータが適切に設定されていることを確認するために、私はconfig_tokenizer.jsonファイルにすべてをダンプすることを推奨します。また、オンラインソースからモデルをダウンロードする際には、必ずconfig_tokenizer.jsonが含まれているかどうかを再度確認してください。

Transformersで使うトークナイザー、BertTokenizerBertJapaneseTokenizerもわかりづらいです。BertTokenizerは2つのステップで構成され、

● デフォルトはtokenize_chinese_chars=trueである基本的なトークナイザー(basic tokenizer)
● 単語分割トークナイザー(wordpiece tokenizer)

BertJapaneseTokenizerBertTokenizerと異なる2つのステップで構成されており、

● 2つのオプションを持つ単語トークナイザー
  ○ デフォルトはtokenize_chinese_chars=falseである基本的なトークナイザー(basic tokenizer)
  ○ mecab トークナイザー(mecab tokenizer)
● 2つのオプションを持つサブワードトークナイザー
  ○ BertTokenizerと同じ単語分割トークナイザー(wordpiece tokenizer)
  ○ 文字トークナイザー(character tokenizer)

tokenize_chinese_chars=falseの場合、basic tokenizerとwordpiece tokenizerの組み合わせは BertTokenizerまたはBertJapaneseTokenizerのいずれも全く同じです。

トークナイザーを誤って設定してしまうと、どれだけ害が及ぶか見てみましょう。以下のグラフは、tokenize_chinese_charsがFalse(TCC=False)として正しく設定した場合に比べ、True(TCC=True)に設定した場合のパフォーマンス低下を示しています。すべての実験で、どのモデルがどのタスクで評価されても、落下は必ず起こります。トークナイザーを正しく設定することはファインチューニングの最初のステップです。小さなミスを犯すと、想定以上にパフォーマンスが悪くなる可能性があります。そうこのステップに何も欠けていないことを確かめなさい!

Fine-Tuning Hyper-parameters

In general, hyper-parameters for fine-tuning should stay the same as those in pre-training, for example, the max sequence length. However, there are several exceptions that should be adjusted to fit the fine-tuning dataset:

● learning rate
● batch size
● number of training epoch

In this section, we’ll discuss how to optimize the above hyper-parameters, as well as showing how max sequence length can influence the performance.

In addition, in QA tasks with unanswerable questions, one parameter called null_score_diff_threshold should also be adjusted to fit the model. It’s technically not a hyper-parameter because it controls the evaluation process instead of the learning process. We would still like to include it here since it is an argument when you run the command.

ファインチューニングのハイパーパラメータ

一般的に、ファインチューニング用のハイパーパラメータは、例えば最大配列長など、事前学習用のパラメータと同じに保つ必要があります。ただし、ファインチューニングデータセットに合わせて調整する必要がある例外はいくつかあります。

● 学習率
● バッチサイズ
● 学習エポック数

このセクションでは、上述のハイパーパラメータを最適化する方法と、最大配列長がパフォーマンスにどのように影響するかについて説明します。

さらに、答えの出ないQAタスクでは、null_score_diff_thresholdという1つのパラメータもモデルに合わせて調整する必要があります。これは技術的にはハイパーパラメータではありません。なぜなら、学習過程ではなく評価プロセスを制御しているからです。コマンド実行時の引数なので、ここでは含めたいところです。

Test Every Possibility

As mentioned in Appendix A.3, Devlin et al., 2019, for the 3 hyper-parameters we do want to tune, their optimized values fall in these ranges for all tasks most of the time:

● batch size: 16, 32
● learning rate: 5e-5, 3e-5, 2e-5
● number of training epochs: 2, 3, 4

The fine-tuning usually doesn’t take too much time, so it wouldn’t hurt to simply try every possibility for your model. However, in our experiment, there are 10 models to evaluate. To get the best results, we exhaustively tested every learning rate while constantly set batch size as 16 and epochs as 3. You may be wondering if there is any pattern for the performance to change along with the hyper-parameters, so that we don’t really have to try every potential value, but let’s take a look at the graph below.

graph6

The graph shows the accuracy change for LDCC task when using different learning rates, namely at 2e-5, 3e-5, and 5e-5. Unfortunately, no obvious pattern that suits every model can be found. Although it seems to go against your instinct, exhaustively trying every possibility is indeed the shortcut for tuning the hyper-parameters.

あらゆる可能性をテストする

付録A.3, Devlin et al., 2019で述べたように、調整したい3つのハイパーパラメータについて、それらの最適化値はほとんどの場合、これらの範囲に収まります。

● バッチサイズ: 16、32
● 学習率: 5e-5、3e-5、2e-5
● 学習エポック数: 2, 3, 4

ファインチューニングは通常あまり時間をかけない、従ってあなたのモデルのためのあらゆる可能性を単に試みるために傷つかない。しかし、我々の実験では、評価すべきモデルが10種類あります。最良の結果を得るために、我々は常にバッチサイズを16、エポックを3に設定しながら、すべての学習率を徹底的にテストしました。パフォーマンスがハイパーパラメータとともに変化するパターンがあるかどうか疑問に思うかもしれません。しかし、下のグラフを見てみましょう。

このグラフは、異なる学習率(2e-5、3e-5、5e-5)を使用した場合のLDCCタスクの精度変化を示しています。残念ながら、どのモデルにも合う明白なパターンは見つかりません。それはあなたの本能に逆らっているようであるが、徹底的にあらゆる可能性を試みることは実際にハイパーパラメータを調節する近道である。

Fit Your Model

While it is suggested using the same max sequence length in fine-tuning as in pre-training, we tried shorter length for some models on the 3 tasks to see how much worse the performance will become. We used 128 as the shorter length for LDCC task, and 384 for the other two QA tasks.

There’s a big drop for the LDCC accuracy when the max sequence length is shorter, however, the performance for QA tasks is barely influenced by it. It does take more time and computing resources to fine-tune and evaluate when max_seq_length is longer. Therefore, it might not be a bad idea to shorten the max_seq_length for QA tasks when there’s no sufficient resources.

graph7

モデルに適合する

事前学習と同じ最大配列長でファインチューニングすることを推奨していますが、3つのタスクでより短い配列長を試して、パフォーマンスがどれほど悪くなるかを調べました。LDCCタスクの短縮長は128、QAタスクの短縮長は384です。

最大配列長が短い場合、LDCC精度には大きな低下がありますが、 しかし、QAタスクのパフォーマンスは、ほとんど影響を受けません。max_seq_lengthがより長い場合、ファインチューニングに多くの時間と計算資源がかかります。したがって、十分なリソースがない場合、QAタスクのmax_seq_lengthを短くすることは悪くないかもしれません。

Unanswerable Questions

For QA tasks with unanswerable questions, adjusting null_score_diff_threshold can further improve the performance. Judging if the answer exists is actually a classification task. It is done by comparing the score of the most possible non-null answer to the score of not having an answer. In mathematical language, when snon-null > snull + τ, it predicts a non-null answer. The τ serves as a threshold, and according to Devlin et al., 2019, the threshold that maximizes the F1 for the dev set should be selected.

When evaluating on the dev set using Transformers, it automatically calculates the best_f1_thresh. So don’t panic even if the performance is not ideal. Apply this threshold to the test set and usually, although it’s not always the case, the performance gets better.

As shown in the graph below, for all the models we tested, applying null_score_diff_threshold improves the EM and F1. Classifying if a question is answerable is crucial for tasks like SQuAD v2.0 or RCQA, however, in the actual evaluation, most efforts are put in finding the start and end position of the answer. While there’s not much information for the classification, null_score_diff_threshold is one of the few parameters we can seize and utilize.

graph7

答えの出ない質問

質問の答えの出ないQAタスクでは、null_score_diff_thresholdを調整するとパフォーマンスがさらに向上します。答えが存在するかどうかを判断することは、実際には分類タスクです。それは答えを持っていないことのスコアと最も可能なnullでない答えのスコアを比較することによってされる。数学言語では、snon-null > snull + τ の場合、nullでない回答を予測する。τは閾値であり、2019年にDevlinらにより、dev setのF1を最大化する閾値が選択されるべきである。

Transformersでdev setを評価した場合、自動的にbest_f1_threshを計算します。だから、パフォーマンスが理想的ではなくてもパニックになりません。テストセットにこのしきい値を適用し、通常、常にそうではありませんが、パフォーマンスが向上します。

以下のグラフのように、テストしたすべてのモデルで、null_score_diff_thresholdを適用することで、EMとF1が向上します。SQuAD v2.0 や RCQA などのタスクでは、質問に回答できるかどうかの分類が重要です。しかし、実際の評価では、ほとんどの努力は答えの開始位置と終了位置を見つけることです。分類のための情報はあまりありませんが、null_score_diff_thresholdは取得して利用できる数少ない引数の1つです。

Final Thoughts

From the strategy for fine-tuning, we can also summarize some valuable advice for pre-training. For example, the max sequence length usually follows the rule the longer the better, and the type of tokenizer tends to influence the performance on certain tasks.

Fine-tuning a BERT model is tricky and needs a lot of patience. There is always a reason when the results are lower than expected, and the reason is probably already mentioned in this article. Find the reason and fix it, and you are back on track.

最終的な思考

ファインチューニングのための戦略から、事前学習のためのいくつかの貴重なアドバイスを要約することもできます。たとえば、最大配列長は通常、ルールが長ければ長いほど、トークナイザーの種類は特定のタスクのパフォーマンスに影響する傾向があります。

BERTモデルのファインチューニングは難しく、忍耐が必要です。結果が予想より低い理由が常にあり、理由はおそらくこの記事で既に述べられている。理由を見つけて修正すれば、軌道に戻ってきます。