[自然言語処理][GPT2]GPT2で日本語文章生成

# はじめに
今回はrinnaの[japanese-gpt-medium](https://huggingface.co/rinna/japanese-gpt2-medium)を用いた文章生成を行なっていきます。
また、芥川龍之介の小説のデータを用いてfine-tuningを行って見るところまでをしてみようと思います！果たして、芥川っぽい小説が完成するのか楽しみですね、、、！

## 開発環境
```
mac OS Monterey
Apple M2チップ
python version = 3.7
```
# 訓練済みモデルの実装
## ライブラリのインストール
今回は[japanese-gpt-medium](https://huggingface.co/rinna/japanese-gpt2-medium)の訓練済みモデルを利用して実装を行なっていきます！
まずは必要なライブラリのインストールを行います。今回はtransformersとsentencepieceを使用します！
```
$ conda install transformers
$ conda install sentencepiece
```
## テキスト生成
### 最もシンプルなテキスト生成
簡単にテキスト生成を行なってみます。
```python:my_text_generation.py
from transformers import T5Tokenizer, AutoModelForCausalLM

tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium")
tokenizer.do_lower_case = True

model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")


text = "こんな夢を見た。"
input = tokenizer.encode(text, return_tensors="pt", add_special_tokens=False)
output = model.generate(input, max_length=100)
print(tokenizer.batch_decode(output))
```
こちらを実行していただくと、以下のような出力が得られるかと思います。非常に不自然な文章ですが、一応テキストは生成されていることが確認できるかと思います！
```
The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.
Setting `pad_token_id` to `eos_token_id`:2 for open-end generation.
['世界は恋に落ちている, 恋は雨上がりのように, 恋は雨上がりのように, 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は雨上がりのように 恋は']
```

### 自然なテキスト生成
もう少し、自然な文章を生成したいのでコードを以下のように改良しましょう〜！解説は下にまとめて書きます。
```python:my_text_generation.py
from transformers import T5Tokenizer, AutoModelForCausalLM

tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium")
tokenizer.do_lower_case = True

model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")


text = "こんな夢を見ていた。"
input = tokenizer.encode(text, return_tensors="pt", add_special_tokens=False)
output = model.generate(
                        input,
                        max_length=100,
                        min_length=10,
                        do_sample=True,
                        top_k=40,
                        temperature=1.5,
                        pad_token_id=tokenizer.pad_token_id,
                        bos_token_id=tokenizer.bos_token_id,
                        eos_token_id=tokenizer.eos_token_id
                        )
print(tokenizer.batch_decode(output))
```
これを実行すると以下のような出力が得られるかと思います！
```
['こんな夢を見ていた。その話を聞いた瞬間に私の体内の回路も変わらなければならないとさえ感じるようになり、そしてある意味その感覚が普通に生活できなくなるような、そんな危機感を覚えるようになった。 そして私は「本当にこれで最後になるかわからない」そう思った。いやでも「そう」したかっただけだ。そしてまたも私自身の寿命を賭け、そう決断した。そのように自分を「死の世界」に押し上げてゆくようなことを。 それでも何とか私は、自分が死の世界に行くこと']
```
内容は意味が通った文章ではありませんが、比較的文章としては読める程度のものが生成されているかと思います！

ここで、model.generate()の引数について一部紹介します。詳しくは[こちら](https://huggingface.co/docs/transformers/main_classes/text_generation#transformers.GenerationMixin.generate:~:text=force_words_ids!%3DNone.-,generate,-%3C)を確認していただけるとわかるかと思います！

<table border="1">
 <tr>
  <th>パラメータ</th>
  <th>詳細</th>
 </tr>
 <tr>
  <td>max_length</td>
  <td>生成される文章の最大長</td>
 </tr>
 <tr>
  <td>min_length</td>
  <td>生成される文章の最短長</td>
 </tr>
 <tr>
  <td>do_sample</td>
  <td>確率で次のトークンを選択するかどうか。Falseなら貪欲法</td>
 </tr>
 <tr>
  <td>early_stopping</td>
  <td>最低num_beams回の同じ文章が繰り返されたら生成を終了するかどうか。<br>Bool型。</td>
 </tr>
 <tr>
  <td>num_beams</td>
  <td>early_stoppingの値</td>
 </tr>
 <tr>
  <td>temperature</td>
  <td>次の単語の確率の調整をする値。</td>
 </tr>
 <tr>
  <td>top_k</td>
  <td>top_kサンプリングの値</td>
 </tr>
 <tr>
  <td>top_p</td>
  <td>top_pサンプリングの値</td>
 </tr>
<tr>
  <td>num_return_sequences</td>
  <td>生成する文章の個数</td>
</tr>
</table>

他にもパラメータはたくさんありますが、ここでは自分が使ったものにとどめておきますっ！

# ファインチューニング
ここまでで簡単な文章生成をすることができました！ここからはファインチューニングを用いて、文のスタイルを調整していきましょう！

## データの取得
今回は青空文庫から芥川龍之介の小説データを拝借しようかと思いますっ！これで芥川っぽい小説が書けるはず、、、✨
文章データからはルビなどの特殊記号を取り除いたものを用意しておいてください。当然別のデータでやってみてもいいかと思います。

## ファインチューニング
実際にファインチューニングを行なっていきます！[こちらを参考](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.Trainer)にしました。

まずはモデルの読み込みをしましょう。
```python:my_fine_tuning.py
from transformers import T5Tokenizer, AutoModelForCausalLM
from transformers import TextDataset, DataCollatorForLanguageModeling
from transformers import Trainer, TrainingArguments,AutoModelWithLMHead

tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium")
tokenizer.do_lower_case = True

model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt2-medium")
```
次に追加訓練に必要な情報を書きます！
```python:my_fine_tuning.py
train_dataset = TextDataset(
        tokenizer=tokenizer,
        file_path="akutagawa.txt",
        block_size=64
        )

data_collator = DataCollatorForLanguageModeling(
    tokenizer=tokenizer,
    mlm=False
    )

args = TrainingArguments(
    output_dir="./",  
    overwrite_output_dir=True,
    num_train_epochs=3,
    per_device_train_batch_size=8, 
    logging_steps=50,  
    save_steps=500
    )

# トレーナーを設定
trainer = Trainer(
    model=model,
    args=args,
    data_collator=data_collator,
    train_dataset=train_dataset,
)

# トレーニング
trainer.train()
```

最後に前回までと同様にテキストを生成してみましょうっ！
```python:my_fine_tuning.py
text = "こんな夢を見ていた。"
input = tokenizer.encode(text, return_tensors="pt", add_special_tokens=False)
output = model.generate(
                        input,
                        max_length=100,
                        min_length=10,
                        do_sample=True,
                        top_k=40,
                        temperature=1.5,
                        pad_token_id=tokenizer.pad_token_id,
                        bos_token_id=tokenizer.bos_token_id,
                        eos_token_id=tokenizer.eos_token_id
                        )
print(tokenizer.batch_decode(output))
```
生成したテキストを見ても分かる通り、若干雰囲気のことなったテキストが生成できているかと思います。
ぜひパラメータなどを変えて遊んでみてください。

# さいごに
今回はGPT2の訓練済みモデルを利用して文章生成を行なってきました！
今回記事を書いていて文章生成って夢があるなって思いました！自分の書いた文章とかでfine-tuningさせたら自分ぽいことを言ってくれるモデルとかも作れるかもですね笑

記事を読んでいただきありがとうございました！もし誤字や内容の誤りなどありましたらご指摘いただけますと幸いです。

# 参考サイト

https://huggingface.co/rinna/japanese-gpt2-medium

https://huggingface.co/docs/transformers/main_classes/tokenizer#tokenizer

https://huggingface.co/docs/transformers/main_classes/text_generation#transformers.GenerationMixin.generate

https://note.com/npaka/n/n5d296d8ae26d#6gupK