おうちでChatGPTやってみたい! ローカルLLM(大規模言語モデル)はどこまでできるか? #ai
はじめに
ChatGPTをはじめとする対話型生成AIが注目・活用されてからの進歩はめざましいものがあります。一方、ChatGPT への入力はAIの学習に利用されうるため、個人情報や機密情報の入力を禁じるなど、注意が必要な点もあります。
個人PCや専用サーバで ChatGPT に類するものを動かすことができれば、対話型生成AIを気兼ねなく活用できるのに----ということで、ご家庭のマシンでどれだけのことができるのか、実際に試してみました。また、用語なども逐一簡単に調べて記載していますが、不正確なものもあるかもしれません。
モチベーション
- ご家庭にある数世代前のマシンで対話型生成AIを動作できるか
- 生成速度はどのくらいか
- 生成結果はどのようなものか
- 結論として「使える」のか
これらを知りたいというのが動機です。
そもそも ChatGPT とは
そもそも「ChatGPT」とは何でしょうか?
まず「Chat」とは、「おしゃべり」や「会話」という意味です。AIとの入出力に、自然な会話文を使用できることを示しています。
では、「GPT」とは何でしょうか? GPTとは Generative Pre-trained Transformer の略で、訳すなら「生成的な、事前学習済みの Transformer」となります。「Transformer」とは簡単に言うと、人間の脳の動きを模した処理を行うニューラルネットワークの一種です。つまりGPTとは簡単に意訳すれば「自然な文章を生成できるように事前に学習を行ったニューラルネットワーク」となるでしょう。
すなわち「ChatGPT」とは大変大雑把に言えば、自然な質問文で入力でき、自然な回答文で出力できる、人間の脳の動きを模した仕組み、となります。
使用マシン
本稿で用意した「ご家庭のマシン」のスペックは次の通りです。
- Intel Core i7-2600 (3.4GHz, 2011年発売)
- NVIDIA GeForce GTX 1050 Ti (メモリ4GB, 2016年発売)
- メモリ 32GB
まったく最先端ではなく一回りも二回りも古いけれど、十分現役で使用可能なスペックだと思います。NVIDIAのカードも搭載しているし…と思っていたら、後で面喰らうことになるのですが…。
大規模言語モデルとは
ここで一旦、「大規模言語モデル (Large Language Model; LLM)」という用語を見てみましょう。まず「言語モデル (Langage Model; LM)」とは簡単に言うと、文や単語の並びに対する確率を学習し、与えられた文脈から次の言葉を予測する統計的な仕組みです。この確率を計算するためのデータが数十億以上という非常に大量で膨大なものを「大規模言語モデル」と呼ぶようです。
ChatGPT で用いられている「GPT-4」「GPT-3.5 Turbo」などの大規模言語モデルは非公開のため、準備したご家庭のマシンにインストールすることができません。
その一方で、公開されており商用利用も可能な大規模言語モデルとして「Llama 2」や、「ELYZA-japanese-Llama-2-*」があります。本稿ではこちらを使い、ご家庭のマシンで ChatGPT 相当のものが実現できるか見ていきます。
ELYZA-japanese-Llama-2-*
大規模言語モデル ELYZA-japanese-Llama-2-* とは、大規模言語モデル Llama 2 をベースに日本語による追加の事前学習を行ったもので、「ELYZA-japanese-Llama-2-7b」と「ELYZA-japanese-Llama-2-13b」が公開されています。モデル名に含まれている「7b」と「13b」とは何でしょうか? これは 7 billion と 13 billion、つまり 70億と 130億、パラメータ数と呼ばれる係数を示しています。パラメータ数が大きいほど、より優れた性能を発揮する可能性がある一方、より多くの計算資源を必要とします。
パラメータ数と必要な計算資源の量
70億に 130億と、文字通り桁外れの数です。ELYZA-japanese-Llama-2-* が必要とする計算資源はどれくらいでしょうか?
まずファイルサイズを見てみましょう。ELYZA-japanese-Llama-2-7b で約 14GB、ELYZA-japanese-Llama-2-13b で約 26GB と、なかなかのビッグサイズです。大規模言語モデルは、それを保存しておくストレージの大きさも重要になってきます。
次に、必要となる GPU メモリ数はいくらでしょうか? おおざっぱに 7b なら 7GB、13b なら 13GB の GPU メモリが必要というように概算できるそうです。つまり、ご家庭の NVIDIA GeForce GTX 1050 Ti の GPU メモリ 4GB では、ELYZA-japanese-Llama-2-13b はもちろん、ELYZA-japanese-Llama-2-7b も無理そうです。雲行きが怪しくなってきました…。
ちなみに2024年3月現在、GPU メモリ 8GB のカードだと 5万円、16GB だと 15万〜20万円するものもありました。これはなかなか…ご家庭では簡単に手が出ません。クラウドで GPU インスタンスを利用するという手もありますが、ここでは一旦考えないこととします。
大規模言語モデルの量子化とは
少ない GPU メモリ量でも何とかならないでしょうか? そこで大規模言語モデルの「量子化 (Quantization)」という手法を見てみましょう。念のためですが「量子コンピュータ」とは関係ありません。
量子化とは、浮動小数点数で表されているパラメータを、より精度の低い浮動小数点数や整数に変換することです。大規模言語モデルの量子化を行うことにより、計算資源の要件を下げることができ、少ない GPU メモリ量でも利用できるようになるかもしれません。その一方、変換によって大規模言語モデルが持っていた情報の一部が失われることで、性能が劣化する可能性もあります。
なお、大規模言語モデルの量子化自体にも大きな計算資源が必要となることに注意が必要です。つまり、ご家庭のマシンで量子化は難しいと思われるため、あらかじめ量子化された大規模言語モデルを利用することになるでしょう。
量子化の種類
Hugging Face は、GitLab や GitHub のような Git ベースのコードレポジトリサービスを運営しています。先述の ELYZA はもちろんのこと、さまざまな大規模言語モデルをこちらから入手することができます。ELYZA-japanese-Llama-2-7b の量子化モデル も配布されているので、今回はこちらを利用させていただきましょう。
ところで、冒頭に「ggufフォーマット変換版」とあります。「gguf」とは何でしょうか? どうやら大規模言語モデルを扱いやすくするためのファイルフォーマットのようです。何の頭文字かは明記されていないのでよくわかりませんでした。少なくとも gg は作者である Georgi Gerganov 氏のイニシャルのようですが…。
さて Files を開くと、多数のファイルがあります。ファイル名の q* や K* などは何を示しているのでしょうか?
まず、q* の数字は量子化ビット数を表しているようです。q2 なら 2ビット量子化、q8 なら 8ビット量子化となります。この数はあるデータを何段階で表すか、ということを示しています。つまり、2ビットであれば2段階、8ビットであれば8段階です。数が少なければ荒く、多ければ細かいことになります。
次に K や L や M や S は何でしょうか? K は K-quantization (K量子化) 方式であることを示しているようです。では L・M・S は? これはそのまんま Large, Medium, Small の頭文字のようで、大きいものほど量子化の際の情報の劣化が少ないようです。
詳しくは次を参照してください。正直なところあまりよくわかっていないです…。
ファイル名の意味するところはわかったので、どれを選べばよいか考えてみましょう。ご家庭のNVIDIA GeForce GTX 1050 Ti の GPU メモリ 4GB として、ひとまずファイルサイズ 4GB 未満のものを考えてみたらよいでしょうか? となると選択するのは、
- ELYZA-japanese-Llama-2-7b-q4_K_M.gguf (4.09 GB)
- ELYZA-japanese-Llama-2-7b-q4_K_S.gguf (3.86 GB)
- ELYZA-japanese-Llama-2-7b-q3_K_L.gguf (3.6 GB)
- ELYZA-japanese-Llama-2-7b-q3_K_M.gguf (3.3 GB)
- ELYZA-japanese-Llama-2-7b-q3_K_S.gguf (2.95 GB)
のいずれかになると思います。GPU メモリ 4GB を越えている q4_K_M は、収まるかどうかの境界値テスト用として選択しました。
llama.cpp
大規模言語モデルのファイルだけでは動きません。「llama.cpp」から利用しましょう。さまざまなハードウェアで最小の手順・最高のパフォーマンスで大規模言語モデルの推論ができることを目標としているとのことです。ちなみに llamap.cpp は先に出てきた gguf と同じ作者である Georgi Gerganov 氏のものです。
では、ご家庭のマシンに llama.cpp を準備しましょう。llama.cpp は精力的に開発されていることと、gguf (ggml) の実験環境のような位置付け のため、本稿の手順通りには動作しないかもしれません。再度、ご家庭のマシンのハードウェアスペックを見てみます。
- Intel Core i7-2600
- NVIDIA GeForce GTX 1050 Ti (メモリ4GB)
- メモリ 32GB
今回はこちらに次のソフトウェアを準備しました。具体的な準備手順は本稿では省略するので、公式サイト等を確認してください。
- Debian GNU/Linux
- NVIDIA Accelerated Linux Graphics Driver 525.147.05
- CUDA Toolkit 12.0.140
次のようになっています。
% lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux trixie/sid Release: n/a Codename: trixie
% nvidia-smi Wed Mar 13 13:46:02 2024 +-----------------------------------------------------------------------------+ | NVIDIA-SMI 525.147.05 Driver Version: 525.147.05 CUDA Version: 12.0 | |-------------------------------+----------------------+----------------------+ | GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC | | Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. | | | | MIG M. | |===============================+======================+======================| | 0 NVIDIA GeForce ... Off | 00000000:01:00.0 On | N/A | | 40% 36C P0 N/A / 75W | 233MiB / 4096MiB | 3% Default | | | | N/A | +-------------------------------+----------------------+----------------------+ +-----------------------------------------------------------------------------+ | Processes: | | GPU GI CI PID Type Process name GPU Memory | | ID ID Usage | |=============================================================================| | 0 N/A N/A 1182 G /usr/lib/xorg/Xorg 135MiB | | 0 N/A N/A 62987 G ...RendererForSitePerProcess 14MiB | | 0 N/A N/A 63164 G ...on=20240310-180200.292000 80MiB | +-----------------------------------------------------------------------------+
まず、llama.cpp はデフォルト設定でビルドします。詳細は https://github.com/ggerganov/llama.cpp をご覧ください。
% make -j I ccache not found. Consider installing it for faster compilation. I llama.cpp build info: I UNAME_S: Linux I UNAME_P: unknown I UNAME_M: x86_64 I CFLAGS: -I. -Icommon -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -DNDEBUG -std=c11 -fPIC -O3 -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function -Wshadow -Wstrict-prototypes -Wpointer-arith -Wmissing-prototypes -Werror=implicit-int -Werror=implicit-function-declaration -pthread -march=native -mtune=native -Wdouble-promotion I CXXFLAGS: -std=c++11 -fPIC -O3 -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function -Wmissing-declarations -Wmissing-noreturn -pthread -march=native -mtune=native -Wno-array-bounds -Wno-format-truncation -Wextra-semi -I. -Icommon -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -DNDEBUG I NVCCFLAGS: -std=c++11 -O3 I LDFLAGS: I CC: cc (Debian 13.2.0-18) 13.2.0 I CXX: g++ (Debian 13.2.0-18) 13.2.0 (中略)
ビルドが正常に完了しました。
はじめての llama.cpp + ELYZA-japanese-Llama-2-7b-*
では、早速 llama.cpp を実行してみましょう。大規模言語モデルはサイズの大きい ELYZA-japanese-Llama-2-7b-q4_K_M.gguf とし、会話文には「クマベンチ」と呼ばれるものを入力してみます。なお、大規模言語モデルはダウンロード済みであるものとします(以降すべて同様にダウンロード済みです)。
% ./main -m ELYZA-japanese-Llama-2-7b-q4_K_M.gguf -n 256 -e -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが 海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST]' Log start main: build = 2409 (306d34be) main: built with cc (Debian 13.2.0-18) 13.2.0 for x86_64-linux-gnu main: seed = 1710466149 llama_model_loader: loaded meta data with 21 key-value pairs and 291 tensors from ELYZA-japanese-Llama-2-7b-q4_K_M.gguf (version GGUF V2) (中略) llm_load_tensors: ggml ctx size = 0.11 MiB llm_load_tensors: CPU buffer size = 2811.02 MiB (中略) [INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST] 猛暑がやってきた今日この頃…そんな中、クマ君は海へ行きました。何故かと言うと「お母さんに会いたい」からです。しばらく前に家族で突然引っ越しをしてしまったので、それ以来クマ君はお母さんのことが気になって仕方がありません。そういえば幼かった頃、お父さんが「もしクマが海へ行けるようになれば恐らくはお母さんの元に会うことだろう」と言ってました。それを耳にしたクマ君は幼い心に「私が海へ行けるようになれば、間違いなくお母さんの元に会える!」と決意するので llama_print_timings: load time = 8534.41 ms llama_print_timings: sample time = 156.05 ms / 256 runs ( 0.61 ms per token, 1640.47 tokens per second) llama_print_timings: prompt eval time = 17892.86 ms / 104 tokens ( 172.05 ms per token, 5.81 tokens per second) llama_print_timings: eval time = 73987.61 ms / 255 runs ( 290.15 ms per token, 3.45 tokens per second) llama_print_timings: total time = 92132.58 ms / 359 tokens Log end
という結果になりました。ところどころ日本語の文法がおかしかったりしますが、ひとまずはじめての llama.cpp + ELYZA-japanese-Llama-2-7b-* に成功しました! 出力される統計情報は Can someone explain what are the meanings of these timings によると、次を意味します。
- load time : 大規模言語モデルの読み込み時間
- sample time : 次の類似トークンの選択時間
- prompt eval time : 次の新しいテキストを生成する前、LLaMaによるプロンプトの処理時間
- eval time : 出力の生成時間
- total time : 合計時間
eval time に注目すると、出力は 3.45 トークン毎秒となります。本家 ChatGPT は数十トークン毎秒らしいので、まったく比較にもならないのですが…動くことは動きました!
なお、実はこれは GPU を使っていません。llama.cpp を GPU 有効で再ビルドする必要があります。
GPU を使った llama.cpp + ELYZA-japanese-Llama-2-7b-*
ご家庭のマシンは NVIDIA GeForce GTX 1050 Ti であり、CUDA Toolkit 12.0.140 をインストール済みなので、https://github.com/ggerganov/llama.cpp?tab=readme-ov-file#cublas の手順に従い llama.cpp を cuBLAS を有効にして再ビルドします。なお、llama.cpp は開発頻度が極めて高いため、次の手順ではビルドできない可能性もあります。必ず公式ドキュメントを確認してください。
% make clean (中略) % make -j LLAMA_CUBLAS=1 I ccache not found. Consider installing it for faster compilation. I llama.cpp build info: I UNAME_S: Linux I UNAME_P: unknown I UNAME_M: x86_64 I CFLAGS: -I. -Icommon -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -DNDEBUG -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/usr/local/cuda/targets/x86_64-linux/include -std=c11 -fPIC -O3 -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function -Wshadow -Wstrict-prototypes -Wpointer-arith -Wmissing-prototypes -Werror=implicit-int -Werror=implicit-function-declaration -pthread -march=native -mtune=native -Wdouble-promotion I CXXFLAGS: -std=c++11 -fPIC -O3 -Wall -Wextra -Wpedantic -Wcast-qual -Wno-unused-function -Wmissing-declarations -Wmissing-noreturn -pthread -march=native -mtune=native -Wno-array-bounds -Wno-format-truncation -Wextra-semi -I. -Icommon -D_XOPEN_SOURCE=600 -D_GNU_SOURCE -DNDEBUG -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/usr/local/cuda/targets/x86_64-linux/include I NVCCFLAGS: -std=c++11 -O3 -use_fast_math --forward-unknown-to-host-compiler -arch=native -DGGML_CUDA_DMMV_X=32 -DGGML_CUDA_MMV_Y=1 -DK_QUANTS_PER_ITERATION=2 -DGGML_CUDA_PEER_MAX_BATCH_SIZE=128 I LDFLAGS: -lcuda -lcublas -lculibos -lcudart -lcublasLt -lpthread -ldl -lrt -L/usr/local/cuda/lib64 -L/usr/lib64 -L/usr/local/cuda/targets/x86_64-linux/lib -L/usr/lib/wsl/lib I CC: cc (Debian 13.2.0-18) 13.2.0 I CXX: g++ (Debian 13.2.0-18) 13.2.0 I NVCC: Build cuda_12.0.r12.0/compiler.32267302_0 (中略)
ビルドが正常に完了しました。
では、早速実行してみましょう。先程と同様に、大規模言語モデルはサイズの大きい ELYZA-japanese-Llama-2-7b-q4_K_M.gguf とし、会話文には「クマベンチ」と呼ばれるものを入力してみます。
% ./main -m ELYZA-japanese-Llama-2-7b-q4_K_M.gguf -n 256 -e -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが 海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST]' Log start main: build = 2409 (306d34be) main: built with cc (Debian 13.2.0-18) 13.2.0 for x86_64-linux-gnu main: seed = 1710468680 ggml_init_cublas: GGML_CUDA_FORCE_MMQ: no ggml_init_cublas: CUDA_USE_TENSOR_CORES: yes ggml_init_cublas: found 1 CUDA devices: Device 0: NVIDIA GeForce GTX 1050 Ti, compute capability 6.1, VMM: yes llama_model_loader: loaded meta data with 21 key-value pairs and 291 tensors from ELYZA-japanese-Llama-2-7b-q4_K_M.gguf (version GGUF V2) (中略) llm_load_tensors: ggml ctx size = 0.11 MiB llm_load_tensors: offloading 0 repeating layers to GPU llm_load_tensors: offloaded 0/33 layers to GPU llm_load_tensors: CPU buffer size = 3891.24 MiB (中略) [INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST] クマが海辺に行ってアザラシと友達になり、最終的には家に帰る 暑かった夏も、少しずつ秋の気配を感じるころ。 そんな日々のちょっとしたハレの日には、きっといつもよりちょっぴりおめかしして出かけたくなりますね。 今回はそんな秋の「ご賑わい」を楽しむ準備万端なコーディネイトが大人気! スペック上、CPUはCore i5-9600K(3.7GHz)、メモリは8GBです。 グラフィックカードはNVIDIA GeForce GTX1060(6GB)となっています。 実際にゲームをやるのかどうかは置いておき llama_print_timings: load time = 688.76 ms llama_print_timings: sample time = 165.62 ms / 256 runs ( 0.65 ms per token, 1545.75 tokens per second) llama_print_timings: prompt eval time = 1804.20 ms / 104 tokens ( 17.35 ms per token, 57.64 tokens per second) llama_print_timings: eval time = 76791.13 ms / 255 runs ( 301.14 ms per token, 3.32 tokens per second) llama_print_timings: total time = 78862.42 ms / 359 tokens Log end
出力内容はともかく…実行はできました。ところが実は、llama.cpp を cuBLAS 使用でビルドしても、まだ GPU を利用していません。ログの「llm_load_tensors:」部分に注目してください。
llm_load_tensors: offloading 0 repeating layers to GPU llm_load_tensors: offloaded 0/33 layers to GPU llm_load_tensors: CPU buffer size = 3891.24 MiB
これは「33レイヤーのうち、0レイヤーを GPU に任せた」つまり GPU はまったく利用していないということを意味します。つまり、どれだけのレイヤーを GPU に任せるかを指定してやる必要があります。ここでは -ngl (--n-gpu-layers) オプションを指定します。まずは 33 レイヤー全部を任せてみましょう。
% ./main -m ELYZA-japanese-Llama-2-7b-q4_K_M.gguf -n 256 -e -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが 海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST]' -ngl 33 (中略) ggml_backend_cuda_buffer_type_alloc_buffer: allocating 3820.94 MiB on device 0: cudaMalloc failed: out of memory llama_model_load: error loading model: failed to allocate buffer llama_load_model_from_file: failed to load model llama_init_from_gpt_params: error: failed to load model 'ELYZA-japanese-Llama-2-7b-q4_K_M.gguf' main: error: unable to load model
エラーになってしまいました。33 フレーム全部を GPU に任せるには大きすぎるのでしょうか? 少しずつ落としてみたところ、27 フレームで成功しました。
% ./main -m ELYZA-japanese-Llama-2-7b-q4_K_M.gguf -n 256 -e -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが 海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST]' -ngl 27 Log start main: build = 2409 (306d34be) main: built with cc (Debian 13.2.0-18) 13.2.0 for x86_64-linux-gnu main: seed = 1710472498 ggml_init_cublas: GGML_CUDA_FORCE_MMQ: no ggml_init_cublas: CUDA_USE_TENSOR_CORES: yes ggml_init_cublas: found 1 CUDA devices: Device 0: NVIDIA GeForce GTX 1050 Ti, compute capability 6.1, VMM: yes llama_model_loader: loaded meta data with 21 key-value pairs and 291 tensors from ELYZA-japanese-Llama-2-7b-q4_K_M.gguf (version GGUF V2) (中略) llm_load_tensors: ggml ctx size = 0.22 MiB llm_load_tensors: offloading 27 repeating layers to GPU llm_load_tensors: offloaded 27/33 layers to GPU llm_load_tensors: CPU buffer size = 3891.24 MiB llm_load_tensors: CUDA0 buffer size = 3114.57 MiB (中略) [INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST] クマが海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。 それでは… 「クマが海辺に行ったんじゃねえか。」アザラシは大きな目でクマを見つめ、顎を上げる。 クマも口を大きく開けて笑う。 2匹の間には何の感情があるというのだろうか? それはたぶん知りたくないことで、気づいてしまったら自分の心を覗き見させられるようで嫌だ。 「ハリネズミ」は、2018年に放送が決定したテレビアニメ『プロメア』(第一期)の主 llama_print_timings: load time = 1051.79 ms llama_print_timings: sample time = 159.84 ms / 256 runs ( 0.62 ms per token, 1601.57 tokens per second) llama_print_timings: prompt eval time = 870.14 ms / 104 tokens ( 8.37 ms per token, 119.52 tokens per second) llama_print_timings: eval time = 30693.23 ms / 255 runs ( 120.37 ms per token, 8.31 tokens per second) llama_print_timings: total time = 31818.62 ms / 359 tokens Log end
内容はともかく、出力が 8.31 トークン毎秒でした。GPU を使用すると、CPU のみの場合と比較して倍以上の生成速度になっています。しかし、本家 ChatGPT に比べると相当遅いです。
大規模言語モデルは GPU のメモリ 4GB を越える大きさの q4_K_M を選びましたが、GPU に任せるレイヤーを制限することで利用することが可能でした。一番小さな q3_K_S の場合は、次のようにすべてのレイヤーを GPU に任せても動作しました。
% ./main -m ELYZA-japanese-Llama-2-7b-q3_K_S.gguf -n 256 -e -p '[INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが 海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST]' -ngl 33 (中略) llm_load_tensors: ggml ctx size = 0.22 MiB llm_load_tensors: offloading 32 repeating layers to GPU llm_load_tensors: offloading non-repeating layers to GPU llm_load_tensors: offloaded 33/33 layers to GPU (中略) [INST] <<SYS>>あなたは誠実で優秀な日本人のアシスタントです。<</SYS>>クマが海辺に行ってアザラシと友達になり、最終的には家に帰るというプロットの短編小説を書いてください。[/INST] クマが海辺にやってきた。 周りを見渡すと、泳ぐ人も、水中カメラを持ち出して動物の姿を観察する人もいなかった。 それはこの場所が珍しい鳥や動物に会えると信じられていたからだ。 クマは、大きな口でぱくり、周りを見回す。 しかし、あまり生活感のない海辺で、水鳥や魚にも会えずがっかりしていたところへ一匹のアザラシがやってきた。 クマはそれを見てすぐに食べると思い込んだ。 しかし、アザラシは逃げないで、むしろ近づいてくれたのでクマは不安になってしまう。 「もし llama_print_timings: load time = 894.62 ms llama_print_timings: sample time = 160.60 ms / 256 runs ( 0.63 ms per token, 1593.99 tokens per second) llama_print_timings: prompt eval time = 1058.72 ms / 104 tokens ( 10.18 ms per token, 98.23 tokens per second) llama_print_timings: eval time = 24061.30 ms / 255 runs ( 94.36 ms per token, 10.60 tokens per second) llama_print_timings: total time = 25380.52 ms / 359 tokens Log end
生成時間もトークン毎秒も向上しました。内容に関しては…どっちもどっちという気がします。
所感
最初に述べたモチベーションについてそれぞれどうであったか見ていきましょう。
ご家庭にある数世代前のマシンで対話型生成AIを動作できるか
公開されている大規模言語モデルである「ELYZA-japanese-Llama-2-*」や、それらを利用できるソフトウェア llama.cpp のおかげで、ご家庭のマシンで対話型生成AIを動作させることが可能ということがわかりました。
ただし、元の大規模言語モデルをそのまま使うことはできなかったため、量子化を施したり、処理を部分的に GPU に任せるというテクニックを使う必要がありました。
生成速度はどのくらいか
Intel Core i7-2600 (3.4GHz) あるいは NVIDIA GeForce GTX 1050 Ti (メモリ 4GB) では出力は約 3〜8 トークン毎秒と、本家 ChatGPT の数十トークン毎秒の10分の1前後と見られます。動かした直後は「動いた! すごい!」という感じで速度は気にならないかもしれませんが、すぐに「ChatGPT に比べて遅い、遅すぎる…」と我慢できなくなってくると思います。
生成結果はどのようなものか
本稿では「クマベンチ」という入力を行ってみました。短編小説らしいものを生成することもあれば、意図から外れた方向に話が進んでしまっていることが多いという印象でした。本稿に掲載した例も、いくつか生成したものの中から比較的まともなもの・穏当なものを選んでいます。そのまま載せるには憚られるような過激な内容が多かったため…。
なお、おかしな結果が生成されることが量子化による情報の劣化が原因かどうか、はっきりとしたことは不明です。ご家庭のマシンでは量子化の有無による比較が困難なためです。量子化を行わない ELYZA-japanese-Llama-2-13b が乗るであろう 16GB のメモリを搭載したグラフィックカードは2024年3月当時15〜20万円といった価格で、即決が難しい金額だからです。
結論として「使える」のか
ご家庭にある数世代前のマシンでは対話型生成AIを 体験 できる、くらいだと思われます。本稿で見てきた通り、体験するだけでも対話型生成AIに関するさまざまな基礎知識を得ることができるため有用とは言えますが、非力なご家庭のマシンでは実用は難しいと考えます。
「ローカルLLM」の意義
ご家庭のマシンで対話型生成AIを動かすのはなかなか難しそうです。にも関わらず、ローカルで実行することの意義は何でしょうか?
コスト
「グラフィックカードが高い」と書きましたが、これは事前に計算可能なものです。一度購入してしまえば基本的にそれで済みます。
オンラインの有料サービスも価格が公開されているならば、同様に事前に予測を立てることができるでしょう。しかし、突然の値上げなどが有り得るように、将来にわたるすべてを計算することは難しいです。
なお、「ローカル」がクラウド上である場合もコストに関しては同様の議論があるでしょう。一方で、インスタンスのリソースを変更する・クラウド事業者そのものを変更するなど、利用者側でコストを制御できる余地は大きいと考えます。
セキュリティ
データのやりとりがローカルで完結するため、盗聴などのおそれがありません。サービス側から漏洩することもありません。
もちろん、「ローカル」がクラウド上である場合は別の議論があります。
プライバシー
プライベートな内容を、他者への応答のための学習に利用される心配事なしに入力できることは大きいでしょう。ここにローカルの価値の大半を置くことができそうです。
確からしさ
サービス提供側の自主規制などで入出力を操作されることもありません。本稿の執筆者のように「この結果は公開できないな…」「この結果は公開してもよい」という判断は、利用者側が持ちます。自主規制でなくとも、モデルのバージョンアップや調整などで生成結果が変わってしまう・再現性が失われることも有り得ますので、モデルのバージョンを自身で制御できるのも強みです。
また、突飛な話かもしれませんが、「生成AIは本当は人間が手動で応答を生成しているのだ」という陰謀論とも無縁でしょう。
カスタマイズ性
本稿では ELYZA-japanese-Llama-2-7b を量子化したものを利用しました。大規模言語モデルはさまざまなものが公開されており、その中から自由に選択することが可能です。追加学習などを行う場合も、各サービスの都合によらず、自由に行えるでしょう。
まとめ
このように、ご家庭にある数世代前のマシンで対話型生成AIを動作できるか、ということを通して、動作はできるが実用は難しい、だが実行することの意義は大きい、ということを見てきました。実用とまではいかなくても、実際に動かしてみることで得られる知識は多数あります。それで得られた知識は、業務用の現役世代のマシン上で活かしていくことができるでしょう。
クリエーションラインでは今後も生成AIに関する調査や情報発信を続けていきたいと思います。
生成AIについて興味関心があり、
ご相談があれば以下よりお問い合わせください