🦉

llama.cpp の動かし方と量子化手法

2023/12/17に公開

はじめに

Turingアドベントカレンダー17日目です!今日は Research チームの柏谷が担当します。

Research チームでは、LLMによる完全自動運転を実現するための技術開発を行っています。その中で重要な技術の1つが量子化です。量子化によって少ビットでパラメータを表現できれば、LLM の膨大なパラメータのデータ圧縮が可能となります。量子化実装はいろいろと考えられますが、今回は実装にアクセス可能な llama.cpp とその量子化技術について見ていきましょう!

llama.cpp とは Georgi Gerganov さんが作った PC の CPU だけで LLM が動くプラットフォームです。その名の通り Llama, Llama2 が動くというだけでなく Bloom, StableLM などいくつかの LLM がサポートされています。LLM は従来、データセンターで大量のGPUリソースのもとで動くのですが、llama.cpp はパラメータ数が比較的小さい LLM に限られますが、それをハードウェアリソースが限られている PC 上で動くというなかなかのスグレモノです。しかもそこそこ使える速度です。本記事では前半で llama.cpp の動かし方について説明します。

後半では llama.cpp の量子化について説明します。仮に7BモデルのパラメータをFP32で構成したとするとパラメータだけで28GB占有してしまいます。これを克服する重要な技術が量子化です。llama.cpp では 2, 3, 4, 5, 6, 8bit 量子化をサポートしており、4bit量子化だと Llama2-7B モデルのサイズはわずか4GB程度です。

まずは llama.cpp をPC上で動かして、次に量子化手法について見ていきましょう!

llama.cpp で Llama2-7B を動かしてみる!

環境構築とインストール

Windows の WSL 環境で説明します。WSL が使える場合、build-essential をインストールするだけです。

$ sudo apt install build-essential

次にリポジトリのクローンとビルドです。

$ git clone https://github.com/ggerganov/llama.cpp
$ cd llama.cpp
$ make

これで、llama.cpp 直下に実行ファイル main ができているはずです。これだけで、あっけなくビルドは完了です。

モデルの用意

次にモデルもダウンロードしてきます。便利なことに HuggingFace 上に量子化済みファイルがあります。ここから対象となるモデルをダウンロードします。ファイル名で “q4_K_S”, “q2_K” などと書いてあるのは量子化の型です。型についてはこちらを参照してください。 ダウンロードしたファイルは「 llama.cpp/models 」に置きます。2023年12月13日現在、上記の場所に置いてあるモデルファイルは GGMF 型式です。現在 llama.cpp は GGUF 型式に移行しており、この型しか動きません。しかし、llama.cpp には変換ツールがいっしょに入っていますので、これを以下のように入力して実行します。

$ python3 convert-llama-ggml-to-gguf.py --input [input file name] --output [output file name]

例えば次のように入力して変換します。

$ python3 convert-llama-ggml-to-gguf.py --input ./models/llama-2-7b-chat.ggmlv3.q8_0.bin output ./models/llama-2-7b-chat.ggmlv3.q8_0.gguf

いよいよ実行!

実行は簡単です。main を実行して、-m でモデルを指定し、-p で指示を与えます。量子化の型ごとに実行させます。

8bit 型 (q8_0)

最初に 8bit 量子化モデルから実行させて行きましょう。8bit 型は精度劣化がほとんどないので比較の基準になります。Llama2-7B 8bit 量子化モデルに品川駅から東京駅への行き方を聞いてみましょう!

$ ./main -m ./models/llama-2-7b-chat.ggmlv3.q8_0.gguf --temp 0.1 -p "### Instruction: Tell me how to get to Tokyo station from Shinagawa station. ### Response:”

Instruction: Tell me how to get to Tokyo station from Shinagawa station. ### Response: To get to Tokyo Station from Shinagawa Station, you can take the JR Yamanote Line train bound for Tokyo Station. From Shinagawa Station, exit through the north side of the station and follow the signs to the JR Yamanote Line platform. Once on the train, take any train headed towards Tokyo Station. The journey takes approximately 15 minutes. Alternatively, you can also take the Keikyu Line train bound for Tokyo Station from Shinagawa Station. This journey takes around 20 minutes. Please note that some trains may not stop at Shinagawa Station, so be sure to check the train schedule in advance. [end of text]

Google 日本語訳です。

説明: 品川駅から東京駅への行き方を教えてください。 ### 回答: 品川駅から東京駅に行くには、JR 山手線の東京駅行きに乗ります。 品川駅からは駅の北側を出て、標識に従ってJR山手線ホームへ向かいます。 電車に乗ったら、東京駅行きの電車に乗ります。 所要時間は約15分です。 または、品川駅から京急線の東京駅行きに乗車することもできます。 所要時間は約 20 分です。 なお、電車によっては品川駅に停車しない場合もありますので、事前に電車の時刻表をご確認ください。 [本文終わり]

微妙な感じで正しくないところもありますが、そこそこ意味をくみ取ってくれています。これが 7B モデルのベースの結果と考えてください。この 8bit の q8_0 型のサイズは 6.67GiB で、私のノートPC(Windows 11 WSL, Core -7 1360P, 32GB 以下、同じ環境) 上での実行時間は約 38秒です。

4bit 型 (q4_K_M)

次に 4bit 量子化 q4_K_M 型で実行してみましょう。

$ ./main -m ./models/llama-2-7b-chat.ggmlv3.q4_K_M.gguf --temp 0.1 -p "### Instruction: Tell me how to get to Tokyo station from Shinagawa station. ### Response:”

Instruction: Tell me how to get to Tokyo station from Shinagawa station. ### Response: Sure! To get to Tokyo Station from Shinagawa Station, you can take the JR Yamanote Line train. From Shinagawa Station, exit through gate 4 and follow the signs to platform 10. Take the train bound for Tokyo Station, and the journey should take around 15 minutes. Alternatively, you can also take the Keikyu Line train from Shinagawa Station to Tokyo Station, which takes around 20 minutes. Please note that some trains may not run directly between these stations, so be sure to check the schedules beforehand. [end of text]

Google 日本語訳です。

説明: 品川駅から東京駅への行き方を教えてください。 ### 応答: もちろんです! 品川駅から東京駅へはJR山手線を利用します。 品川駅からは4番ゲートを出て、標識に従って10番線ホームへ。東京駅行きに乗車し、所要時間は約15分です。 また、品川駅から東京駅まで京急線を利用することもでき、所要時間は約 20 分です。 なお、一部の列車は両駅間を直通運転しない場合がありますので、事前に時刻表をご確認ください。 [本文終わり]

この型の結果も微妙ですが、同様にそこそこ意味をくみ取ってくれています。8bit の結果と比べると、あまり劣化している感じはないかと思います。この q4_K_M 型でのサイズは 3.8GiB で、ノートPCでの実行時間は約21秒です。4bit 化にともなって、演算回数という点では増えているのですが、パラメータサイズが小さくなっているので、キャッシュにヒットしやすくなっていると考えられます。

2bit 型 (q2_K)

最後に 2bit 量子化 q2_K 型です。はたして 2bit で機能するのでしょうか?

./main -m ./models/llama-2-7b-chat.ggmlv3.q2_K.gguf --temp 0.1 -p "### Instruction: Tell me how to get to Tokyo station from Shinagawa station. ### Response:”

Instruction: Tell me how to get to Tokyo station from Shinagawa station. ### Response: Sure, I'd be happy to help! To get from Shinagawa station to Tokyo station, you have several options:

  1. JR Train: You can take a JR train from Shinagawa station to Tokyo station. The journey takes around 20-30 minutes, depending on the train type and the time of day. From Tokyo station, you can transfer to other JR trains or subways to reach your destination.
  2. Keikyu Line: You can also take the Keikyu line from Shinagawa station to Tokyo station. This journey takes around 15-20 minutes. From Tokyo station, you can transfer to other Keikyu trains or subways to reach your destination.
  3. Tokyo Metro: If you prefer to use the Tokyo Metro, you can take the Yoden-Rapid train from Shinagawa station to Tokyo station. This journey takes around 20-30 minutes. From Tokyo station, you can transfer to other Tokyo Metro trains or subways to reach your destination.
  4. Tokyo Waterfront Line: Another option is to take the Tokyo Waterfront Line from Shinagawa station to Tokyo station. This journey takes around 15-20 minutes. From Tokyo station, you can transfer to other Tokyo Metro trains or subways to reach your destination.

Google 日本語訳です。

説明: 品川駅から東京駅への行き方を教えてください。 ### 回答: はい、喜んでお手伝いさせていただきます。 品川駅から東京駅まで行くには、いくつかの方法があります。

  1. JR 電車: 品川駅から東京駅まで JR 電車をご利用いただけます。 所要時間は列車の種類や時間帯にもよりますが、約20~30分程度です。 東京駅からは、JRや地下鉄を乗り継いで目的地まで行くことができます。
  2. 京急線: 品川駅から東京駅まで京急線を利用することもできます。 所要時間は約 15 ~ 20 分です。 東京駅からは、他の京急電車や地下鉄に乗り換えて目的地まで行くことができます。
  3. 東京メトロ: 東京メトロをご利用の場合は、品川駅から東京駅まで予電快速電車に乗車できます。 所要時間は約 20 ~ 30 分です。 東京駅からは、東京メトロの他の電車や地下鉄に乗り換えて目的地まで行くことができます。
  4. 東京臨海線: 品川駅から東京駅まで東京臨海線に乗るのも選択肢です。 所要時間は約 15 ~ 20 分です。 東京駅からは、東京メトロの他の電車や地下鉄に乗り換えて目的地まで行くことができます。

京急(Keikyu)、東京メトロに加えて、東京臨海線(Tokyo Waterfront Line) という謎の路線まで登場しますが(笑)、文の構造自体や回答としての方向性は維持しています。q2_K 型でのサイズは 2.67GiB です。実行時間は約46秒と長くなっています。
このように 4bit くらいであれば、元の 8bit の精度からはそれほど劣化していない感じです。2bit でも用途次第では使えるかもしれません。もちろん、きちんとした精度評価は必要です。このように大幅にパラメータサイズを減らせる量子化の仕組みはどうなっているのでしょうか。llama.cpp の量子化手法を詳しく見ていきましょう!

llama.cpp と GGML, GGUF について

冒頭で書きましたが、llama.cpp を開発したのが Georgi Gerganov(GG) さんです。この GG さんが開発した ML(Machine Learning) ライブラリが GGML です。llama.cpp は GGML を使って実装されています。GG さん。すごい方ですね。llama.cpp だけでなく、ML ライブラリまで作ってます。この GGML は 量子化された Tensor を扱う型定義と、様々な量子化関数(内積計算など)や ML 向けの softmax など不可欠な関数を多数扱えます。GGML はC言語で書かれており、FP16 をサポートする他、2,3,4,5,6,8 bit の量子化型をサポートします。加えて、Apple の M1/M2/M3 シリーズの CPU (ARM64 arch) の SIMD 演算命令(NEON) や、Intek x86 の SIMD 演算命令 (AVX/AVX2) も最適化実装しており、CPU 上で高速に実行するための様々な工夫が折り込まれています。
GGML は ML ライブラリであると同時にフォーマットの意味でもあります。llama.cpp で動かす場合は GGML フォーマットでモデルが定義されている必要があるのですが、llama.cpp は GGML をベースにさらに拡張性を高めた GGUF フォーマットに2023年8月に移行しました。これ以降、llama.cpp で LLaMA 以外の LLM も動くようになってきました。
GGUF は GGML を使った推論用のモデルと Executor を保存するためのバイナリフォーマットです。現時点(2023/12)での最新 llama.cpp ではモデルのフォーマットは GGUF でないと動きません。

GGML 量子化型

いよいよGGML で定義されている量子化の手法について見ていきましょう。

まず、図1-1 を見てください。これは fp32 で定義されたパラメータを int4 に変換するイメージです。実際に出現するデータの範囲(この図だと -2.9~3.2)をみて、それを int4 の範囲に最大限にスケールすることを考えます。この場合、4bit だと16段階以上にはなりませんし、正負で領域が非対称(-8~7)、かつ正負のどちらかにかたよっていると、例えばパラメータが全て正だとすると、負の領域は未使用で情報量的に有効利用されていません。これはあまりいい手法とは言えないでしょう。
そこで、図1-2 のように int4 ではなく uint4 にするとどうなるでしょう。変換前のパラメータの最小値(図の場合 -2.9)を0,最大値(図の場合 3.2)を15 とすれば、4bit の最大限の表現力を使えます。
この考え方をあるデータ(パラメータ)全体ではなく、ある一定のかたまりごとに定義することにすると、より 4bit の情報をきめ細かく扱えます。パラメータ全体でみるとばらついていても、ある一定量で区切るとデータが偏っていることも多いのです。図1-3 で説明します。この図ではあるデータのまとまりを block、その block がいくつか集まって super-block を形成します。このようなときに super-block での最小値をゲタとして定義し、そこを基準に super-block での範囲をスケールさせます。ここまでだと、図1-2 と同じです。ここからさらに super-block よりも小さいデータのまとまりである block に対して、2段階でスケールをかけていくことにします。そうするとより細かく 4bit の幅を定義できます。
GGML では以上の考え方で 4bit + α の付加データを持たせることにより精度を上げています。GGML ではどの型も super-block は 256個単位ですが、block の個数は型によって異なります。Q4_K (4bit) では 32個ですが、Q2_K (2bit), Q3_K (3bit) では 16 個で、情報量が少ない分、block をきめ細かくして付加ビットを増やしています。一方、Q8_K では block 自体がありません。super-block の一階層のみです。

Q4_K 型 (4bit)

次に具体例をとって詳しく見ていきましょう。

これは Q4_K 型の構造体定義です。qs というのが 4bit データを2つ uint8_t 型にパッキングして、合計256 個 (super-block)分のデータを配列で持っています。d は図1-3 の super-block のスケールで、scales となっているのが、block の scale と min(ゲタ)をパッキングしたデータです。scale と min は 6bit で、これを super-block を構成する block 8個に対してそれぞれ定義しています。少しわかりにくいですが、uinit8_t scales[12] は次のようにパッキングされています。

このような感じで、6bit x 2 (scale と min) x 8 = 96 bit = 12 byte がこのようにデータが詰め込まれています。次に、この構造体を図示すると次のようになっています。

データを32個まとめて扱い block とし、block ごとに min と scales の 6bit データを持っている。そして、block を8個集めて super-block を構成し、super-block で dmin(ゲタ)と d(スケール)を持っている。という構造です。
ではこれをもとにどのようにして量子化データが作られているのか、それはこの量子化型からどのようにしてデータをもとのデータに復元できるかを見ればわかりやすいと思います。図示すると次のようになります。

このような復元過程を経て元のデータになります。量子化 4bit データは左上の qs です。ここから構造体内にある scales, d, mins, dmin を図のように計算します。
以上、見てきたように量子化 Q4_K 型は 256 個のデータをまとめて扱うのですが、1要素あたりにするとどの程度のデータサイズになるのでしょうか。構造体のサイズを見ると、4(qs) x 256 + 6(scale, min) x 2 x 8 + 16 (d) + 16 (dmin) = 1152 bit です。これを要素数 256 で割ると 1152/256 = 4.5 (bit / element) となります。4bit 量子化というのは各要素 4bit に 0.5bit の付加 bit をつけることにより、精度を上げているといえます。

Q2_K 型 (2bit)、Q3_K 型 (3bit)

こちらは Q4_K よりも要素あたりの情報量が少ないので super-block のサイズは同じですが、block サイズは 16 個として、よりきめ細かくスケールさせようとしています。定義は次のようになっています。

Q2_K の要素あたりのデータサイズは (2 x 256 + 4 x 2 x 16 + 16 + 16) / 256 = 2.625 bit / element となります。
同様に Q3_K では (256 x 3 + 6 x 16 + 16) / 256 ≒ 3.44 bit / element です。

この中では Q8_K 型は特殊です。スケール、ゲタでは block という階層がなく、super-block という単位でスケール値を持っているだけです。Q8_K は中間表現と内積計算のときに使われる型で、そのため llama.cpp 内部の計算過程では頻出します。また、計算を早くするために bsums という block(ここでは 16個)単位で合計値を持っています。これにより、他の型との計算を早くできるようになっています。要素あたりのサイズはそれぞれ次の通りです。
Q5_K 型:(5 x 256 + 6 x 2 x 8 + 16 + 16) / 256 = 5.5 bit / element
Q6_K 型:(6 x 256 + 8 x 16 + 16) / 256 ≒ 6.56 bit / element

k-quant:llama.cpp での量子化処理

これまで見てきたように、GGML の量子化関数だけを使って、量子化や各量子化型の演算ができます。では、llama.cpp の量子化手法 k-quant とは何を行っているのでしょうか。llama.cpp のサイトに説明があります。4bit 量子化だけでも Q4_K_M と Q4_K_S があります。これはどう違うのでしょうか。Q4_K_S は基本的に全ての Tensor に対して、Q4_K を使っているのですが、Q4_K_M は attention.wv と feed_forward.w2 の Tensor の半分に Q6_K を使っています。これは出力に関係するところは精度を高めようということなのでしょう。実際に LLaMA2 での Layer 処理を見ていきます。llama.cpp 上で、LLaMA2-7B-Q4_K_M を実行させると、各 Layer のログが出てきます。

llama_model_loader: - tensor    0:                token_embd.weight q4_K     [  4096, 32000,     1,     1 ]
llama_model_loader: - tensor    1:               output_norm.weight f32      [  4096,     1,     1,     1 ]
llama_model_loader: - tensor    2:                    output.weight q6_K     [  4096, 32000,     1,     1 ]
llama_model_loader: - tensor    3:              blk.0.attn_q.weight q4_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor    4:              blk.0.attn_k.weight q4_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor    5:              blk.0.attn_v.weight q6_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor    6:         blk.0.attn_output.weight q4_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor    7:           blk.0.attn_norm.weight f32      [  4096,     1,     1,     1 ]
llama_model_loader: - tensor    8:            blk.0.ffn_gate.weight q4_K     [  4096, 11008,     1,     1 ]
llama_model_loader: - tensor    9:            blk.0.ffn_down.weight q6_K     [ 11008,  4096,     1,     1 ]
llama_model_loader: - tensor   10:              blk.0.ffn_up.weight q4_K     [  4096, 11008,     1,     1 ]
llama_model_loader: - tensor   11:            blk.0.ffn_norm.weight f32      [  4096,     1,     1,     1 ]
llama_model_loader: - tensor   12:              blk.1.attn_q.weight q4_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor   13:              blk.1.attn_k.weight q4_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor   14:              blk.1.attn_v.weight q6_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor   15:         blk.1.attn_output.weight q4_K     [  4096,  4096,     1,     1 ]
llama_model_loader: - tensor   16:           blk.1.attn_norm.weight f32      [  4096,     1,     1,     1 ]
llama_model_loader: - tensor   17:            blk.1.ffn_gate.weight q4_K     [  4096, 11008,     1,     1 ]
llama_model_loader: - tensor   18:            blk.1.ffn_down.weight q6_K     [ 11008,  4096,     1,     1 ]
llama_model_loader: - tensor   19:              blk.1.ffn_up.weight q4_K     [  4096, 11008,     1,     1 ]
...

このログの中で、q6_K となっている Layer が Q6_K になっている Layer です。LLaMA2-7B では全体で 291 Layer で構成されていますが、33 Layer に Q4_K のかわりに Q6_K が使われています。このように精度が求められる Layer に効果的により上位の量子化型を使っているのです。

まとめと今後

以上、みてきたように llama.cpp, GGML で使われている量子化処理はLLM のパラメータのデータサイズを削減するのに効果的です。
一方、冒頭述べたように LLM はデータセンターで大量のGPUリソースのもとで動くものです。量子化でパラメータのデータサイズを削減できたとしても、LLM を自動車のような Edge 環境で実現可能なのかという課題があります。そこで Turing では LLM 推論アクセラレータの開発を行います。
https://twitter.com/issei_y/status/1722537248867172466
https://twitter.com/issei_y/status/1723206269018984499

採用情報

Turing では自動運転モデルの学習や、LLM 推論アクセラレータの開発など、完全自動運転の実現に必要な様々な技術開発を行っています。興味がある方は、Turing の公式 Web サイト採用情報などをご覧ください。話を聞きたいという方はCTOの青木さんの X(旧Twitter) DMや採用ページの応募フォーム からでもお気軽にご連絡ください。

Tech Blog - Turing

Discussion