最新のニューラル会話モデルでおしゃべりしよう! - GPT-2でチャットボット作成
概要
- GPTChat という、日本語向けの GPT-2 言語モデル、およびそれをベースとした会話モデルの学習・生成 CLI を作成しました。
- GPTChat を使って日本語 Wikipedia で GPT-2 を学習し、ファインチューニングして日本語の会話モデルを作成しました。
あらすじ
2018-2019 年は GPT とその後継である GPT-2 が話題になりました。 GPT および GPT-2は、大規模なウェブテキストから事前学習した言語モデルで、特定のタスクにファインチューニングすることで多くのベンチマークタスクでSOTAを達成しました。
では、GPT や GPT-2 をチャットボット向けにファインチューニングすることは可能でしょうか? チャットボットへの適用手法は [1] で提案されており、2018年に開催されたConvAI2という対話コンペティションの自動評価部門にて一位に輝いています。 この手法に興味がある方は、 [1] の他に著者本人が解説した [2] があるので合わせて参照してください。
- [1] TransferTransfo: A Transfer Learning Approach for Neural Network Based Conversational Agents by Thomas Wolf et al. (https://arxiv.org/abs/1901.08149)
- [2] How to build a State-of-the-Art Conversational AI with Transfer Learning by Thomas Wolf. (https://medium.com/huggingface/how-to-build-a-state-of-the-art-conversational-ai-with-transfer-learning-2d818ac26313)
こうなってくると、日本語のチャットボットを作成できないかと期待しますね! しかし、GPT-2を使って日本語でチャットボットを作ろうとした時、立ちはだかる壁があります。
一つ目は、GPT-2の公開されている事前学習モデルは英語を中心に学習されていることです。 GPT-2は事前学習モデルをファインチューニングしてタスクを解きます。 例えばチャットボットを作ろうとすると、GPT-2の事前学習モデルを「入力発話」から「応答発話」を生成するようにファインチューニングするわけです。 そのファインチューニングに必要な GPT-2 の事前学習モデルは公開されているのですが、英語を中心に学習されているため日本語には適していません。 そのため、日本語用の GPT-2 の事前学習モデルを作成する必要があります。
二つ目は、トークナイザの問題です。 GPT-2 のトークナイザは、入力文をスペース単位で単語に分割し、単語にバイト単位で多く共通する部分をまとめてトークンとして表現する Byte Pair Encoding という手法を用います。 この手法で学習したトークナイザの事前学習モデルは提供されいるのですが、こちらも英語を主に学習されているため日本語には不向きです。 後ほど改めて紹介する 🤗 Transformers が提供している学習済みトークナイザで日本語をトークナイズすると次のようになります。
1 2 3 4 |
|
日本語はスペースで単語が分けられていないため、トークナイザは文「お腹が空いた」を一単語として認識し、その後あらかじめ学習しておいたトークン単位に分割しています。 バイト単位での分割のため、入力した文字数よりも多くのトークンが出現していることもわかります。
このような観点から、GPT-2 を日本語で使おうとしたとき、
- 日本語用のトークナイザに変更する
- その上で、GPT-2 を事前学習する
必要があります。
以上を踏まえて、 GPTChat という GPT-2 の学習・生成 CLI を作成しました。 GPTChatではトークナイザを日本語向けに変更をした上で、GPT-2 の事前学習モデルの学習スクリプトと、[1] [2] を元にした手法で GPT-2 の事前学習モデルをチャットボット向けにファインチューニングする学習スクリプトを提供しています。
まず、GPTChat の GPT-2 モデルは、先ほどトークナイザの実例で使った HuggingFaceの 🤗 Transformers を使っています。
次にトークナイザは、GPT-2 のデフォルトのトークナイザ transformers.GPT2Tokenizer
ではなく、BERT 用の日本語向けのトークナイザ transformers.BertJapaneseTokenizer
とその学習済みモデルを用いています。
transformers.BertJapaneseTokenizer
は transformers に v2.3.0 より導入されたトークナイザで、東北大学により公開されていたものがマージされました。
詳しくは次を参照ください。
おはようござえます、日本の友達
— Hugging Face (@huggingface) December 13, 2019
Hello, Friends from Japan 🇯🇵!
Thanks to @NlpTohoku, we now have a state-of-the-art Japanese language model in Transformers, `bert-base-japanese`.
Can you guess what the model outputs in the masked LM task below? pic.twitter.com/XIBUu7wrex
transformers.BertJapaneseTokenizer
は、GitHubのリポジトリによると、文を MeCab で分かち書きしたのち、SentencePieceの Byte Pair Encoding によってトークナイズを行います。
実際に使って挙動を確かめてみましょう。
1 2 3 4 |
|
結果をみると、まず MeCab で「お腹」「が」「空い」「た」と分割され、その後 Byte Pari Encoding で「お」「##腹」「が」「空い」「た」と分割されたことが見えますね。
最後に、GPTChat の会話モデルは [1] [2] を参考に、一問一答型の会話を行うモデルとして実装しました。 [1] [2] では、対話に個性や履歴を考慮していますが、GPTChatでは個性や履歴は考慮せずシンプルに一問一答を行う会話を対象とします。
以上で、GPTChat を作成したあらすじと概略は終わりです! この後は、 GPTChat の使い方を説明します。 今回は日本語 Wikipedia で GPT-2 を学習して事前学習モデルを作成したのち、そのモデルをチャットボット向けにファインチューニングして実際に会話するところまで行ってみたいと思います。
GPTChatのインストール
Docker イメージをビルドするのが簡単です。
1 2 3 |
|
ソースからインストールすることも可能です。 この場合は、必要な環境設定を Dockerfile で確認してください。
1 |
|
GPT-2 モデル
学習データの作成
はじめに、日本語 Wikipedia のダンプデータをダウンロードして学習データとして使えるように整形します。
1 2 3 4 5 |
|
GPT-2 モデルの学習
それでは gptchat.gpt.train
を使って BaseModel の GPT-2 を学習してみましょう。
1 |
|
--tokenizer_model=bert-base-japanese
というパラメータを指定していることに注意してください。
bert-base-japanese
を指定することで transformers.BertJapaneseTokenizer
を利用します。
日本語モデルを学習する場合は指定するようにしてください。
--data
で先ほど作成した学習データを指定しましょう。
--gpu
オプションで学習に GPU を利用するように設定しています。
今回は学習には RTX 2080 Ti を用いました。
学習中はだいたい 10GB 程度 GPU メモリを使用していることがわかります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
--checkpoint_steps
ごとに、 --output_dir
以下にモデルが保存されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
モデルのパラメータを確認したい場合は、各モデルのディレクトリ以下の config.json
を確認してください。
モデルの評価
学習したモデルに対して gptchat.gpt.evaluate
を使ってテストセットに対してパープレキシティを計算できます。
70万ステップ後の学習モデルをテストセット corpus/test.txt
で評価してみましょう。
1 2 |
|
文生成
文を生成するには gpchat.gpt.generate
を用います。
1 2 3 4 5 |
|
Wikipediaにのっているような文章が生成されていますね!
会話モデル
GPT-2 の事前学習モデルができたので、次はそれをファインチューニングして会話モデルを学習してみましょう。
GPT-2 を対話モデルとして使うためには、「入力発話」と「応答発話」の間にセパレータを入れてモデルに入力します。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | |
---|---|---|---|---|---|---|---|---|---|
単語 | \<bos> | 餃子 | が | 食べ | たい | \<sep> | 美味し | そう | \<eos> |
そして学習時には「応答発話」の言語モデルを学習するようにします。
それに加えて、 [1] [2] での手法にならい、生成した応答発話が応答として適切かどうか分類するタスクも同時に学習します。 一単語ごとに生成してできた文が全体として応答発話に適切かどうかを判定しようというわけです。 この学習を行うために、学習データには事前に distractor と呼ばれる入力発話に対して適切でない応答発話を複数個付与しておき、学習時に分類します。
学習データの準備
用意する学習データセットは入力発話と応答発話をタブで区切ったデータです。 このデータは各自準備してください。 今回は、70万ペア程度の会話データを学習に用いました。
このデータを chat/train.txt
として用意します。
1 2 |
|
このデータに対して、 gptchat.chat.add_distructors
で distractor を付与します。
1 |
|
すると、応答発話の後にタブ区切りで --num_distractors
で指定した数の distractor が付与されます。
distractor は応答発話中からランダムで --num_distractors
個選ばれます。
今回は --num_distractors=2
と指定しているので、二つの発話が新たに付与されているのがわかります。
1 2 |
|
会話モデルの学習
データの準備ができたら、 gptchat.chat.train
で学習します。
1 |
|
先ほど学習した GPT-2 の事前学習モデルを --model
で指定してください。
BaseModel と同様に、 --checkpoint_steps
ステップごとにモデルが--output_dir
以下に保存されます。
応答生成
学習が完了したら、 gpchat.chat.generate
で応答生成してみましょう。
--model
に学習したモデルを指定します。
今回は35万ステップ後の学習モデルを利用します。
1 2 3 4 5 6 7 |
|
おおおー!いい感じですね!
APIとして利用するために、HTTP サーバとして提供するための gptchat.chat.serve
も用意しています。
1 |
|
/generate
エンドポイントに対して {"text": "こんにちは"}
のようにリクエストを送るとレスポンスとしてモデルの出力が得られます。
1 2 3 4 5 6 |
|
まとめ
- 日本語向けの GPT-2 の言語モデルおよび会話モデルの学習・生成 CLI である GPTChat を作成しました。
- GPTChat を日本語 Wikipedia で事前学習し、ファインチューニングすることで日本語の会話モデルが作成できることを確認しました。