オンプレLLM/技術記録
3回にわたって、VRAM 12GBの機体で177BのMoEを動かした話を書いてきました。最後はいちばん重要で、いちばん語られない話です。llama-bench の数字は、実運用の数字ではありません。我々の環境では2.5倍ずれました。
ベンチマーク値と実運用値の乖離
llama-bench(文脈ゼロの瞬間値):生成 16.7 / プロンプト処理 131 tok/s- 常駐サーバ・文脈663トークン(実運用):生成 6.2〜7.1 / プロンプト処理 61.7 tok/s
llama-bench の tg128 は文脈をほぼ持たない状態での瞬間値です。実運用では会話履歴とシステムプロンプトが積み上がり、アテンションの計算量とCPU⇔GPUの往復が増えます。ハードウェアの購入判断を llama-bench の値で行うと、確実に見誤ります。
同じ罠を3回踏み、間違った理屈を組み立てた
ここからが、この検証でいちばん書いておくべき話です。我々は同じ測定ミスを3回繰り返しました。
- 1回目:初回ベンチ 5.2 tok/s(正しくは 16.7)
- 2回目:プロンプト処理 12.9 tok/s(正しくは 88.9)
- 3回目:要因計画の基準条件 5.39 tok/s(正しくは 16.8)
原因はいずれも同じ、104GBがページキャッシュに載り切る前に測っていたことです。3回目は -p 64 -n 16 というウォームアップを入れていたにもかかわらず起きました。64トークンでは全専門家に触れず、モデルが読み込まれないのです。
問題は2回目でした。「入力4,066トークンで12.9 tok/s、短い入力の5分の1」——この数字に対して、我々は次の説明を組み立てました。
(誤った説明)生成時は1トークンあたり「512人中10人」で済むが、プロンプト処理はバッチで流すため、バッチ内トークンが活性化する専門家の和集合はほぼ全員になる。長い入力では104GBを丸ごと舐めることになり、トップKルーティングの利得が消える——MoE+CPUオフロードの構造的な弱点である。
もっともらしく聞こえます。MoEの仕組みとも矛盾しません。そして完全に間違っていました。
汚染の原因は単純でした。llama-bench(mmap使用)が104GBをページキャッシュに載せた直後、常駐サービスを再起動して --no-mmap で別の104GBを匿名メモリに確保したのです。125GiBのRAMに208GBを載せようとした状態で測っていました。
条件を揃えて測り直すと、入力3,895トークンで88.9 tok/s。短い入力(663トークンで61.7 tok/s)よりも速い——バッチが大きいほどGPUの利用効率が上がるという、ごく標準的な挙動でした。劣化などしていなかったのです。
教訓はチューニングの話ではありません。おかしな数字が出たとき、我々は「それを説明する理屈」を作れてしまうということです。しかも理屈が整っているほど、測定そのものを疑わなくなります。この説明は記事の草稿にも一度書かれ、公開直前に実測で覆りました。
実タスク検証:素の設定では失敗し、文法束縛で通った
社内の要約メモリ基盤には、登録内容を別のLLMに査読させる仕組みがあります。入出力の機密性が高く、非同期のバックグラウンド処理——理屈の上ではローカルLLMの最良の適用先です。本番と同じシステムプロンプト・同じJSONスキーマで、223行のPythonを査読させました。
- 1回目(
json_object・思考あり):679秒/reasoning 10,565字/出力2,500トークンで上限到達/JSON 0バイト - 2回目(
json_schema・思考抑制):105秒/reasoning 0字/453トークンで正常終了/json.loads が直接成功
1回目は、推論モデルが思考だけで max_tokens を使い切りました。11分待って成果物ゼロです。response_format: json_object は「JSONで答えてください」というお願いにすぎず、強制力がありません。
2回目は2点変えました。chat_template_kwargs で思考を止め、response_format を json_schema に切り替える。llama.cppは渡されたJSON SchemaをGBNF文法へ変換し、サンプリング時に文法外のトークンを禁止します。verdictを3値、severityを4値のenumにしておけば、構造的に不正な出力を生成できません。
結果は105秒で3件の指摘。中身を実コードと照合したところ、行番号は3件とも正確、存在しないコードの捏造はゼロでした。うち1件は「docstringに書かれた『弾かずに丸める』という契約と、型が違えば例外が出る実装の矛盾」を突いたもので、人間が見落としやすい類です。
ただし1件あたり105秒という時間は変わりません。非同期のバッチ処理なら十分ですが、対話的なエージェント動作には向きません——往復回数がそのまま所要時間になるからです。
GPUを増やせば解決するのか:傾きから外挿する
構築編で得た「専門家1層をVRAMに移すと0.73ms/token短縮、1層=1.73GB」という傾きから、増設効果を計算できます。
- 3060×1(現在):3/48層がVRAM / 生成 17.3 tok/s
- 3060×2:約10/48層 / 19〜20 tok/s(+12〜18%)
- 3060×4:約23/48層 / 22〜24 tok/s(+35%)
GPUを4倍にしても1.35倍にしかなりません。生成時間の72%を占めるのはRAMからの専門家読み出しで、GPUが肩代わりできるのはそのうちVRAMに収まる分だけだからです。一方、メモリ帯域そのものを増やす——例えば4chから12ch構成へ移す——なら、その72%全体に効きます。この構成における投資対効果は、GPU枚数ではなく帯域で決まります。
なお、推論エンジン側でも打ち手はあります。オフロードしたMoEをテンソル並列で分割し、各GPUが専門家の一部だけを保持する方式です。層分割と違いスロットあたりの容量が実際に減るため、同じVRAMにより多く載ります。当社では別系統で検証を進めており、稿を改めて書きます。
結論:代替ではなく、受け皿
- 外部に出せない情報を扱う:ローカルが唯一の選択肢
- 非同期のバッチ処理(査読・要約・分類):ローカルで実用(1件105秒。待てる処理なら問題にならない)
- 短い対話・下書き・整理:ローカルで十分(6〜7 tok/sは黙読速度と同等)
- 長文の読解・横断調査:クラウド(文脈8,192トークンの上限がある)
- エージェント動作・実装作業:クラウド(往復回数がそのまま時間になる)
ローカルLLMを「クラウドAPIの安い代替」として導入すると、ほぼ確実に期待外れになります。クラウドに出せない仕事の受け皿として位置づければ、既存機材とメモリ増設だけで成立します。今回の追加投資はメモリ代の数万円のみ、GPUの買い足しはゼロでした。
当社は、お手元の遊休ハードウェアを活用した「社内専用AI基盤」の構築を支援しています。
既存機材の適性診断は無料。買う前に、config.json とメモリ帯域から「動かせるモデルと速度」を計算してお示しします。
お問い合わせフォーム(「社内AI基盤 相談希望」とお書き添えください)/ info@flagship-ai.jp
シリーズ「レガシー to AI 実録」
- 総集編:1台のPCで22システム・24.6万行 ― AI開発3.5ヶ月の実測記録
- 実録①:20年もののJavaを、約1週間でフル刷新
- 実録②:既存コード0行変更で、基幹系に新しい入口を作る
- 実録③:1ヶ月級の構成検討を、プロトタイプ2日+実測で
- 実録⑦:ベンチ16.7・実運用6.6 ― 帯域律速の現実
- 実録⑤:VRAM 12GBで177Bを動かす、MoE時代のモデル選定
- 実録⑥:同じ機体で速度が3倍、--n-cpu-moe と3つの罠
- 実録⑦:ベンチ16.7・実運用6.6 ― 帯域律速の現実(本記事)
※本記事の数値は2026年9月時点、Threadripper 1950X / DDR4-2400 4ch 128GB / RTX 3060 12GB / llama.cpp CUDAビルドでの自社実測です。構成により結果は異なります。