huggingface/transformers のツールに GPT2 の fine-tuning と文章生成があったので日本語で試したら動いた

テキストを適当に学習して、自動生成っぽい文章を作りたい人向けです。[huggingface/transformers](https://github.com/huggingface/transformers) の[公式の fine-tuning 方法](https://huggingface.co/transformers/training.html)ではコードを書く説明がありますが、それっぽい文章生成だけであれば

1. データの準備
2. Example にある [run_language_modeling.py の実行](https://github.com/huggingface/transformers/tree/master/examples/language-modeling)
3. Example にある [run_generation.py の実行](https://github.com/huggingface/transformers/tree/master/examples/text-generation)

だけで、できました。[huggingface/transformers](https://github.com/huggingface/transformers) は 2020/09/22 時点の master を利用しているので、3.1.0 です。以下 Google Colab で試しています。(notebook は[こちら](https://github.com/tset-tset-tset/hoge/blob/master/huggingface_transformers_GPT2.ipynb)です)

# 1. データの準備

学習に利用するテキストを準備します。とりあえず日本語対応はあまり考えず GPT2 の Pretrained モデル同様の [GPT2Tokenizer](https://github.com/huggingface/transformers/blob/master/src/transformers/tokenization_gpt2.py) で読み込む予定なので、UTF-8 であれば何でも良いと思います。

今回は[乃木坂 46 の新内眞衣のブログ](http://blog.nogizaka46.com/mai.shinuchi/)をテキストにして利用します。

```text:original.txt
皆さんこんばんは！
新内眞衣です(=ﾟωﾟ)ﾉ

(生写真のナース！憧れだった！嬉)


前回のブログでは
たくさんの方々に
『卒業おめでとう！』って頂き、
本当に嬉しかったです。

ありがとうございます



卒業式から数日経ちましたが、
まだ学生じゃなくなったと
いう実感は湧きません
～～～
```

上記を学習用の `train.txt` と `eval.txt` に分割します。

```bash
!gdown https://drive.google.com/uc?id=1KT4P31peQWuYFvOCfU39dYzLmtBDXUu2
!head -n1000  original.txt > eval.txt
!tail -n+1001 original.txt > train.txt
!wc -l *.txt
```

# 2. Example にある [run_language_modeling.py の実行](https://github.com/huggingface/transformers/tree/master/examples/language-modeling)
(2020/11/26 追記: 最新版でスクリプトが変更になっているので、最後に存在した v3.4.0 を指定しています)

```bash
!git clone https://github.com/huggingface/transformers.git -b v3.4.0
!cd transformers/; pip install -e .
```

git 上のサンプルを落としてくるので、ついでに transformers もインストールします。

```bash
%load_ext tensorboard
%tensorboard --logdir runs
```

tensorboard をロードしておけば loss の下がり方が見やすいですが任意です。(runs はデフォルトだと 500 step 毎にログが書き込まれるので、その後リロードすれば見えます)

```bash
## 初回実行、まだチェックポイントが無い場合
!python transformers/examples/language-modeling/run_language_modeling.py \
    --output_dir=output_gpt2 \
    --model_type=gpt2 \
    --model_name_or_path=gpt2 \
    --do_train \
    --train_data_file=train.txt \
    --do_eval \
    --eval_data_file=eval.txt \
    --per_device_train_batch_size=2 \
    --per_device_eval_batch_size=2 \
    --num_train_epochs=10 \
    --save_steps=5000 \
    --save_total_limit=3

## 以前のチェックポイントを継続する場合
!python transformers/examples/language-modeling/run_language_modeling.py \
    --output_dir=output_gpt2 \
    --model_type=gpt2 \
    --model_name_or_path=output_gpt2 \
    --overwrite_output_dir \
    --do_train \
    --train_data_file=train.txt \
    --do_eval \
    --eval_data_file=eval.txt \
    --per_device_train_batch_size=2 \
    --per_device_eval_batch_size=2 \
    --num_train_epochs=10 \
    --save_steps=5000 \
    --save_total_limit=3
```

他の引数は [run_language_modeling.py のソース](https://github.com/huggingface/transformers/blob/master/examples/language-modeling/run_language_modeling.py) や、 [Trainer クラス](https://huggingface.co/transformers/main_classes/trainer.html#trainingarguments)を参考にしてください。

- 初回実行時の `--model_name_or_path=gpt2` は、gpt2 ディレクトリのことではなく、[HuggingFace の Pretrained モデルを指定しています](https://huggingface.co/transformers/pretrained_models.html)。
- `--per_device_train_batch_size` と `--per_device_eval_batch_size` のデフォルトは `8` ですが、そのままだと `RuntimeError: CUDA out of memory` が出たので `2` に絞っています。
- `--save_steps=5000` はデフォルトの `500` だと 1 GB 強のチェックポイントが頻繁にできて容量的に辛かったので変更しました。`--save_total_limit=3` で 4 つめ以降のチェックポイントは削除されますが、Google Drive 上に直接出力していると、ゴミ箱に移動されるだけで容量が減るわけではないので注意です。


# 3. Example にある [run_generation.py の実行](https://github.com/huggingface/transformers/tree/master/examples/text-generation)

```bash
!python transformers/examples/text-generation/run_generation.py \
    --model_type=gpt2 \
    --model_name_or_path=output_gpt2 \
    --prompt "こんにちは。 " \
    --seed=${RANDOM} \
    --length 1000
```

他の引数は [run_generation.py のソース](https://github.com/huggingface/transformers/blob/master/examples/text-generation/run_generation.py)も参考にしてください。上記のように学習済のモデルを指定して実行すると、以下のように出ます。
(入力に空行が多いため、出力も空行が多くなっています)


```text:出力
=== GENERATED SEQUENCE 1 ===
こんにちは。  本当にお世話になりました。



来てくれた皆さんは
絶賛お稽古中で全力で迎えることを
幸せに思います。



そして

14枚目シングル
は今回選抜メンバーとして
活動させていただきます。


今回のシングルでは
選抜に入っても
入らなくてもね

『16枚目のシングル『17枚目』で

たくさんの方々に
来ていただいたし

それが必死になって
思っていたら、

今回選抜に入ることが出来るのは
本当に嬉しいです。


それでも
私が初めてという今回選抜に
入ることが出来るのは
本当に嬉しいです。


それでも
今回選抜に
入ることが出来るのは
私だけの燃え尽き芸能界ドリームだった
と思っています。


活動してくれてるからこそ
成長出来てくれているからさ
悩んだり、

これからも頑張ります。





------------------------------





24枚目も選抜メンバーとして
活動させていただくことになりました。


やっぱり何度選ばれても嬉しいし
こうした報告も出来るのも
幸せだなと思います。


今回選抜に入ることが出来た
理由の一つに
支えられてるからこそ
この感情を持って自分を持って
私の目標だったり
していたカッコいい人と自分のことを
考えていました。


1年前じゃ考えず
からもこんな立ち位置だし
心の底から背中を押す番
なんですよね。


そんな立ち位置を考えると
背中を押す番
なんですが乃木坂として
背中を押す番組になっていました。



今回のシングルも

これからも生きているけど

どうしたらいいのか
また選抜メンバーになれること
全力で挑んできたいです。




これからも生きていけると
思うんです。

生きていけると
支えてくれることはあります。


それでもやっぱり
活動を任せることの2年。



選抜への道は辛く
```