ヅカの芸名を自動生成するAIを作る(7)：GPT-2編(3)

# これまでのあらすじ

前回は[こちら](https://qiita.com/muto-jo/items/460ffb50924fd24010d6)

宝塚の歴代生徒一覧から、80期以降の生徒の姓名をGPT-2の日本語モデル(rinna公開)をベースにファインチューニングさせたところ。

# ファインチューニング実行結果

うまくいきました。

```JSON
{
    "epoch": 10.0,
    "eval_accuracy": 0.8900700553926361,
    "eval_loss": 0.5546898245811462,
    "eval_runtime": 384.3219,
    "eval_samples": 24,
    "eval_samples_per_second": 0.062,
    "eval_steps_per_second": 0.062,
    "perplexity": 1.741400761285859,
    "train_loss": 0.9064921061197917,
    "train_runtime": 24105.1482,
    "train_samples": 24,
    "train_samples_per_second": 0.01,
    "train_steps_per_second": 0.01
}
```

ファインチューニングにかかった時間、6:24:32である。
eval_accuracyは0.89と精度でているように思うが、eval_lossが0.55、train_lossは0.9となっているのが気になる…

ともあれ、使ってみよう。

# 使ってみた

GPT-2の試用に使ったスクリプトをもとに生成された学習モデルを使うのと、学習データに入れた書き出しにする。

```Python
from transformers import T5Tokenizer, AutoModelForCausalLM

# load tokenizer
tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium")

# load pre-trained model
model = AutoModelForCausalLM.from_pretrained("./fine_tuning/fine_tuned_model/")

# Set input word
_input = tokenizer.encode("
宝塚に新しい団員が増えた。新しい団員の名前は、[SEP]", return_tensors="pt")

# inference
output = model.generate(_input, do_sample=True, max_length=10, num_return_sequences=3)

# inferred output
print(tokenizer.batch_decode(output))
```

結果はこちらである。

```Python
['
 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 
 日高',
 '
 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 
 帆',
 '
 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 
 彩']
```

なんともまあ微妙な結果である…。
一応、漢字は、宝塚でよく使われる類の漢字が出ている。何回か繰り返してみたが、「帆」「彩」あたりはよく出てくる。

tokenizerに食わせるinputを `"
宝塚に新しい団員が増えた。新しい団員の名前は、[SEP]彩海[SEP]"` と苗字を与えて名前の方を生成させてみる。

```Python
['
 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 彩海[SEP] 
 ひ',
 '
 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 彩海[SEP] 
 ち',
 '
 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 彩海[SEP] 
 み']
```

名前は出てきたが…。

# 苗字と名前を2回に分けて生成させてみる

GPT-2は次の文の予測モデルなので、苗字と名前を分けると、苗字で一回の予測、名前で一回の予測なのかもしれない。
ということで、生成された苗字を使って名前を生成させてみる。

```Python
# Set input word　for generate family name
_input = tokenizer.encode("
宝塚に新しい団員が増えた。新しい団員の名前は、[SEP]", return_tensors="pt")

# inference
family_names = model.generate(_input, do_sample=True, max_length=10, num_return_sequences=3)

# inferred output
for gen_family_result in tokenizer.batch_decode(family_names):
        family_name = gen_family_result.split('[SEP] 
')[1]
        family_name = family_name.replace('
 ', '')
        
        _input = tokenizer.encode("
宝塚に新しい団員が増えた。新しい団員の名前は、[SEP]{}[SEP]".format(family_name), return_tensors="pt")
        first_names = model.generate(_input, do_sample=True, max_length=10, num_return_sequences=3)
        
        for gen_first_result in tokenizer.batch_decode(first_names):
            print(gen_first_result)
```

苗字3つ、苗字1つに対し名前も3つ、計9つの芸名を生成させてみる。
結果がこちらである（比較的マシなやつ…）

```Python

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 春[SEP] 
 夏

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 春[SEP] 
 澄

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 春[SEP] 
 芽

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 芽[SEP] 
 ゆり

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 芽[SEP] 
 こころ

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 芽[SEP] 
 海

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 春[SEP] 
 旬

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 春[SEP] 
 梅

 宝塚に新しい団員が増えた。新しい団員の名前は、[SEP] 春[SEP] 
 舞
```

澄、ゆり、こころ、海、旬、舞あたりは実際にあっても良さそうな名前であるが、苗字の方があまりらしくない。

うーん、やはり学習時の損失率が高いのが影響しているのだろうか…
あるいはシンプルに名前の生成のような文章でないものは学習しづらいのか…

ともあれ、今回の記事はここで終える。

次回は自分で作ってみようかと思うが、もう少しGPT-2でうまく生成できない理由を深追いしたい気もする。
もしこの記事を読まれた方で、アドバイスや意見などあればコメントいただけると嬉しいです。