GPT3やChatGPTのAPIなどの有料機能を用いず、自宅で動く誰でもダウンロードできるLLM(大規模言語モデル)を、雑談用途にファインチューニングし、さらにキャラクター性を獲得させることが最終目標です。
そこに至るために必要な、雑談=文意に沿った短文同士の応答をまずできるようにしました。
今回紹介する方法で以下のような雑談が可能となりました。
おはよう→昨日はどうも。
ラーメン食べる?→今日もカレー食べてくる。
ラーメン美味しいよね→おいしかったな。
1. 日本語モデルを雑談用にファインチューニング
モデル選定理由 先駆者の知見を踏まえて
ここではGPT-2のrinna社ファインチューニングモデルを用います。
英語の言語モデルをいきなりキャラ付けして会話できるようにするのは無謀のため。。。
また、OPT-175B系列のモデルは商用利用できないので、MITライセンス(商用利用を無料でできる)という意味でもrinna/japanese-gpt2シリーズおすすめです。
なお、家庭で動くGPT-2程度のパラメーター数では、大規模な学習なしに英語以外を話させることができません。
(殆どの自宅で動くサイズではないものの、GPT-NEOX-20Bなど日本語以外で訓練したハズのモデルも、パラメーター数が多いと日本語も話せるようになるそうです)
そこそこのウェイトを占める Pile CC データセットから jusText では日本語や中国語は扱えないので, それらの言語は除いたとあります.
それでも GPT-NEOX-Japanese-2.7b とかよりもいいかんじにふるまう感はあります.
gpt-neox-20b を 3090 x 2 で動かすメモ(3090 x 1 でもギリ動く!)
本家のGPT2使うのはStableDiffusionにほんの少しの画像で、好きなキャラの画像を生成できるようにファインチューニングするくらい無茶です。
StableDiffusion→WaifuDiffusinなどの二次元絵モデル→好みのキャラ用モデル
のように段階を踏むことが大切です。
(これはベースモデルがGPT-2以外でも同様です)
というわけで、まず日本語を話せるrinna/japanese-gpt2-mediumを使用していきます。
今回は1,000セット程度の会話コーパスで雑談用に調整してみます。
(その後、さらにキャラ付けファインチューニングをする2段階構成もしてみたいですね)
そのため、largeモデルなど大きいモデルでは、1000程度の会話では上手くチューニングが反映されない可能性(事例↓)があり、mediumにしました。
残念ながらlargeモデルは期待のテキストはあまりできませんでした。
smallモデルの方が期待のテキストができてた気がします。
ファインチューニングのデータセットがあまりに小さすぎるのも原因にありそうなので、大きな対話データセットを用意して再挑戦してみようと思います。
gpt2-japaneseの使い方 (3) – largeモデルによる日本語テキスト生成
雑談のデータセットをどうするか
幸い名古屋大学の雑談コーパスが誰でも(研究者以外でも)使える形で公開されていました。
「名大会話コーパス」にアクセスして、「文字化資料ダウンロード」します。


data129まで数人の話者の雑談がそれぞれ文字になったものが、zipファイル内にあります。
今回はお試しということで、適当にdata005.txtとdata087.txtの2つから、発話者の表示や記号などを取り除き、以下の形式でまとめました。
<s>発話[SEP]応答</s>
データの例↓(会話コーパスにありそうな内容を私が独自に考えたものであって、上記コーパスから一部転載したわけではありません)
<s>こんにちは。[SEP]うん、こんにちは。</s>
<s>京都?[SEP]そうそう。</s>
<s>中々グルメじゃん。[SEP]まあ休日暇だからね。</s>
実際にファインチューニングしてみた 雑談できるかな??
上記のような会話を1302行羅列したテキストファイルを、train.txtとして保存し、この方法でファインチューニングしました。
そしてファインチューニング済みのモデルを下のコードで動かしてみました。
from transformers import T5Tokenizer, AutoModelForCausalLM
import torch
import re
tokenizer = T5Tokenizer.from_pretrained("rinna/japanese-gpt2-medium")
model = AutoModelForCausalLM.from_pretrained(
"C:/中略/gpt-train/talking-test1"
)
# torch_dtype=torch.float16,
model = model.to("cuda")
def generate_reply(inp): # <s>はろー![SEP]こんにちは。</s>
input_text = "<s>" + str(inp) + "[SEP]"
input_ids = tokenizer.encode(input_text, return_tensors="pt")
with torch.no_grad():
output_ids = model.generate(
input_ids.to(model.device),
do_sample=True,
top_p=0.95,
top_k=100,
num_return_sequences=1,
max_new_tokens=100,
bad_words_ids=[[1], [5]],
no_repeat_ngram_size=1,
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])
sent = (
output.replace(inp, "")
.replace(" ", "")
.replace("<s>", "")
.replace("</s>", "")
.replace("[SEP]", "")
)
tex = ["。", "?", "?", "!", "!", "."]
for t in tex:
if t in sent:
sent = sent.split(t)[0] + t
else:
sent = sent
return sent
while True:
input_text = input("話しかけてね!:")
output = generate_reply(input_text)
print("-" * 15, "返答", "-" * 15)
print(output)
チャット・雑談ということで、 ["。", "?", "?", "!", "!", "."]
が出現するまでの一文を出力させます。
それでは通常の「rinna/japanese-gpt2-medium」とファインチューニング済みの場合を比較してみます。
2. ファインチューニングの成果を確認してみる
デフォルトのrinna/japanese-gpt2-mediumの場合
話しかけてね!:おはよう
--------------- 返答 ---------------
さんが書きました:お手数をおかけします、宜しく。
話しかけてね!:おはよう
--------------- 返答 ---------------
は、nhk名古屋放送局で放送されていたテレビ番組である。
話しかけてね!:おはよう
--------------- 返答 ---------------
さて、昨日は東京の天気が雨だったのですね...。
話しかけてね!:おはよう
--------------- 返答 ---------------
@wawa_pです!7月29日、31日の2日間にわたって「jxサンフラワーフェスティバル2015」がさいたまスーパーアリーナで開催されたんだけどみなさんはご覧になられました?ちなみにこちらの画像をクリックすると記事に移動します。
話しかけてね!:おはよう
--------------- 返答 ---------------
朝食会が開催されました。
デフォルトでは、入力「おはよう」に対して、挨拶感が薄いですね。
続いて、適当に質問してみます。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
_comments(0)第19回「おやじの盆休み」に参加させていただきました。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
まあ,食べちゃう(*<unk>∀́)ハァーハッ!!(11/25更新、19時まで有効のチケットをお持ちの方が対象です。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
»blogを書いてる場合じゃねぇ~ん。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
_spice-page2『いいね!』をいただきました!ありがとうございます(^o[...]今年で50回目を迎えるこの祭りは、4つの会場に分かれそれぞれ趣向の違う多彩なイベントが行われています。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
_spice-新ファッションプレスその日、私は午後11時30分頃から食べていたので(この後午前0時まで仕事ですよ)。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
:おた☆スケ【声優情報サイト】今年7月にテレビアニメ「ひぐらしのなく頃に解」で、小夜子をヒロインにしたスピンオフ作品の第1弾が発表された。
ブログの情報を学びまくりですね。
なお、私のコードが悪いため入力文の一部が出力に含まれてしまったので、そこだけ手動で削除しましたが、他一切改変していません。
入力に続く文章を出力するモデルのため、想定どおりな動作ではありますが、雑談らしさは微塵もありません。
ファインチューニング済みの場合
ファインチューニング済みは、それっぽい内容です。
話しかけてね!:おはよう
--------------- 返答 ---------------
昨日、今日ねもうちょっと元気だったけどさ。
話しかけてね!:おはよう
--------------- 返答 ---------------
いそやか君はね。
話しかけてね!:おはよう
--------------- 返答 ---------------
今日はお仕事が早く終わったので、3時半からアニメと読書をして過ごしました。
話しかけてね!:おはよう
--------------- 返答 ---------------
>それは、こっちの方にも失礼かな?まあいいや。
話しかけてね!:おはよう
--------------- 返答 ---------------
昨日はどうも。
同様に訊いてみます。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
1杯も食べたことないよ。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
チャーハンとカキ氷ぐらい。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
今日もカレー食べてくる。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
わさび(*^ー<unk>)bグッジョブ!!今食べたい。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
今日食べたのは、なんだっけか。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
そう、いーや。
話しかけてね!:ラーメン食べる?
--------------- 返答 ---------------
麺屋武蔵ではご飯とチャーハンが付いて1000円くらいでした。
このファインチューニングの目的は短文入力→短文出力という応答形式を覚えさせることだったのですが、思わぬ副産物として入力文に沿い出力文が食べ物関連になっていることがわかります。
よりデータセットを増やすことで、特定の学習元の応答に引っ張られず、入力文に沿った出力になることが期待できる結果となりました。
(学習元データに80000文字中、「ラーメン」は5件、「食べる」は6件含まれていましたが、いずれの文章とも違う応答をしているので、学習データを大体そのまま出力してしまう過学習では無さそうです)
疑問系だけでなく、入力した文章に同調することもそこそこできてます。
話しかけてね!:ラーメン美味しいよね
--------------- 返答 ---------------
おいしかったな。
話しかけてね!:ラーメン美味しいよね
--------------- 返答 ---------------
あ、いいな行ってみよう。
話しかけてね!:ラーメン美味しいよね
--------------- 返答 ---------------
麺類もとても好き。
話しかけてね!:ラーメン美味しいよね
--------------- 返答 ---------------
そうだよ。
名古屋大学コーパスの僅か2%程度の会話セットでこうなったということは、全会話コーパスを使用したらどうなるか楽しみですね。
その場合は学習対象をlarge相当(?)の「rinna/japanese-gpt-1b」にする必要がありそうです。
3. まとめ ローカルWindows環境で動作する雑談専門LLMを作る(試行)
ウェブ上にあった先駆者様の知見からわかったこと
- 学習元モデルは、素のGPT-2でなく、日本語が予め出力できるモデルを選ぼう
- 学習データが1000やそこらなら、smallやmediumモデルを使用すること
今回やってみてわかったこと
- 名古屋大学の会話コーパスを比較的少ない(1000セットの発話と応答)データだけ用いても
- そこそこ雑談らしくなった
- 短文応答形式を学習できた
- 入力した疑問に微妙な精度で回答する
- 入力内容にある程度同調してくれる
名古屋大学データセットの2%だけを使用してこれって凄いですよね!!(大興奮)
追記:このファインチューニングモデルよりも、もっと雑談にふさわしいrinna-3.6b対話用モデルが登場しました。
【API不使用】AITuberをWindowsで動かしてみる
画像生成AI「Stable Diffusion」を動かす方法
コード生成AI「santacoder」をWindowsローカル環境で動かす方法