powered by TechFeed
表示モード
ハウツー

2021年製ノートPCの4GB GPUでGemma 4が76トークン/秒で動く — 9.5GBのモデルを1.2GBに圧縮して完全ローカル動作させる手順

10月3日、dev.toのユーザーgdeが「Gemma 4 at Over 70 Tokens/s on a 2021 Laptop's 4 GB GPU: The Live Demo, Step by Step」と題した記事を公開した。本来9.5GiBあるGemma 4のモデルを1.2GiBまで圧縮し、2021年製ノートPCの4GB GPU上で76トークン/秒という実用的な速度で完全ローカル動作させた、という内容だ。LLMのローカル実行はハイエンドGPUが前提とされがちだが、この記事はその常識を覆す具体的な手順を示している。

10月3日、dev.toのユーザーgdeが「Gemma 4 at Over 70 Tokens/s on a 2021 Laptop's 4 GB GPU: The Live Demo, Step by Step」と題した記事を公開した。本来9.5GiBあるGemma 4のモデルを1.2GiBまで圧縮し、2021年製ノートPCの4GB GPU上で76トークン/秒という実用的な速度で完全ローカル動作させた、という内容だ。LLMのローカル実行はハイエンドGPUが前提とされがちだが、この記事はその常識を覆す具体的な手順を示している。


Gemma 4 E2Bとは何か

Gemma 4はGoogleが2025年に公開したオープンウェイトモデルファミリーで、「E2B」はそのうち20億パラメータ(2B)規模の命令チューニング済みモデルを指す。スマートフォンやノートPCといったエッジデバイスでの動作を想定した軽量モデルという位置づけだ。

モデル名の「E」はper-layer embedding(層ごとのエンベディングテーブル)を意味する。通常のトランスフォーマーモデルが全層で共通のエンベディングを使うのに対し、E2Bは各層に独自のエンベディングテーブル(per_layer_token_embd)を持つ。このテーブルはファイルサイズの約半分を占めるほど大きいが、llama.cppはこれをメモリマップ(遅延読み込み)で扱うため、GPUのVRAMには展開されずシステムRAMに留まる。結果として、ファイルサイズに比べてVRAM消費が大幅に抑えられる構造になっている。


本来9.5GBのモデルが、なぜ1.2GBに収まるのか

上記のエンベディング構造に加えて、圧縮の核心を担うのがQAT(Quantization-Aware Training)だ。通常の量子化(Post-Training Quantization)は学習済みモデルの重みを後から低ビットに丸めるため、精度が劣化しやすい。一方QATは「4ビットで表現されることを前提として」学習を行う手法で、モデルが量子化誤差を織り込んだ重みを獲得するため、品質の劣化を抑えながら大幅なサイズ圧縮を実現できる。GoogleはこのQAT済みの重みをHugging Face上で公開している。

形式 重みサイズ 4GiB GPUに収まるか
BF16 9.5 GiB ❌
INT8 約4.8 GB ❌
Google公式 QAT GGUF 1.31 GiB ✅
再パック QAT GGUF 1.20 GiB ✅🥇

「再パック」が1.12倍速い理由

記事の核心はここだ。Googleが公開しているQAT GGUFは、トランスフォーマー層こそQ4_0形式だが、2つのエンベディングテーブルはQ6_K形式で格納されており、ファイルの67.7%を占める。

gemma-4-E2B_q4_0-it.gguf
  Q6_K      2.257 GB  (67.7%)
  Q4_0      1.048 GB  (31.4%)
  F16       0.028 GB  ( 0.8%)

さらに問題なのは量子化のステップ幅計算だ。Q4_0量子化では、32個の重みをひとまとめにした「ブロック」ごとにスケールファクター(ステップ幅)を保持し、各重みをそのスケールで割って整数に丸める。GoogleのGGUFはllama.cppのデフォルト処理でこのステップ幅を再計算しており、「ブロック内の最大値を8で割る」方式を使う。これにより、QAT学習時に最適化されたグリッドからずれた値が生じ、速度と精度の双方に影響が出る。

再パック版(xbill9/gemma-4-E2B-it-qat-q4_0-exact-gguf)は、gguf_exact.pyというスクリプトで以下を行う:

  • GoogleのGGUFのヘッダー、トークナイザー、チャットテンプレートをバイト単位でそのままコピー
  • 全275層のマトリクス、両エンベディングテーブル、per_layer_model_projをすべてQ4_0に統一
  • 各32値ブロックについて、訓練時のステップ幅を最小二乗法で精緻化(量子化後の値がQAT学習時の元の値に最もよく近似するようステップ幅を最適化する)

結果として、llama-benchによる計測では再パック版が81.75 tok/s、Google公式GGUFが73.26 tok/sで、同一カード上で1.12倍速い。


実際の動作確認:4ステップで動く

使用ハードウェアは以下の構成だ:

Lenovo Yoga 9(2021年製)
CPU: Intel Core i7-10750H @ 2.60GHz
RAM: 16GB
GPU: NVIDIA GeForce GTX 1650 Ti Max-Q (4096 MiB, 40W TDP)

GTX 1650 TiはCompute Capability 7.5で、テンソルコアを持たない。データセンター向けのT4と同じ世代のアーキテクチャではあるが、テンソルコアが省略されており、行列演算はすべて通常のCUDAコアで処理される。

Step 1:GGUFのダウンロード

hf download xbill9/gemma-4-E2B-it-qat-q4_0-exact-gguf gemma-4-E2B-it-q4_0-exact.gguf \
  --local-dir ~/models/gemma-4-E2B-it-qat-q4_0-exact-v2

必要なディスク容量は約3GB。

Step 2:サーバー設定

N_GPU_LAYERS=99        # 全トランスフォーマー層をGPUに乗せる
CONTEXT_SIZE=8192
KV_CACHE_TYPE=f16      # q8_0にするとデコードが12%低下するため
FLASH_ATTENTION=1      # このカードで+4.8%のデコード速度向上
PARALLEL_SLOTS=1       # 1ユーザーがカード全体を占有
REASONING=off          # デフォルトはオフ(後述)

Step 3:起動と確認

gpu start
ask -q "hi"

起動は4秒。llama.cppがファイルをメモリマップするため高速だ。

Step 4:フットプリント確認

NVIDIA GeForce GTX 1650 Ti: 1493 MiB / 4096 MiB使用, 40.43W, 47℃
llama-server: 1488 MiB

VRAMの36%(1488 MiB)しか使っていない状態で、カード全体の電力上限(40W)で動作している。


実測パフォーマンス

短い質問への応答は0.38秒でターミナルに表示される。5回連続で250語の説明を生成した計測結果は以下のとおりだ:

{"run":1,"predicted_per_second":76.14}
{"run":2,"predicted_per_second":76.36}
{"run":3,"predicted_per_second":76.20}
{"run":4,"predicted_per_second":76.87}
{"run":5,"predicted_per_second":77.14}

76〜77トークン/秒で安定している。llama-serverの内蔵チャット画面(http://127.0.0.1:8080)でも77.35 tok/sを記録している。

なお、Thinking機能(-Tフラグ)を有効にすると、「9.11と9.9ではどちらが大きいか」という質問で9.36秒かかる。デフォルトでオフになっているのはこのためだ。


再現性の担保

再パック用スクリプトgguf_exact.pyもリポジトリに公開されており、GoogleのHugging Face公開ファイルから誰でも同じGGUFを再現してSHA256ハッシュで検証できる。クラウドなし、アカウントなし、追加インストールなしで動く構成になっている。

プライバシーやコスト、オフライン環境での利用を理由にLLMのローカル実行を求める声は根強い。本記事が示すのは、その選択肢が一部の高価なワークステーションだけのものではなく、数年前のコンシューマー向けノートPCでも現実的に成立するという事実だ。

詳細はGemma 4 at Over 70 Tokens/s on a 2021 Laptop's 4 GB GPU: The Live Demo, Step by Stepを参照していただきたい。