Poetry で環境構築して大規模言語モデル Rinna を動かして文章生成させてみた

ようやく安定した Python 環境で GPT が動かせるようになりましたよ~。

以前から格闘していたローカル LLM。

今回は rinna/japanese-gpt-neox-3.6b-instruction-sft-v2 という大規模言語モデルを使って、ローカルで文章生成をさせてみる。

Rinna って何

今回、GPT 関連の用語やモデルを色々調べていたので、素人なりに参考文献をまとめておく。

これらの文献でだいぶ理解が深まった。

WSL 上の Poetry で環境構築してみる

以前の記事でも愚痴ったが、Python 界隈はいつまで経っても環境構築の再現性が乏しいというか、その辺の解説を無視してるモノが多いなと思っている。なので今回も Poetry でランタイムのバージョンや必要なライブラリを吐き出しておくことにする。

WSL 上で作業開始する時点で、次のような状態。

$ python3 -V
Python 3.11.2

$ poetry -V
Poetry (version 1.8.3)

まずは Poetry プロジェクトを作る。

$ poetry new practice
$ cd ./practice/

# 以下に処理を書く
$ touch ./practice/__main__.py

次に必要なライブラリのインストール。後述するコード内で明示的に import しているのは torchtransformers の2つだけなのだが、実行時にエラーが出たりして怒られたので、後ろ3つもインストールしている。

$ poetry add torch transformers accelerate sentencepiece protobuf

コーディング

コードは Hugging Face にある Rinna 公式のサンプルコードをベースに、色んな文献を見たり、実行時のワーニングメッセージを見たりして調整した。

import datetime

import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = 'rinna/japanese-gpt-neox-3.6b-instruction-sft-v2'
print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Start : Model Name [', model_name, ']')

print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Tokenizer')  # ワーニングが出るから legacy と clean_up_tokenization_spaces を入れた・float16 は高速化のため
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False, legacy=False, clean_up_tokenization_spaces=True, torch_dtype=torch.float16)
print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Model')
model = AutoModelForCausalLM.from_pretrained(model_name)

print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Is Cuda Available?')
if torch.cuda.is_available():
    print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Use Cuda')
    model = model.to('cuda')

input_text = 'こんにちは世界!'
print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Prompt : [', input_text, ']')

print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Prompt Tokenizer')
inputs = tokenizer(input_text, add_special_tokens=False, return_tensors='pt').to(model.device)

print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Model Generate')
with torch.no_grad():
    outputs = model.generate(
        **inputs,
        do_sample=True,
        max_new_tokens=256,
        temperature=0.7,
        repetition_penalty=1.1,
        pad_token_id=tokenizer.pad_token_id,
        bos_token_id=tokenizer.bos_token_id,
        eos_token_id=tokenizer.eos_token_id
    )

print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), 'Decode')
output_text = tokenizer.decode(outputs[0], clean_up_tokenization_spaces=True, skip_special_tokens=True)

print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), '----------')
print(output_text)
print(datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9))), '==========')

実行時間を見るために print 文を挟んでいるが、主な流れは次のとおり。

動かしてみる

コーディングしたら以下のコマンドで実行する。

$ poetry run python -m practice

実行例は以下のとおり。初回はモデルのダウンロードが入ってだいぶ時間がかかるが、2回目以降はキャッシュされるので、モデルのロードと計算時間だけになる。

$ poetry run python -m practice
2024-08-10 13:47:15.382350+09:00 Start : Model Name [ rinna/japanese-gpt-neox-3.6b-instruction-sft-v2 ]
2024-08-10 13:47:15.382409+09:00 Tokenizer
2024-08-10 13:47:16.179210+09:00 Model
2024-08-10 13:47:51.498773+09:00 Is Cuda Available?
2024-08-10 13:47:55.486486+09:00 Use Cuda
2024-08-10 13:48:31.154504+09:00 Prompt : [ 私は一風変わった猫を飼っています。その猫はいま ]
2024-08-10 13:48:31.154595+09:00 Prompt Tokenizer
2024-08-10 13:48:31.302476+09:00 Model Generate
2024-08-10 13:49:51.914125+09:00 Decode
2024-08-10 13:49:51.938365+09:00 ----------
私は一風変わった猫を飼っています。その猫はいま、私のお腹の上に寝ています。私のお腹の上というのは、私が彼女の上に座れる唯一の場所です。そして私は彼女の頭や顔に触れて安心します。そうやって寝るのが好きです。
2024-08-10 13:49:51.938428+09:00 ==========

検証に利用したマシンは i7-7700K・GTX1080・RAM 32GB という構成なのだが、実行開始から出力終了までに2分ちょっとかかっている。コレは torch_dtype=torch.float16 の指定を入れても入れなくても同じくらいで、max_new_tokens を大きくすると時間が長くなる印象だ。

GPU をちゃんと使えているのはパフォーマンスモニタでも確認できていて、GTX1080 だとコレが速度的に限界なのかなーという感じ。ChatGPT のようにキビキビと高速に返事してもらうのは難しそうだ。

プロジェクト全量を GitHub に置きました

いつものように、これら検証用コードを含めた Poetry プロジェクトは以下の GitHub リポジトリに置いてある。

環境再現時は以下のコマンドで依存ライブラリをインストールして上げてから $ poetry run すれば OK。

$ poetry install

GPT-2 より精度は上がっている

以前の記事 (2021-04-11 GPT2 再挑戦して WSL で日本語文章を自動生成できた) と比べてみると、同じマシンを使っているのでハード的なスペックは同じで、実行時間は一文あたり2分程度でコレもほぼ同じ。

しかしながら、出力される文章の精度が Rinna GPT-NeoX はかなりそれっぽくなっていて、実行速度を無視すれば OpenAI ChatGPT の GPT-3.5 に迫る精度なのではないだろうか。

今回は Poetry でプロジェクト環境を整えることも出来たし、ワケも分からず書いていたコードの意味もだいぶ理解できた。あとは他にも色々な日本語対応の大規模言語モデルがあるので、今回検証した「りんな」と比較して今後も遊んでみようと思う。