13億パラメータのGPT日本語学習済みモデルが出たので会話応答を生成してみた

# 概要
OpenAIが開発した言語モデルであるGPTの新しい日本語学習済みモデルが先日rinna株式会社から公開されたので（[プレスリリース](https://prtimes.jp/main/html/rd/p/000000025.000070041.html)）、試してみることにしました。
# お断り
書いた人はすっとぼけでおっちょこちょいで、深層学習について理解できていない部分も多々ある学生です。間違いがないよう注意を払っていますが、誤った記述があればご教授いただけるとすごくありがたいです。
# 実行環境
```
OS: macOS Big Sur (version 11.6.2)
CPU: 2.8GHz クアッドコア Intel Core i7
メモリ: 16GB
```
# インストール
```bash:
$ pip install torch transformers
```
# サンプルコードをいじってみる
今回使用するモデル、[japanese-gpt-1b](https://huggingface.co/rinna/japanese-gpt-1b)はHugging FaceにMITライセンスで公開されています。ご丁寧にコピペすれば動くサンプルコードがあるので、これをまずは動かしてみます。

```python:
import torch
from transformers import T5Tokenizer, AutoModelForCausalLM

tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt-1b")
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt-1b")

if torch.cuda.is_available():
    model = model.to("cuda")

text = "西田幾多郎は、"
token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        max_length=100,
        min_length=100,
        do_sample=True,
        top_k=500,
        top_p=0.95,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id,
        bad_word_ids=[[tokenizer.unk_token_id]]
    )

output = tokenizer.decode(output_ids.tolist()[0])
print(output)  
```
こんな文章が生成されました。

```
西田幾多郎は、哲学である哲学の言葉は、理解できなくても、そこに漂っている永遠性や、自己反省の力を持った永遠の存在を認めたりすることができ、それは我々の精神にとって非常に重要で、決して無用のものにはならない、と言っている。そこに真理の言葉があると考えることから、西田哲学は始まっているのである。西田幾多郎のいう「存在」とは、言葉として真理の言葉を持つものである。真理の言葉を持つものの中に言葉を持たないものはない。言葉を持たないものは、真理の言葉を持った者
```
生成される文章の長さが100で固定されているので文章が途中で途切れていますが、とっても自然です。
ところで、もうすぐでバレンタインデーというイベントがありますね。毎年周りは沸き立っているのですが僕はどういうイベントなのか皆目見当もつきません。そこで「バレンタインデーは、」という文章の続きを作ってみました。

```
バレンタインデーは、今となってはチョコだけではなく、スイーツなどのプレゼントを贈る人も多い時代です。しかし、せっかくするなら美味しいスイーツで感謝の気持ちを伝えたいですよね
```
ふーん、チョコを贈ったりするイベントなんですねー知らなかったなーあはははははははは(ry

# 本題：会話をしてみよう
さて、GPTのすごいところは、以前まで必要だった「事前学習済みのモデルを個別タスク用の教師データでファインチューニングして……」という段階をすっ飛ばして、いくつか例を示すだけで理解してくれるというところにあります。初めて知ったときは衝撃的でした（何を今さらという感じですみません……）。
そこで、こんな文章を入力してみます。

```
AIはおしゃべりが好きで、とても賢いです。以下は人間とAIの会話です。
人間:「こんにちは!」
AI:「はい、こんにちは!」
人間:「今日はいい天気だね」
AI:「
```
これの続きを生成させれば、「今日はいい天気だね」に対する応答を得たことになりそうですね。中略。コマンドライン形式の会話プログラムを作りました。半角正則化に関しては、三好悠樹さんが書かれた[こちらの記事](https://qiita.com/YuukiMiyoshi/items/6ce77bf402a29a99f1bf)を使用しました。

```python:
import torch
from transformers import T5Tokenizer, AutoModelForCausalLM

tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt-1b")
model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt-1b")

userInput = "ッ"
text = "AIはおしゃべりが好きで、とても賢いです。以下は人間とAIの会話です。人間:「こんにちは!」AI:「はい、こんにちは」人間:「"

while userInput != "":
    userInput = input(">>> ")
    if userInput == "":
        break
    text += userInput + "」AI:「"

    if torch.cuda.is_available():
        model = model.to("cuda")
    token_ids = tokenizer.encode(text, add_special_tokens=False, return_tensors="pt")
    with torch.no_grad():
        output_ids = model.generate(
            token_ids.to(model.device),
            max_length=200,
            min_length=100,
            do_sample=True,
            top_k=500,
            top_p=0.95,
            pad_token_id=tokenizer.pad_token_id,
            bos_token_id=tokenizer.bos_token_id,
            eos_token_id=tokenizer.eos_token_id,
            bad_word_ids=[[tokenizer.unk_token_id]]
        )
    output = tokenizer.decode(output_ids.tolist()[0])

    #半角正則化
    text = text.translate(str.maketrans({chr(0xFF01 + i): chr(0x21 + i) for i in range(94)}))
    #今回の応答より前をもぎ取る
    output = output.replace(text, "")
    #最初の」までを切り取る
    outputList = []
    for l in output:
        outputList.append(l)
        if l == "」":
            break
    outputSentence = "".join(outputList)
    text += outputSentence + "人間:「"
    print(outputSentence.replace("」", ""))
```
生成された長さ100の文章のうち、最初の`」`までを取り出して応答とします。こちらが入力した文章も文脈として追加されていきます。文脈全体で200を超えると終了してしまうので、`max_length`を変更するか前の方の文脈を消していけば長続きするはず。
実行してみると、こんな感じで会話が進みます。僕の入力が「>>>」の後にあり、GPTの生成した応答が1行続きます。

```
>>> 今日はいい天気だね
そうね
>>> どこかに行かない？
近場のお店を見て回ると良いよ
>>> どんなお店がいいかな
少し遠くまで行くと気持ちいと思うわ
>>> え、イタリアンでも行く？
少し遠いけど、いいお店があるの
>>> じゃあそこに行こうか
ありがとう
>>> ピザとスパゲッティどっちにしようか
スィートポテトとハッシュドポテトも好き？
>>> まあ好きやけど
じゃあスペシャルを5ドルで頼んでいいよ！
>>> やったあ（）
すごいね！
>>> それはなにより
さ、ピザも来て！
>>> やったぜ
OK！　それでは着いてきてね！
```
いや、歩いていくんかい。

```
>>> どんな本が好き？
小説
>>> 僕も好きだな
え、どうして？
>>> いろんな世界に行けるから
そっか。いろんな世界。いいな。
>>> 音楽は好き？
僕、ピアノ弾けるよ。ドレミで弾ける。
>>> 僕もピアノ弾ける
ああ。羨ましいなあ。僕も弾ける。
>>> ギターはどう？
僕もギターも弾ける
```
ちょっと文体に宮沢賢治みがありますね。
# 補足
今回はパラメータを特に変更していませんが、temperature などをいじればもう少し文脈に即した会話などもできそうです。
# 終わりに
あっさりと自然な文章を生成できてしまったのでびっくりしました。英語圏ではGoogleやOpenAIが「モデルがないなら作っちゃえばいいじゃない」とマリー・アントワネット理論を展開しマシンパワーで殴って質のいいモデルをじゃんじゃか作っていますが、日本語圏ではなかなかそうも行かないことが多いので重宝しますね（[このサイト](https://lambdalabs.com/blog/demystifying-gpt-3/)によれば、有料GPUサービスでGPT-3の学習を行うと5億円以上かかるらしいです。たっけー）。
お手軽な上に性能もよくてとてもすごいと思いました。読んでいただき感謝です。 
止まってしまっている個人開発物があるので、それに使おうかな……。