AI・機械学習
13年前のGPUなしXeonでGemma 4 26Bを秒間5トークンで実行する
Running Gemma 4 26B at 5 tokens/SEC on a 13-year-old Xeon with no GPU (neomindlabs.com)
要約
この記事は、13年前のGPU非搭載のXeonサーバーでGoogleのGemma 4 26Bモデルを秒間約5トークンで実行することに成功した体験談です。著者は、古いハードウェアで最新のAIモデルを動作させるための技術的な課題と、AIアシスタント(Claude)を活用してCPU命令セットの互換性問題を解決したプロセスを詳細に解説しています。これは、高価なGPUに頼らずともローカルでAIモデルを実行できる可能性を示すものです。
全文翻訳
私の地下室には、最新の言語モデルを実行するには不向きなサーバーがあります。それは13年ほど前のHP StoreVirtualストレージボックスを再利用したもので、Ivy Bridge世代のXeonが2基搭載されていますが、GPUはありません。ディスクを格納するために作られたもので、計算処理のためではありません。しかし、今週からGoogleのGemma 4、260億パラメータのオープンウェイト混合エキスパートモデルを、秒間約5トークンで実行できるようになりました。これは読み取り速度に相当します。
ハードウェア
再利用したHP StoreVirtual:デュアルXeon E5-2690 v2(Ivy Bridge、2013年製)、DDR3、GPUなし
命令セット
AVX1のみ — AVX2、FMA3なし
モデル
Gemma 4 26B-A4B(MoE)、Q8_0
デコード
約5.2トークン/秒
プロンプト評価
約16トークン/秒
ボックスのコスト
300ドル未満
誰でもGPUをレンタルできます。しかし、最新のMoEモデルと古いエンタープライズボックスを用意して、それらを中間地点で適合させるのはより困難です。そして、このギャップこそが私がこの記事を書く理由です。「AIに強い」ということは、静かに「サブスクリプションを支払うこと」を意味するようになりました。真のスキルは異なると私は考えています。それは、モデルを十分に理解し、誰もあなたのためにパッケージ化していない問題にそれを向け、そして返された答えが実際に正しいかどうかを判断することです。ですから、私たちがそれに長けていると主張するのではなく、ここでは、本来協力するはずのないハードウェアで、実際に動作した例を示します。
記事のきっかけ
数週間前、「10年前のXeonがあれば十分」という記事がHacker Newsで話題になりました。著者は、ik_llama.cppと注意深く選ばれた約25個のフラグを使用して、GPUなしの2016年製Xeonと128GBの低速DDR3でGemma 4を実行しています。それは素晴らしい読み物であり、現代の推論プレイブックのあらゆるトリック(投機的デコーディング、CPU対応の混合エキスパートルーティング、CPUに移植されたフラッシュアテンション、実行時ウェイト再パッキング)を活用しています。まさにエンジニアリングです。「私もXeonを持っている」と思いました。実際、いくつかあります。それで試してみましたが、動作しませんでした。
AIエージェントが実際に役立つこと
ビルドは起動時に失敗しました。その失敗をClaudeに渡し、何が問題か尋ねました。答えは迅速かつ具体的でした。著者の2016年製チップはBroadwell製です。私のものはIvy Bridge、Intelが「v2」と呼ぶ世代です。そのフォークの高速カーネルはAVX2とFMA3を想定していますが、これらの命令セットは2014年に出荷されたHaswell、「v3」世代まで搭載されませんでした。私のCPUは、コードが書かれた命令よりも古いのです。最適化されたパスが存在しなかったため、実行できませんでした。そこで、当然のフォローアップとして質問しました。それでも実行できるようにできないか?私はすでに最初の試みとして無料モデルで近づきましたが、成功しませんでした。Claudeはその半ば完成したアプローチを引き継ぎ、それが正しいものであることに同意し、それを完成させました。ホットパスを再構築して、存在しない命令を呼び出すのではなく、AVX2以前のチップでクリーンにフォールバックできるようにしました。これが私が気にかけている部分です。これは、「修正しろ」と一度入力して、動作するパッチが返ってくるというものではありませんでした。誰かが、他の人が書いたパフォーマンスクリティカルなC++を読み、特定のマイクロアーキテクチャでカーネルが無効になっている理由を突き止め、フォークを使用する価値があった最適化をすべて失うことなく、それを回避する必要がありました。Claudeはその作業を行いました。私の仕事はより狭いものでした。適切な実験を実行し、出力が最終的に正しいかどうかを認識することです。私は感銘を受けました。
結果
Gemma 4の26B混合エキスパートモデルは、モデルのアーキテクチャが存在する前に引退したハードウェア上で、読み取り速度でテキストを生成するようになりました。元の記事はトークン/秒の数値を公開しておらず、「読み取り速度」だけでしたので、ここに具体的な数値を記載します。13年前のシリコン上で、ほぼ無料で、秒間約5トークンです。動作証明:地下のボックスで、CPUのみでGemma 4 26Bが応答しています。パッチはikawrakow/ik_llama.cpp#2138で公開されており、正確な差分を確認したい場合は参照できます。執筆時点ではまだオープンでメンテナーのレビュー待ちなので、現時点ではブランチから実行してください。希望としては、古いエンタープライズハードウェアを持っている他の誰もがローカルモデルを維持できるようにすることです。有料APIがダウンした場合のフォールバックとして、またはトークンあたりの料金を支払うのが意味をなさない場合に、低コストで低速なバッチジョブを処理するためです。
実際のバグを知りたい人のために
さらに進む前に、完全な開示を行います。私はC++プログラマーではありません。スタックトレースを読み、ビルドシステムを操作する方法は知っていますが、量子化された行列乗算エンジンのためにカーネルフォールバックを手書きしたわけではありませんし、そうしたと偽るつもりもありません。私がやったのは運転することです。実験を実行し、出力を読み、次の質問をし、「正しい」とはどうあるべきかを知っていました。診断とパッチは、サーバー自体で実行されているClaudeインスタンスから提供されました。それが修正した内容を書き出すように依頼し、このセクションの残りは、わずかに編集されたその要約です。もしあなたがHacker Newsから実際の詳細分析のためにここに来たなら、この部分はあなた向けです。
実際に壊れていたもの
私たちが求めていたエンジンはik_llama.cpp、つまりGemma 4のMoE推論が依存する最適化を追加したllama.cppのikawrakowによるフォークでした。これはAVX2を最低限として想定しています。このボックスのXeon E5-2690 v2はAVX1を持っていますが、AVX2は持っていません。ビルド時にGGML_USE_IQK_MULMATをオフにすると、コードベースのほとんどはそれを尊重します。高速パスはコンパイルアウトされ、モデルは通常のスカラー/SSE数学にフォールバックします。これは通常のQ8_0行列乗算には問題ありません。2つのグラフ演算が例外です。Gemma 4 MoEフィードフォワードネットワークは、MOE_FUSED_UP_GATE(エキスパートごとのゲート+アップ行列乗算をSwiGLUと融合したもの)とFUSED_UP_GATE(その密な類似体)を生成します。どちらも計算ディスパッチャー内のGGML_USE_IQK_MULMATで#ifガードされていますが、グラフビルダーはそれらを無条件に発行します。このビルドでは、ディスパッチャーのスイッチにそれらの演算子の列挙型に対応するケースがなかったため、デフォルトにフォールスルーし、各エキスパートFFNの宛先テンソルは、そのメモリバッファにすでにあったものにサイレントに計算されませんでした。Gemma 4 26Bは30層で、トークンごとにアクティブなエキスパートが8つあるため、各フォワードパスは約240個のテンソルを消費しました。症状は、流暢に見える多言語のナンセンスでした。トークンIDは262Kの語彙全体に均一に広がり、モデルはタイ語、韓国語、<unused>センチネル、または英語の断片を同様に喜んで出力しました。温度0では決定的で、シングルスレッド実行とマルチスレッド実行の間でバイト単位で同一で、NaNはどこにもありませんでした。単に、各レイヤーで大きな定数によって隠れ状態が押し出され、最終的なソフトマックスがフラットになるだけでした。その決定性が、それを解明する鍵となりました。Claudeはサンプリング前の生のロジットを計測し、トップ5トークンと範囲、平均、NaNカウントを出力しました。その数値が決定的な証拠となりました。最初の予測トークンの平均ロジットがゼロ付近であるべきなのに+16になり、語彙の約80%が正のロジットを持つという状態でした。ランダムな破損はそうは見えません。これほどきれいなバイアスは、隠れ状態の大きなチャンクが小さな正の浮動小数点数を含む初期化されていないメモリである場合にのみ発生します。
修正
フォークのメインブランチの上に3つのコミットが追加されました。
コンパイル修正。
iqk_quantize.cppのquantize_row_q8_0_x4およびquantize_row_q8_1_x4_Tのスカラー#elseブランチは、実際にはスカラーではありませんでした。それらはまだhsum_i32_8やその他のAVX2ヘルパーを参照していました。これらはポータブルなスカラーループとして書き直され、ggml.cおよびggml-quants.cから漏れ出ている少数の stray IQK呼び出しの周りに#if GGML_USE_IQK_MULMATガードが追加され、iqk_cpu_ops.cppがスタンドアロンでコンパイルされるための欠落していたインクルードも追加されました。これらがないと、フォークは非AVX2ハードウェアではまったくビルドできません。
ランタイムバグ。
ディスパッチャーを変更する代わりに、修正により、グラフビルダーは、このビルドで計算パスを持つ演算子を発行するようになります。ggml_moe_up_gateでは、GGML_USE_IQK_MULMATがオフの場合:重みが結合されたup_gate_expsテンソル(形状[n_embd, 2*n_ff, n_experts]、ゲートは前半、アップは後半)である場合、それを2つのggml_view_3dスライスに分割し、2つの別々のggml_mul_mat_id呼び出しを実行し、それらをggml_fused_mul_unary(gate, up, SILU)で結合します。ゲートとアップがすでに別々の重みである場合、分割をスキップし、同じ2つのmul-mat-IDと融合mul-unaryを実行します。ggml_fused_up_gate(非MoEレイヤーで使用される密なバージョン)も同様の処理を受けます。関与するすべての演算子は、すでに動作する非IQK実装を持っています(mul_mat_idは標準のggml、fused_mul_unaryはSILUと乗算を1パスで行います)。変更全体は#if !GGML_USE_IQK_MULMATの後ろにあり、AVX2ビルドは