機械学習
記事内に商品プロモーションを含む場合があります

GoogleのGemmaをmacbook air(M2)で動かしてみる|大規模言語モデルを動かす

tadanori

Google GemmaをMacbook Air(M2, 16GB)で動かしてみました。利用したのは、gemma-2bです。試行錯誤した結果、動くには動きましたが、実用的な速度では動きませんでした。

大規模言語モデル関連記事一覧はこちら
大規模言語モデル(LLM)関連の記事一覧
大規模言語モデル(LLM)関連の記事一覧

はじめに

Gemmaは、2024年2月21日に発表されたGoogleが開発した大規模言語モデル(LLM)です。AI開発者や研究者による商用利用や再配布が可能なオープンソースとして提供されています。

llama.cppがgemmaにも対応したので早速動かしてみました。

結論としては、Macbook Air(M2, 16GB)で動作させるのは、速度的にかなり厳しいです。

大規模言語モデル(LLM)をCUDAで動かす場合はこちらの記事が参考になると思います

大規模言語モデル(Llama2/Gemma/Calm2)を動かしてみる(使い方)
大規模言語モデル(Llama2/Gemma/Calm2)を動かしてみる(使い方)

M1/M2の性能に興味がある場合、Macbook Air(M2)でYOLOv8を動かした結果も参考になるかもしれません

M1とM2 MacのGPUの実力をチェック|実行環境を構築し、YOLOv8(学習)でベンチマークしてみた
M1とM2 MacのGPUの実力をチェック|実行環境を構築し、YOLOv8(学習)でベンチマークしてみた

手順

以下、Gemma-2bをダウンロードして動作させるまでの手順です。Gemma-2bをMacで動かすには、lamma.cppを利用します。

lamma.cppをダウンロード

githubからclone

lamma.cppをcloneします。

Lamma.cpp
引用元:https://github.com/ggerganov/llama.cpp
git clone https://github.com/ggerganov/llama.cpp

ダウンロードした、llama.cppのフォルダに移動してコンパイルします。

Macで動作させるには、LLAMA_METAL=1を加えてmakeしておきます。

cd llama.cpp
LLAMA_METAL=1 make

gemma-2bをダウンロード

IDとパスワードを聞かれたら、HuggingFaceのID(メールアドレス)と、パスワードのかわりにアクセストークンを入力。

アクセストークンは以下から入手します。
https://huggingface.co/settings/tokens

cd models
git clone https://huggingface.co/google/gemma-2b

動作テスト

gemma-2bには、llama.cppで利用できるgemma-2b.ggufが含まれているので変換する必要はありません。

動作確認には、以下のコマンドを実行します

エラーが出る場合は-ngl 18を小さな値に書き換えてみてください。小さな値にすると遅くなります。

./main -m models/gemma-2b/gemma-2b.gguf -ngl 18 -n 64 -p "Who is Einstein?"

実行はかなり遅いです。上記のコマンドの実行では、出力が完了するまで10分くらいのかかりました。

原因は、-ngl 18とすべての層をGPUで実行できていないからです。

実行結果

 % ./main -m models/gemma-2b/gemma-2b.gguf -ngl 18 -n 64 -p "Who is Einstein?"
Log start
main: build = 2297 (052051d8)
main: built with Apple clang version 15.0.0 (clang-1500.1.0.2.5) for arm64-apple-darwin23.3.0
main: seed  = 1709296895
llama_model_loader: loaded meta data with 19 key-value pairs and 164 tensors from models/gemma-2b/gemma-2b.gguf (version GGUF V3 (latest))
llama_model_loader: Dumping metadata keys/values. Note: KV overrides do not apply in this output.
llama_model_loader: - kv   0:                       general.architecture str              = gemma
llama_model_loader: - kv   1:                               general.name str              = gemma-2b
llama_model_loader: - kv   2:                       gemma.context_length u32              = 8192
llama_model_loader: - kv   3:                          gemma.block_count u32              = 18
llama_model_loader: - kv   4:                     gemma.embedding_length u32              = 2048
llama_model_loader: - kv   5:                  gemma.feed_forward_length u32              = 16384
llama_model_loader: - kv   6:                 gemma.attention.head_count u32              = 8
llama_model_loader: - kv   7:              gemma.attention.head_count_kv u32              = 1
llama_model_loader: - kv   8:                 gemma.attention.key_length u32              = 256
llama_model_loader: - kv   9:               gemma.attention.value_length u32              = 256
llama_model_loader: - kv  10:     gemma.attention.layer_norm_rms_epsilon f32              = 0.000001
llama_model_loader: - kv  11:                       tokenizer.ggml.model str              = llama
llama_model_loader: - kv  12:                tokenizer.ggml.bos_token_id u32              = 2
llama_model_loader: - kv  13:                tokenizer.ggml.eos_token_id u32              = 1
llama_model_loader: - kv  14:            tokenizer.ggml.padding_token_id u32              = 0
llama_model_loader: - kv  15:            tokenizer.ggml.unknown_token_id u32              = 3
llama_model_loader: - kv  16:                      tokenizer.ggml.tokens arr[str,256128]  = ["<pad>", "<eos>", "<bos>", "<unk>", ...
llama_model_loader: - kv  17:                      tokenizer.ggml.scores arr[f32,256128]  = [0.000000, 0.000000, 0.000000, 0.0000...
llama_model_loader: - kv  18:                  tokenizer.ggml.token_type arr[i32,256128]  = [3, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, ...
llama_model_loader: - type  f32:  164 tensors
llm_load_vocab: mismatch in special tokens definition ( 544/256128 vs 388/256128 ).
llm_load_print_meta: format           = GGUF V3 (latest)
llm_load_print_meta: arch             = gemma
llm_load_print_meta: vocab type       = SPM
llm_load_print_meta: n_vocab          = 256128
llm_load_print_meta: n_merges         = 0
llm_load_print_meta: n_ctx_train      = 8192
llm_load_print_meta: n_embd           = 2048
llm_load_print_meta: n_head           = 8
llm_load_print_meta: n_head_kv        = 1
llm_load_print_meta: n_layer          = 18
llm_load_print_meta: n_rot            = 256
llm_load_print_meta: n_embd_head_k    = 256
llm_load_print_meta: n_embd_head_v    = 256
llm_load_print_meta: n_gqa            = 8
llm_load_print_meta: n_embd_k_gqa     = 256
llm_load_print_meta: n_embd_v_gqa     = 256
llm_load_print_meta: f_norm_eps       = 0.0e+00
llm_load_print_meta: f_norm_rms_eps   = 1.0e-06
llm_load_print_meta: f_clamp_kqv      = 0.0e+00
llm_load_print_meta: f_max_alibi_bias = 0.0e+00
llm_load_print_meta: n_ff             = 16384
llm_load_print_meta: n_expert         = 0
llm_load_print_meta: n_expert_used    = 0
llm_load_print_meta: pooling type     = 0
llm_load_print_meta: rope type        = 2
llm_load_print_meta: rope scaling     = linear
llm_load_print_meta: freq_base_train  = 10000.0
llm_load_print_meta: freq_scale_train = 1
llm_load_print_meta: n_yarn_orig_ctx  = 8192
llm_load_print_meta: rope_finetuned   = unknown
llm_load_print_meta: model type       = 2B
llm_load_print_meta: model ftype      = all F32 (guessed)
llm_load_print_meta: model params     = 2.51 B
llm_load_print_meta: model size       = 9.34 GiB (32.00 BPW)
llm_load_print_meta: general.name     = gemma-2b
llm_load_print_meta: BOS token        = 2 '<bos>'
llm_load_print_meta: EOS token        = 1 '<eos>'
llm_load_print_meta: UNK token        = 3 '<unk>'
llm_load_print_meta: PAD token        = 0 '<pad>'
llm_load_print_meta: LF token         = 227 '<0x0A>'
llm_load_tensors: ggml ctx size =    0.13 MiB
ggml_backend_metal_buffer_from_ptr: allocated buffer, size =  7560.30 MiB, ( 7560.36 / 10922.67)
llm_load_tensors: offloading 18 repeating layers to GPU
llm_load_tensors: offloaded 18/19 layers to GPU
llm_load_tensors:      Metal buffer size =  7560.29 MiB
llm_load_tensors:        CPU buffer size =  2001.01 MiB
..............................................................
llama_new_context_with_model: n_ctx      = 512
llama_new_context_with_model: freq_base  = 10000.0
llama_new_context_with_model: freq_scale = 1
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M2
ggml_metal_init: picking default device: Apple M2
ggml_metal_init: default.metallib not found, loading from source
ggml_metal_init: GGML_METAL_PATH_RESOURCES = nil
ggml_metal_init: loading '/Users/tadanori/llama2/llama.cpp/ggml-metal.metal'
ggml_metal_init: GPU name:   Apple M2
ggml_metal_init: GPU family: MTLGPUFamilyApple8  (1008)
ggml_metal_init: GPU family: MTLGPUFamilyCommon3 (3003)
ggml_metal_init: GPU family: MTLGPUFamilyMetal3  (5001)
ggml_metal_init: simdgroup reduction support   = true
ggml_metal_init: simdgroup matrix mul. support = true
ggml_metal_init: hasUnifiedMemory              = true
ggml_metal_init: recommendedMaxWorkingSetSize  = 11453.25 MB
ggml_backend_metal_buffer_type_alloc_buffer: allocated buffer, size =     9.00 MiB, ( 7571.17 / 10922.67)
llama_kv_cache_init:      Metal KV buffer size =     9.00 MiB
llama_new_context_with_model: KV self size  =    9.00 MiB, K (f16):    4.50 MiB, V (f16):    4.50 MiB
llama_new_context_with_model:        CPU input buffer size   =     6.01 MiB
ggml_backend_metal_buffer_type_alloc_buffer: allocated buffer, size =    77.02 MiB, ( 7648.19 / 10922.67)
llama_new_context_with_model:      Metal compute buffer size =    77.00 MiB
llama_new_context_with_model:        CPU compute buffer size =   508.25 MiB
llama_new_context_with_model: graph splits (measure): 3

system_info: n_threads = 4 / 8 | AVX = 0 | AVX_VNNI = 0 | AVX2 = 0 | AVX512 = 0 | AVX512_VBMI = 0 | AVX512_VNNI = 0 | FMA = 0 | NEON = 1 | ARM_FMA = 1 | F16C = 0 | FP16_VA = 1 | WASM_SIMD = 0 | BLAS = 1 | SSE3 = 0 | SSSE3 = 0 | VSX = 0 | MATMUL_INT8 = 0 |
sampling:
	repeat_last_n = 64, repeat_penalty = 1.100, frequency_penalty = 0.000, presence_penalty = 0.000
	top_k = 40, tfs_z = 1.000, top_p = 0.950, min_p = 0.050, typical_p = 1.000, temp = 0.800
	mirostat = 0, mirostat_lr = 0.100, mirostat_ent = 5.000
sampling order:
CFG -> Penalties -> top_k -> tfs_z -> typical_p -> top_p -> min_p -> temperature
generate: n_ctx = 512, n_batch = 512, n_predict = 64, n_keep = 1


 Who is Einstein?

1. His full name was Albert Einstein

2. He was born on March 14th, 1879 in Ulm, Germany

3. He died at the age of 66 on April 01st, <strong>1955</strong>

4. He married twice;
llama_print_timings:        load time =    4348.82 ms
llama_print_timings:      sample time =      37.85 ms /    64 runs   (    0.59 ms per token,  1691.02 tokens per second)
llama_print_timings: prompt eval time =    6321.34 ms /     5 tokens ( 1264.27 ms per token,     0.79 tokens per second)
llama_print_timings:        eval time =  523694.68 ms /    63 runs   ( 8312.61 ms per token,     0.12 tokens per second)
llama_print_timings:       total time =  530167.11 ms /    68 tokens
ggml_metal_free: deallocating
Log end
 

Gemma-2bの返答だけを抜き出すと以下になります。意外とそれっぽい回答が返ってきました。誕生日と死亡年月日はあっていますが、年齢は66歳ではなく76歳です。

 Who is Einstein?

1. His full name was Albert Einstein

2. He was born on March 14th, 1879 in Ulm, Germany

3. He died at the age of 66 on April 01st, <strong>1955</strong>

以上、興味本位でMacbook Airで動かしてみましたが、Google Colabで動作させたほうが快適です。やはりMacbook Airでは厳しいようです。M2 Ultraとかになれば全然違うんだろうとは思いますが・・・

今回、速度が遅かった理由は、メモリ不足で一部のレイヤーをCPUで実行しているためです。メモリがもっと多ければ、もう少し普通に動いたと思います。16GBだと、ブラウザなどが開いているとメモリが厳しい感じです。MacでLLMを動かしたい場合はなるべく、メモリを多く搭載した方がよいと思います。

4ビット量子化バージョンで実行(2024.03.23追記)

メモリ不足で動かない場合は、少ないビット数で量子化されたバージョンを使うことで解決することがあります。

HuggingFaceに量子化したバージョンも公開されていましたので、そちらも試してみました。

量子化したバージョン:https://huggingface.co/mlabonne/gemma-2b-GGUF

いくつかの量子化違いのバージョンがありますが、今回は2つを試してみました。

gemma-2b.Q4_0.gguf

"q4_0: Original quant method, 4-bit.”と説明に書かれたバージョンです。4bitに量子化されたバージョンで、gemma-2b/gemma-2b.ggufが10GBに対して1.42GBと非常に小さくなっています。

実行には、以下のコマンドを用いました。GPUで全ての層を実行できるので、-nglオプションは必要ありません。

./main -m gemma-2b.Q4_0.gguf -n 64 -p "Who is Einstein?"

実行すると、以下のような結果が出力されました。出力はかなり高速で、下記の出力が完了するまで3.8秒でした。

Who is Einstein?

 apprehendient of apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient apprehendient

gemma-2b.Q8_0.gguf

もう1つは、量子化されたモデルの中で最も大きなモデルです。説明には"q8_0: Almost indistinguishable from float16. High resource use and slow. Not recommended for most users."と書かれています。サイズは2.67GBと大きいですが、10GBからは随分小さくなっています。

実行コマンドは以下になります。

./main -m gemma-2b.Q8_0.gguf -n 64 -p "Who is Einstein?"

こちらの出力は以下の通りです。出力に要した時間は7.8秒でした

Who is Einstein?

Einstein’s most well-known achievement was his theory of relativity. This theory was developed by Albert Einstein, an Austrian-born theoretical physicist and mathematician.

Einstein’s theory of relativity is a fundamental theory of physics that provides a unified description of the fundamental forces of nature. It describes how gravity, electromagnetism

生まれが間違っていて、後半の文章(下線部)も怪しいですが、それっぽい結果が返ってきました。

結論として、量子化すればなんとか動かせる感じです。ただ、量子化しすぎると変な結果が出力されやすくなるようです。

ドキュメントには、Not recommended for most users.と書かれていますが、この文章が8秒くらいで出力されるのであれば、gemma-2b.Q8_0.ggufを使ってもよいのではないかと感じました。

gemma-2b.ggufが遅かったのは、GPUで全ての処理を実行できなかったからです。llama.cppのベンチマークを見る限り、量子化しない方が早いみたいなので、メモリがあれば、今回の結果ほど遅くはならなかったと思われます。

おまけ(gemma-7bの量子化バージョンを動かす)

gemma-2bの量子化バージョンがサクサクうごいたので、gemma-7bの量子化版も動かしてみました。以下のリポジトリから、gemma-7b-it.Q4_0.ggufをダウンロードして試してみました。

量子化したバージョン:https://huggingface.co/mlabonne/gemma-7b-it-GGUF

容量は、5.04GBで、gemma-2b.ggufより小さいです。

今回は、-itのついたモデルなので、質問・回答の形でプロンプトを入力しています。

./main -m gemma-7b-it.Q4_0.gguf -p "<start_of_turn>user\nWho is Einstein?<end_of_turn>\nmodel\n" -n 64

動かした結果が以下になります。メモリ不足にならなかったみたいで、処理時間は12.4秒と結構高速でした。

量子化した7Bの方が2Bより賢そうなので、7Bを量子化したものを動かした方がよいかもしれません。

<start_of_turn>user
Who is Einstein?<end_of_turn>
model
Albert Einstein was a German-born theoretical physicist who revolutionized scientific understanding with his theories on relativity. Born in 1879, Einstein was a precocious child who struggled with conventional schooling but excelled in mathematics and physics.

**Key Contributions:**

* **Theory of Relativity:** In 1905

なぜ、GPUのメモリ不足になるのか。

MacはCPUとGPUは同じメモリを共有するので、「なぜCPUで動かせるのにGPUのメモリが不足するの?」と思った方もいるかもしれません。どうも、GPUに割り当てることができるメモリ容量には制限があるようで、16GBのすべてをGPUに割り当てることができるわけではないようです。このため、モデル自身がメモリに入っても、GPUに割り当てることができる上限のサイズを超えた場合は、エラーとなってしまうようです

まとめ

Macbook AirでGoogle Gemmaを興味本意で動作させてみました。2Bならなんとかなるかと思いましたが、動作することは動作するけどかなり実行速度が遅いという結果でした。

また、量子化すれば、それなりの速度で動作させることが可能なことも確認できました。個人的んは、ローカルで動作させるメリットをあまり感じませんが、ノートパソコンで動作させることができるのは面白いと思いました。

おすすめ書籍

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

記事URLをコピーしました