RTX3060とGemma-4-12b

Artificial Intelligence

GeForce RTX 3060は意外にやる子

過日、まさかの3万YEN切る価格で入手したRTX 3060ですが、実は以前会社員時代に最初に会社で購入したGPUでもあります。2022年当時の相場だとおよそ80,000JPYでしたね。このころは暗号資産・暗号通過の方でGPUが非常に重宝されていた時代の末期ぐらいだったと思ってて、それ故に仮想通過採掘で悪用されないように、採掘処理を検出したら能力を落とすロジックが組み込まれたGPUが良く売られていて、この子もそんな一つでした。

RTX3060 GPU

今回、このGPUでどんなモデルをどんなふうに動かしてるかを書いてみたいと思います。

メモリ使用量の把握

現在、私はRTX 3060上で Gemma-4-12B-it-QAT のUnsloth版4ビット量子化版に、MTP機能を有したドラフトモデルを付与して動かしています。引数としては以下の通りです。

私は基本的にGoogle DeepmindがリリースしたQATトレーニングを施したモデルに対して、UnslothがUnsloth Dynamic 2.0によってQ4_K_XL(4.5bit量子化)処理を施したモデルを使っています。これに対して、同リポジトリ上に置かれているMTPモデルを適用させ、必要に応じて高速化が図れるような体制にしている感じです。

llama.cppはGemma-4に対応させる際、きちんと元のモデルの意図した使い方を考慮していて、ちゃんとアドオン設定でMTPを実装できるように組んでくれていて、このあたりはあんまり困るところはありませんでした。また、これらのモデルはDifyやHermes Agentを通じて様々な環境から触ることが多いため、プロンプトキャッシュは軒並みゼロに設定しています。

/opt/llama/bin/llama-server --model /opt/llama/models/gemma-4-12B-it-qat-UD-Q4_K_XL.gguf \
  --model-draft /opt/llama/models/mtp-gemma-4-12B-it.gguf \
  --mmproj /opt/llama/models/mmproj-gemma4-12b-it-qat.gguf \
   -t 4 --prio 2 --temp 1.0 --top-p 0.95 --top-k 64 --host 0.0.0.0 --port 8001 \
   --device CUDA0 -mg 0 -sm layer --fit on \
   -c 163840 -ctv q8_0 -ctk q8_0 --no-warmup --no-cache-prompt --cache-ram 0 \
   --spec-type draft-mtp --spec-draft-n-max 4 --spec-draft-device CUDA0 \
   -fa on --chat-template-kwargs '{"enable_thinking":true}' \
   --log-verbosity 4

なお、メモリ使用量を把握する場合、「–log-verbosity 4」の追加をしたほうが良いかと思われます。
途中で仕様変更がなされて、以前はモデルのロード、メモリアサインの情報がログに乗ってたんですが、現在これらのログは info(3) ではなく、trace(4)に載るようになってるみたいです。

ウェイトデータ

ウェイトデータは以下のように載ってました。

0.06.207.952 I load_tensors:   CPU_Mapped model buffer size =   540.00 MiB
0.06.207.954 I load_tensors:        CUDA0 model buffer size =  6390.19 MiB

一部540MiB分を残してすべてのウェイトはGPUに載ってます。おそらくはfit:onの設定が効いているのでしょうか、なによりGoogle Deepmindの施したQATの成果が表れてるのかな?と言う感じでした。

KVキャッシュデータ

KVキャッシュデータは以下のようになってました。

0.07.196.912 W llama_context: n_ctx_seq (163840) < n_ctx_train (262144) -- the full capacity of the model will not be utilized
0.07.199.111 I llama_context:  CUDA_Host  output buffer size =     4.00 MiB
0.07.199.116 I llama_kv_cache_iswa: creating non-SWA KV cache, size = 163840 cells
0.07.201.205 I llama_kv_cache:      CUDA0 KV buffer size =  1360.00 MiB
0.07.205.428 I llama_kv_cache: size = 1360.00 MiB (163840 cells,   8 layers,  4/1 seqs), K (q8_0):  680.00 MiB, V (q8_0):  680.00 MiB
0.07.205.431 I llama_kv_cache: attn_rot_k = 1, n_embd_head_k_all = 512
0.07.205.432 I llama_kv_cache: attn_rot_v = 1, n_embd_head_k_all = 512
0.07.205.699 I llama_kv_cache_iswa: creating     SWA KV cache, size = 4608 cells
0.07.206.013 I llama_kv_cache:      CUDA0 KV buffer size =   765.00 MiB
0.07.208.381 I llama_kv_cache: size =  765.00 MiB (  4608 cells,  40 layers,  4/1 seqs), K (q8_0):  382.50 MiB, V (q8_0):  382.50 MiB
0.07.208.385 I llama_kv_cache: attn_rot_k = 1, n_embd_head_k_all = 256
0.07.208.385 I llama_kv_cache: attn_rot_v = 1, n_embd_head_k_all = 256

私の環境では最大コンテキストを160kトークンに設定しています。
基本的なKVキャッシュとしては、K:680MiB、V:680MiBとなっており、合計1,360MiBほど確保されています。
これとは別にスライディングウィンドウが使用するメモリ領域としては、K:382.5MiB、V:382.5MiBとなっており、こちらの合計は765MiB確保されています。

結果として、全体で2,125MiBが確保されている状態です。

Gated DeltaNetが確保する計算バッファ

Gated DeltaNetというのは、Qwenシリーズでよく用いられる言葉ですが、この仕組みは名前こそ違えどGemmaシリーズでも類似した仕組みを持っているということで、llama.cpp上では同じ仕組みとみなして処理をしているように見えました。

0.07.208.462 I sched_reserve: reserving ...
0.07.209.224 I sched_reserve: resolving fused Gated Delta Net support:
0.07.210.852 I sched_reserve: fused Gated Delta Net (autoregressive) enabled
0.07.212.335 I sched_reserve: fused Gated Delta Net (chunked) enabled
0.07.298.835 I sched_reserve:      CUDA0 compute buffer size =   533.80 MiB
0.07.298.841 I sched_reserve:  CUDA_Host compute buffer size =   180.80 MiB
0.07.298.842 I sched_reserve: graph nodes  = 2546
0.07.298.842 I sched_reserve: graph splits = 2
0.07.298.843 I sched_reserve: reserve took 90.38 ms, sched copies = 1

このことから、これらの処理に533.8MiBが計算用バッファサイズとして確保されているようです。

MTP(Assistant)モデル用ウェイトデータ

今回使用するモデルでは、MTP機能を持つドラフトモデルも適用しています。Gemma-4はQwenと異なりモデル内にその機能を確保せずに、アドオンする形で実装する方式を採用しています。

0.08.193.535 I load_tensors:   CPU_Mapped model buffer size =   144.00 MiB
0.08.193.536 I load_tensors:        CUDA0 model buffer size =   226.90 MiB

その中でCUDAデバイスに設置してるデータ量は226.9MiBであり、GPUにマッピングされています。

MTP(Assistant)モデル用Gated DeltaNet計算バッファ

0.08.314.638 I sched_reserve:      CUDA0 compute buffer size =   532.78 MiB
0.08.314.644 I sched_reserve:  CUDA_Host compute buffer size =   180.79 MiB

MTP向けには532.78MiBの容量が確保されていました。

マルチモーダル用モデルエンコーダのウェイトデータ

--- vision hparams ---
0.08.315.661 I load_hparams: image_size:         224
0.08.315.661 I load_hparams: patch_size:         48
0.08.315.662 I load_hparams: has_llava_proj:     0
0.08.315.662 I load_hparams: minicpmv_version:   0
0.08.315.662 I load_hparams: n_merge:            1
0.08.315.663 I load_hparams: n_wa_pattern: 0
0.08.315.663 I load_hparams: image_min_pixels:   92160
0.08.315.664 I load_hparams: image_max_pixels:   645120
0.08.315.664 I
0.08.315.666 I load_hparams: model size:         167.00 MiB
0.08.315.667 I load_hparams: metadata size:      0.00 MiB
0.08.438.501 I clip_ctx: CLIP using CUDA0 backend
0.08.439.230 I load_hparams: projector:          gemma4ua
0.08.439.232 I load_hparams: n_embd:             640
0.08.439.233 I load_hparams: n_head:             1
0.08.439.233 I load_hparams: n_ff:               0
0.08.439.239 I load_hparams: n_layer:            0
0.08.439.240 I load_hparams: ffn_op:             gelu_quick
0.08.439.240 I load_hparams: projection_dim:     3840
0.08.439.241 I
--- audio hparams ---
0.08.439.241 I load_hparams: n_mel_bins:         640
0.08.439.242 I load_hparams: proj_stack_factor:  0
0.08.439.242 I load_hparams: audio_chunk_len:    0
0.08.439.242 I load_hparams: audio_sample_rate:  16000
0.08.439.243 I load_hparams: audio_n_fft:        -1
0.08.439.243 I load_hparams: audio_window_len:   -1
0.08.439.244 I load_hparams: audio_hop_len:      -1
0.08.439.244 I
0.08.439.248 I load_hparams: model size:         167.00 MiB
0.08.439.249 I load_hparams: metadata size:      0.00 MiB

各エンコーダに対してモデルサイズ167.0MiBが確保されていました。

マルチモーダル用Gated DeltaNet計算バッファ

マルチモーダル用にも以下のようにメモリ確保が行われていたようで、その容量はおよそMTP用のそれと同じになっていました。

0.08.846.090 I sched_reserve:      CUDA0 compute buffer size =   532.78 MiB
0.08.846.096 I sched_reserve:  CUDA_Host compute buffer size =   180.79 MiB

合計値を割り出してみる

合計値は以下の通りとなりました。

厳密な意味では実はこの合計値よりわずかにGPU側のメモリ使用量は下回っています(10,208MiBとなっていた)が、おおむね使用量に合致するということで、これぐらいのメモリは確保されるということなのだと思われます。割とぎりぎりまでメモリを消費する形となりました。

性能はどうか?

速度については、以下の通り確認できました。個人的には及第点なんじゃないの?と言う感じです。それゆえにこれを主力において現在は使用しています。

区分速度最高値最低値
入力速度774.45 tps1040.82 tps117.52 tps
出力速度40.44 tps84.46 tps30.2 tps

出力においては、MTPなしで動かした場合、およそ50tps程度からスタートし、ターンが重なるとともに徐々に速度が低下していく印象でしたが、特にコーディングを伴う場合において、MTPモデルを動かした場合はかなり速度が高く維持されており、およそ70-80tps程度が記録されていました。

読み込みにおいては、MTPある・なしに関係なく1000tps程度からスタートし、ターンとともに劣化していく動作は共通しており、そこは出力速度ほどに影響はしてないように思えました。

そうした面から見て、MTPモデルの適用はやっぱり投機実行結果によって大きく左右はされますが、致命的に遅くなるほどにはなっていないのかな?と言うのが今のところの所感です。

モデル出力内容の吟味

出力内容はしっかりしていると思います。文章関係のタスクでそれほど致命的な間違いは犯しません。

しかし、Difyで動かす場合に少々ツールを使用するターン数が少ないのが気になります。大体1ターンで調査を終わってしまう癖があり、Qwenと違って後で振り返ろうとしません。それによって、猪突猛進に推論するなぁと。こういう所は、Hermes Agentを介することによりある程度是正されるのかなと感じています。
しかし、ぶっちゃけ行ってしまえば思い切りGeminiらしさが出てるんだなぁという気もしますね。

OpenAI GPTを使った人がGemini/Gemmaで戸惑うのは、こういう一発屋発想をし、そのまま猪突猛進で突き進んでしまうところにあるかなと思います。そこのバランスのとり方と言いますか、指示の送り方と言いますか、そこが私自身にとっても課題だなぁと感じています。

決して馬鹿にはできないRTX3060の底力

RTX3060のメモリ帯域は 360GB/s あります。
RTX3050(224GB/s)と比べても 136GB/s ほどの開きがあり、その帯域量はRyzen AIシリーズやDGX-Sparkなどのユニファイドメモリよりもずっと速いのです。さらに言えばまさかのRTX 4060Ti-16GB(288GB/s)よりも速いです。

それを考えると、いまだ決して侮れない製品ともいえます。TensorCoreも備わってれば、Flash Attentionも動きますし、古いからと言ってバカにはできないGPUと言えるでしょう(それでもRTX 5060Ti-16GB(448GB/s) には負けるんですけど・・)。

また、会社員時代においても

  • NVIDIA L4
  • NVIDIA P100

というEnterprise GPUを差し置いて、このRTX 3060の処理速度が全体的に見て見れば最も高速でした。ファインチューニングやフラグシップモデルを動かす場合においてもこのGPUは重宝していたので、これからもしばらくは役に立ってくれるのではないか・・と感じているところです。

コメント

タイトルとURLをコピーしました