日本語特化GPT言語モデルJapanese-GPT-1bで簡単チャットボット

スポンサーリンク
自然言語処理
スポンサーリンク

Japanese-GPT-1bはrinna株式会社が公開している商用利用可能なMITライセンスで提供されている日本語言語モデルです。transformersの学習モデルに対して、適切な事前学習を実施することにより、前後の文脈に沿った自然な文章を作り出してくれます。今回は、Japanese-GPT-1bを使って簡単にチャットボットを作ってみます。パラメータ数は13億ということですので、今話題のGPT-3の先代のGPT-2レベルの日本語特化モデルという位置づけです。今回はそのモデルを直接使ってチャットボットを作成してみます。

日本語に特化した13億パラメータのGPT言語モデルを公開(rinna株式会社プレスリリース)

事前準備

今回はUbuntu上にチャットボットを作成します。以下の環境を事前に作成します。
・Windows11
・Ubuntu22.04
・Python3.10
・venv仮想環境
・transformersをインストール
詳しくは下のリンク先の記事をご参照ください。

Japanese-GPT-1bを使うにはセンテンスを分割するSentencePieceのライブラリが必要なので、pipでインストールしておきます。

> pip install sentencepiece

Japanese-GPT-1bモデルでの作成

コードは下記の公式ページのコード例を参考に作成しました。パラメータは以下のような感じが自然に返してくれます。会話形式で続きを生成することにより、自然な会話をするようにしています。また、話をするキャラクターをプロンプトに入れることにより、ある程度性格などを反映するようにしました。少しコードが拙いのは許してください。話の流れを記憶できていなかったり、なんの制御もされていないので、不自然な会話や暴力的な発言が生成されたりしますが、簡単なコードでチャットボットが作れました。

コード例:

import torch
from transformers import T5Tokenizer, AutoModelForCausalLM

# 会話文を入れる
def my_turn():
    while True:
        s = input("話してください?")
        if s == "":
            exit()
        elif len(s)<=40:
            break
    return s

# bot生成
class ChatBot:
    def __init__(self, bot_chara):
        self.bot_chara = bot_chara        

    def conv_generate(self, prompt, tokenizer, model):

        token_ids = tokenizer.encode(prompt, add_special_tokens=False, return_tensors="pt")
        prompt_length = len(prompt)

        with torch.no_grad():
            output_ids = model.generate(
                token_ids.to(model.device),
                max_length=100,
                min_length=100,
                do_sample=True,
                num_beams=20,
                early_stopping=True,
                no_repeat_ngram_size=1,
                remove_invalid_values=True,
                temperature=0.8,

                pad_token_id=tokenizer.pad_token_id,
                bos_token_id=tokenizer.bos_token_id,
                eos_token_id=tokenizer.eos_token_id,
            )

        output = tokenizer.decode(output_ids.tolist()[0])
        res = output[prompt_length:]
        if "」" in res:
            res = res.split("」")[0]
        print(self.bot_chara+":「"+res+"」")

        return output[0:(prompt_length+len(res))]

# promptの字数を調整
def reducer(prompt):
    if len(prompt) >= 100:
        return prompt[-100:]
    return prompt

# キャラクター選択
def select_chara():
    print("誰と話しますか?")
    chara_list = ["フリー入力","正統派美少女", "しっかり者の女性", "理系女子","もの知りのおばあちゃん", "電話のオペレーターのおねえさん"]
    for i in range(len(chara_list)):
        print(i+1, ": "+chara_list[i])
    chara_no = int(input("キャラクタを選んでください(番号で選択)?"))
    if chara_no == 1:
        return input("話したい人を入れてください(フリーで入力)?")
    elif chara_no>=2 and chara_no<=len(chara_list):
        return chara_list[chara_no-1]
    else:
        exit()

# 実行部
def main():
    bot_chara = select_chara()
    print(bot_chara+"ですね。")
    print()

    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")

    chatbot = ChatBot(bot_chara)
    initial_prompt = chatbot.bot_chara+":「楽しいお話をしましょう。」"
    print(initial_prompt)
    text = initial_prompt

    while True:
        text = text + "私:「"+my_turn()+"」"+chatbot.bot_chara+":「"
        text = reducer(text)
        text = chatbot.conv_generate(text, tokenizer, model)

if __name__ == "__main__":
    main()   
   

使ってみた

実行例1:美少女と趣味の話
$ python3 chatbot_v1.py

誰と話しますか?
1 : フリー入力
2 : 正統派美少女
3 : しっかり者の女性
4 : 理系女子
5 : もの知りのおばあちゃん
6 : 電話のオペレーターのおねえさん
キャラクタを選んでください(番号で選択)?2
正統派美少女ですね。

正統派美少女:「楽しいお話をしましょう。」
> 休みの日は何をしているんですか?
正統派美少女:「家でゴロゴロしたり、友達と遊んだりして過ごしています。趣味はゲームとアニメ鑑賞です♪」
> そうなんですね。アニメは何が好きですか?
正統派美少女:「最近だと『Re:ゼロから始める異世界生活』にハマっています!エミリアやレムが可愛くて癒されます(* ́ω`*)」
> 私もリゼロは最近のは見てないけど昔のシリーズは見ました。
正統派美少女:「そうなんですね。では、好きなキャラクターは誰ですか?」
> やっぱ、エミリアかな。
正統派美少女:「私はレムが好きです♪」
> 毒舌の子ですよね。
正統派美少女:「そうですね(笑)。」
> 読書はしますか?
正統派美少女:「本を読むのは好きですね。最近だと『進撃の巨人』を読破しましたよ!」
> 漫画??
正統派美少女:「はい、大好きです!」
> 普通の本は読まないんですか?
正統派美少女:「小説とかも読みますけど、『ハリー・ポッターと賢者の石』『ナルニア国物語/第2章:カスピアン王子の角笛』(いずれもJ.Kローリング著)などのファンタジーや児童文学が特に」
> ハリーポッターは映画は見ました。
正統派美少女:「私は見ていないです。」
> そうなんですね。意外です。
正統派美少女:「でも、すごく面白かったですよ!原作も読みたくなりました!」
> 英語の原作ですか?
正統派美少女:「はい!」
> 英語、得意なんですね。何か話してくれませんか?
正統派美少女:「えっと・・・何て言えばいいんですかねぇ・・・」
> 例えば、挨拶とか。
正統派美少女:「はぁ~い(笑)じゃあ、『おはようございます』って英語でなんて言うんでしたっけ?『Have a nice day!』で合ってると思うんですけど」
> ふつうはGood moringでいいと思うけど。
正統派美少女:「あ、そっか。じゃぁそうしよっかな(笑)」

中身のない会話wですが、それなりに成り立っていますね。。。

実行例2:信長降臨
$ python3 chatbot_v1.py

誰と話しますか?
1 : フリー入力
2 : 正統派美少女
3 : しっかり者の女性
4 : 理系女子
5 : もの知りのおばあちゃん
6 : 電話のオペレーターのおねえさん
キャラクタを選んでください(番号で選択)?1
話したい人を入れてください(フリーで入力)?織田信長
織田信長ですね。

織田信長:「楽しいお話をしましょう。」
> 普段休みの日は何をしているんですか?
織田信長:「家でゴロゴロしていることが多いですね。テレビを見たり、本を読んだりして過ごしていますね」
> ライバルは誰ですか?
織田信長:「それは秘密です(笑)」
> 嫌いな人とかいませんか?
織田信長:「いますよ!でもそんなの関係ないですよ!(キリッ)」
> 部下で頼りになるのは?
織田信長:「はい、武田信玄と上杉謙信ですね。この二人には頭が上がりませんね。」
> 二人はライバルでは?
織田信長:「そうですか?私は同盟を結ばせてもらっているだけですから」
> 今度本能寺に行きませんか?
織田信長:「いいですよ、一緒に行きましょう」
> 何も起こりませんかね。
織田信長:「何か起こりますよ。明智光秀が謀反を起こしますから」
> えぇ!!大丈夫なんですか?
織田信長:「何が起こるか分からないのが戦国時代です」
> 豊臣秀吉との関係はどうなんですか?
織田信長:「秀吉とは仲がいいですよ。一緒に酒を酌み交わすこともありますよ」
> 明智光秀とは普段は仲がいいんですか?
織田信長:「いや、それはないですね(笑)」
> きつく当たったりしてませんか?
織田信長:「そんなことはないですけどね」
> 光秀さんは大分信長さんを恨んでいるかも。
織田信長:「そうでしょうね。本能寺の変で信長が亡くなってから秀吉が天下を取るまで10年しかかからなかったんですからね」
> 人生に悔いはないですか?
織田信長:「いや、後悔は山ほどありますよ明智光秀を討てなかったこととかね」
> それは失礼しました。
織田信長:「ではまたいつかお会いしましょう」

最初の出だしは、例1と同じように行くかと思いましたが、戦国時代の話に乗ってくれました。(史実とかなり違いますが。。。)

終わりに

今回、日本語に特化したJapanese-GPT-1bの学習済みモデルを用いることにより、ヒトに近い会話を比較的簡単に実装できることが分かりました。今、はやりのchatGPTのベースになっているGPT-3などのより精度の高いモデルを使ったり、今回は簡単なキャラクター付けですが、これまでの会話の内容を蓄積したり、感情の因子を反映させることにより、もっと人間らしい会話ができる可能性がありますね。

参考

【広告】GPT使うなら、GPU搭載のPCがおススメです。自作もよいけど、難しい人はBTOのPCもコスパ良いですよ。

パソコン工房GeForce RTX 3070 Ti / 3060 / 3050搭載ノート大幅値下げ! デル株式会社 HP Directplus -HP公式オンラインストア-

コメント

  1. もずく より:

    初めまして!
    記事の方興味深く読まさせていただきました
    自分の環境でも動作させてみたのですが、このコードの場合、キャラ付け、いわゆる初期設定というのはinitial_promptに与えている文のみで行っているのでしょうか?
    また、過去の文脈とかってどれくらい認識しているのでしょうか?
    お暇があれば教えていただきたいです…!

    • タカ より:

      もずくさん、ご質問ありがとうございます。
      このchatBotのキャラ付けは会話文の冒頭の “正統派美少女:「” の部分で行っています。毎回Botの会話の前にこの部分を入れることで、そのキャラクターになりきって応答してくれます。この言語モデルの「続きを出力してくれる性質」を利用しています。
      過去の文脈については、モデルの仕様上直前の会話文しか与えていませんので、反映していません。人間がうまく会話することで、直前の会話からchatBotがそれらしい応答をしてくれているだけです。
      今流行りのGPT-4などを使うと、長文をbotに与えることができますが。

      • もずく より:

        返信ありがとうございます!!
        その点も工夫による技ってことなんですね….
        いろいろ試してみたいと思います!ありがとうございます!