2023年03月23日追記
この投稿での事例は、1トークンの推論に約1分を要する。とにかく動作させてみることを念頭にしている。swapを用いず、小規模なモデルをオン・メモリで動作させることで、実用に適うパフォーマンスで動作させることができる。そのような事例を、次の投稿で紹介している。関心があれば参照してほしい。
impsbl.hatenablog.jp
本文
いわゆる「AI」をPCで運用するには、GPUとVRAMをはじめとする潤沢な計算リソースが求められる。"ggerganov/ggml"*1を利用すると、GPT (Generative Pre-trained Transformer)のように大規模言語モデルに基づいた推論を、普及機レベルのPCでも動かすことができる。
とはいえ最初に触れておくと、この投稿で紹介している方法では、動作はしているものの、実用的なものではない。先日、Raspberry Piで動作させたというTwitterを見かけた*2。このパフォーマンスが、1トークン当たり10秒であるのに対して、この投稿の環境では1トークン当たり1分を要している。動きはするが、はるかに遅いのだ。ちなみにggerganov氏のMacBookでは125ミリ秒だ。
On my 32GB MacBook M1 Pro, I achieve an inference speed of about 125 ms/token or about ~6 words per second
ggml/examples/gpt-j at master · ggerganov/ggml · GitHub
"ggerganov/ggml"に含まれる"gpt-j"は、16GB RAMを要求される。実用性は置いて、動作させるだけならば8GB RAMを搭載したPCでも、8GB swapを活用することで動作させることができる。とりあえず動かし、そのパフォーマンスを確認するのが、今回の投稿だ。
次のスペックの環境で検証している。
OS | Clear Linux 38590*3 |
CPU | Intel Core i5-8250U |
RAM | 8GB |
Storage | SSD: 256GB |
ggmlのビルド、インストール
cmakeとmakeを搭載しているLinux環境であれば、簡単にggmlをビルド、インストールできる。GitHubページに掲載されているように、
を順番にコマンド実行すればよい。"ggml"をホーム・ディレクトリにダウンロードし、"gpt-j"だけをビルドするには、次のコマンドを実行する。"gpt-j"がディレクトリ"~/ggml/build/bin/へ出力される。
cd ~ git clone https://github.com/ggerganov/ggml cd ggml mkdir build cd build cmake .. make -j4 gpt-j
GPT-J 6Bモデルのダウンロード
"gpt-j"を実行するにはGPTモデルが必要だ。そのダウンロードに用いる"download-ggml-model.sh"が用意されているのだが、この投稿では使用しない。このモデルのサイズは12GBあり、馴染みの方法で直接ダウンロードしたほうが良いと思うのだ。Hugging Faceから"ggml-model-gpt-jt-6B.bin"を直接ダウンロードすることにした。ダウンロードしたモデルは、"~/ggml/build/bin/"へ保存する。
swapの作成
GitHubに記載されているように、GPTモデルをメモリ上へロードするために、"gpt-j"は16GB RAMを要求する。
No video card required. You just need to have 16 GB of RAM.
ggml/examples/gpt-j at master · ggerganov/ggml · GitHub
必要なメモリ領域を確保できなければ、segmentation faultによりプログラムが終了する。このような事態を避けるために、swap領域を作成する。次のコマンドで、スワップ・ファイル"swap.img"を"~/ggml/build/bin/"に作成している。
sudo dd if=/dev/zero of=~/ggml/build/bin/swap.img bs=1M count=8096 sudo chown 0:0 ~/ggml/build/bin/swap.img sudo chmod 600 ~/ggml/build/bin/swap.img sudo mkswap ~/ggml/build/bin/swap.img sudo swapon ~/ggml/build/bin/swap.img
"gpt-j"の実行
"gpt-j"を実行する前に、そのオプションの一部について触れておく。これらは"examples/utils.cpp"に定義されている。
-m | GPTモデルのパス |
-n | トークン数token 言うなれば、返答の単語数 |
-p | プロンプト |
-t | 処理スレッド数 |
この投稿では"gpt-j"とモデルは同じパスに保存されている。従って、典型的なコマンドは
cd ~/ggml/build/bin ./gpt-j -m ggml-model-gpt-j-6B.bin -p "hello"
処理パフォーマンスを比較するため、異なるオプションで実行する。
./gpt-j -n 5 -t 4 -m ggml-model-gpt-j-6B.bin -p "hello" ./gpt-j -n 5 -t 8 -m ggml-model-gpt-j-6B.bin -p "hello" ./gpt-j -n 10 -t 4 -m ggml-model-gpt-j-6B.bin -p "hello"
処理スレッド数を増やすと、トークン当たりの予測時間を短縮できる。一方、トークン数が予測時間に影響を与えることはない。検証機は、トークン一つの予測に約1分を要しているのが分かる。
デフォルト設定でコマンド実行した場合、その終了までに約4時間を要した。
./gpt-j -m ggml-model-gpt-j-6B.bin -p "hello"
🔎result of the commands above
Google Colabで実行する
"gpt-j"はGoogle Colab*4でも実行できるのだが、そのプロセスは開始数分で強制終了される。
!git clone https://github.com/ggerganov/ggml %cd /content/ggml %mkdir build %cd build !cmake .. !make -j4 gpt-j !../examples/gpt-j/download-ggml-model.sh 6B !./bin/gpt-j -m models/gpt-j-6B/ggml-model.bin -p "who are you?" !./bin/gpt-j -m models/gpt-j-6B/ggml-model.bin -p "Do you know what day today is?"
余談 - Apple M1 CPU、GPUの併用と限界
ggerganov氏がggmlを記述した動機は、自身のMacBookでモデル推論を動作させることだった。特に「Implementation details」で語られていることが興味深い。ARM NEONによる128bit演算がM1 CPU上で動作する。同時に、M1 GPUに対して一部の処理を任せようとしてもパフォーマンスが向上しないのだという。
その理由として、M1 CPUとM1 GPUが共有するメモリ空間とチャネルにて、特にCPU側とのやり取りでメモリ帯域が占有されていたからではないか、と推測されている。さらに、CPUとGPUにパフォーマンス上の違いがないこともあり、併用しても意味がないことにも触れられている。
However, to my surprise, using MPS together with the CPU did not lead to any performance improvement at all. My conclusion was that the 8-thread NEON CPU computation is already saturating the memory bandwidth of the M1 and since the CPU and the GPU on the MacBook are sharing that bandwidth, it does not help to offload the computation to the GPU. Another observation was that the MPS GPU matrix multiplication using 16-bit floats had the same performance as the 8-thread NEON CPU implementation. Again, I explain this with a saturated memory channel. But of course, my explanation could be totally wrong and somehow the implementation wasn't utilizing the resources correctly.
In the end, I decided to not use MPS or the GPU all together.
*2: I've sucefully runned LLaMA 7B model on my 4GB RAM Raspberry Pi 4. It's super slow about 10sec/token. But it looks we can run powerful cognitive pipelines on a cheap hardware. pic.twitter.com/XDbvM2U5GY
twitter.com