Home About Contact
Hugging Face , PyTorch , CUDA , GPT , Rinna , Transformers

ローカルPCで Rinna 3.6B (rinnna japanese-gpt-neox-3.6b) を試した

Ubuntu Server 22.04 + GPU 12GB で Rinna 3.6B を動かしたので、その備忘録です。

モチベーションとしては、LlamaIndex で実現しているのと同じようなことをこのモデルなどを活用しながらつくることができないかと思っています。

CUDAは現時点での最新の12.1 を使用しました。

$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2023 NVIDIA Corporation
Built on Mon_Apr__3_17:16:06_PDT_2023
Cuda compilation tools, release 12.1, V12.1.105
Build cuda_12.1.r12.1/compiler.32688072_0

動かした方の基本は https://huggingface.co/rinna/japanese-gpt-neox-3.6b-instruction-sft のページをご覧ください。

いわゆる Rinna 3.6B と言われているモデルは japanese-gpt-neox-3.6b-instruction-sft と japanese-gpt-neox-3.6b の2つがありますが、 instruction-sft がついている方は、 付いていない方(japanese-gpt-neox-3.6b) をベースにそれを fine-tuning して会話できるようにしたものです。

The model is based on rinna/japanese-gpt-neox-3.6b and has been finetuned to serve as a instruction-following conversational agent.

float16に量子化して動かす

12GB の GPU では、そのままでは動かないので、モデルのロード部分を以下のようにしました。

#model = AutoModelForCausalLM.from_pretrained("rinna/japanese-gpt-neox-3.6b-instruction-sft")

model = AutoModelForCausalLM.from_pretrained(
   "rinna/japanese-gpt-neox-3.6b-instruction-sft",
   torch_dtype=torch.float16)

モデルロード時のオプションに torch_dtype=torch.float16 を追加。

こちらのページを参考にしました。
【Python】手持ちのGPUがVRAM12Gだけど「Rinna-3.6B」とお話がしたい!!!

コード全体では次のようになります。

main.py

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

model_name = "rinna/japanese-gpt-neox-3.6b-instruction-sft"

tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False, torch_dtype=torch.float16)
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)

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

prompt = "ユーザー: 日本のおすすめの観光地を教えてください。<NL>システム: どの地域の観光地が知りたいですか?<NL>ユーザー: 渋谷の観光地を教えてください。<NL>システム:"

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

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        do_sample=True,
        max_new_tokens=128,
        temperature=0.7,
        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][token_ids.size(1):])
output = output.replace("<NL>", "\n")
print(output)

実行してみると、以下のような結果になりました。

$ time python3 main.py
わかりました。まずは、ハチ公像から始めましょう。ハチ公像は、日本の最も有名な観光スポットの1つです。ハチ公像は、日本の建築家である安藤忠雄によって設計されま
した。ハチ公像は、日本の彫刻家である安藤忠雄によって設計されました。ハチ公像は、日本の彫刻家である安藤忠雄によって設計されました。ハチ公像は、日本の彫刻家である安藤忠雄によって設計されました。ハチ公像は、日本の彫刻家である安藤忠雄によって設計されました。ハチ公像は、日本の彫刻家である安藤忠雄によって設計されました。ハチ公像は

real    1m28.380s
user    1m16.951s
sys     0m9.055s

これで動かすことはできたのですが、一回実行するごとに 1分30秒ほどの時間がかかっています。 ロード時間が遅いだけなので、開発中でなければ問題になることはないかもしれませんが、開発中はちょっと遅すぎてつらいです。

さらに int8に量子化して動かす

さらに調べてみると int8 まで量子化する例がありましたので、試しました。

話題のrinna-3.6bをColab無料枠で動かしたい!

追加でいくつかのモジュールを入れることで int8 に量子化して少ないメモリで動かすことができるらしいです。 詳しくは https://huggingface.co/docs/accelerate/index を読みましょう。

モデルをロードする部分を以下のように変更します。

model = AutoModelForCausalLM.from_pretrained(
    "rinna/japanese-gpt-neox-3.6b-instruction-sft",
    device_map = "auto",
    load_in_8bit=True)

device_map と load_in_8bit オプションを追加します。

device_map の部分は auto ではなく明示的に指定した方がよいかもしれません。 以下のように:

device_map = infer_auto_device_map(
    model,
    max_memory={0: "10GiB", "cpu": "10GiB"},
    no_split_module_classes=["GPTNeoXLayer"],
    dtype=torch.int8)

model = AutoModelForCausalLM.from_pretrained(
    "rinna/japanese-gpt-neox-3.6b-instruction-sft",
    device_map = device_map,
    load_in_8bit=True)

コードは全体 main.py

モデルをロードする部分以外のコードに変更はありません。

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

model_name = "rinna/japanese-gpt-neox-3.6b-instruction-sft"

tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=False)
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    device_map = "auto",
    load_in_8bit=True)

prompt = "ユーザー: 日本のおすすめの観光地を教えてください。<NL>システム: どの地域の観光地が知りたいですか?<NL>ユーザー: 渋谷の観光地を教えてください。<NL>システム:"

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

with torch.no_grad():
    output_ids = model.generate(
        token_ids.to(model.device),
        do_sample=True,
        max_new_tokens=128,
        temperature=0.7,
        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][token_ids.size(1):])
output = output.replace("<NL>", "\n")
print(output)

実行してみます。

$ time python3 main.py
わかりました。まずは、ハチ公像から始めましょう。ハチ公像は、日本の東京にある有名な観光スポットです。ハチ公像は、日本の忠犬ハチ公にちなんで名付けられた像です。ハチ公像は、日本の東京にある有名な観光スポットです。</s>

real    0m28.014s
user    0m19.643s
sys     0m8.601s

約30秒で実行できました。先ほどの float16 と比べて 1/3 の時間で実行できました。これなら耐えられるかも。

どんな回答が得られるのか

さて、一番関心のある Rinna の回答例をいくつかみてみましょう。

例1: 逆に質問された例

わかりました。どのような観光スポットをお探しですか?

質問したのに、質問でかえされるとちょっとイラッとしますね。 しかし、そのあとも続けて会話できるのだから、これはこれで良いのかもしれない。

例2: うまくいかなった例

わかりました。渋谷の観光地は、ハチ公像、スクランブル交差点、109、スクランブルエッグ、ハチ公像、忠犬ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公
像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ公像、ハチ

ハチ公像推しがすごい。

環境構築

最後に int8 で動かすための環境構築方法をメモしておきます。 OSは Ubuntu Server 22.04 です。

Python:

$ python3 --version
Python 3.10.6

CUDA:

$ nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2023 NVIDIA Corporation
Built on Mon_Apr__3_17:16:06_PDT_2023
Cuda compilation tools, release 12.1, V12.1.105
Build cuda_12.1.r12.1/compiler.32688072_0

j-gpt-neox-3.6b 環境を venv でつくります。

$ python3 -m venv ~/.local/j-gpt-neox-3.6b
$ source ~/.local/j-gpt-neox-3.6b/bin/activate

必要なモジュールを入れます。

(j-gpt-neox-3.6b) $ pip install transformers sentencepiece accelerate bitsandbytes scipy

PyTorch も必要ですが、明示的に指定しなくても上記コマンドで入りました。

これでAutoModelForCausualLMの方のモデルのロードはできるのですが、Tokenizer はさらに protobuf モジュールが必要です。

しかも pip install protobuf してからコード(main.py)を動かすとバージョンを下げろ(ダウングレードしろ)とのエラーがでます。 結論としては 3.19.0 を指定してインストールすることで作動しました。

(j-gpt-neox-3.6b) $ pip install protobuf==3.19.0

以上で環境構築完了です。 あとは main.py のコードを実行するだけです。

最後にまとめとして、この段階での pip 環境を確認しておきます。

(j-gpt-neox-3.6b) $ pip list
Package                  Version
------------------------ ----------
accelerate               0.19.0
bitsandbytes             0.39.0
certifi                  2023.5.7
charset-normalizer       3.1.0
cmake                    3.26.3
filelock                 3.12.0
fsspec                   2023.5.0
huggingface-hub          0.14.1
idna                     3.4
Jinja2                   3.1.2
lit                      16.0.5
MarkupSafe               2.1.2
mpmath                   1.3.0
networkx                 3.1
numpy                    1.24.3
nvidia-cublas-cu11       11.10.3.66
nvidia-cuda-cupti-cu11   11.7.101
nvidia-cuda-nvrtc-cu11   11.7.99
nvidia-cuda-runtime-cu11 11.7.99
nvidia-cudnn-cu11        8.5.0.96
nvidia-cufft-cu11        10.9.0.58
nvidia-curand-cu11       10.2.10.91
nvidia-cusolver-cu11     11.4.0.1
nvidia-cusparse-cu11     11.7.4.91
nvidia-nccl-cu11         2.14.3
nvidia-nvtx-cu11         11.7.91
packaging                23.1
pip                      22.0.2
protobuf                 3.19.0
psutil                   5.9.5
PyYAML                   6.0
regex                    2023.5.5
requests                 2.31.0
scipy                    1.10.1
sentencepiece            0.1.99
setuptools               59.6.0
sympy                    1.12
tokenizers               0.13.3
torch                    2.0.1
tqdm                     4.65.0
transformers             4.29.2
triton                   2.0.0
typing_extensions        4.6.1
urllib3                  2.0.2
wheel                    0.40.0

ちなみに、CUDA 11.5 で試した場合、これ作動しなかったので注意してください。 CUDA 12.1 にすることで動くようになりました。

CUDA 11.5 で作動しなかった原因は(詳しくはわかりませんが、どうやら) bitsandbytes モジュールが CUDA 11.5 では作動しないからのように思えました。 CUDA 11.8 まで上げればOK的な情報もありました。

以上です。