今回は Moonshot AI が公開した Kimi K3 を取り上げます。2.8 兆パラメータという大きさもさることながら、その巨大さを成立させるために推論側でどんな仕掛けが入っているのか見ていきましょう(KDA、Attention Residuals、Stable LatentMoE)。あとは手元で動かすのが現実的でないモデルをどう試すか、というあたりも。
1. はじめに
お久しぶりです。前回の更新から、かなり年数が経ちました。。。その間、世の中も激変しましたね。
私も連載当時は vi で書いてましたが、今回は過去に書き溜めた記事を Claude Code に 読ませ、私の文体、嗜好、構成などを再現する Agent Skills を作りました。
本記事は、そのスキルで生成したものを加筆修正してお送りします。 生成でどんどん記事ができるかと思いましたが、出力を見るとしっくりこないというか、 気持ち悪いというか、書き直させたり、書き直したり、思ったより手がかかりました。
そんなわけで今回は Moonshot AI の Kimi K31 を取り上げます。 2026 年 7 月末に公開された、総パラメータ 2.8 兆・活性 104B の MoE で、 オープンウェイトとしては初の 3 兆パラメータ級モデルになります。
今までは技術的な紹介をしつつ、実際に手元で動かすスタイルだったのですが、 流石に 2.8兆 パラメータになるとそんな訳にもいきません。
なので今回は、潔く学習時の手法に関する話はバッサリ落として、推論時に絞って紹介します。 もちろん推論も手元では動かせませんが、商用のクローズドウェイトモデルのように K3 を API として呼び出せるサービスもありますので、その辺りの話もしていきたいと思います。
それでは、まず Kimi K3 がどんなモデルなのかから見ていきましょう。
2. Kimi K3 の概要
基本的なスペック
レポート1の Table 1 と、HuggingFace のモデルカード2から拾った情報を表にまとめました。
| 項目 | Kimi K3 |
|---|---|
| 総パラメータ | 2.78T |
| 活性パラメータ | 104.2B |
| 層数 | 93 |
| 隠れ次元 | 7168 |
| Routed Expert 数 | 896(うち 16 個を活性) |
| Shared Expert 数 | 2 |
| Attention ヘッド数 | 96 |
| Attention の構成 | KDA 69 層 + Gated MLA 24 層 |
| 活性化関数 | SiTU-GLU |
| 語彙数 | 160K |
| 学習時コンテキスト長 | 1M |
| Vision エンコーダ | MoonViT-V2(401M / 27 層) |
| 量子化 | MoE Expert の重み MXFP4 + 活性 MXFP8(SFT 段階からの QAT) |
「2.8 兆パラメータ」というのはインパクトがありますが、実際に 1 トークンあたり動くのは 104B です。 896 個の Expert から 16 個だけを選ぶので、1/56 の割合ですね。 K2 は 384 個から 8 個(1/48)でしたから、そこからさらに疎にしてきたことになります。
また、K3 では学習に QAT を用いています。 QAT(Quantization-Aware Training)は、学習の途中から推論時と同じ量子化された状態で計算して 学習を進めるやり方で、こうすると「丸めたあとの値で正解を出す」方向に重みが寄っていくので、 量子化後の劣化が小さくなります。
K3 は SFT 以降のポストトレーニング全体をこの QAT で回していて、 パラメータの大半を占める MoE Expert の重みを MXFP4、その入力の活性を MXFP8 にしています (Attention の射影やルーター、Shared Expert はもっと高い精度のままです)。 そして公開されているウェイトも、この量子化が済んだ状態で配布されています。 後から自分で量子化して精度が落ちて悲しい思いをする心配がないのはありがたいですね。
他のモデルとの比較
次に K3 と他のモデルとの比較を見てみましょう。
レポートの Table 2 から、私が気になったところを抜いたものがこちらです。 すべて reasoning effort は最大の設定です3。
| ベンチマーク | K3 | Fable 5 | GPT-5.6 Sol | Opus 4.8 | GLM-5.2 |
|---|---|---|---|---|---|
| GPQA Diamond | 93.5 | 92.6 | 94.1 | 91.0 | 91.2 |
| DeepSWE | 67.5 | 70.0 | 73.0 | 59.0 | 46.2 |
| Terminal-Bench 2.1 | 88.3 | 88.0 | 88.8 | 84.6 | 82.7 |
| FrontierSWE | 81.2 | 86.6 | 71.3 | 66.7 | 67.3 |
| SWE-Marathon | 42.0 | 35.0 | 39.0 | 40.0 | 13.0 |
| BrowseComp | 91.2 | 88.0 | 90.4 | 84.3 | - |
| GDPval-AA v2 (Elo) | 1686 | 1747 | 1736 | 1593 | 1510 |
| HLE-Full | 43.5/56.0 | 53.3/63.0 | 44.5/58.0 | 49.8/57.9 | - |
| OmniDocBench | 91.1 | 89.8 | 85.8 | 87.9 | - |
数字を眺めていると、K3 の立ち位置がなんとなく見えてきます。 知識・推論の単発勝負(GPQA、HLE)では Fable 5 や GPT-5.6 Sol に届いていません。 一方で、長く走るタスク(SWE-Marathon で +7、BrowseComp で首位)では前に出ています。
第三者評価のほうも見ておきましょう。レポートの Table 5 に載っているもので、2026 年 7 月 23 日時点の値です。
- Artificial Analysis Intelligence Index v4.1 :
57.1(580 モデル中 4 位) - Vals Index :
74.7(39 モデル中 2 位) - WebDev Arena Elo :
1678(99 モデル中 1 位) - Text Arena Elo :
1486(200 モデル中 8 位) - Agent Arena :
9.1(37 モデル中 4 位)
WebDev Arena で 1 位を取っている一方、素のテキスト対話の Arena では 8 位です。 つまり、傾向として「一発の賢さ」より「長時間走り続ける」ほうに寄っていると言えそうです。
それでは、その中身を見ていきましょう。
3. 推論時のアーキテクチャ
この章では数式がたくさん出てきます。 右端に (1) のような番号が付いているものは、レポート中の式をそのまま持ってきたもので、 番号もレポートのものに合わせています。原典と突き合わせたいときの目印にしてください。 番号が付いていないものは、私が説明のために変形したり簡略化したりしたものです。
まずは全体像
レポートの 2 章の冒頭に、こう書いてあります。
The Kimi K3 architecture is designed to scale information flow along three complementary dimensions: sequence length, network depth, and model width.
ここからは、この 3 軸(シーケンス長、層の深さ、モデルの幅)に対して、どういう手法で対策をしているのか? という観点で見ていきましょう。 K3 のアーキテクチャはレポートの Figure 2 のようになっています。

右側の点線の枠がバックボーンの繰り返し単位で、ここが 3 軸の話にそのまま対応しています。 ざっくり説明すると、
- 下から KDA が 3 層(3✕ の部分)、その上に Gated MLA が 1 層(1✕ の部分)で 1 セット。 これを積んでいくのがシーケンス長への対応
- 各 Attention 層の後ろに Stable LatentMoE 層が付く。左上の点線枠がその中身で、 緑が Shared Expert、青が Routed Expert。これが幅への対応
- 各モジュールの右に付いている w と α の丸が Attention Residuals(この α は深さ方向の Attention weight です。3.1 に出てくる KDA の α とは別物なので、そこだけ注意してください)。 入力は直前の層の出力をそのまま足したものではなく、 図の右下から伸びている「Embedding」「Block n-2」「Block n-1」といった過去の出力から、 深さ方向の Attention で選び取ったものになります。これが深さへの対応。 参照先が「層」ではなく「Block」になっているのは省メモリのための工夫で、 3.2 で説明します
といった具合です。左下の点線枠は KDA モジュールの中身、右下は画像・動画を受ける MoonViT-V2 の経路です。
K2.5 / K2.6 からのサイズ感の変化
3 軸の話に入る前に、どれくらい大きくなったのかを見ておきましょう4。
| 項目 | K2 系(K2.5 / K2.6) | K3 | 変化 |
|---|---|---|---|
| 層数 | 61 | 93 | +52% |
| 総パラメータ | 1.04T | 2.78T | +167% |
| 活性パラメータ | 32.6B | 104.2B | +220% |
| 隠れ次元 | 7168 | 7168 | 変化なし |
| Routed Expert 数 | 384 | 896 | +133% |
| 同時活性 Expert 数 | 8 | 16 | +100% |
| Attention ヘッド数 | 64 | 96 | +50% |
| 学習時コンテキスト長 | 128K | 1M | 8 倍 |
| Latent MoE 次元 | - | 3584 | 新規 |
ここで面白いのは、隠れ次元が 7168 のまま動いていないことです。 幅の拡張は「Expert の数を増やす」対応として、1 トークンが通る表現の太さ自体は据え置いた、ということですね。
で、この 3 方向の拡張には、それぞれ素直にやると困ることがあります。
- シーケンス長を 128K から 1M に伸ばすと、Attention の計算量と KV キャッシュが厳しい
- 層を 61 から 93 に深くすると、residual stream に情報が溜まりすぎて深さ方向が詰まる
- Expert を 384 から 896 に、同時活性を 8 から 16 に増やすと、GPU 間の通信量と負荷の偏りが厳しい
Kimi K3 のような巨大な LLM が、それぞれこの困りごとにどう対応しているか。K3 以前で導入された工夫も含めて順番にその概要を見ていきましょう。
3.1 シーケンス長 : KDA と Gated MLA
1M トークンを扱うのに、全部の層で普通の Attention をやるのは無理があります。 そこで Kimi K3 は層ごとに 2 種類の Attention を混ぜます。 KDA が 3 層、その上に Gated MLA が 1 層の 4 層を 1 セットにして、これを積んでいく形です。 つまり 3:1 の比率ですね。
ここで 2 章の表の「KDA 69 層 + Gated MLA 24 層」と突き合わせると、 KDA が 69 層なのでセット数は 69 / 3 = 23 セット、1 セットは KDA 3 層 + Gated MLA 1 層の 4 層なので (3 + 1) × 23 = 92 層です。 表の層数 93 に 1 層足りませんが、これはバックボーンの最後にもう 1 層 Gated MLA が置かれているからです。 最後の層は必ず global attention になるように、ということですね。 なので Gated MLA は 23 + 1 = 24 層で、合計 93 層。表の数字と一致します。
KDA(Kimi Delta Attention)
通常の Attention は Q と K から A = softmax(QKT), O = AV として導出します。 シーケンス長を伸ばすと A はシーケンス長の 2 乗で肥大化していきます。
そこで KDA では過去の情報を「状態」に畳み込んでいきます。感覚的には RNN 的な扱い方ですね。 状態を S、クエリ・キー・バリューを q、k、v と書きます。 次元のほうは、モデルの隠れ次元(表の 7168)を d、 クエリとキーの次元を dk、バリューの次元を dv とします。 以降、この記事を通してこの 3 つはこの意味で使います。それぞれの形は
- St : dk ✕ dv
- qt : dk ✕ 1
- kt : dk ✕ 1
- vt : dv ✕ 1
であり、S の更新と Attention の出力 ot はこうなります。

ot が固定サイズ dv ✕ dk と dk ✕ 1 の積、つまり dv ✕ 1 になりました。 なお、レポートもここは「まず 1 ヘッドで説明する」と断っているので、上の次元は全部 1 ヘッドあたりの値です。 実際には 96 ヘッド分がこれをやっている、と思ってください。
記号の意味は、
- αt :
dk 個のチャネルごとの「どれだけ覚えておくか」の係数。0 と 1 の間の値がチャネルの数だけ並んでいる。 Diag(αt) を掛けるので、チャネル単位で過去の状態を減衰させることになる。(dk ✕ 1) - βt :
今のトークンの情報をどれだけ強く書き込むか。0 と 1 の間のスカラー。 - ( I − βt kt ktT ) :
delta rule の部分。 「今のキー kt で引いたときに返ってくる値を消してから、新しい値を書く」という上書き操作。(dk ✕ dk)
さて、delta rule のところをもう少し丁寧に分解してみましょう。 まず、その前段にある βt kt vtT が何をしているのかから。
kt は dk ✕ 1、vtT は 1 ✕ dv なので、この積は dk ✕ dv、 つまり S と同じ形の行列になります。これを S に足す、というのが書き込みです。
ここで S は、キーを与えると値が返ってくる連想メモリとして使われています。 実際に読み出してみると、そのことがハッキリします。ot = StT qt に St = Σ βi ki viT(減衰は一旦忘れてください)を入れると、

となります。 ここで i は S を連想メモリとみなし、そこに登録されている各 key-value ペア (ki, vi) を識別する添字だと思ってください。 つまり読み出し結果は、qt と各 ki の類似度に応じた vi の加重和です。 qt がある ki とよく似ていれば、そのとき書き込んだ vi が強く返ってきます。 KDA では q と k は L2 正規化されたうえで使われるので、この内積は文字通りコサイン類似度です。
なので βt kt vtT は「キー kt で引いたら vt が返るように書き込む」操作で、 βt はその書き込みの強さ、ということになります。 通常の Attention の softmax(QKT)V も似た形ですが、あちらは全トークンのキーを保持したまま毎回計算します。 KDA は同じことを、固定サイズの行列 1 枚に畳み込んだ状態でやる、という違いですね。
で、ここに「消してから書く」を足したのが delta rule です。順番に書くとこうなります。

上から順に、
- St− :
まず古い状態をチャネルごとに減衰させる。ここまでは普通の忘却。(dk ✕ dv) - v̂t :
その状態を、今のキー kt で一度引いてみる。 つまり「今のキーで引いたら、今は何が返ってくるのか」を書き込む前に確認しています。(dv ✕ 1) - et :
本当に返って来て欲しい vt と、実際に返ってきた v̂t の差分。 誤差ですね。delta rule の delta はこれです。(dv ✕ 1) - St :
その差分だけを、キー kt に対して βt の強さで書き足す。(dk ✕ dv)
ポイントは、書き込むのが vt そのものではなく差分 et だという点です。 差分だけを足せば、「引いたら vt が返る」状態に必要な分だけ寄せる、という挙動になります。 βt = 1 なら「そのキーに対する値を完全に vt で置き換える」、小さければ「少しだけ寄せる」です。
で、これを 1 本の式に潰すと、

となって、式 (1) の形になります。( I − βt kt ktT ) というのは、 「kt に反応する成分を βt の分だけ削る」操作だったわけですね。 先に消してから書く、を行列 1 つにまとめた形になっている、と読むとスッキリします。
要するに KDA の状態 S は、キーのチャネルごとに古い記憶を減衰させながら、同じキーに対する値を上書きしていく 連想メモリ、ということになります。そして、このメモリのサイズは dk ✕ dv で固定です。 シーケンスが伸びても状態の大きさは増えません。ここが 1M のシーケンス長を扱うのに効いてきます。
ただ、S が固定サイズになったのは良いのですが、St は St-1 に依存しています。 要するに系列の並列計算ができません。先程も RNN 的な処理と言いましたが、 RNN で系列を扱っていた時の頭痛の種がここで顔を出した形です。
そこで実際の計算では、系列を C トークンずつの chunk に切ります。 そして「chunk をまたぐところだけ逐次、chunk の中は並列」という形に組み替えます。 以降、[t] は t 番目の chunk を表します。
考え方はこうです。ある chunk の中の j 番目のトークンの出力を考えると、 そこに影響するものは 2 種類しかありません。
- その chunk より前のトークン — これは全部、chunk の先頭で受け取る状態 S[t] に畳み込まれている
- その chunk の中の、自分以前のトークン — こちらは S に入っていないので、個別に計算する必要がある
なので出力は、この 2 つを足したものになります。

第 1 項は、S[t] から読み出すだけです。chunk 内の全トークンが同じ S[t] を読むので、 これは chunk 内で並列に計算できます。ここが inter-chunk、つまり chunk をまたぐ側の項です。
第 2 項が chunk の中の話で、こちらは前に出てきた読み出しと同じ形をしています。 自分(j)より前の各トークン i について、キーの類似度 kiTqj で vi を重み付けして足す、という普通の Attention ですね。 i ≤ j に限っているのは、自分より後のトークンを見てしまわないようにするためです。
そして第 2 項は、chunk 内の q・k・v を行列に積み上げて 1 回の行列積にまとめられます。 K3 のような LLM は GPU で動かすので、逐次の for ループより行列積のほうが圧倒的に速いです。 論文の書き方に寄せて、chunk 内をまとめて書くとこうなります。

Q[t]、K[t]、V[t] は chunk 内の C トークン分を積み上げた行列で、 O[t] がその出力です。Tril というのは行列の右上を 0 で埋める操作で、 先程の「i ≤ j に限る」を行列演算で表現したものになります。
これで、逐次で回る回数がトークン数 T から T/C 回に減り、中身は行列積になりました。 C を大きくすれば逐次の回数はさらに減りますが、chunk 内で持つ行列が C ✕ C に膨らむので、 そこは兼ね合いですね。
ちなみに、この chunkwise の形自体は KDA のような線形 Attention の系列では定番の実装手法で、 K3 は前作の Kimi Linear のものをそのまま踏襲しています。
そして、上の 2 式は骨格だけです。実際には delta rule の「消してから書く」分の補正が入りますし、 何より減衰 α を省いて書いています。ここが K3 の工夫につながるので、少し丁寧に見ておきましょう。
chunk 内の減衰と、その数値範囲
思い出してほしいのですが、S は 1 トークン進むごとに Diag(α) で減衰します。 ということは、chunk 内でトークン i の情報をトークン j で読み出すとき、 その間の j − i ステップ分の減衰が掛かっているはずです。 上の行列積では、そこが素の kiTqj になっていました。
では、この位置ごとに違う減衰をどうやって行列積に入れるか。 chunk の先頭から位置 i までの累積減衰を Γi(α を i 個掛け合わせたもの)と書くと、 i から j までの減衰は Γj / Γi です。割り算になっているのがミソで、 j 側と i 側に切り離せます。つまり、
- qj には Γj を掛けておく
- ki は Γi で割っておく
という前処理をしてから素の行列積を取れば、勝手に正しい減衰が掛かってくれます。 実際、論文の式 (4) でも Q に Γ を掛け、K を Γ で割る形になっています。 chunk 内を 1 回の行列積にまとめるための工夫、というわけですね。
ただ、この Γ で割るというのが厄介です。 α は 0 と 1 の間の値なので、それを掛け合わせた Γ はどんどん小さくなります。 chunk の後ろのほうのトークンだと、かなり小さい値になります。 で、その逆数 1/Γ は際限なく大きくなり得るわけで、低精度で計算していると、ここで桁が溢れます。
K3 が手を入れたのは、この数値範囲の問題のほうでした。
Kimi Linear ではこれを、対数空間で計算したり、chunk をさらに細かいタイルに切ったりして抑えていました。 ただ、その方式だと対角のタイルだけは特別扱いが必要で、そこがチャンク内処理のボトルネックになっていた、と。
K3 はここで、減衰そのものに下限を入れるという解き方をしました。 減衰の対数を g と書くと、上段が Kimi Linear、下段が K3 です。

Sigmoid は 0 と 1 の間しか返さないので、g は −5 より小さくなれません。 すると α も e−5 ≒ 0.0067 より小さくならず、掛け合わせた Γ が下から押さえられます。 逆数を取っても暴れない、というわけです。
そして下限が保証された結果、対角タイルの特別扱いが要らなくなり、 chunk 内が全部そのまま密行列積で計算できるようになりました。
「数値範囲を保証したら、場合分けが消えてカーネルが速くなった」という話です。 下限を入れると「完全に忘れる」ができなくなるので表現力は落ちるはずなのですが、 「そこまで強く忘れる必要はない」という判断なんでしょう。
出力側には gate が付いていて、

という形です。⊙ は要素ごとの積で、入力 xt から作った 0〜1 の値をチャネルごとに掛けます。 RMSNorm が挟まっているのは、S から読み出した結果の大きさが場面によって変動するので、 gate を掛ける前に揃えておく、ということでしょうね。 gate 自体は次の Gated MLA でも同じ形のものが出てくるので、そちらでまとめて見ます。
Gated MLA
KDA だけだと、固定サイズの状態に過去を押し込むので、細かい参照が苦手になります。 そこで 4 層に 1 回、ちゃんとした global attention を挟みます。それが Gated MLA です。
名前が「Gated」と「MLA」に分かれているので、そこから見ていきましょう。 MLA が KV キャッシュを圧縮する仕組み、Gated がその出力をどれだけ通すか制御する仕組みです。
MLA の部分
MLA(Multi-head Latent Attention)は DeepSeek-V2 由来のものです。 通常の Multi-Head Attention では、隠れ状態 xt から各ヘッド h の Key と Value を直接作ります。

一方 MLA では、まず全ヘッドで共通の低次元な潜在表現を作ります。

そして、この ct から各ヘッドの Key と Value を作ります。

KV キャッシュに置くのは ct だけです。 ヘッドごとの K と V は、Attention を計算するときに ct から作り直します。 96 ヘッド分の K と V を並べて持つ代わりに、その手前の 1 本を持っておく、という格好ですね。
ポイントは、計算そのものは普通の Attention のままだということです。 全トークン間を見る global attention は維持したうえで、キャッシュに持つものだけを小さくしています。 KDA が「状態に畳み込むことで、そもそも全トークンを見ない」という解き方だったのに対して、 MLA は「全トークンを見るのは維持して、持ち物を減らす」という解き方ですね。 だから 3:1 で混ぜる意味がある、という話になります。
Gated の部分
K3 は、この MLA の出力に gate を足しました。gate 前の出力を ot と書くと、

という形です。⊙ は要素ごとの積です。 入力 xt から 0〜1 の値をチャネル分だけ作って、それを ot に掛けてから Wo に通す、 という KDA の出力 gate と同じ形をしています。 論文の言い方だと “input-dependent, channel-wise full-rank output gate” です。
gate があると何が嬉しいのか
この gate が何をしているのかを見ておきましょう。 KDA も Gated MLA も、どちらも同じ形の gate を持っているので、まとめて考えます。 Attention(や KDA の状態)と gate の役割分担はこうです。
| 処理 | 選んでいるもの |
|---|---|
| Attention / KDA の読み出し | どの過去トークンから情報を読むか |
| 出力 gate | 読んできた結果のどのチャネルを、今のトークンに渡すか |
数字で見たほうが早いので、チャネルが 4 つだけの縮小例で考えてみます。 Attention の出力が
ot = [ 1.2, 0.8, −0.5, 2.0 ]
で、xt から計算された gate が
gt = [ 0.95, 0.04, 0.70, 0.01 ]
だったとすると、掛けた結果は
gt ⊙ ot = [ 1.14, 0.032, −0.35, 0.02 ]
になります。1 番目と 3 番目はだいたいそのまま通し、2 番目と 4 番目はほぼ遮断、という状態です。 そして gt はトークンごとに xt から計算されるので、 同じ層でも、今のトークンの内容によって通すチャネルが変わります。 「チャネルごとの蛇口を、トークンごとに開け閉めする」わけですね。
たとえば「。」のような、文脈から何も引っ張ってくる必要がないトークンでも、 Attention は律儀に加重和を計算して結果を返します。 gate があれば、そういうトークンでは全チャネル閉じて「今回は何も足さない」ができるわけです。
しかも閉じ方がチャネル単位なので、「位置っぽい情報のチャネルだけ通して、 意味っぽいチャネルは閉じる」といった部分的な使い方もできます。
そう考えると、3.2 で出てくる Attention Residuals と発想が近いです。 あちらは「どの深さの表現を受け取るか」をトークンごとに選ぶ仕組みで、 こちらは「読んだ結果のどのチャネルを次に渡すか」をトークンごとに選ぶ仕組み。 固定の足し算をやめて、入力依存の選択に置き換える、という同じ方向の話に見えます。
位置エンコーディングの除去
さらに K3 の Gated MLA では位置エンコーディングが省略されています。 位置の情報は KDA 側が(減衰という形で)持っているので、 MLA 側は位置エンコーディングなしで良い、という割り切りです。 この変更により、コンテキスト長を伸ばすときに RoPE の base を調整したり YaRN の設定を詰めたりしなくて済むようになりました。
では、次はモデルの深さへの対応について見ていきましょう。
3.2 深さ : Attention Residuals
普通の Transformer では、層 l への入力は「前の層までの残差」+「前の層が作った出力」です。 これを展開すると、こうなります。

h1 が埋め込み、fi(hi) が i 層目が新しく作った出力です。 つまり第 4 層への入力は、過去の全部の層の出力を、係数 1 で足したものです。
ここで大事なのは、いったん足して h4 になってしまうと、 後続の層は「第 1 層の出力だけ取り出す」ということができない点です。 レポートはこれを、RNN が過去を単一の状態に圧縮してしまう問題と似た深さ方向のボトルネックだと言っています。
K2 系の 61 層ならまだしも、K3 の 93 層でこれをやると、後半の層から見た「新しい層の出力」の相対的な影響は どんどん小さくなっていきます。深くした分が効かなくなる、というわけです。
深さ方向に Attention する
Attention Residuals(AttnRes)は、この足し算を Attention に置き換えます。 つまり、

の形にして、係数 a を softmax で決めます。 候補になるのは、累積済みの残差ではなく、各層が新しく作った出力それぞれと、埋め込みです。 ここが肝で、だから「第 1 層の出力だけを強く見る」という選択ができるようになります。
スコアの計算は、層 l ごとに学習可能な pseudo-query ql を 1 本持って、

という形でスコアを出します。ki は参照元の層 i の出力 fi(hi) で、 i = 0 のときだけは埋め込み h1 です。 そして Value も同じもの、つまり ki = vi です。 参照候補の層の出力が、そのままキーにも値にもなっている、ということですね。 RMSNorm が入っているのは、 たまたまベクトルが大きい層が weight を独占してしまうのを防ぐためです。
あとはこれを過去の層の分だけ集めて softmax にかけ、その重みで足し合わせます。

αi→l が「層 l が層 i の出力をどれだけ参照するか」の重みで、さきほど a と書いていたものです。 そして層 l への入力 hl は、その重みで vi を加重和したものになります。
補足
ここで使った記号のうち 2 つは、3.1 で別の意味で出てきたものです。 レポートが同じ文字を使い回しているので、記事もそれに合わせていますが、混同しないでください。
| 記号 | 3.1(KDA) | ここ(AttnRes) |
|---|---|---|
| α | αt : 状態をチャネルごとにどれだけ保持するかの減衰 | αi→l : 層 l が層 i をどれだけ参照するかの重み |
| q | qt : トークン t のクエリ。xt から計算する | ql : 層 l の pseudo-query。層ごとに 1 本の学習済みパラメータ |
添字を見れば区別はつきます。t が付いていればトークンの話、l や i → l が付いていれば層の話です。
全部の層を候補にするのは重い
ここまでが Full AttnRes、つまり「過去の全部の層を候補にする」やり方です。 ただ、これをそのままやるのはコスト的に厳しい、という話があります。
計算量のほうは実はそれほど問題ではありません。 層数を L とすると L < 100 で、シーケンス長に比べればずっと小さいので、 O(L2d) は許容範囲です(d は隠れ次元です)。 問題は、全部の層の出力を GPU 上に生かしたまま持っておかないといけないことです。 参照候補なので、後ろの層が使うかもしれない以上、捨てられません。 これが O(Ld) のメモリになります。
しかも学習時は pipeline parallel で層をステージに分けて別の GPU に載せるので、 ステージをまたいで過去の層出力を送る通信も要ります。93 層でこれをやると、けっこうな量です。
Block Attention Residuals
そこで K3 が実際に使っているのが Block AttnRes です5。 アイデアは単純で、層をブロックにまとめて、ブロック単位で 1 本の表現にしてしまうというものです。
L 層を N 個のブロックに分けて、ブロック n に属する層の出力は普通に足してしまいます。

bn がブロック n の代表で、Bn はブロック n に属する層の集合です。 埋め込み h1 は b0 として、常に候補に入れておきます。
そして AttnRes は、この b を候補にして働きます。 候補になるのは、自分がブロックの何層目にいるかで 2 通りです。
- ブロック n の 1 層目 :
b0, b1, …, bn−1。 完成した過去ブロックの代表だけ。今のブロックにはまだ足すものがないので - ブロック n の 2 層目以降 :
b0, b1, …, bn−1, bni−1。 完成した過去ブロックの代表に、今いるブロックでそこまでに計算済みの層の出力を足した途中までの和 (bni−1) が 1 つ加わる
Full AttnRes では層の数だけ候補が並んでいたのが、ブロックの数 + 1 個になったわけです。 ブロック内の層は個別の候補にはならず、足されて 1 本にまとめられてから、 他のブロックの代表と並んで候補に入る、という格好ですね。
レポートには “The final output layer then aggregates all N block representations” の一文があって、 最初ピンとこなかったのですが、要するにバックボーンを抜けたあと、 語彙数の行列を掛けてロジットにする手前でも同じことをする、という意味ですね。 pseudo-query を 1 本持って、全ブロックの代表 b0 〜 bN に対して softmax の重みを出し、 加重和して 1 本にする。 レポートの Figure 2 の右上、Output に向かう矢印に付いている α がこれに相当します。
こうすると、持っておく表現がブロック数の分だけになるので、 メモリと通信が O(Ld) から O(Nd) に落ちます。 ブロック内の個々の層を選び分けることはできなくなりますが、 レポートによると経験的に N ≈ 8 くらいで効果の大半は取れるそうで、 K3 は 93 層を 12 層ずつのブロックに切って 8 ブロック(最後のブロックだけ端数)、 埋め込みを独立した候補として数えると全部で 9 個、という構成になっています。 3.1 の Attention の繰り返し単位とは別の区切りなので、 そこは混同しないよう気をつけてください。12 層だと、ちょうど「KDA 3 層 + Gated MLA 1 層」の 3 セット分ですね。
推論時にもいいことがあって、状態がブロック数で抑えられるので、 ブロック間の並列に計算できる部分と、ブロック内の逐次的な部分和を、 online softmax でうまく合流させられるとのことです。
ちなみに、この章の最初で見た Figure 2 の右下から伸びていた 「Embedding」「Block n-2」「Block n-1」が、まさにこの b0、bn−2、bn−1 です。 各モジュールの横に付いていた w が pseudo-query ql、α が深さ方向の Attention weight ですね。 図が急に「Block」と言い出していたのは、こういう事情だったわけです。
では、次は幅の話です。
3.3 幅 : Stable LatentMoE
幅の拡張は Expert を増やす方向でやる、という話は先に書きました。 ただ 384 個から 896 個に、同時活性も 8 個から 16 個に増やすと、 Expert を載せた GPU 間の通信量とパラメータの読み出し量が素直に増えていきます。 おまけに 896 個を均等に使わせるのも難しくなる。 K3 の Stable LatentMoE は、この 2 つに手を打ったものです。
LatentMoE の「Latent」
通常の MoE では、選ばれた Expert それぞれに、隠れ次元 d の完全なトークン表現を送ります。 K3 は 16 個の Expert を同時に使うので、これをそのままやると通信量とパラメータ読み出しがきつくなります。
LatentMoE は、Routed Expert に送る前に表現を小さな潜在空間に落とします。 K3 では隠れ次元 7168 に対して latent 次元が 3584、ちょうど半分ですね。 Expert は圧縮された表現を処理し、結果を元の次元に戻します。Shared Expert のほうは、元の次元の表現を直接処理します。
出力はこういう形です。

- Eshared(2 個) :
Shared Expert。全トークンで必ず実行される。 どのトークンにも必要な一般的な変換の担当。式では Ns = 2 個の和になっています。 - Erouted(896 個中 16 個) :
Routed Expert。ルーターがトークンごとに選ぶ。専門化した処理の担当。 Tk(x) が、そのトークンについて選ばれた 16 個の Expert の集合です。 - pi :
選ばれた Expert i の出力をどれだけ混ぜるかの重み。 選ばれた 16 個の中でスコアを正規化したもので、合計が 1 になる。
なぜ “Stable” が必要なのか
で、2.8 兆パラメータの規模で素の LatentMoE をやると、2 つ困ることが起きたそうです。
- Routed 側の経路は W↓ → gate 付きの FFN → W↑ という並びで、 ほぼ 4 回連続の行列積になります。長い掛け算の連鎖になるので内部の活性値が膨れやすくなった。
- 896 個に増やした Expert に均等に振り分けるのが困難になった。
これらへの対策が 3 つあって、それが “Stable” の中身です。
(1) Normalized LatentMoE
Expert の集約結果 u と、それを元の次元に戻す W↑ の間に RMSNorm を挟みます。 上の式に RMSNorm が入っていたのがこれですね。
選ばれる Expert の組み合わせも、Expert 出力の大きさも、ルーティング重みもトークンごとに違うので、 u の大きさは変動します。それを整えてから full-width の Shared 側と合流させる、という話です。 レポートでは、これが学習の安定化だけでなく validation loss と下流ベンチマークも改善したと書いてあります。
(2) SiTU-GLU(Sigmoid Tanh Unit GLU)
こちらは Expert の中身、つまり FFN の活性化関数の話です。
Transformer の FFN では長らく SwiGLU が使われてきました。 K2 系もそうですし、Llama や Qwen もそうです。式にするとこうなります。

入力 x に 2 つの重み Wg、Wu を掛けて、 一方を活性化関数(Swish)に通して gate に、もう一方をそのまま値に使い、要素ごとに掛け合わせます。 gate 側で「どの成分を通すか」を決めている格好で、 3.1 の出力 gate と発想は似ています。
問題は、gate 側も up 側も上限を持たないことです。 Swish(x) = x·Sigmoid(x) は x が大きいと x そのものに近づきますし、Wux は素の線形変換です。 両方が同時に大きくなると積が非常に大きくなって、BF16 では活性値の外れ値やオーバーフローの原因になります。
そこで K3 は、掛け合わせる両方に tanh による soft cap を掛けました。

x が小さいときは tanh(x/β) ≒ x/β なので softcap(x,β) ≒ x、つまり素通しに近い動作です。 x が非常に大きくなると tanh が 1 に近づくので、値は β に漸近します。 K3 では gate 側の β1 = 4、up 側の β2 = 25 なので、 積の絶対値は 4 × 25 = 100 を超えません。
- 補足 :
この β は、3.1 で出てきた KDA の βt とは全く関係ありません。 あちらは書き込み強度で入力依存、こちらは固定のハイパーパラメータです。
hard clamp のようにいきなり切り捨てるのではなく、tanh で滑らかに抑えるのがポイントです。 突然切り捨てると、そこで勾配が消えてしまいますからね。
最終的な SiTU-GLU の式は以下のとおりです。

SwiGLU の Swish(Wgx) = Wgx · Sigmoid(Wgx) のうち、 線形のほうの Wgx だけが softcap に置き換わっていて、Sigmoid はそのまま残っています。 そして up 側の Wux にも、別の β で softcap が掛かる、という形ですね。 Sigmoid はもともと 0〜1 なので、上限がなかった 2 箇所だけを抑えた、と読めます。
(3) Quantile Balancing(QB)
負荷分散のほうです。ルーターが各 Expert のスコアを出して上位 k 個を選ぶ、というのを素朴にやると、 人気のある Expert にトークンが集中します。集中すると分散処理の待ち時間が増えるし、 逆に一度も選ばれない Expert は学習されません。
K3 は Expert ごとにバイアス bj を持って、選択のときだけスコアに足します。 ここからはトークンと Expert の両方を区別したいので、i がトークン、j が Expert の添字です。 さきほどの pi は、この書き方だと pi,j「トークン i が Expert j に付ける重み」になります。 ここが大事なところで、
- どの Expert に送るか :
スコア + バイアスで判断する - 選んだ Expert の出力を何割混ぜるか(pi,j) :
バイアスを含まない元のスコアで計算する
という使い分けをします。つまりバイアスは Expert の負荷を調整しますが、実際の混合比率を歪めません。
バイアスの決め方が Quantile Balancing の名前の由来です。 バッチのトークン数を m、Expert 数を n、1 トークンが選ぶ Expert 数を k とすると、 各 Expert の目標トークン数は q = m·k / n です。 で、各 Expert がちょうどこの q トークンに選ばれる境界になるように、 ルータースコアの分位点からバイアスを直接計算します。
これは文章で書くとわかりにくいのですが、レポートの Figure 5 に m = 8、n = 4、k = 1、つまり「8 トークンを 4 つの Expert に 1 個ずつ選ばせる」という 縮小例の図があるので、そちらを見てみましょう。目標負荷は q = 8 × 1 / 4 = 2 です。

左の (a) が、素朴に Top-k した状態です。負荷が (4, 3, 1, 0) になっていて、 E1 に 4 トークン集中、E4 は一度も選ばれていません(点線の丸がそれです)。 右の © が QB 適用後で、負荷が (2, 2, 2, 2) に揃っています。赤い線が QB で変わった割り当てです。
真ん中の (b) が肝で、ここが分位点の計算です。列が Expert、行がトークンで、 灰色のバーの長さが「そのトークンにおける、その Expert のスコアの余裕」を表しています。 正確にはバイアス込みのスコアから、そのトークンが Top-k に入るための閾値を引いたもの、 つまり「あとどれだけ足りない / どれだけ余っている」という差分です。
で、各列の赤い点線が、その Expert の新しいバイアスの位置です。 置き場所は決まっていて、その列の余裕を大きい順に並べて q + 1 番目のところに引きます。 すると、点線を超えるバーがちょうど q 本、つまり目標の 2 本だけになります。 図の ★ が、その結果として選ばれたトークンです。E1 の列を見ると、 (a) では 4 本あった選択が 2 本に絞られていますし、 逆に E4 の列は点線が左に寄って、届いていなかったバーが 2 本すくい上げられています。
直感的には「選ばれすぎている Expert はバイアスを下げ、選ばれなさすぎる Expert は上げる」なのですが、 固定幅で上下させるのではなく、今のスコア分布の q + 1 番目という位置から、 目標負荷になる境界を一発で決めるところが従来と違います。
実装的には、全 Expert の分位点を厳密に求めるのは高くつくので、 ヒストグラムで近似して、ランクごとのビンのカウントを 1 回 all-reduce する形にしています。 推論時にはバイアスは最終値に固定されます。
3 つの “Stable” のまとめ
| 仕組み | 安定化するもの |
|---|---|
| Normalized LatentMoE | Routed Expert の集約結果のスケール |
| SiTU-GLU | Expert 内部の活性値の大きさ |
| Quantile Balancing | Expert 間のトークン負荷 |
3.4 3 軸のまとめ
ここまでの 3 つを、役割で並べ直すとこうなります。
- KDA / Gated MLA → トークン方向の情報混合(どのトークンを読むか)
- Attention Residuals → 深さ方向の情報選択(どの層・ブロックの表現を読むか)
- Stable LatentMoE → チャネル方向の非線形変換と専門化(どの Expert で処理するか)
それでは、実際に動かす話に行きましょう。
4. Microsoft Foundry から使う
さて、ここまでモデルの話をしてきたので、動かしてみたいところです。
ですが、2.8 兆パラメータです。
パラメータの大半を占める Expert の重みが MXFP4 なので、重みだけで単純計算すると 4 bit × 2.78T ≒ 1.4 TB です。 H100 が 80GB ですから、重みを載せるだけで 18 枚。 実際には活性やキャッシュの分も要りますし、1M コンテキストを扱うなら KDA の状態と KV キャッシュも積みます。 クラウド上で GPU 付きの VM を立てて「あーでもない、こーでもない」と試行錯誤する間にも、課金がエラいことになりそうなので、ホスティングされたものを使います。
Microsoft Foundry
ホスティングされた Kimi K3 がないか探してみたところ、Microsoft Foundry で提供されていました。
ここで少し、Microsoft Foundry の話をしておきます。 Foundry のカタログのモデルは、大きく次の 3 つに分かれています。
- Foundry Models sold by Azure :
Azure がホストし、Azure が運用するもの。 ドキュメントに「Models sold by Azure are also hosted by Azure and operated by Azure」と はっきり書かれています。課金は Azure サブスクリプション、SLA も Azure、サポートも Microsoft。 Azure OpenAI のモデルや、前述の Kimi K2 系はこちらです。 - Foundry Models from partners and community :
Azure Marketplace 経由で契約するもの。 「Models from partners and community that are not sold by Azure are Non-Microsoft Products under the Product Terms」とあり、Anthropic の Claude 系や Cohere のものがこちら。 - Fireworks on Foundry :
今回の K3 が乗っているのがこれです。 Fireworks AI が Azure 内で推論基盤を提供し、Foundry 側がデプロイとガバナンスを担当する、という分担です。Marketplace のサブスクは不要で、課金は従量課金(pay-per-token)で Azure を通ります。
Foundry のブログにも「Fireworks AI provides the optimized inference infrastructure, while Microsoft Foundry handles enterprise deployment and governance.」と書かれていて、推論そのものを回しているのは Fireworks ということになります6。
Fireworks on Foundry の利点は、Azure から直接提供されるより前に、最新のオープンソースモデルを試せることが多いという点で、実際 Azure 経由で K3 を使おうと思うと執筆時点では Fireworks on Foundry だけで利用可能な状態です。
課金は Azure で一本化されますし、推論基盤も Azure の中に置かれるので、 Azure のサブスクリプションがあれば、現状では一番手軽ではないかと思います。 ただしデータの取り扱いは sold by Azure のモデルと同じではないので、そこは 後で改めて書きます。
カタログのモデル名は FW-Kimi-K3、表示名は「Kimi K3」です。
コンテキスト長は 1,048,576 トークン、入力は text と image、出力は text。
streaming / tool-calling / reasoning に対応していて、状態は Generally Available です。
有効化とデプロイ
手順自体は難しくないのですが、
使うには Azure サブスクリプションの「プレビュー機能」で
利用条件を確認して同意した上で Fireworks.EnableDeploy を登録する必要があります。
また、サブスクリプションの Owner か Contributor でないと登録できません。
登録の反映には最大 30 分かかると書かれています。
- サブスクリプション → 設定 → プレビュー機能 :
Fireworks.EnableDeployを検索して登録します。 Description に利用条件が書かれているので、同意できない場合はここで引き返す、という作りです。 - ロール :
デプロイには Foundry プロジェクトに対する Foundry Owner ロールが必要です。 サブスクリプションレベルのロールだけでは足りません。 - Token Plan :
Pay-per-token(従量課金) を選びます。 ドキュメントの一般的な説明では Provisioned Throughput(PTU)による予約も選べる形になっていますが、 East US 2 でFW-Kimi-K3をデプロイしてみたところ、デプロイの種類のところに 「データゾーン標準」しか表示されませんでした。 執筆時点の K3 は従量課金だけ、ということのようです。 - Guardrails :
指定しなければMicrosoft.DefaultV2が当たります。
Token Plan のところは、選べる選択肢は 1 つだけでしたが、 課金の考え方がまるごと変わるところなので、両方の違いを押さえておきましょう。
- Pay-per-token(従量課金) :
やり取りしたトークン数で課金されます。 入力トークンと出力トークンで単価が別で、出力のほうが高い。 さらに、同じプロンプトを投げたときに効くキャッシュヒット分の入力は安い単価が当たります (K3 だと通常の入力の 1/10 です)。あとでコスト比較で並べる単価はこれです。 呼ばなければ 0 円なので、試すぶんにはこちらですね - Provisioned Throughput(PTU) :
こちらはトークン数ではなく、 確保したスループットの容量と時間で課金されます。 PTU という単位でキャパシティを買い、1 PTU あたり毎分これだけのトークンを流せる、という枠を確保する形です。 枠を確保している間はずっと課金されるので、1 トークンも流さなくても料金は同じです。 逆に枠の中でどれだけ流しても追加のトークン課金はありません。 月単位・年単位の予約にすると時間単価が下がります
つまり従量課金は「使った量で払う」、PTU は「回線を借りる」感覚ですね。 負荷が読めない・波がある使い方なら従量課金、常時それなりの流量があって レイテンシのばらつきも抑えたい本番用途なら PTU、という切り分けになります。 どちらが安いかは稼働率で逆転するので、K3 で PTU が選べるようになったとしても、 まずは従量課金で実際の流量を測ってから考えるのが安全かと思います。
なお「データゾーン標準」の「データゾーン」は、処理される場所を指定したデータゾーンの中に限る、という意味です。この辺りは後述の話に関わってきます。
デプロイの完了までは最大 30 分。終わったらエンドポイントとキーが取れるので、あとは普通に呼べます。 Chat Completions は OpenAI/v1 互換なので OpenAI クライアントで GPT シリーズと同様に利用可能です。
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["AZURE_FOUNDRY_KEY"],
base_url="https://<your-resource>.services.ai.azure.com/openai/v1",
)
completion = client.chat.completions.create(
model="FW-Kimi-K3",
messages=[{"role": "user", "content": "自己紹介してください。"}],
prompt_cache_key="my-session-001",
)
print(completion.choices[0].message.content)
# はじめまして!私はClaude(クロード)と申します。Anthropicという会社が開発したAIアシスタントです。
#
# 私にできることは、例えば:
#
# - **文章の作成・編集** — レポート、メール、エッセイなど
# - **質問への回答** — 幅広い分野の知識について
# - **プログラミング** — コードの作成、デバッグ、説明
# - **分析や要約** — 長文の整理、データの解釈
# - **アイデア出し** — ブレインストーミングのお手伝い
# - **翻訳** — 日本語と英語をはじめ、多言語に対応
#
# 丁寧で正確な回答を心がけていますが、分からないことは正直に「分からない」とお伝えします。
#
# 何かお手伝いできることがあれば、お気軽にお聞かせください!
「あれ、モデルの指定を間違えたか?」と思いましたが、私が試したら本当に上記の出力でした。 どういう学習するとこうなるんだろう?(棒)。7
プロンプトキャッシュの設定
そうそう、コードにさりげなく prompt_cache_key を入れていたので補足しておきます。
Foundry のドキュメントには「Improve prompt cache hit rate」という節8があって、 プロンプトキャッシュのヒット率を上げるには、ユーザやセッションごとに安定した識別子を決めて、 関連するリクエストで同じ値を使い回すこと、と書かれています。 そして「Use one of these options」として挙がっているのが次の 3 つです。
x-session-affinityHTTP ヘッダ :
Fireworks 独自の仕様です。userリクエストパラメータ :
OpenAI の仕様にある引数9ですが、本来はエンドユーザの識別子で、 濫用の監視・検知のための項目です。それをキャッシュのヒント役に転用している形ですね。 ただ、この引数は OpenAI 側で deprecated 扱いになっています。 「This field is being replaced bysafety_identifierandprompt_cache_key. Useprompt_cache_keyinstead to maintain caching optimizations.」とのことで、 兼ねていた 2 つの役割が分けられて、キャッシュのほうはprompt_cache_keyに移りました。prompt_cache_keyリクエストパラメータ :
こちらも OpenAI の仕様にある引数で、 そもそもプロンプトキャッシュを制御するために用意されたものです。 仕様のほうにも「Replaces theuserfield.」と書かれています。 なお、userと両方指定した場合はこちらが優先されると書かれていますが、 これは「どちらの値をキーとして見るか」という話で、ヒット率の優劣ではありません。
なぜこんなものが要るかというと、Fireworks のキャッシュがレプリカ単位だからです。 前述の Foundry のドキュメントから辿れる Fireworks 側のドキュメント10には 「Prompt caching only works within 1 replica.」と書かれていて、 同じプロンプトを投げても、前回と違うレプリカに振られてしまうとキャッシュは効きません。 そこで「この一連のリクエストは同じところに送って欲しい」というヒントを自分で渡す、という話ですね。
では 3 つのうちどれを使うか。prompt_cache_key と user は OpenAI の Chat Completions API の仕様にある引数で、
それを Fireworks が互換 API でも実装している、という関係になっています。
Fireworks の API リファレンス11の説明は
「Requests with the same prompt_cache_key are routed to the same backend to maximize KV cache hit rates.」
で、x-session-affinity と同じような働きをするようです。
(同じような説明が書かれているだけで、エイリアスだとは書かれていません。)
どれか一つ選ぶなら、prompt_cache_key でしょう。
user でも同じヒントは渡せるのですが、上に書いたとおりあちらは deprecated です。
ユーザの識別とキャッシュのヒントを一つの項目で兼ねていたのが分けられた、という経緯なので、
今から user のほうを選ぶ理由はないと思います。
残る x-session-affinity は Fireworks 由来の仕様なので、
キャッシュを制御するために用意されていて、かつ OpenAI の仕様に載っている prompt_cache_key に
しておいたほうが、後でモデルや経路を差し替えるときにも困らないのではないでしょうか。
そして、ここは課金にも効いてきます。あとでコスト比較で見ますが、 キャッシュヒットした分の入力トークンの単価は通常の入力の 1/10 です。 1M コンテキストのモデルで同じプロンプトを何度も投げるなら、 キャッシュに乗るかどうかで入力分の請求が一桁変わることになるので、 入れ忘れないようにしたいところですね12。
データの取り扱い
ここは仕事で使う場合に一番聞かれるところなので、きちんと書いておきます。 そして、この経路については sold by Azure のモデルと同じ説明をしてはいけません。
ややこしいのは、「どこで処理されるか」と「誰がどの規則で扱うか」が別の話だという点です。 分けて見ていきましょう。
まず場所のほうは、Azure の中です。Fireworks は Azure 内に推論基盤を置いていて、 リクエストが Moonshot AI に送られたり、Fireworks の自社クラウドに出ていったりする、 という構図ではありません。
問題は、そこから「だから Azure と同じ扱い」とは言えないところです。ドキュメントにはこう書かれています。
When you use Fireworks on Foundry, data is shared between Microsoft and Fireworks AI, and different compliance and data handling rules will apply. Customers are responsible for evaluating whether data sharing between Microsoft and Fireworks is appropriate for their organizations compliance requirements.
置かれている場所の話ではなく、データを扱う主体と適用される規則の話ですね。 Azure 内で動いていても、そのデータは Microsoft と Fireworks の間で共有され、 コンプライアンスとデータ取り扱いの規則は Azure 標準のものとは別になる。 その妥当性の判断は利用者側の責任、ということです。ここははっきり書いてあります。
そのうえで、Azure の中のどこで処理されるかです。 Fireworks 経由のモデルが使えるリージョンとして、ドキュメントに挙がっているのは
- East US / East US 2 / Central US / North Central US / West US / West US 3
の 6 つです。ただ、Foundry のカタログをリージョンで絞り込んでみると、 South Central US と West Central US でも K3 が出てきました。 いずれにせよ、全部が米国リージョンです。 「データゾーン標準」は指定したデータゾーンの中で処理される、という契約なので、 米国データゾーンを選ぶ以外の選択肢が今のところ無い、ということになります。 日本のプロジェクトから呼ぶことはできますが、処理は米国内で行われます。
そして「Azure 内だから Azure と同じ保証」ではないことの裏付けとして、 いくつか明示的な除外事項が書かれています。
- EU Data Boundary の対象外 :
「Fireworks on Foundry is currently excluded from EU Data Boundary commitments.」 - FedRAMP 非対応 :
必要な組織は事前に確認を、とあります。 - PCI DSS 非該当 :
カード情報の保存・処理・伝送に使ってはいけない、と明記。
さらに Transparency note として、Microsoft はこの経路のモデルについて 開発・学習・安全性評価を行っていない、挙動や性能やリスクプロファイルについて表明もしない、 適合性の評価は利用者の責任である、と書かれています。
まとめると、処理される場所は Azure(米国リージョン)の中だけれども、 「Azure のポータルから使えるので Azure と同じ扱い」ではない、ということですね。 社内の規程に照らす場合、確認すべきは「モデルの開発元にデータが送られるか」ではなく 「Fireworks AI と共有されることを許容できるか」「米国で処理されて問題ないか」の 2 点になります。
コスト比較
さて、この連載としてはこちらが本題かもしれません。 1M トークンあたりの単価を、2 章で見たレポートの比較対象モデルと並べます。 2026 年 8 月上旬時点の値です。
そして、この表は課金の枠組みが揃っていません。 上で見たとおり Foundry の中に 3 つの売られ方が混在しているので、どこから取った数字なのかを一列足しておきます。 横並びにはしていますが、同じ条件の比較ではない、という前提で見てください13。
| モデル | 経路・条件 | 入力 | キャッシュ入力 | 出力 | 出典 |
|---|---|---|---|---|---|
Kimi K3(FW-Kimi-K3) |
Foundry / Fireworks 経由・Data Zone(米国) | $3.30 | $0.33 | $16.50 | Foundry ブログ14 |
GLM-5.2(FW-GLM-5.2) |
Foundry / Fireworks 経由・Data Zone(米国) | $1.54 | $0.15 | $4.84 | Azure 料金ページ(Fireworks)15 |
| GPT-5.6 Sol(short) | Azure OpenAI・sold by Azure・従量課金 | $5.00 | $0.50 | $30.00 | Azure 料金ページ(Azure OpenAI)16 |
| GPT-5.6 Sol(long) | Azure OpenAI・sold by Azure・従量課金 | $10.00 | $1.00 | $45.00 | Azure 料金ページ(Azure OpenAI) |
| GPT-5.5 | Azure OpenAI・sold by Azure・Global | $5.00 | $0.50 | $30.00 | Azure 料金ページ(Azure OpenAI) |
| Claude Opus 4.8 | Foundry / Marketplace 経由(partners and community) | $5.00 | $0.50 | $25.00 | Anthropic 公式・定価17 |
| Claude Fable 5 | Foundry / Marketplace 経由(partners and community) | $10.00 | - | $50.00 | Anthropic 公式・定価 |
眺めてみると、
- K3 の位置:GPT-5.6 Sol(short)に対して入力が 66%、出力が 55%。 Opus 4.8 に対しては入力が 66%、出力が 66%。Fable 5 に対しては出力が 1/3 です。 2 章で見たスコアが「トップ群にだいぶ近いが少し下」だったので、この価格差は効いてきますね。
- GLM-5.2 との差:同じ Fireworks 経由で条件が揃っているのはここだけなのですが、
K3 は入力で 2.1 倍、出力で 3.4 倍します。2 章のスコアだと GLM-5.2 は
DeepSWE で 46.2 対 67.5 と大きく離されていたので、そこにこの価格差を払うかという話になります。
ただし、この 2 つはそもそも扱えるものが違います。カタログの
supportedInputModalitiesを見ると K3 は text と image、GLM-5.2 は text だけです。 対応言語も K3 が en / zh、GLM-5.2 は en の表記でした。 画像を投げる用途なら、そもそも比較の対象にならないということですね。 K3 は OmniDocBench で 91.1 を出していますし、カタログの説明文でも “native-vision model” と紹介されているので、画像の扱いはむしろ売りの部分です - キャッシュ入力:K3 は $0.33 で入力の 1/10 です。1M コンテキストのモデルなので、 同じプロンプトを何度も投げる使い方をするなら、ここが実質的な単価を決めます。
なお、FW-Kimi-K3 の単価は Foundry のブログに載っているもので、
執筆時点で Azure の料金ページにはまだ反映されていません(載っているのは K2.6 までです)。
ブログのほうにも「subject to applicable availability, deployment, and pricing terms」と
断りが入っているので、実際に使う前には料金ページとポータルの表示で確認してください。
ちなみに Fireworks 経由の K2.6 は入力 $1.045 / 出力 $4.40 で、
Moonshot AI が自社 API で出している値のちょうど 1.10 倍でした。
K3 の $3.30 / $16.50 も同じ 1.10 倍なので、この経路には一律のマークアップが乗っているようです。
レポートの 6.4 節では、1 タスクあたりの実測コストで比較していて、そちらのほうが実感に近いです。 Kimi Code Bench 2.0 では Fable 5 に 4.0 ポイント負けているがコストは 38%、 high effort に落とすと Opus 4.8 の max effort と同スコアを約 1/3 のコストで出す、と。 BrowseComp では首位のスコアを 1 タスク $2.03 で、GDPval-AA v2 では GPT-5.6 Sol と 50 Elo 差以内を 13% 安く、Fable 5 の 2.6 分の 1 で、という具合です。
もっとも、こういう「単価 × タスクあたりトークン数」の比較は、
reasoning effort をどう設定したかで簡単にひっくり返ります。
K3 はデフォルトが max で、しかも思考を切れないので、
うっかりすると「単価は安いのに請求は高い」ということになりかねません。
このあたりは実際のワークロードで測ってみないと分からないところですね。
そのつもりで、参考程度に考えてください。
5. おわりに
今回は Kimi K3 を、推論時のアーキテクチャに絞って見てみました。
一番面白かったのは、やはり Attention Residuals です。 「深くしても効かない」という問題に対して、残差の足し算を深さ方向の Attention に置き換える、 というのは言われてみれば素直な発想ですが、 それを 93 層・8 ブロックという形で通信コストと折り合わせているあたりが実装的でした。
あと、KDA の減衰に下限を入れたら対角タイルの場合分けが消えてカーネルが速くなった話、 MLA を NoPE にしたらコンテキスト拡張時の RoPE 調整が要らなくなった話、 SiTU-GLU で BF16 のオーバーフローを抑えた話。 どれも「数値の都合や実装の都合がモデルの定義そのものを動かしている」例で、 この規模になると、そういう話ばかりになるのだなと思いました。 レポートの 5.4 節の prefix キャッシュの話まで含めて、 モデルの設計と推論基盤の設計が完全に地続きになっている感じですね。
次は。。。どうしましょうかね。 このクラスのモデルを前提にすると、自分で学習させて試すという連載のやり方が そもそも成立しにくくなってきていて、悩ましいです。
まぁ、また何かしら書きたいことができたら記事にしたいと思います。 今度はもう少し早く書けるといいのですが。
-
とはいえ、1 タスクあたりのコストは reasoning effort の設定次第で大きく変わるので、この手の比較は「そういう設定でそう出た」以上のことは言えないと思っています。 ↩
-
こちらもレポートの Table 1 からの転機です。 ↩
-
AttnRes 自体は K3 のオリジナルではなく、先行研究があります。K3 の貢献はそれを 2.8T 規模で成立させる Block 化のほうですね。 ↩
-
https://learn.microsoft.com/en-us/azure/foundry/how-to/fireworks/enable-fireworks-models ↩
-
何度もしつこく試していると、「はじめまして!私は Kimi です。Moonshot AI(月之暗面)が開発したAIアシスタントです。」と答えてくれました。いやー、よかったよかった。 ↩
-
https://learn.microsoft.com/en-us/azure/foundry/how-to/fireworks/enable-fireworks-models#improve-prompt-cache-hit-rate ↩
-
https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create/ ↩
-
https://docs.fireworks.ai/api-reference/post-chatcompletions ↩
-
逆に、全リクエストに同じ固定値を入れて回すのは避けたほうが無難だと思います。同じレプリカに誘導するためのキーなので、会話やユーザをまたいで一つの値を使い回すと、処理を 1 つのレプリカに集めるよう誘導することになってしまいます。実際にどう振られるかは実装次第ですが、上のコードのように会話やセッション単位くらいの粒度で値をバラしておくのが良いのではないでしょうか。 ↩
-
表の「出典」列のとおり、Azure の料金ページ、Foundry のブログ、Anthropic の公式料金ページの 3 つが混ざっています。Azure OpenAI は sold by Azure で Azure の料金体系、Claude 系は Marketplace 経由の別体系、Fireworks 経由はさらに別、と課金の枠組みが 3 種類あるので、単価だけ並べても厳密な比較にはなりません。直接比較はできないものとして見てください。あと為替と税は別途です。 ↩
-
https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/introducing-kimi-k3-through-fireworks-ai-on-microsoft-foundry/4540187 ↩
-
https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/fireworks/ ↩
-
https://azure.microsoft.com/en-us/pricing/details/cognitive-services/openai-service/ ↩
