PLaMo 3アーキテクチャ徹底解析

2026年10月26日公開

PLaMo 3アーキテクチャ徹底解析

PLaMo 3は、Preferred Networks(PFN)が情報通信研究機構(NICT)と共同で開発している大規模言語モデルです[8]。
本記事は、PLaMo 3 31B[1]の公開実装と学習済み重みをCodexに分析させ、その結果を基に作成しました。モデルの構造と計算手順、学習済み重みに見られる特徴を解説します。

対象はPLaMo 3 NICT 2604 31B Base(2604版)で、参照先はHugging Faceのpfnet/plamo-3-nict-2604-31b-baseのrevision e26c257e2175b05db69f01b52f4edf6fa4118f53です。公開実装はTransformers 5系を前提とするmodeling_plamo.pyで、vLLM用のsitecustomize.pyも同梱されています。

比較に使うPLaMo 3 NICT 31B Base[5]は、最大系列長4096の4K版です。revisionは9c1fa626です。トークナイザの定義ファイルとコードは、2604版とバイト単位で同一です。

Gemma 4の比較対象はgoogle/gemma-4-31Bの事前学習版で、revisionは5bbc2fb1c1b2c611d06e3d9f23c170ba21659d89です。実装はTransformers 5.12.1のmodeling_gemma4.py[7]を参照しています。実行環境と測定条件は§10に示します。

  1. [5] Preferred Networks, Inc., "PLaMo 3 NICT 31B Base (model card, config.json, modeling_plamo.py; revision 9c1fa626)," Hugging Face, 2026.
  2. [7] Hugging Face, "modeling_gemma4.py (Transformers v5.12.1)," GitHub, 2026.
PLaMo 3 31Bは、過去のトークン列から次のトークンを予測するデコーダ型Transformerです。各層では、処理中のトークンにほかのトークンの情報を取り込むAttentionを使います。局所Attentionを7層、大域Attentionを1層置く周期を、全64層で繰り返します。
局所Attentionは、近傍の一定数のトークンからなる範囲(窓)だけを参照します。窓内だけを計算すれば計算量を抑えられ、窓外の計算結果を破棄すれば、過去の計算結果を保存するKVキャッシュの容量も抑えられます。自分自身を含む過去のトークン全体を直接参照するのは、8層に1回の大域Attentionです。
主に扱うPLaMo 3 NICT 2604 31B Base(以下、2604版)は、120Bモデルの出力を用いる蒸留で学習されています。対応する文脈長は262,144トークンです。位置符号化(トークンの位置情報の与え方)にはRoPEを使い、大域層には長文脈向けの拡張であるYaRNを適用します(§6.3)。文脈長4096のPLaMo 3 NICT 31B Base(以下、4K版)とは層数や次元数が同じですが、位置符号化の設定と学習済み重みが異なります。
比較対象は、同じく局所Attentionと大域Attentionを併用するGemma 4 31B[6]です。両モデルは、局所層と大域層の比率や、Attentionのヘッド次元、正規化、位置符号化の選び方が異なります。これらの違いと、計算手順やKVキャッシュ容量への影響を比べます。
再実装では、次の3点を確認します。本文の式と定数で計算した出力を、公開実装の出力と比較した結果も示します(§10)。対象はモデル本体で、トークナイザの詳細は扱いません。
  • 各計算で使うベクトルの次元数を、設定ファイルと重みの形状に合わせること(§2.1)。
  • 正規化で各成分に掛ける係数を、保存された重みから正しく求めること(§3.2)。
  • 過去の計算結果を保存・再利用する処理と、位置情報を反映する処理の順序を、公開実装に合わせること(§4.4)。
PLaMo 3とGemma 4の全体構成を、トークンIDからlogitsへ上から下にたどる比較図。隠れ次元Dと層数Lは変数。PLaMo 3は局所7層・大域1層の8層周期で窓幅2048、Gemma 4は局所5層・大域1層の6層周期で窓幅1024。語彙数は107520と262144。どちらも入力埋め込みと出力ヘッドを共有するが、埋め込みの√D倍とlogitsのsoftcapはGemma 4だけにある。
図1:PLaMo 3とGemma 4の全体構成 31BモデルのトークンIDからlogits(候補トークンごとの出力スコア)までの計算を示し、モデルサイズに依存する次元と層数は変数にしています。\(B\)はバッチサイズ、\(T\)は今回処理するトークン数、\(D\)は隠れ次元、\(L\)は層数です。青色は局所ブロック、オレンジ色は大域ブロックで、周期を繰り返して\(L\)層を構成します。
PLaMo 3とGemma 4のデコーダブロックの比較図。入力から下向きにAttentionとMLPを処理し、それぞれの前後にRMSNormを置き、正規化した出力を残差に加える。PLaMo 3は保存された重みにオフセットを加えてゲインを求め、Gemma 4は保存された重みそのものを使う。PLaMo 3のヘッド次元は128で、Gemma 4は局所用と大域用を別の変数で表す。MLPはPLaMo 3がSiLU、Gemma 4がtanh近似GELU。Gemma 4だけが最後の残差加算後に層ごとの係数を掛ける。
図2:デコーダブロックと残差経路の比較 両モデルとも、AttentionとMLP(トークンごとの特徴変換)の前後にRMSNormを置き、正規化した出力を残差に加えます。PLaMo 3のゲインは保存された重み\(w\)とオフセットの和で、Gemma 4は\(w\)そのものを使い、加算後のブロック出力に層ごとの係数\(s_\ell\)を掛けます。\(F\)は中間次元、\(H_q,H_{kv}\)はQuery・KVヘッド数、\(d_{\mathrm{local}},d_{\mathrm{global}}\)はGemma 4の局所・大域のヘッド次元で、\(H_{kv}\)もGemma 4では局所・大域で別の値です。

1. 局所Attentionと大域Attentionによる長文処理

Attentionは、あるトークンを処理するときに、ほかのトークンから情報を集めて組み合わせる仕組みです。どのトークンの情報をどれくらい取り込むかを重みで表し、その重みに応じてベクトルを足し合わせます。
局所層には、自分自身と直前の一定数のトークンに参照範囲を限るSliding Window Attention[9](SWA)を使います。大域層では、自分自身を含む過去のトークン全体を参照します。
前世代のPLaMo 2[10]は、MambaとSWAを組み合わせたSamba[11]系の構成でした。PLaMo 3では、Gemma 3[12]と同様に、通常のAttentionとSWAを組み合わせる構成へ移行しています。変更の背景には、長い系列での性能向上の難しさと、対応する推論エンジンやアプリケーションの少なさがありました[8]。
入力列をまとめて処理する段階がprefill、続くトークンを1つずつ生成する段階がdecodeです。decodeのたびに過去のトークンの計算をやり直さないよう、再利用するベクトルを保存するのがKVキャッシュです。保存するのは、参照先の照合に使うKeyと、取り込む情報を表すValueです。
大域層では、prefillのAttention計算量が系列長の2乗に比例し、KVキャッシュ容量は系列長に比例して増えます。
局所層では、窓内だけを計算するカーネルを使えば、窓幅を固定したときのprefillのAttention計算量を系列長に比例する程度に抑えられます。系列長が窓幅2048を超えると、decodeの各ステップで参照するトークン数は一定になり、1ステップあたりのAttention計算量も増えません。窓外のKey/Valueを破棄するキャッシュなら、保存容量も一定になります。
窓内のトークンを表すベクトルには、前の層で取り込んだ、さらに古いトークンの情報も含まれることがあります。局所層も、そのベクトルを通じて、現在の窓より前の情報を間接的に使えます。
PLaMo 3は局所層を7層続けた後に大域層を1層置き、この8層の周期を全64層で繰り返します。
Gemma 4[13]も局所層と大域層を組み合わせ、31Bでは局所5層に対して大域1層の比率です。PLaMo 3とは、層の比率や窓幅に加え、ヘッド次元、正規化で学習するパラメータ、位置符号化も異なります(§8)。

2. 設定と保存された重みから分かるモデル構成

2.1. 主要な設定値

Attentionでは、今回処理するトークンと、情報を取り込む参照先のトークンを区別します。以降、まとめて処理する系列数をバッチサイズ\(B\)、各系列で今回処理するトークン数を\(T\)とします。参照先との照合に使うQueryは、今回の\(T\)トークン分です。\(S\)はAttentionへ渡すKey/Valueのトークン数で、キャッシュを使う場合は更新後の数を指します。キャッシュが空のprefillでは\(S = T\)、1トークンずつ生成するdecodeでは\(T = 1\)になります。系列長が窓幅を超えると、decode時の\(S\)は局所層と大域層で異なります。
各ブロックは、トークン間の情報を集めるAttentionと、トークンごとに特徴を変換する多層パーセプトロン(MLP)からなります。Attentionでは、複数のヘッドがそれぞれ参照先への重みを求め、情報を組み合わせます。各ヘッドが使うベクトルの次元数がヘッド次元です。設定ファイルconfig.json[1]の主要な値を次の表に示します。
設定項目値記号・役割
vocab_size107520語彙数\(V\)。埋め込みの行数と一致
hidden_size5120残差ストリーム(層間で受け渡すベクトル)の次元数\(D\)。隠れ次元とも呼ぶ
num_hidden_layers64デコーダブロック数\(L\)
intermediate_size25600MLPの中間次元\(F\)(\(5D\))。gate/upそれぞれの射影出力の次元数(§7)
num_attention_heads64Queryヘッド数\(H_q\)
num_key_value_heads8Key/Valueヘッド数\(H_{kv}\)
head_dim1281ヘッドの次元数\(d\)。Q/K/Vで共通
rms_norm_eps\(10^{-6}\)RMSNormで二乗平均に加える\(\epsilon\)
window_size2048局所Attentionの窓幅\(W\)
sliding_window_pattern88層ごとに大域Attention
rope_local_theta10000局所層のRoPEの基底
rope_theta1000000大域層のRoPEの基底
initial_context_length4096YaRNの元の文脈長\(L_0\)(§6.3)
rope_scaling_factor256大域層のYaRNの係数\(s\)(§6.3)
max_position_embeddings262144設定に記録された最大系列長
scale_embeddingfalse埋め込みを\(\sqrt{D}\)倍しない(§2.4)
linear_typefp8公開実装では参照されない(重みはBF16)
保存されている全642テンソルは16ビット浮動小数点のBF16で、射影には通常のnn.Linearを使います。公開実装のLinearType列挙型にはnormal、fp8、mxfp8が定義されていますが、モデルの構築や順伝播の分岐には使われていません。linear_typeの値は8B Baseではnormal、31Bモデルの4K版・2604版ではfp8ですが、この設定値は公開重みの保存形式や射影の計算を変えません。
Queryの総次元数\(H_q d = 8192\)は、隠れ次元\(D = 5120\)より大きくなっています。再実装でヘッド次元を\(D / H_q\)から求めると80になり、保存された重みの形状と一致しません。設定のhead_dimに従って128を使います。Queryの総次元数が隠れ次元を上回る点は、Gemma 4も同じです(§8)。

2.2. 公開重みのキー名とパラメータ数

公開重みの各テンソルには、どの計算に使うかを示すキー名が付いています。各ブロックのキー名は、model.layers.layers.{i}.で始まります。iは0始まりの層番号です。重みを変換する際は、layersが2回続く点に注意してください。ブロック内では、mixer属性がAttention、mlp属性がトークンごとの特徴変換(MLP)に対応します。これらの属性名は全64層で共通です。
モデル全体のクラスPlamo3ForCausalLMは、model属性にPlamo3Modelを持ちます。埋め込みのキー名はmodel.embed_tokens.weight、最終RMSNormはmodel.norm.weightです。ブロック列はPlamo3Model.layersのPlamo3Decoderが管理し、そのlayers属性にModuleListを持ちます。この2段の階層が、キー名のlayers.layersに対応します。
1ブロックに保存されているテンソルは計10個です。内訳は、AttentionとMLPの射影行列が各2個、Q/K正規化のパラメータが2個、AttentionとMLPの入出力を正規化するパラメータが計4個です。線形層にバイアスはありません。パラメータ数は1ブロックで487,608,576、64ブロックの合計で31,206,948,864です。
表では、共通の接頭辞model.layers.layers.{i}.をキー名から省いています。PyTorchの線形層の重みは、行が出力、列が入力に対応する\([\text{out}, \text{in}]\)の形状です。RMSNormが保存するのは、実効ゲインとオフセットの差分です(§3.2)。
キー名形状
mixer.qkv_proj.weight\(10240 \times 5120\)
mixer.o_proj.weight\(5120 \times 8192\)
mixer.q_norm.weight、mixer.k_norm.weight各128
mlp.gate_up_proj.weight\(51200 \times 5120\)
mlp.down_proj.weight\(5120 \times 25600\)
pre_mixer_norm.weight、post_mixer_norm.weight、pre_mlp_norm.weight、post_mlp_norm.weight各5120
入力埋め込みはトークンIDを入力ベクトルに変換し、出力ヘッドはブロック列の出力から候補トークンごとのスコアを求めます。PLaMo 3は両者の行列を共有し、専用のlm_head.weightは保存していません。Gemma 4も入出力の行列を共有します(§8)。
総パラメータ数は、埋め込み、64ブロック、最終RMSNormを合わせて31,757,456,384(約317.6億)です。

1ブロックの内訳は、QKV射影が52,428,800、出力射影が41,943,040、gate/up射影が262,144,000、down射影が131,072,000、RMSNormが\(4 \times 5120 + 2 \times 128 = 20{,}736\)パラメータです。合計は487,608,576で、モデル全体では\(550{,}502{,}400 + 64 \times 487{,}608{,}576 + 5{,}120 = 31{,}757{,}456{,}384\)になります。

設定ファイルにtie_word_embeddingsの指定はなく、公開実装では既定値のTrueが使われます。保存されている全テンソルの要素数も同じ値で、テンソル数は\(64 \times 10 + 2 = 642\)です。RoPEの周波数は学習パラメータに含まれず、実行時にバッファとして作られます。公開実装は出力ヘッドの行数を語彙数以上の16の倍数に揃えますが、107,520は16で割り切れるため、追加行はありません。

2.3. モデルサイズごとの構成・演算回数・読み込み量

PLaMo 3の610M、2B、8B、31Bは、基本構造が共通しています。いずれもヘッド次元128、窓幅の設定値2048、8層周期、語彙数107,520です。610Mは金融向けのFin Instruct、2Bと8BはNICT Base、31BはNICT 2604 Baseの値を使い、隠れ次元、層数、MLPの中間次元、ヘッド数、パラメータ数を比べます。
入力埋め込みのパラメータ数は\(VD\)です。入力埋め込みと出力ヘッドを共有するモデルでは、この行列を総パラメータ数に1回だけ数えます。610M Fin Instructは両者を共有しないため、総数は両方を含めて約8.90億になります。「610M」はモデル名として表記しています。
演算回数とメモリ読み込み量は、各系列の1トークンを処理するdecodeについて概算します。表の演算回数とKVキャッシュの読み込み量は1トークンあたり、重みの読み込み量はバッチ全体の1ステップあたりの値です。処理中のトークンを含めた系列長を4096とし、大域層では4096トークン、局所層では直近2048トークンを参照する条件で比べます。
重みとKVキャッシュはBF16とし、行列計算の積和を2 FLOPで数えます。1 GFLOPは\(10^9\)回の浮動小数点演算です。局所窓は、2604版の数え方に合わせて自分を含む2048トークンとします。旧実装との1トークンの差は、この概算には含めません(§5.1)。
項目610M[4]2B[2]8B[3]31B[1]
総パラメータ数\(8.90 \times 10^{8}\)\(2.60 \times 10^{9}\)\(8.09 \times 10^{9}\)\(3.18 \times 10^{10}\)
入力埋め込みの
パラメータ数
\(1.65 \times 10^{8}\)\(2.75 \times 10^{8}\)\(4.40 \times 10^{8}\)\(5.51 \times 10^{8}\)
隠れ次元\(D\)1536256040965120
層数\(L\)16243264
MLPの中間次元\(F\)6144102401638425600
Queryヘッド数\(H_q\)16243264
Key/Valueヘッド数\(H_{kv}\)1448
入力埋め込みと
出力ヘッド
共有なし共有共有共有
decodeの概算演算回数
(GFLOP/トークン)
1.755.8917.3968.34
うち内積・重み付き和の割合17.24%11.54%6.95%7.07%
重みの読み込み量
(GiB/ステップ)
1.354.8515.0759.15
KVキャッシュの読み込み量
(MiB/トークン)
18108144576
K/Vの追加保存に
必要な容量(KiB/トークン)
局所層: 7
大域層: 1
合計: 8
局所層: 42
大域層: 6
合計: 48
局所層: 56
大域層: 8
合計: 64
局所層: 224
大域層: 32
合計: 256
表の「K/Vの追加保存に必要な容量」は、窓外のK/Vを破棄せずに、新たな1トークンのK/Vを保存する場合の値です。局所層・大域層の値は、それぞれ該当する全層の合計で、バッチ全体の1ステップでは\(B\)倍になります。局所層が参照するのは直近2048トークンだけなので、窓外のK/Vを保存しておいても、表の読み込み量は変わりません。一方、局所層のキャッシュ容量は系列長に比例して増えます。§5.2では、窓外のK/Vを破棄し、局所層のキャッシュ容量を一定にする場合を扱います。
演算回数には出力ヘッドを含む線形射影と、Attentionの内積・重み付き和を含めています。表の割合は、内積・重み付き和の演算回数を合計で割った値です。QKV射影と出力射影は、この割合の分子には含めません。
読み込み量は、行列重みをバッチ全体で1回、各系列のKとVをそれぞれ1回読む場合の概算で、実測値ではありません。重みはバッチ内で使い回せますが、KVキャッシュは系列ごとに異なります。そのため、バッチサイズ\(B\)の1ステップでは、表の演算回数とKVキャッシュの読み込み量をそれぞれ\(B\)倍します。
31Bでの重みとKVキャッシュの読み込み量の合計は、バッチ全体の1ステップで約\(59.15 + 0.5625B\) GiBです。生成する1トークンあたりでは、約\(59.15/B + 0.5625\) GiBになります。詳しい計算式と省略した処理を補足します。

語彙数を\(V\)、隠れ次元を\(D\)、層数を\(L\)、MLPの中間次元を\(F\)、Query・KVヘッド数を\(H_q,H_{kv}\)、ヘッド次元を\(d\)とします。出力ヘッドを含む線形射影の行列要素数\(P_{\mathrm{lin}}\)は、次の式で求められます。各ブロックのQKV・出力射影が\(2Dd(H_q+H_{kv})\)、gate/up/down射影が\(3DF\)、出力ヘッドが\(VD\)要素です。

\[ P_{\mathrm{lin}} = L\left[2Dd(H_q+H_{kv}) + 3DF\right] + VD \]

大域層数を\(N_g=L/8\)、局所層数を\(N_l=7L/8\)、系列長を\(N\)、窓幅を\(W\)とし、全層の参照トークン数の合計を\(A=N_gN+N_l\min(N,W)\)と置きます。合計演算回数\(C\)は次の式で概算しています。第1項が線形射影、第2項がAttentionの内積と重み付き和です。表では\(B=1\)、\(N=4096\)、\(W=2048\)、\(d=128\)を代入し、第2項を\(C\)で割って内積・重み付き和の割合を求めています。

\[ C \approx 2BP_{\mathrm{lin}} + 4BH_qdA \]

BF16は1要素2バイトなので、行列重みの読み込み量は\(2P_{\mathrm{lin}}\)バイト、KとVの読み込み量は計\(4BH_{kv}dA\)バイトです。GQAでは、同じKVヘッドを参照するQueryヘッド間でKとVを使い回すと仮定しています。系列間でのKVキャッシュの共有は仮定していません。1 MiBは\(2^{20}\)バイト、1 GiBは\(2^{30}\)バイトです。

新しい1トークンのKとVは、1層あたり計\(4H_{kv}d\)バイトです。窓外のK/Vを破棄しない場合、1トークンあたりの追加保存量は局所層全体で\(4N_lH_{kv}d\)バイト、大域層全体で\(4N_gH_{kv}d\)バイト、全層では\(4LH_{kv}d\)バイトになります。1 KiBは\(2^{10}\)バイトです。31Bでは局所56層で224 KiB、大域8層で32 KiB、合計256 KiBです。

演算回数と読み込み量の概算では、正規化・softmax・RoPE・活性化関数・残差加算などの演算、入力埋め込みの検索、正規化の重みや中間データの読み込みを省いています。追加保存量はK/Vの要素数から求めた容量であり、KVキャッシュの再配置などに伴う書き込みは数えていません。新しいKとVも含めて参照範囲を数えており、それらがメモリへの保存前に使われる場合との差も無視しています。実際のメモリ転送量は、重みやK/Vがハードウェアのキャッシュに残るかどうかや、カーネルによる重み・K/Vの再読み込み、中間データの保存方法によって変わります。

31Bの4K版と2604版は、層数や次元数が同じなので、この表では1列にまとめています。

31Bモデルの4K版は、2604版と層数や次元数が同じです。計算に関わる設定では、大域層にYaRNを適用しないことと、最大系列長が4096であることの2点だけが異なります。重みは一致せず、同名の射影行列と埋め込みを比較したコサイン類似度は、ほぼ0でした。

2つのチェックポイントの同名テンソルをそれぞれ1次元に並べ、コサイン類似度を求めました。埋め込みと全64層のqkv_proj、o_proj、gate_up_proj、down_projでは、すべて−0.001〜0.001でした。ただし、ニューロンの並べ替えや、入出力の関係を保った重みの変換(再パラメータ化)でも、重みの類似度は変わるため、この測定から学習履歴を特定することはできません。

オフセットを足す前のRMSNormの重みについて、各層で求めたコサイン類似度の中央値はpost_mixer_normが0.74、post_mlp_normが0.90、q_normとk_normが約0.79でした。最終RMSNormの類似度は0.95、入力側のRMSNormでは0.04〜0.08です。出力側のゲインの層ごとの傾向も似ています(§9.2)。BF16の値がビット単位で一致する要素の割合は、どのテンソルでも0.1〜0.8%でした。

2.4. 入力からlogitsまでの計算

まず、各トークンのIDに対応するベクトルを埋め込み行列から取り出し、64個のブロックで順に更新します。入力ID列を\(I \in \{0, \dots, V-1\}^{B \times T}\)、埋め込み行列を\(E \in \mathbb{R}^{V \times D}\)とすると、計算は次の式で表せます。
\[ \begin{eqnarray} X_0 &=& E[I], \\ X_{\ell+1} &=& \operatorname{Block}_\ell(X_\ell) \quad (\ell = 0, \dots, 63) \end{eqnarray} \]
最後のブロックまで処理したベクトルから、次のトークンの候補ごとのスコア(logits)を求めます。最後のブロックの出力\(X_{64}\)に、オフセット1のRMSNorm(§3.2)を適用してから、入力埋め込みと同じ行列\(E\)を転置して掛けます。計算式は\(\operatorname{logits} = \operatorname{RMSNorm}(X_{64})\, E^\mathsf{T}\)です。
入力埋め込みはそのまま使い、Gemma 4のように\(\sqrt{D}\)倍しません。出力スコアを一定の範囲に滑らかに収めるsoftcapも使わず、順伝播(forward)で求めたlogitsをそのまま返します。softmaxによる確率への変換や、次のトークンの選択は、生成処理側で行います。

3. ブロックの構造とオフセット付きRMSNorm

3.1. 残差を含むブロックの式

各ブロックでは、入力ベクトルにAttentionの出力を加え、続いてMLPの出力を加えて更新します。処理前のベクトルを残し、処理結果を足して更新するのが残差加算です。AttentionとMLPの前後にはRMSNormを置き、正規化した出力を加算に使います(図2)。
Attentionの前後の正規化を\(N_{\mathrm{pre\_attn}}\)、\(N_{\mathrm{post\_attn}}\)、MLPの前後を\(N_{\mathrm{pre\_mlp}}\)、\(N_{\mathrm{post\_mlp}}\)と書きます。入力\(X\)から出力\(Y\)への計算は、次の2段階です。
\[ \begin{eqnarray} A &=& X + N_{\mathrm{post\_attn}}\bigl(\operatorname{Attention}(N_{\mathrm{pre\_attn}}(X))\bigr), \\ Y &=& A + N_{\mathrm{post\_mlp}}\bigl(\operatorname{MLP}(N_{\mathrm{pre\_mlp}}(A))\bigr) \end{eqnarray} \]
Attention側の計算順序は、pre_mixer_norm、mixer、post_mixer_normです。その出力を残差に加えた\(A\)を、MLP側のpre_mlp_norm、mlp、post_mlp_normへ順に渡します。残差に加えるテンソルの形状は、どちらも\([B, T, 5120]\)です。Gemma 4も、両処理の前後に計4つのRMSNormを置きます(§8)。

3.2. 重みとオフセットの和による実効ゲイン

RMSNorm[14]は、入力ベクトル全体の大きさの違いが、後続の計算に与える影響を抑える正規化です。入力の二乗平均平方根(RMS)で各成分を割り、成分ごとの学習可能な係数(ゲイン)を掛けます。分母には、二乗平均に小さな定数を加えた値の平方根を使います。PLaMo 3では、\(n\)次元のベクトル\(x\)を次の式で正規化します。
\[ \boxed{\;N^{(c)}_w(x) = (c + w) \odot \frac{x}{\sqrt{\frac{1}{n}\sum_{r=1}^{n} x_r^2 + \epsilon}}\;} \]
\(\odot\)は要素ごとの積、\(\epsilon = 10^{-6}\)は入力が0でも分母が0にならないように加える定数です。LayerNormのように入力から平均を引く処理はありません。\(w\)は学習して保存するベクトル、\(c\)はコード内の定数です。各成分に掛ける係数\(\gamma = c + w\)を実効ゲインと呼びます。\(c\)はオフセットにあたり、適用箇所ごとに次の値を使います。
適用箇所実装名オフセット\(c\)正規化する次元数\(n\)
Attention入力pre_mixer_norm15120
Attention出力post_mixer_norm\(1/5 = 0.2\)5120
MLP入力pre_mlp_norm15120
MLP出力post_mlp_norm\(5^{-1.5} \approx 0.0894\)5120
Querymixer.q_norm1ヘッドごとの128
Keymixer.k_norm1ヘッドごとの128
最終出力model.norm15120
重み\(w\)の初期値は0で、このとき実効ゲインはオフセット\(c\)に等しく、Attention出力では0.2、MLP出力では約0.089です。学習する\(w\)は、実効ゲインとオフセットの差分です。Gemma 4は、ゲインそのものを保存する点が異なります(§8)。

公開実装のコメントには、すべてのオフセットを1にすると性能が低下したとあります。ただし、評価条件や低下量、0.2と\(5^{-1.5}\)を選んだ理由は記されていません。Plamo3RMSNormのdocstringは、weight decay(重み減衰)を適用したときのパラメータ更新がtorch.nn.RMSNormと異なる点を挙げています。また、推論用に置き換える場合は、オフセットを重みへ足す必要があると注記しています。

weight decayは、学習する重みを0へ近づける更新です。\(w\)をその対象に含め、AdamWのように勾配による更新と分離して減衰させる場合を考えます。学習率を\(\eta\)、減衰係数を\(\lambda\)とすると、減衰だけによる更新は\(w' = (1 - \eta\lambda)w\)です。このとき、実効ゲインは\(\gamma' = (1 - \eta\lambda)\gamma + \eta\lambda c\)となり、\(c\)へ近づきます。一方、オフセットを足し込んだ\(\gamma\)に同じ減衰を掛けると、0へ近づきます。オフセットを重みに足しておけば推論時の出力は保てますが、その重みに同じweight decayを適用して学習を続けると、実効ゲインの変化は元のモデルと異なります。この導出はRMSNormにもweight decayを適用する場合の性質で、実際の学習設定を示すものではありません。

通常のRMSNormカーネルに置き換えるときは、\(w\)にオフセットを足した\(\gamma\)をゲインとして渡す必要があります。vLLMのPLaMo 3実装[15]と、モデルに同梱されたvLLM用のsitecustomize.pyは、どちらも重みの読み込み時にオフセットを足します。
公開実装の_plamo3_rms_normは、二乗平均と、各成分を分母で割る処理を32ビット浮動小数点(FP32)で計算します。分母で割った値を入力のデータ型へ戻してから、(offset + weight)を掛けます。Gemma 4の実装は、ゲインの乗算までFP32で計算してから入力のデータ型へ戻す順序です。オフセットを重みへ足し込むときも、加算に使うデータ型と丸めの段階によって値は変わりえます。BF16でビット単位の一致を求める場合は、数式に加え、オフセットの加算、型変換、乗算の順序まで合わせる必要があります。

4. Attentionの計算手順とテンソルの形状

PLaMo 3とGemma 4のAttention内部を上から下に比較する図。正規化済み入力からQ・K・Vを作り、各レーンで処理する。PLaMo 3はQ/Kを正規化し、K/Vのキャッシュ更新後にQとキャッシュ全体のKへRoPEを適用する。Gemma 4はVもゲインなしで正規化し、今回のQ/KにRoPEを適用してからK/Vをキャッシュへ保存する。Gemma 4の大域層はK/Vの射影出力を共有する。両モデルともGQAの出力を連結し、出力射影で隠れ次元Dへ戻す。次元とヘッド数は変数で示し、窓幅などは具体値を残す。
図3:Attentionの計算手順の比較(キャッシュ有効時) PLaMo 3はRoPE適用前、Gemma 4は適用後のKを保存し、Gemma 4はVもゲインなしで正規化します。\(d\)はヘッド次元、\(S\)はキャッシュから返されるK/Vのトークン数で、形状はヘッド軸を前に揃えた表記です。PLaMo 3はキャッシュ全体のKを毎回回転させ、Gemma 4は今回の入力のKだけを回転させてから過去のKと連結します。

4.1. QKV射影の分割位置

処理中のトークンが参照先から情報を取り込むために、AttentionはQuery(Q)、Key(K)、Value(V)という3種類のベクトルを使います。Qは処理中のトークン、KとVは参照先のトークンに対応します。QとKの内積から各参照先への重みを求め、その重みによるVの重み付き和が出力です。
Q/K/Vは、各トークンの入力ベクトルに重み行列を掛ける線形変換(射影)で作ります。公開実装は、この3つの射影を1つの行列にまとめています。重みを正しく分割すれば、個別の行列で射影する場合と数学的には同じ出力を得られます。
pre_mixer_normの出力を\(U \in \mathbb{R}^{B \times T \times 5120}\)とすると、\([Q_{\text{flat}}, K_{\text{flat}}, V_{\text{flat}}] = U W_{\text{qkv}}^\mathsf{T}\)で一度に射影できます。出力の次元数は、\(64 \times 128 + 8 \times 128 + 8 \times 128 = 10240\)です。
射影出力の最後の軸を、Q、K、Vの順に分割します。対応する区間は\([0{:}8192]\)、\([8192{:}9216]\)、\([9216{:}10240]\)で、いずれも先頭を含み、末尾を含みません。再実装で重みを分離するときも、qkv_proj.weightの行をこの境界で分けます。ヘッドごとにQ/K/Vを交互に取り出すと、本来Q・K・Vに使うべき重みを取り違えます。
分割した各テンソルを\([B, T, H, d]\)へ形状変更し、ヘッド軸とトークン軸を入れ替えます。\(H\)は各射影のヘッド数です。並べ替え後の形状は、Qが\([B, 64, T, 128]\)、KとVが\([B, 8, T, 128]\)になります。

4.2. GQAによるKey/Valueの共有

複数のQueryヘッドでKey/Valueを共有すると、ヘッドごとに参照先への重みを求めながら、KVキャッシュに保存するベクトルの数を減らせます。この方法がGrouped-Query Attention[16](GQA)で、PLaMo 3 31BとGemma 4 31Bの両方に使われています。PLaMo 3 31BはQueryヘッドが64個、Key/Valueヘッドが8個で、連続する8個のQueryヘッドが同じ1組のK/Vヘッドを参照します。参照先への重みはQueryヘッドごとに求めますが、重み付き和の対象となるValueは共通です。KVキャッシュに保存するのは8ヘッド分なので、K/Vも64ヘッドにした場合に比べて容量は1/8になります。
各K/Vヘッドを8回ずつ繰り返し、\([B, 64, S, 128]\)に広げれば、通常のマルチヘッドAttentionとして計算できます。公開実装はPyTorchのscaled_dot_product_attentionにenable_gqa=Trueを渡して、K/Vヘッドを共有するAttentionの計算をPyTorch側に任せています。両者は数学的には同じで、§10の参照実装は明示的に繰り返す方法です。キャッシュに保存する段階では、64ヘッド分へ複製する必要はありません。
Gemma 4 31Bの局所層は、Queryが32ヘッド、K/Vが16ヘッドで、2個のQueryヘッドがK/Vを共有します。大域層はQueryが32ヘッド、K/Vが4ヘッドで、8個のQueryヘッドがK/Vを共有します。共有する仕組みは同じでも、ヘッド数とヘッド次元が異なるため、1トークンあたりのキャッシュ容量は同じではありません(§5.2・§8)。

4.3. ヘッドごとのQ/K正規化

Q/KをRMSNormで正規化すると、射影後のベクトル全体の大きさがAttentionスコアに与える影響を抑えられます。PLaMo 3は、QとKにそれぞれq_normとk_normでヘッドごとのRMSNormを適用します。どちらもオフセット1、\(\epsilon = 10^{-6}\)で、トークン・ヘッドごとに128次元のベクトルを正規化します。ゲインの形状は\([128]\)で、Q用は64個のQueryヘッド、K用は8個のKVヘッドに共通です。Vには正規化を適用しません。
HenryらのQuery-Key Normalization[21]は、QとKをL2ノルムで単位長に揃え、内積に学習可能なスカラーを掛ける方法です。PLaMo 3は、成分ごとのゲインを持つRMSNormと、通常のAttentionの係数\(1/\sqrt{128}\)を使います。QとKを正規化する点は共通ですが、正規化の分母と、学習する係数の数・掛ける位置が異なります。
  1. [21] Alex Henry, Prudhvi Raj Dachapally, Shubham Shantaram Pawar, Yuxuan Chen, "Query-Key Normalization for Transformers," Findings of EMNLP, 2020. (L2正規化と学習可能なスカラーを使うQuery-Key Normalization)
Gemma 4もQ/KをRMSNormで正規化しますが、ゲインの保存形式と、内積に掛ける係数が異なります。さらに、Gemma 4はVにもゲインなしのRMSNormを適用します(§8)。

4.4. KVキャッシュとRoPEの適用順序

公開実装では、Q/K/Vを射影して形状を変え、Q/KのRMSNorm、キャッシュ更新、RoPE、Attentionの順に計算します。キャッシュに使うのはTransformersのDynamicCacheです。正規化済み・RoPE適用前のKと、正規化していないVを保存し、Qは保存しません。RoPEを適用する対象は、キャッシュから返されたK全体と今回のQです。したがって大域層では、過去のKも順伝播のたびに回転し直します。
Gemma 4は、正規化したKにRoPEを適用してから保存します。PLaMo 3を別の推論エンジンで動かす際は、その実装がRoPE適用前と適用後のどちらのKを保存するかを確認する必要があります。保存形式が異なるままキャッシュを渡すと、RoPEの二重適用や適用漏れにつながります。
RoPEに渡す位置番号は、Attentionの内部で生成する連番です。Kには\(0, \dots, S-1\)、Qにはその末尾の\(S - T, \dots, S - 1\)を割り当てます。局所層で古いトークンを取り除くと、残ったトークンの位置番号も数え直します。QとKの位置番号は同じ量だけ移るため、位置差は変わりません。丸め誤差を除けば、RoPE適用後の内積とAttentionスコアも同じです。

局所層では、TransformersのDynamicSlidingWindowLayerがキャッシュ更新後に末尾の最大\(W - 1 = 2047\)トークンを残します。次のdecodeでは新しい1個を加えた2048個にKの位置\(0, \dots, 2047\)を割り当て、Qの位置を2047とします。固定周波数のRoPEでは、QとKの位置を同じ量だけ平行移動しても内積は変わりません。この実装のYaRNも固定周波数と共通の振幅補正を使うため、同じ性質を保ちます。ただし、有限精度の丸め誤差まで一致するとは限りません。

引数のposition_idsはAttentionへ渡されず、内部で作った連番が使われます。不等間隔の位置を指定する場合や、複数の系列を連結した入力で位置をリセットする場合は、引数の位置情報をAttentionへ渡すように実装を変更する必要があります。

4.5. GQAの式と出力射影

処理中のトークンのQと、各参照先のKを照合して重みを求め、その重みに応じてVを足し合わせます。参照させない位置は、softmax後の重みが0になるようにマスクで除外します。
正規化・回転後のQとKを\(Q', K'\)、加算マスクを\(M\)とします。0始まりのQueryヘッド番号\(h\)に対応するKVヘッドは、\(g(h) = \lfloor h / 8 \rfloor\)です。バッチ内の系列を\(b\)、Query・Keyのトークン位置をそれぞれ\(t,s\)、ヘッド内の成分を\(r\)で表すと、参照先への重み\(A\)と各ヘッドの出力\(O\)は次の式で求められます。
\[ \begin{eqnarray} A_{b,h,t,s} &=& \operatorname{softmax}_s\left(\frac{\sum_{r=0}^{127} Q'_{b,h,t,r}\, K'_{b,g(h),s,r}}{\sqrt{128}} + M_{b,t,s}\right), \\ O_{b,h,t,r} &=& \sum_s A_{b,h,t,s}\, V_{b,g(h),s,r} \end{eqnarray} \]
内積に掛ける係数は\(1/\sqrt{128}\)で、softmax後の重みの偏りを調整する温度パラメータを、この係数とは別に学習することはありません。Attentionスコアへのsoftcapと、Attentionのドロップアウトも使いません。上の式は各要素の計算を示したもので、最適化されたカーネルはスコア行列全体をメモリ上に保持せずに計算できます。
各ヘッドが集めた情報をトークンごとにまとめ、残差に加えられる隠れ次元5120のベクトルへ変換します。そのために、出力の軸を\([B, 64, T, 128]\)から\([B, T, 64, 128]\)へ並べ替え、64ヘッド分を連結して\([B, T, 8192]\)とします。これにo_proj.weight(\(5120 \times 8192\))を掛けて隠れ次元へ戻します。

5. 局所/大域Attentionの配置とKVキャッシュ

5.1. 局所7層・大域1層の配置

0始まりの層番号を\(\ell\)とすると、\((\ell + 1) \bmod 8 = 0\)を満たすのが大域層です。該当するのは7、15、23、31、39、47、55、63の8層で、最終層も含みます。残りの56層は局所層です。
次のトークンを予測するときに未来の情報を使わないよう、参照先は自分自身と過去のトークンに限ります。位置\(i\)のQueryが位置\(j\)のKeyを参照できるのは、大域層では因果条件\(j \le i\)を満たす場合です。
局所層では、さらに\(i - j < W\)を満たす必要があります。2604版の窓に入るのは、自分自身と直前の2047トークン、計2048トークンです。4K版の旧実装では計2049トークンを参照するため、同じ窓幅の設定でも範囲が異なります。参照できない位置のスコアに\(-\infty\)を足し、softmax後の重みを0にします。

swa_maskはtriu(diagonal=-(window_size - 1))で帯状のマスクを作ります。参照先との距離は\(W - 1\)以下なので、自分を含めて最大\(W = 2048\)個です。局所層のキャッシュは\(W - 1\)個を残し、次のdecodeで新しい1個が加わるため、prefillとdecodeの参照範囲は一致します。

4K版に同梱された旧実装はtriu(diagonal=-window_size)を使い、自分を含めて最大\(W + 1 = 2049\)個を参照します。独自のキャッシュクラスが残すのも末尾\(W\)個です。「窓幅2048」に含めるトークン数は、実装の版によって1個異なります。

2604版にも、マスクを省略できる条件のassertが\(S \le W + 1\)のまま残っています。そのため、学習モードでマスクを省略し、キャッシュなしで\(T = W + 1\)を入力すると、本来は窓外となる1トークンも参照対象に入ります。ランダム重みの小型モデル(\(W = 4\))では、この条件で学習モードと評価モードのlogitsに最大0.15の差が出ました(§10)。

8トークンのAttentionマスクの例。行はQuery、列はKeyに対応する。左の大域層は自分自身とそれ以前の全トークンを参照できる下三角のパターン。右の局所層は窓幅4で、自分自身と直前の最大3トークンだけを参照できる帯状のパターン。
図4:大域層と局所層のマスク(\(W = 4\)に縮小した説明用) 青色は参照できる位置、灰色は\(-\infty\)でマスクされる位置を示します。局所層の参照範囲は、自分と直前\(W - 1\)個を含む帯状になります。実際の窓幅\(W\)は2048です。

5.2. KVキャッシュ容量の計算

KVキャッシュの容量は、保存するトークン数と、1トークン分のK/Vのデータ量から求められます。大域層は過去全体を保存し、局所層は窓内だけを残す場合を考えます。
処理中のトークンを含めた系列長を\(N\)、自分を含む窓幅を\(W\)とします。KVヘッド数は\(H_{kv}\)、ヘッド次元は\(d\)、1要素のバイト数は\(b\)です。大域層数を\(N_g\)、局所層数を\(N_l\)とすると、バッチサイズ1のKVキャッシュ容量は次の式で概算できます。
\[ C_{\text{KV}} \approx 2\, b\, H_{kv}\, d \left[ N_g N + N_l \min(N, W) \right] \]
先頭の2はKとVの分です。BF16では\(b = 2\)なので、PLaMo 3 31Bのキャッシュは、1層・1トークンあたり4 KiBになります。大域8層分のキャッシュは1トークン増えるごとに32 KiB増え、局所56層分は約448 MiBで頭打ちです。全64層を大域にした場合と比べると、32Kトークンで約1/5.6、256Kトークンで約1/7.6の容量に収まります。1Kは1024トークンを表し、ここで数える容量はKVテンソルの分だけです。
表には、バッチサイズ1、BF16の条件で計算した値を示します。窓幅は自分を含むトークン数とし、PLaMo 3は2048、Gemma 4は1024です。実際の局所キャッシュでは、更新後に残すトークン数の上限は窓幅より1個少なくなります。この1個の差は概算では無視しています。
系列長\(N\)PLaMo 3 31BGemma 4 31BPLaMo 3 31Bを全層大域にした場合
4,096576 MiB1,120 MiB1 GiB
32,7681,472 MiB3,360 MiB8 GiB
131,0724,544 MiB11,040 MiB32 GiB
262,1448,640 MiB21,280 MiB64 GiB

Gemma 4の大域10層は4ヘッド×512次元のKとVを持ち、1層・1トークンあたり8 KiBです。局所50層は16ヘッド×256次元で、1層・1トークンあたり16 KiBになります。局所層の合計容量は、約800 MiBで頭打ちです。

attention_k_eq_vは射影の共有を指定します。共有する射影出力を\(Z\)、ゲイン\(\gamma\)のRMSNormを\(N_\gamma\)とすると、大域層では\(K = \operatorname{RoPE}(N_{\gamma_k}(Z))\)、\(V = N_1(Z)\)です。上の表は、KとVを別々に保持するTransformersの実装に対応します。共通の射影出力だけを保存し、KとVを再計算する実装では、保存するテンソルに合わせて容量を計算し直す必要があります。

1要素1バイトと仮定すると、1Kトークンで128 MiB、128Kトークンで約2.2 GiBとなり、丸めを考慮すると、公開されている比較表[8]の値と一致します。本記事で示すKVキャッシュ容量には、パラメータ本体・マスク・活性値・logitsに必要なメモリ、メモリ割り当ての断片化による余分な使用量、prefill中の一時領域を含めていません。

  1. [8] Shuji Suzuki, "大規模言語モデルの次期バージョン PLaMo 3 シリーズにおける8B, 31Bの小規模モデルによる事前学習の検証," Preferred Networks Tech Blog, 2025.
Gemma 4 31Bは、大域層でKとVの射影を共有しますが、Transformersの実装は処理後のKとVを別々に保存します[7]。キャッシュ容量は大域層1層・1トークンあたり8 KiBで、大域10層分では1トークン増えるごとに80 KiB増加します。長い系列で局所層の分を無視できる場合、容量はPLaMo 3の約2.5倍です。
系列長1Kから256Kに対するKVキャッシュ容量の両対数グラフ。BF16・バッチサイズ1で、PLaMo 3は1Kで256 MiB、256Kで8.4 GiB、Gemma 4は1Kで880 MiB、256Kで20.8 GiB。PLaMo 3を全64層大域にした場合は256Kで64 GiBになる。
図5:KVキャッシュ容量の計算結果(BF16、バッチサイズ1) 系列長に比例して増えるのは大域層のキャッシュで、局所層の分は窓幅で頭打ちになります。両モデルともKとVを別々に保存する場合の計算結果で、実行時の総メモリ使用量ではありません。

5.3. 計算量と実装上のメモリ使用量

Attentionのスコア計算量は、入力トークンと参照先のトークンを何組照合するかで変わります。ヘッド数とヘッド次元を固定し、キャッシュなしのprefillを考えると、窓の範囲だけを計算するカーネルでは局所層が\(O(TW)\)、大域層が\(O(T^2)\)です。大域層を減らすと長い系列の計算量を抑えられます。ただし、MLPや射影の計算は残るため、大域層の比率だけでは、モデル全体がどれだけ速くなるかは求められません。
公開実装は、参照できない位置も含め、QueryとKeyの全\(T \times S\)組に対応するマスクを配列として作ります。局所Attentionを使う構成でも、長い入力ではマスク自体に多くのメモリが必要です。長文脈を少ないメモリで扱うには、マスク全体を配列として作らず、窓内だけを計算できる推論エンジンが必要になります。

公開実装はprefillで\(T \times S\)の因果マスクと局所マスクを配列として構築し、scaled_dot_product_attentionに渡します。マスクで参照先を制限するだけでは、窓外のQ/Kの内積計算まで省略されるとは限りません。設定上の最大系列長\(T = S = 262144\)でFP32のマスクを1枚作ると、\(T \times S\)要素に各4バイトを使うため、256 GiBが必要です。これは形状とデータ型から計算した値で、実行時の測定値ではありません。

4K版の旧実装にあったflash_attnを呼ぶ分岐は、2604版では削除されています。vLLMの実装は、窓幅を指定できるAttentionバックエンドを使います。

6. RoPEの回転ペアと大域層のYaRN

6.1. split-halfの回転ペア

RoPE(Rotary Position Embedding)[17]は、Attentionでトークン同士を照合するときに、その位置差を反映する位置符号化です。QとKの各ヘッド内で成分を2つずつ組(回転ペア)にし、2成分からなるベクトルをトークン位置に比例した角度で回転させます。回転後のQとKの内積に現れる位置情報は、2つのトークンの位置差で決まります。
PLaMo 3は、ヘッド内の128成分を前半64成分と後半64成分に分け、番号\(j\)と\(j + 64\)の成分を組にするsplit-half形式(GPT-NeoX形式)を使います。
トークンの位置を\(p\)、回転ペア番号を\(j = 0, \dots, 63\)とします。ペアをなす2成分を\(a_j\)、\(b_j\)とすると、回転後の成分\(a'_j\)、\(b'_j\)は次の式で求められます。
\[ \begin{bmatrix} a'_j \\ b'_j \end{bmatrix} = \begin{bmatrix} \cos(p\omega_j) & -\sin(p\omega_j) \\ \sin(p\omega_j) & \cos(p\omega_j) \end{bmatrix} \begin{bmatrix} a_j \\ b_j \end{bmatrix}, \qquad \omega_j = \theta^{-2j/128} \]
トークン位置が1つ進むごとに回す角度が、角周波数\(\omega_j\)です。\(\theta\)は、この角周波数を決めるRoPEの基底です。\(\omega_j\)は\(j = 0\)で最も高く、波長は\(2\pi\)トークンになります。\(j\)が大きいほど周波数は低く、\(j = 63\)で最低になります。
Q/Kのヘッド内の成分を並べ替えるときは、射影行列の行とRMSNormのゲインも同じ順に並べ替えます。隣接する成分を組にするRoPE(GPT-J形式)への移植でも、この変換が必要です。
Q/K正規化とRoPEの順序を入れ替えると、局所層でもAttentionに渡すQ/Kの値が変わります。先に各成分を異なる倍率で伸縮してから回転する場合と、回転してから伸縮する場合では、得られるベクトルが一般に異なるためです。順序を入れ替えた場合、大域層ではさらに、YaRNの振幅補正(§6.3)が後段のRMSNormでほぼ相殺されます。

6.2. 局所層と大域層の基底の違い

局所層は基底\(\theta = 10^4\)、大域層は\(\theta = 10^6\)を使います。基底が大きいほど、\(j > 0\)の回転ペアの波長は長くなります。過去全体を参照する大域層では、同じ角度だけ回転するまでのトークン間距離が長くなる設定です。Gemma 3とGemma 4も、局所層に\(10^4\)、大域層に\(10^6\)を使います[12, 6]。
ヘッド内の回転ペアを示す帯。PLaMo 3は全層でヘッド次元128、64対すべてを回転させる。Gemma 4の局所層はヘッド次元256、128対すべてを回転させる。大域層はヘッド次元512で、256対のうち高周波側の64対だけを回転させ、低周波側の192対は回転させない。
図6:ヘッド内の回転ペア(PLaMo 3とGemma 4) ヘッド次元を\(d\)とすると、回転ペアは成分番号\(j\)と\(j + d/2\)の組です。PLaMo 3は全64対を回転させますが、Gemma 4の大域層はp-RoPE(§6.4)により、低周波側の3/4を回転させません。色が濃いほど周波数が高いことを示します。

6.3. 大域層のYaRN

YaRN[18]は、長い文脈に対応させるため、RoPEの周波数とAttentionスコアの倍率を調整する方法です。2604版は、大域層のRoPEに適用します。
高周波側の回転ペアでは元の周波数を保ち、低周波側では周波数を係数\(s\)で割ります。その中間の回転ペアでは、元の周波数と\(s\)で割った周波数の間を補間します。
係数は、rope_scaling_factorに設定された\(s = 256\)です。最大系列長262,144は元の文脈長4096の64倍ですが、この64を係数に使うと公開実装とは異なる周波数になります。
公開実装は、回転ペア番号に応じて周波数を線形に補間します。補間範囲の境界の端数は丸めず、そのまま補間係数の計算に使います。
元の文脈長\(L_0 = 4096\)、\(\beta_{\text{fast}} = 32\)、\(\beta_{\text{slow}} = 1\)、基底\(\theta = 10^6\)から、補間範囲の境界を求めます。 \[ \begin{eqnarray} j_{\text{low}} &=& 64 \frac{\ln\bigl(L_0 / (2\pi\beta_{\text{fast}})\bigr)}{\ln\theta} \approx 13.96, \\ j_{\text{high}} &=& 64 \frac{\ln\bigl(L_0 / (2\pi\beta_{\text{slow}})\bigr)}{\ln\theta} \approx 30.02 \end{eqnarray} \] 次に\(\alpha_j = \operatorname{clip}\bigl((j - j_{\text{low}}) / (j_{\text{high}} - j_{\text{low}}),\, 0,\, 1\bigr)\)を計算し、補間後の角周波数を\(\tilde\omega_j = \omega_j [(1 - \alpha_j) + \alpha_j / 256]\)とします。\(\operatorname{clip}\)は値を0以上1以下に制限する操作です。境界を整数へ丸めると、補間後の周波数が変わります。
\(\cos\)と\(\sin\)の両方に、振幅補正として\(1 + 0.1 \ln 256 \approx 1.5545\)を掛けます。QとKがともにこの倍率で拡大するため、内積は振幅補正なしの場合の約2.42倍になります。Q/K正規化はRoPEより前にあるため、この倍率はAttentionスコアにも残ります。

YaRNは、Q・Kに共通の係数\(0.1 \ln s + 1\)を掛け、周波数を変えたときのAttentionの重みの分布を補正します。論文ではこの係数を温度\(t\)による\(\sqrt{1/t}\)として導入していますが、公開実装はcos/sinのテーブルへ係数を掛けるため、本記事では振幅補正と呼びます。通常のAttentionの係数\(1/\sqrt{128}\)も含めると、振幅補正のない回転後の内積に掛かる係数は\(2.4165 / \sqrt{128} \approx 0.2136\)です。

\(L_0 \times 256 = 1{,}048{,}576\)は、設定上の最大系列長262,144と異なります。YaRNの係数\(s\)だけから、対応できる文脈長や、その長さでの精度を求めることはできません。YaRNの数理はYaRNの解説記事で扱っています。

BF16で公開実装と照合する場合は、三角関数の丸めにも注意が必要です。公開実装は角度とcos/sinをFP32で計算し、cos/sinを入力のデータ型へ戻してから振幅補正の係数を掛けます。§10の参照実装はFP32を対象としており、このBF16への型変換は省いています。

Plamo3Config.rope_parametersは、config.jsonの値から局所層・大域層それぞれのRoPE設定を生成して返すプロパティです。Transformers 5.12.1では、旧名のrope_scalingからも同じ値を取得できます[22]。JSONにrope_scalingがないことだけでは、YaRNが無効とは判断できません。

参照したvLLM本体[15]は、層の種類ごとのrope_parametersを読みます。一方、同梱のsitecustomize.pyは旧名のrope_scalingを読む別実装です。層の種類ごとの設定との互換性や、vLLMでの実行結果は確認していません。

  1. [22] Hugging Face, "configuration_utils.py (Transformers v5.12.1)," GitHub, 2026.
  2. [15] vLLM Project, "vllm/model_executor/models/plamo3.py (commit d881812)," GitHub, 2026.
局所層はYaRNの対象外で、基底\(10^4\)の通常のRoPEを使います。層数や次元数が同じ4K版[5]は、大域層にもYaRNを適用しません。
回転ペア番号jに対するYaRNの周波数倍率。縦軸は対数。jが約13.96以下では倍率1で、約13.96から30.02の間では1/256へ補間し、それ以降は1/256で一定になる。
図7:大域層におけるYaRNの周波数倍率 公開実装と同じ式で求めた、元の角周波数に対する倍率です。高周波側(小さい\(j\))では1、低周波側では1/256で、その間を線形に補間します。

6.4. Gemma 4のp-RoPE

p-RoPE[19]は、ヘッド内の一部の回転ペアだけに位置に応じた回転を掛け、残りのペアは回転させずに使う位置符号化です。回転させるペアの割合を\(p\)で表します。Gemma 4の大域層はヘッド次元512で、\(p = 0.25\)とし、256対のうち高周波側の64対だけを回転させます。残りの192対には、位置に応じた回転を掛けません。

Transformersのproportional形式[23]は、角周波数を計算するとき、指数の分母に元のヘッド次元512を使います。そのうえで、低周波側の角周波数を0にします。回転ペアは\((j, j + 256)\)で、角周波数は次の値です。

\[ \omega_j = \begin{cases} (10^6)^{-2j/512} & (0 \le j < 64), \\ 0 & (64 \le j < 256). \end{cases} \]

回転の対象となるのは、成分番号0〜63と256〜319の計128成分です。先頭128成分に通常の128次元RoPEを適用する方法とは、回転ペアも周波数も異なる点に注意が必要です。周波数0の回転ペアではcosが1、sinが0となり、元の値がそのまま残ります。

Barberoらは、Gemma 7Bの解析で低周波側の回転ペアがトークン同士の内容の照合に使われていると解釈し、その回転を取り除く方法を提案しています[19]。

  1. [23] Hugging Face, "modeling_rope_utils.py (Transformers v5.12.1)," GitHub, 2026.
  2. [19] Federico Barbero, Alex Vitvitskyi, Christos Perivolaropoulos, Razvan Pascanu, Petar Veličković, "Round and Round We Go! What makes Rotary Positional Encodings useful?," ICLR, 2025.
2604版の大域層は、低周波側でもYaRNで周波数を1/256に下げて回転させます。Gemma 4の大域層は、低周波側の192対には回転を掛けません。

7. SwiGLUによるMLPと射影行列の分割

MLPは、各トークンのベクトルを個別に変換する部分で、各層ではすべてのトークンに同じ射影行列を使います。PLaMo 3では、入力をgateとupの2通りに射影し、gate側から求めた係数でup側の各成分を調整します。
gate側の係数を求める活性化関数にはSiLUを使います。このゲート付きMLPがSwiGLU[20]です。pre_mlp_normで正規化した入力を\(U \in \mathbb{R}^{B \times T \times 5120}\)とすると、計算は次の式で表せます。
\[ \begin{eqnarray} [G, U_{\text{up}}] &=& U W_{\mathrm{gate\_up}}^\mathsf{T}, \\ \operatorname{MLP}(U) &=& \bigl[\operatorname{SiLU}(G) \odot U_{\text{up}}\bigr] W_{\text{down}}^\mathsf{T} \end{eqnarray} \]
射影後の\(G\)と\(U_{\text{up}}\)は、どちらも形状\([B, T, 25600]\)です。gate_up_proj.weightの先頭25600行がgate、後半25600行がupに対応します。この境界で行列を分けて使っても、射影出力は数学的に同じです。gate側の出力\(G\)だけに\(\operatorname{SiLU}(g) = g\,\sigma(g)\)を適用し、up側の出力\(U_{\text{up}}\)と要素ごとに掛けます。\(\sigma(g) = 1/(1 + e^{-g})\)はシグモイド関数です。
gate側のSiLU出力は負にもなり、up側の成分の符号を反転させることもあります。線形層にはバイアスがなく、ドロップアウトも使いません。
Gemma 4のMLPも、活性化関数を通したgate出力とup出力の積を使います。活性化関数はtanh近似のGELU、中間次元は\(4D = 21504\)で、PLaMo 3のSiLU・\(5D = 25600\)とは異なります。

8. Gemma 4 31Bとの共通点と相違点

Gemma 4 31Bの事前学習版は、約307億パラメータのテキストデコーダと、約5.8億パラメータの画像エンコーダからなります。比較するのは60層・隠れ次元5376のテキスト部分です。
両モデルに共通するのは、局所/大域Attentionの併用、GQA、Q/KのRMSNorm、AttentionとMLPの入出力に置くRMSNorm、入力埋め込みと出力ヘッドの共有です。一方、局所層と大域層の比率は7:1と5:1、ヘッド次元はPLaMo 3が全層128、Gemma 4が局所256・大域512と異なります。次の表では、こうした構成と計算手順を同じ項目で比べます。
役割PLaMo 3Gemma 4
1ブロックPlamo3DecoderLayerGemma4TextDecoderLayer
Attention入力のRMSNormpre_mixer_norminput_layernorm
Attentionmixer(qkv_projを分割)self_attn(q_proj、k_proj。局所層はv_projも使用)
Attention出力のRMSNormpost_mixer_normpost_attention_layernorm
MLP入力のRMSNormpre_mlp_normpre_feedforward_layernorm
MLPmlp.gate_up_projを分割、down_projmlp.gate_proj、up_proj、down_proj
MLP出力のRMSNormpost_mlp_normpost_feedforward_layernorm
層ごとのスカラーなしlayer_scalar(ブロック出力に乗算)
最終RMSNormmodel.normmodel.norm
項目PLaMo 3 31BGemma 4 31B
ブロック数 / 隠れ次元\(D\)64 / 512060 / 5376
MLPの中間次元\(F\) / 活性化関数25600(\(5D\))/ SiLU21504(\(4D\))/ tanh近似GELU
Queryヘッド数 / KVヘッド数64 / 8局所32 / 16、大域32 / 4
ヘッド次元128(全層)局所256、大域512
Queryの総次元数8192(\(> D\))局所8192、大域16384(\(> D\))
大域層のValueKとは別に射影Kと射影出力を共有し、V用に正規化(attention_k_eq_v)
語彙数107,520262,144
局所:大域の周期7:1(大域8層、最後は大域)5:1(大域10層、最後は大域)
局所窓2048(自分を含む。4K版の旧実装は2049)1024(自分を含む)
設定上の最大系列長262,144(4K版は4096)262,144
RoPEの基底(局所 / 大域)\(10^4\) / \(10^6\)\(10^4\) / \(10^6\)
大域層のRoPEの拡張YaRN(係数256、振幅補正あり。4K版はなし)p-RoPE(\(p = 0.25\)、256対中64対だけ回転)
Q/K正規化RMSNorm、ゲイン128次元、オフセット1RMSNorm、ゲインは局所256次元・大域512次元
Vの正規化なしゲインなしのRMSNorm
Q/Kの内積に掛ける固定係数\(1/\sqrt{128}\)1(公開重みのゲインによる係数は§9.4)
RMSNormのゲインの求め方\(c + w\)(出力側の\(c\)は0.2と\(5^{-1.5}\))\(w\)そのもの(初期値1)
入力埋め込みの\(\sqrt{D}\)倍なし(scale_embedding = false)あり
埋め込みと出力ヘッドの共有ありあり
出力logitsのsoftcapなし\(30 \tanh(\cdot / 30)\)
キャッシュに保存するK正規化後・RoPE前正規化後・RoPE後
キャッシュ容量を左右するのは、局所層・大域層の数、窓幅、KVヘッド数とヘッド次元です。PLaMo 3はGemma 4より大域層が少なく、局所窓の幅は2倍です。Gemma 4はK/V射影を共有しますが、TransformersではKとVを別々に保存します。局所層分の割合が小さくなる長い系列では、Gemma 4のキャッシュ容量はPLaMo 3の約2.5倍になります(§5.2)。
出力側にRMSNormを置く点は共通ですが、ゲインを求めるために学習・保存する値は異なります。PLaMo 3はオフセットに小さな定数を使い、実効ゲインとの差分を学習します。Gemma 4が学習するのは、初期値1のゲインそのものです。
Gemma 4はさらに、層ごとの係数layer_scalarをブロック出力全体に掛けます。公開重みの値はすべて0より大きく1未満で、ブロック出力全体を縮小する倍率になっています。

Gemma4TextDecoderLayerは、残差加算を終えたブロック出力にlayer_scalarを掛けてから、次のブロックへ渡します。Transformersでは、1要素のバッファとして登録されており、学習パラメータには含まれません[7]。初期値は1ですが、公開重みには層ごとに異なる値が保存されています(§9.4)。

入力を正の定数倍する前後で二乗平均が\(\epsilon\)を十分に上回る場合、RMSNormの出力はほぼ変わりません。そのため、スカラーでブロック出力を縮めても、次のAttentionに渡す正規化済み入力はほぼ同じです。一方、残差経路の値は縮むので、新たに加えるAttention出力・MLP出力と、残差との大きさの比が変わります。加算後のベクトルの方向も変わりうるため、中間層のスカラーを変えたことによる値の違いは、最終RMSNormを通した出力にも残ることがあります。

  1. [7] Hugging Face, "modeling_gemma4.py (Transformers v5.12.1)," GitHub, 2026.
大域層では、RoPEによって位置差を内積に反映する成分の割合も異なります。PLaMo 3は全層で128次元のヘッドを使い、全64対に位置に応じた回転を掛けます。Gemma 4の大域層は512次元で、その3/4には位置に応じた回転を掛けません。この3/4の成分に対応する内積の項には、その層のRoPEによる位置差の効果が加わりません(§6.4)。

9. 学習済み重みの統計的な特徴

2604版の公開重みでは、深い層ほど出力側の正規化ゲインが大きくなる傾向と、Q/Kの高周波側の一部の成分でゲインが小さくなる傾向が見られます。また、浅い層のMLPには、他の層より重みの標準偏差が小さい層があります。ただし、重みの標準偏差が小さくても、残差に加える出力のRMSが小さいとは限りません。

9.1. 比較対象と統計量

比較対象は、PLaMo 3 31Bの2604版とGemma 4 31Bのテキスト部分です。重みのばらつきを母標準偏差で、値全体の大きさをRMSで比べます。これらに平均、分位点、超過尖度を加えた統計量を、重みの全要素から計算しています。RMSNormは、重みから求めた実効ゲインで比較します。

PLaMo 3 31Bの2604版は全642テンソル(31,757,456,384要素)、Gemma 4 31Bはテキスト部分の全832テンソル(30,697,345,340要素)が対象です。Gemma 4の要素数には、学習パラメータに加えて60個のlayer_scalarも含みます。

BF16は16ビットで値を表すため、ビット列は65,536通りです。重みをsafetensorsのバイト列から直接読み、テンソルごとに各ビット列の出現回数を数え、対応する数値を使って64ビット浮動小数点(FP64)で統計量を計算しています。標本抽出はしていません。

標準偏差は母標準偏差(ddof = 0)です。RMSは\(\sqrt{\operatorname{mean}(w^2)}\)で、平均を引かずに計算します。超過尖度は、平均から大きく離れた値の影響を強く受ける指標です。正規分布で0となる定義を使います。分位点は経験分布関数の逆関数で求め、BF16で表現できる値の間は補間しません。標本抽出による誤差はありませんが、FP64の計算には丸め誤差があります。

PLaMoのQKV射影は§4.1の境界でQ/K/Vに、gate/up射影は§7の境界でgateとupに分け、部分ごとに統計量を計算しました。これらは元のテンソルの一部なので、総パラメータ数には重複して足しません。実効ゲインは、重みとオフセットの和をFP64で計算した値です。BF16で加算したときの丸めは含みません。

両モデルの重みにNaNとInfはなく、PLaMoの重みにはちょうど0の値もありませんでした。

9.2. 出力側の正規化ゲインと残差の大きさ

各層のRMSNormについて、実効ゲインの全5120成分を平均した値を平均ゲインと呼びます。2604版の公開重みでは、出力側の平均ゲインが深い層ほど大きくなる傾向が見られました。Attention出力のpost_mixer_normは、層0が0.116、層1が0.098で、初期値に相当するオフセット0.2を下回ります。層53で初めて0.2を超え、最終層は0.269です。
MLP出力のpost_mlp_normでは、層7の平均ゲイン0.021が最小です。層23で初めてオフセットの約0.089を上回り、最終層は0.358です。一方、入力側の平均ゲインは、pre_mixer_normが0.977〜1.035、pre_mlp_normが0.905〜1.204の範囲にあります。出力側では入力側より層間の差が大きく、特に浅い層でオフセットを下回っています。
4K版でも、出力側のゲインに似た傾向が見られました。Attention出力の平均ゲインは、層0が0.104、最終層が0.247です。各層の平均ゲインを2つのチェックポイント間で比べると、相関係数はAttention出力側が0.93、MLP出力側が0.99でした。
層番号0から63に対する正規化ゲインの全成分の平均。入力側は約1で、出力側は浅い層で小さく、深い層ほど大きくなる傾向がある。Attention出力側は約0.10から0.27、MLP出力側は約0.02から0.36。初期値に相当するオフセット0.2と約0.089を破線で示す。
図8:PLaMo 3 31B(2604版)の実効ゲイン\(\gamma = c + w\)の全成分の平均 横軸下のオレンジ色の目盛は大域層を示します。出力側の平均ゲインは、浅い層ではオフセット(破線)より小さく、深い層ではそれを上回ります。
日本語の1文を入力し、各ブロック出力の末尾トークンで測った2604版の残差のRMSは、埋め込み直後の0.017から最終ブロックの0.97まで約57倍に増えました。途中、層5〜7で一時的に0.85まで増え、直後の層8では0.10に下がっています。
残差のRMSが大きい層では、同じRMSの出力を加えても、残差に対する出力のRMSの比は小さくなります。深い層でゲインが大きくなる傾向は、加算する出力を大きくしてこの比を保つという解釈と整合します。ただし、残差の測定は1文に限られ、この比を保つ効果は検証していません。

入力文は「これからの人工知能技術は、計算資源の制約の中でどこまで」です。PLaMoのトークナイザでは、文頭を表すBOSトークンを含めて12トークンになります。Gemmaでは同じ入力文が17トークンになり、図9で比べるのは各モデルの末尾トークンです。RMSは入力に依存するため、この1文の値を他の入力にも当てはめることはできません。

PLaMoでは、BOSトークンの残差のRMSが終盤の層で6.3に達し、他のトークンの6倍以上になっていました。ここで測ったのは活性値の大きさです。特定のトークンに参照の重みが集中するattention sinkを確認するには、Attentionの重みの分布も調べる必要があります。

層7(最初の大域層)でRMSが一時的に大きくなる傾向は、BOS以外のトークンにも共通です。4K版でも、層7で0.17まで増えました。残差と出力側のゲインがともに大きくなる理由を特定するには、複数の入力や学習過程の調査が必要です。

同じ入力文で測ったGemma 4の残差のRMSは、\(\sqrt{D}\)倍した埋め込みで1.0、中盤で5を超え、終盤には0.7まで下がりました。Attention出力とMLP出力の加算だけでなく、layer_scalarの乗算もこの推移に関わります。出力側のRMSNormの配置は共通でも、残差ストリームの大きさは両モデルで異なる推移を示しました。
同じ日本語文の末尾トークンに対する残差のRMS。PLaMo 3は埋め込み直後の約0.02から最終ブロックの約1へ増え、層7では一時的に0.85になる。Gemma 4は1から始まり、中盤で5を超えた後、終盤で0.7まで下がる。右端の最終RMSNorm後は、PLaMo 3が2.8、Gemma 4が5.6。
図9:残差ストリームのRMSの推移(同じ日本語文、末尾トークン) PLaMo 3の残差のRMSは層7付近で一時的に大きくなりますが、全体では深い層ほど増す傾向があります。Gemma 4のRMSは1から始まり、中盤で最大になった後に下がります。右端の点は最終RMSNorm後の値です。いずれも1文に対する実測値で、入力によって変わります。

9.3. Q/Kゲインの成分ごとの違い

2604版の公開重みでは、同じ回転ペアの2成分でゲインが大きく異なる例が見られました。実効ゲインの絶対値が0.05未満の成分は、Q・Kそれぞれ64層×128成分の計8192成分のうち、Qで282成分、Kで246成分でした。これらはすべて、層31以降の高周波側の回転ペア(\(j \le 16\))に属していました。同じ回転ペアの2成分がそろって閾値未満になる例はありませんでした。

q_normとk_normの実効ゲイン\(1 + w\)は、高周波側の回転ペアに属する一部の成分で小さくなっていました。成分番号を\(i\)とすると、対応する回転ペア番号は\(j = i \bmod 64\)です。\(|\gamma| < 0.05\)の成分はすべて層31以降にあり、\(j \le 16\)の回転ペアに属します。

回転ペアごとに平均したゲインは、高周波側の\(j = 0\)付近では約0.6、低周波側の\(j \ge 48\)では約1.4で、その間もほぼ単調に増えます。低周波側ではほぼ一様ですが、高周波側では層や成分による差が大きくなっています。

Q/K正規化の実効ゲインのヒートマップ。縦軸は層番号0から63、横軸は成分番号0から127で、前半と後半の対応する成分が回転ペアになる。高周波側に小さなゲインが集中し、絶対値0.05未満を示すオレンジ色は層31以降にだけ現れる。低周波側は濃い青色で、値はほぼ一様。
図10:Q/K正規化の実効ゲイン(64層×128成分) 成分番号\(j\)と\(j + 64\)の2成分が回転ペアです。オレンジ色は\(|\gamma| < 0.05\)の成分です。Qのゲインは64個のQueryヘッドで、Kのゲインは8個のKVヘッドで共有されます。

QでもKでも、同じ回転ペア\((j, j + 64)\)の2成分がともに閾値未満になる例は、64層×64対のいずれにもありませんでした。片側が小さいとき、もう一方のゲインは多くの場合1を超えています。RoPEは2成分の値を混ぜて回転させるため、片側のゲインが小さいだけで、その回転ペアがAttentionスコアに位置差を反映しなくなったとは言えません。

高周波側の回転ペアでゲインが成分間で大きく異なる傾向は、4K版と610M Fin Instructにも見られました。この傾向が長文処理や日本語の性能にどう影響するかは、重みの測定だけでは分かりません。

Qの該当282成分を回転ペア番号ごとに数えると、\(j = 0\)から13までは順に18、25、17、26、23、20、30、21、18、22、16、19、16、9成分でした。\(j = 14\)と16に1成分ずつあり、それ以外は0成分です。Kは\(j \le 13\)に245成分、\(j = 16\)に1成分あります。

ゲインの最小値はQ・Kともに0.0039で、負の値はありませんでした。最大値はQが2.55、Kが2.26です。

4K版で同じ閾値に該当するのは、Qが273成分、Kが166成分で、いずれも\(j \le 16\)にあります。16層の610M Fin Instructでは、Qの153成分が\(j \le 14\)、Kの101成分が\(j \le 18\)に分布します。両モデルとも、同じ回転ペアの2成分がそろって閾値未満になる例はありませんでした。

ゲインの成分ごとの差が参照先への重みをどう変えるかは、チェックポイントだけでは分かりません。実際の入力に対するQ/Kの活性値を調べ、ゲインを変更したときのAttentionスコアや、遠いトークンに割り当てる重みを比較する必要があります。

Gemma 4では、各層のQ用・K用ゲインはそれぞれのベクトル内で一定でした(§9.4)。PLaMo 3では回転前に成分ごとに異なる倍率を掛けるので、再実装でもQ/K正規化とRoPEの順序を保つ必要があります(§6.1)。

9.4. Gemma 4のゲインと射影行列の大きさ

Gemma 4 31BのQ/Kゲインは、ヘッド次元と同じ要素数のベクトルとして保存されています。公開重みでは、各層のQ用ゲインは全成分が同じ値で、K用ゲインも同様でした。Q側は0.95〜1.05、K側は局所層で0.120〜0.132、大域層で0.060〜0.065です。PLaMo 3に見られた成分ごとの差は、Gemma 4の公開重みにはありません。
Gemma 4のAttentionは、内積に\(1/\sqrt{d}\)を掛けません。公開重みのQ用・K用ゲインはそれぞれ全成分が同じ値なので、両者の積を内積全体の係数として扱えます。この係数は逆温度と呼ばれます。内積の値を固定して比べると、逆温度が大きいほどsoftmax後の重みが一部の参照先に集中します。ただし、公開重みで全成分が同じでも、学習時からゲインをスカラーとして扱っていたかどうかは分かりません。
Q用・K用のゲインがそれぞれ層内で一定なら、その積を内積全体に掛ける形へ書き換えられます。局所層(\(d = 256\))では、ゲインの積は通常の係数\(1/\sqrt{256} = 0.0625\)の約2倍です。大域層(\(d = 512\))では、\(1/\sqrt{512} \approx 0.044\)の約1.4倍になります。ゲインを掛ける前の正規化済みQ/Kを固定して比べると、Gemma 4のゲインの積を使うほうが、一部の参照先に重みが集中しやすくなります。
Gemma 4のlayer_scalarの各層の値は、最初の2層が0.097と0.070、最後の層が0.040と小さく、中間の層は0.46〜0.99の範囲にあります。最初の2層のスカラーの積は約1/150です。残差経路を通る入力埋め込みの項には、この倍率が掛かります。各層では係数を掛ける前にAttention出力とMLP出力が加わるため、ブロック出力全体が1/150になるわけではありません。図9では、層1の出力のRMSは埋め込みの約半分です。
各層のBF16の値を、小数第3位まで丸めて示します。表の各行は、層番号の小さい順に並べています。
層番号layer_scalarの値
0〜90.097、0.070、0.992、0.969、0.984、0.879、0.914、0.852、0.859、0.867
10〜190.914、0.812、0.719、0.973、0.977、0.840、0.977、0.844、0.969、0.906
20〜290.902、0.957、0.910、0.695、0.836、0.793、0.664、0.641、0.457、0.562
30〜390.695、0.758、0.766、0.785、0.758、0.668、0.789、0.809、0.797、0.684
40〜490.590、0.645、0.766、0.809、0.848、0.871、0.895、0.879、0.875、0.832
50〜590.867、0.871、0.703、0.828、0.906、0.871、0.844、0.809、0.727、0.040

全60層の積は約\(1.5 \times 10^{-9}\)ですが、各層でAttention出力とMLP出力が加わるため、この積だけで最終出力の大きさや方向は説明できません。中間層のスカラーは、残差と新たに加える出力の比率を変えます(§8)。

最終層で0.040を掛けた出力は、別の出力を加えることなく最終RMSNormへ渡されます。今回の入力文の末尾トークンでは、最終RMSNorm前のRMSは約0.7でした。二乗平均に対する\(\epsilon = 10^{-6}\)の比は約\(2 \times 10^{-6}\)と十分小さいため、出力を縮小した倍率は最終RMSNormでほぼ相殺されます。

Gemma 4 31Bの層ごとのスカラーの棒グラフ。層0は0.097、層1は0.070、層59は0.040で、他の層は約0.46から0.99の範囲にある。大域層をオレンジ色で示す。
図11:Gemma 4 31Bの層ごとのlayer_scalar 最初の局所2層と最後の大域層だけが0.1未満で、ブロック出力を0.1倍未満に縮めます。大域層をオレンジ色で示しています。
Gemma 4の入力側のRMSNormには、全成分の平均が数十から数百に達するゲインもあります。正規化後の値にはさらに射影行列を掛けるため、ゲインだけでは射影後の値の大きさは分かりません。
各層の平均ゲインについて、層間での最小値から最大値までを示します。右列は、全層・全成分を通じた最小値と最大値です。最終RMSNormは1個なので、平均ゲインも1つの値になります。
RMSNormの実装名平均ゲインの範囲(層間)全層・全成分の最小 / 最大
input_layernorm4.9〜47.6−54 / 1248
post_attention_layernorm0.018〜1.680.00003 / 20
pre_feedforward_layernorm2.2〜218−100 / 860
post_feedforward_layernorm0.032〜1.90.0002 / 50
norm(最終)11.4−2.6 / 510
Gemma 4の射影行列では、入力次元数が大きい行列ほど、重みの標準偏差が小さくなっています。標準偏差に入力次元数\(\mathrm{fan\_in}\)の平方根を掛けると、約0.95〜1.05に収まります。同じ層では、q_projとk_projの標準偏差の相対差は\(10^{-4}\)未満で、gate_projとup_projも同様です。埋め込みの標準偏差は0.01364で、\(\sqrt{5376}\)倍すると約0.9998になります。
このように入力次元数ごとに重みの標準偏差が揃っていることから、射影行列の重みをある係数で割り、RMSNormのゲインに同じ係数を掛けて調整した可能性が考えられます。ただし、調整の有無や時期は特定できません。

RMSNormの直後に線形層がある場合は、ゲインと行列の重みの間で倍率を移しても、数学的には同じ出力を得られます。線形層の行列を\(W\)、ゲインを掛ける前の正規化済み入力を\(\hat{x}\)とすると、列ベクトルの記法では\(W\operatorname{diag}(\gamma)\hat{x}\)と書けます。\(W\)の列を0でない係数で割り、対応する\(\gamma\)の成分に同じ係数を掛けても、射影後の値は変わりません。同じRMSNormから複数の射影へ分岐する場合は、すべての射影を同じ対応で変換します。Gemma 4の標準偏差が揃うことは、このような再パラメータ化と整合します。ただし、出力射影を含むすべての行列に、この変換が使われたとは断定できません。

PLaMo 3では、Q/K射影の後にQ/KのRMSNorm、出力射影とdown射影の後に出力側のRMSNormがあります。これらの行列全体を正の定数倍すると、射影出力も同じ倍率で変わります。定数倍の前後で二乗平均が\(\epsilon\)を十分に上回るなら、RMSNormで割る分母もほぼ同じ倍率で変わり、正規化後の値はほぼ元と同じです。ただし、層7のMLP出力のように二乗平均が\(\epsilon\)より小さい場合は例外です(§9.5)。重みの絶対的な大きさだけでは、初期化方式や残差に加える出力の大きさを求めにくい点は、両モデルに共通します。

PLaMo 3の層8以降では、AttentionのQ/K/V・出力射影と、MLPのgate/up/down射影の重みの標準偏差は約0.0048〜0.0068に収まります。入力次元数は5120、8192、25600と異なりますが、標準偏差は同程度です。埋め込みの標準偏差は0.0153です。層0〜7のMLPには、層8以降とは異なる特徴があります(§9.5)。
PLaMo 3のAttentionの出力射影とMLPのdown射影には、平均からの距離が標準偏差の5倍を超える値も見られます。外れ値の頻度や超過尖度は、射影行列の種類によって大きく異なります。

PLaMo 3のo_projでは、層ごとの超過尖度の中央値は115、最大は層16の3506です。平均からの距離が標準偏差の5倍を超える要素は0.040%あります。正規分布なら約0.00006%です。down_projも層6では超過尖度4270ですが、層8以降のgate/upは0.1〜0.6です。

Gemma 4の射影行列の超過尖度は、種類ごとに層間の中央値を取ると約0.34〜3.01に収まります。ただし、個別にはv_projの9.38のような例もあり、埋め込みの超過尖度は111です。超過尖度だけで分布全体の形を説明したり、性能の優劣を判断したりすることはできません。

9.5. 浅い層のMLPの重みと正規化後の出力

MLPのgate、up、down射影では、重みの標準偏差が層2、3、4、6で0.0006〜0.0011と、層8以降のおよそ1/5〜1/10になっています。層0、1、5、7も0.003〜0.005で、やや小さな値です。Attentionのo_projの標準偏差は、層0〜7で0.004〜0.005でした。
表は、層0〜8の重みの標準偏差と活性値のRMSです。活性値の測定には§9.2の入力文を使い、BOSを除く11トークンを対象にしました。「SiLU入力」は、gate射影の出力を活性化関数へ渡す直前の値です。AttentionとMLPの出力は、それぞれ出力側のRMSNormを通した後に残差へ加えます。
層gate / up / downの重みの標準偏差o_projの重みの標準偏差SiLU入力のRMSMLP出力のRMS(正規化前)MLP出力のRMS(正規化後)Attention出力のRMS(正規化前)Attention出力のRMS(正規化後)
00.0037 / 0.0037 / 0.00410.00480.2550.58470.0222.30.135
10.0046 / 0.0045 / 0.00480.00390.7776.4440.04817.30.134
20.0010 / 0.0010 / 0.00110.00420.52133.110.04360.40.102
30.0006 / 0.0006 / 0.00060.00390.770108.60.04247.80.103
40.0006 / 0.0006 / 0.00060.00450.35211.890.03723.30.125
50.0036 / 0.0035 / 0.00360.00460.1450.6450.0678.760.125
60.0006 / 0.0006 / 0.00060.00510.0600.65420.0724.820.200
70.0031 / 0.0031 / 0.00340.00450.0130.000210.0472.470.448
80.0057 / 0.0057 / 0.00570.00590.0370.00540.3780.2530.376
この9層のMLP出力を比べると、正規化前のRMSは5桁以上異なります。正規化後のAttention出力とMLP出力のRMSは、0.02〜0.5の範囲です。出力側のRMSNormで差は縮まりますが、これらは入力文に対する実測値で、ゲインだけからは求められません。
§9.2の入力文からBOSを除いた11トークンでは、正規化前のMLP出力のRMSは層3が108.6、層7が0.00021で、5桁以上の差がありました。層3は重みの標準偏差が小さい層の1つですが、この入力に対する正規化前の出力のRMSは大きくなっています。重みの標準偏差だけでは、出力のRMSの大小は説明できません。
出力側のRMSNormで差が縮まっても、ゲインだけから正規化後のRMSは決まりません。各成分のゲインが異なるため、入力ベクトルのどの成分が大きいかも、正規化後のRMSに影響します。さらに、層7の正規化前のRMSは約0.00021で、\(\sqrt{\epsilon} = 0.001\)より小さい値です。この入力で\(\epsilon\)を省いてRMSだけで割ると、正規化後のRMSを過大に見積もります。
RMSNormへ渡す1トークン分のベクトルを\(x\)、実効ゲインを\(\gamma\)とします。RMSNormを\(N_\gamma\)と書くと、出力のRMSは次の関係を満たします。 \[ \operatorname{RMS}(N_\gamma(x))^2 = \frac{\operatorname{mean}(\gamma^2 \odot x^2)}{\operatorname{mean}(x^2) + \epsilon}. \] 分子にはゲインと活性値の積が含まれるため、ゲインだけでは出力のRMSは決まりません。また、入力を正の定数\(a\)倍したときは、次の式で表せます。 \[ N_\gamma(a x) = \gamma \odot \frac{x}{\sqrt{\operatorname{mean}(x^2) + \epsilon/a^2}} \] 入力を定数倍しても正規化後の値がほぼ変わらないのは、変更前と変更後のどちらでも、二乗平均が\(\epsilon\)に比べて十分大きい場合です。層7のようにMLP出力が小さい場合は、downやupの行列全体を正の定数倍すると、残差に加えるMLP出力のRMSも変わります。
gate側のSiLUでは、入力の値に応じて掛ける倍率が変わります。SiLUは入力\(g\)に\(\sigma(g)\)を掛ける関数です。\(g\)が0に近いと倍率は約1/2となり、\(\operatorname{SiLU}(g) = g\,\sigma(g)\)を\(g/2\)で近似できます。同じ入力文に対するSiLU入力のRMSは、層6〜8では0.01〜0.06でした。一方、重みの標準偏差が小さい層2〜4では0.35〜0.77で、深い層の多くと同程度です。SiLUを0付近の線形関数で近似できるかは、重みの標準偏差だけでは判断できません。SiLUを線形近似しても、入力に依存するgateとupの積を取るため、MLP全体は線形な変換にはなりません。
4K版には、特定の浅い層だけ重みの標準偏差が小さくなる偏りはありません。全層のMLPで0.0045〜0.0068に収まります。一方、この入力文に対する層7のSiLU入力のRMSは、4K版でも0.035と小さな値でした。層7のgate射影が小さな値を出す点は、両チェックポイントに共通します。重みの標準偏差の大小と、活性値のRMSの大小は、どちらのチェックポイントでも単純には対応しません。

10. 参照実装と公開実装の照合

本文の式と定数から、公開実装と同じlogitsを計算できるかを確かめました。照合に使う参照実装はsafetensorsから重みを直接読み込み、モデルの公開実装を呼び出さずに計算します。2604版の公開重み・FP32・12トークンの条件で両実装のlogitsを比べると、最大絶対誤差は\(9.5 \times 10^{-6}\)でした。
掲載するコードの実行条件は、CPU・FP32・バッチサイズ1で、パディングとキャッシュは使いません。wにはFP32へ変換した重みの辞書、idsには形状\([1, T]\)のトークンID列を渡します。
import math
import torch
import torch.nn.functional as F

EPS, DH = 1e-6, 128
OFF = dict(
    pre_mixer=1.0, post_mixer=1 / 5,
    pre_mlp=1.0, post_mlp=5 ** -1.5,
    q=1.0, k=1.0, final=1.0,
)

def rms_norm(x, weight, offset):            # (c + w) ⊙ x / sqrt(mean(x^2) + ε)
    y = x.float() * torch.rsqrt(x.float().pow(2).mean(-1, keepdim=True) + EPS)
    return (offset + weight) * y.to(x.dtype)

def rope(x, theta, yarn):                    # split-half形式で回転。大域層にはYaRNを適用
    T = x.shape[-2]
    freq = theta ** (torch.arange(0, DH, 2).float() / DH)
    inv, conc = 1.0 / freq, 1.0
    if yarn:                                 # L0 = 4096, s = 256, β_fast = 32, β_slow = 1
        low = 64 * math.log(4096 / (32 * 2 * math.pi)) / math.log(theta)
        high = 64 * math.log(4096 / (1 * 2 * math.pi)) / math.log(theta)
        mask = 1 - ((torch.arange(64).float() - low) / (high - low)).clamp(0, 1)
        inv = (1.0 / (256 * freq)) * (1 - mask) + (1.0 / freq) * mask
        conc = 0.1 * math.log(256) + 1.0      # 振幅補正。cosとsinの両方に掛ける
    ang = torch.arange(T).float()[:, None] * inv[None]
    cos, sin = (
        torch.cat([ang, ang], -1).cos() * conc,
        torch.cat([ang, ang], -1).sin() * conc,
    )
    x1, x2 = x[..., :DH // 2], x[..., DH // 2:]
    return x * cos + torch.cat([-x2, x1], -1) * sin

def block(x, w, i):
    p = f"model.layers.layers.{i}."
    is_global = (i + 1) % 8 == 0
    T = x.shape[1]
    u = rms_norm(x, w[p + "pre_mixer_norm.weight"], OFF["pre_mixer"])
    q, k, v = torch.split(
        u @ w[p + "mixer.qkv_proj.weight"].T,
        [64 * 128, 8 * 128, 8 * 128], -1,
    )
    q = q.view(1, T, 64, 128).transpose(1, 2)        # [B, 64, T, 128]
    k = k.view(1, T, 8, 128).transpose(1, 2)         # [B, 8, T, 128]
    v = v.view(1, T, 8, 128).transpose(1, 2)
    q = rms_norm(q, w[p + "mixer.q_norm.weight"], OFF["q"])
    k = rms_norm(k, w[p + "mixer.k_norm.weight"], OFF["k"])
    theta = 1000000 if is_global else 10000
    q, k = rope(q, theta, is_global), rope(k, theta, is_global)
    # GQA:連続する8個のQueryヘッドが同じ1組のK/Vヘッドを参照
    k = k.repeat_interleave(8, 1)
    v = v.repeat_interleave(8, 1)
    s = (q @ k.transpose(-1, -2)) / math.sqrt(128)
    i_, j_ = torch.arange(T)[:, None], torch.arange(T)[None, :]
    allowed = j_ <= i_
    if not is_global:
        allowed &= (i_ - j_) <= 2047        # 局所層:自分自身と直前の2047トークン
    s = s.masked_fill(~allowed, float("-inf"))
    a = (torch.softmax(s, -1) @ v).transpose(1, 2).reshape(1, T, 64 * 128)
    x = x + rms_norm(
        a @ w[p + "mixer.o_proj.weight"].T,
        w[p + "post_mixer_norm.weight"], OFF["post_mixer"],
    )
    u = rms_norm(x, w[p + "pre_mlp_norm.weight"], OFF["pre_mlp"])
    g, up = (u @ w[p + "mlp.gate_up_proj.weight"].T).chunk(2, -1)
    m = (F.silu(g) * up) @ w[p + "mlp.down_proj.weight"].T
    return x + rms_norm(m, w[p + "post_mlp_norm.weight"], OFF["post_mlp"])

def forward(ids, w):
    x = w["model.embed_tokens.weight"][ids]          # 埋め込みを√D倍しない
    for i in range(64):
        x = block(x, w, i)
    x = rms_norm(x, w["model.norm.weight"], OFF["final"])
    return x @ w["model.embed_tokens.weight"].T     # 出力ヘッドは埋め込みと共有
入力文は§9.2と同じで、PLaMoではBOSを含めて12トークンになります。全64層の計算を終えて得られたlogitsの絶対値は最大18.2で、各位置でlogitsが最大になるトークンは両実装で一致しました。公開実装をBF16で動かすと、FP32で動かした場合との差は最大0.165です。この入力では、FP32同士での実装間の差は、BF16とFP32の差より約4桁小さくなりました。
公開重みを使ったFP32・12トークンの比較では、一括処理と1トークンずつのdecodeのlogitsの最大絶対誤差は\(1.0 \times 10^{-5}\)でした。窓幅を超える入力は、2604版の公開実装にランダム重みを与えた小型モデルで比較し、1トークンずつのdecodeと、入力を分割したprefillのいずれも、一括処理とのlogitsの最大絶対誤差は\(2.7 \times 10^{-7}\)以下でした。公開重みで窓幅を超える入力や、最大系列長262,144トークンでの動作は未検証です。
検証条件最大絶対誤差
公開重み・全64層:参照実装と公開実装2604版、FP32、\(T = 12\)、キャッシュなし\(9.5 \times 10^{-6}\)(平均絶対誤差\(1.2 \times 10^{-6}\))
公開実装:BF16とFP322604版、\(T = 12\)、キャッシュなし0.165(平均絶対誤差0.019)。最大スコアのトークンは一致
公開重み:一括処理と1トークンずつのdecode2604版、FP32、系列長12\(1.0 \times 10^{-5}\)
小型モデル:一括処理と1トークンずつのdecode8層、\(D = 48\)、ヘッド次元16、\(W = 4\)、系列長12、FP32\(2.7 \times 10^{-7}\)(4K版の旧実装では\(3.3 \times 10^{-7}\))
小型モデル:一括処理と3・6・3トークンに分けたprefill同上\(2.1 \times 10^{-7}\)(旧実装では\(2.8 \times 10^{-7}\))
小型モデル:学習モードでのマスク省略と評価モード上記の小型モデルで\(T = 4, 5, 6\)\(T = 5\)のみ0.15(旧実装では0)

公開重みの照合には、PyTorch 2.12.1(CPU)とTransformers 5.12.1を使いました。モデルカードはPyTorch 2.9.0以下を指定していますが、この検証では2.12.1で実行しています。両実装ともFP32で重みを読み込むため、重みだけでそれぞれ約127 GBが必要です。CPUは96スレッドで実行しました。小型モデルは、公開実装のクラスにランダム重みを与えたものです。4K版の旧実装の検証にはTransformers 4.57.1を使っています。

再実装では、次の仕様と重みの対応を確認します。
  • 保存されたキー名model.layers.layers.{i}.からの重みの対応付けと、1つにまとめた射影行列の分割位置(§2.2・§4.1・§7)
  • 設定ファイルのヘッド次元128と、RMSNormの適用箇所ごとのオフセット(§2.1・§3.2)
  • Q/K正規化とRoPEの適用順序、split-halfの回転ペア、大域層のYaRNと振幅補正(§6.1・§6.3)
  • キャッシュに保存するKがRoPE適用前の値であることと、Q/Kへの位置番号の割り当て(§4.4)
  • 0始まりの層番号に対応する局所/大域層の配置と、窓幅に含めるトークン数(§5.1)
  • 入力埋め込みと出力ヘッドの重みの共有(§2.2)
  • BF16での型変換、オフセット加算、乗算の順序(§3.2・§6.3)
実装を照合するときは、まずFP32・短い入力・バッチサイズ1・キャッシュなしの条件で、各ブロックの出力を比較します。その後、キャッシュ、窓の境界、パディング、BF16、高速カーネルの順に条件を増やすと、不一致が生じる段階を特定しやすくなります。

11. まとめ

PLaMo 3 31Bは、局所7層・大域1層の周期で、長い系列のAttention計算量とKVキャッシュ容量を抑えるデコーダ型Transformerです。Gemma 4 31Bも局所/大域Attention、GQA、Q/K正規化、AttentionとMLPの入出力の正規化を使います。両者の違いは、層の比率、ヘッド次元、正規化で保存する値、位置符号化、残差の大きさの調整方法にあります。
2604版の公開重みでは、深い層ほど出力側の正規化ゲインが大きくなる傾向と、高周波側の一部の成分でQ/Kゲインが小さくなる傾向が見られました。浅い層のMLPでは重みの標準偏差が小さくても、正規化前の出力が大きい例があります。残差に加わる出力の大きさは、重みの標準偏差だけでなく、正規化のゲインと\(\epsilon\)、実際の入力に対する活性値を合わせて見る必要があります。
再実装では、ヘッド次元128とRMSNormのオフセットを正しく設定し、Q/K正規化とRoPEの適用順序を公開実装と合わせます。公開実装のキャッシュに保存されるKは、RoPE適用前の値です。2604版では、大域層のYaRNの周波数・振幅補正も必要です。本文の式による参照実装と公開実装のlogitsの最大絶対誤差は、公開重み・FP32・12トークンの条件で\(9.5 \times 10^{-6}\)でした。公開重みを使った長文脈での動作は、別途検証が必要です。

参考文献

  1. [1] Preferred Networks, Inc., "PLaMo 3 NICT 2604 31B Base (model card, config.json, modeling_plamo.py; revision e26c257e)," Hugging Face, 2026. (本記事の主対象。版を固定した設定ファイル、公開実装、重み)
  2. [2] Preferred Networks, Inc., "PLaMo 3 NICT 2B Base (model card and config.json)," Hugging Face, 2026. (モデルサイズの比較)
  3. [3] Preferred Networks, Inc., "PLaMo 3 NICT 8B Base (model card and config.json)," Hugging Face, 2026. (モデルサイズの比較)
  4. [4] Preferred Networks, Inc., "PLaMo 3 610M Fin Instruct (model card and config.json)," Hugging Face, 2026. (金融向け派生モデルの構成とパラメータ数)
  5. [5] Preferred Networks, Inc., "PLaMo 3 NICT 31B Base (model card, config.json, modeling_plamo.py; revision 9c1fa626)," Hugging Face, 2026. (層数や次元数が同じ4K版。比較用の重みと、窓の数え方が異なる旧実装)
  6. [6] Google DeepMind, "Gemma 4 31B (model card and config.json)," Hugging Face, 2026. (比較対象。設定ファイルと重み)
  7. [7] Hugging Face, "modeling_gemma4.py (Transformers v5.12.1)," GitHub, 2026. (Gemma 4の正規化・RoPE・KVキャッシュの公開実装)
  8. [8] Shuji Suzuki, "大規模言語モデルの次期バージョン PLaMo 3 シリーズにおける8B, 31Bの小規模モデルによる事前学習の検証," Preferred Networks Tech Blog, 2025. (PLaMo 3のアーキテクチャ変更の背景、KVキャッシュの比較表)
  9. [9] Iz Beltagy, Matthew E. Peters, Arman Cohan, "Longformer: The Long-Document Transformer," arXiv:2004.05150, 2020. (Sliding Window Attentionを含むLongformer)
  10. [10] Preferred Networks, Kaizaburo Chubachi, others, "PLaMo 2 Technical Report," arXiv:2509.04897, 2025. (前世代PLaMo 2の技術報告)
  11. [11] Liliang Ren, Yang Liu, Yadong Lu, Yelong Shen, Chen Liang, Weizhu Chen, "Samba: Simple Hybrid State Space Models for Efficient Unlimited Context Language Modeling," ICLR, 2025. (PLaMo 2が採用していたMambaとSWAの混合構成)
  12. [12] Gemma Team, "Gemma 3 Technical Report," arXiv:2503.19786, 2025. (局所/大域Attentionの併用とQ/K正規化を持つGemma 3)
  13. [13] Gemma Team, "Gemma 4 Technical Report," arXiv:2607.02770, 2026. (Gemma 4の5:1の層比率、大域層でのKの再利用、p-RoPE)
  14. [14] Biao Zhang, Rico Sennrich, "Root Mean Square Layer Normalization," NeurIPS, 2019. (RMSNormの原典)
  15. [15] vLLM Project, "vllm/model_executor/models/plamo3.py (commit d881812)," GitHub, 2026. (RMSNormのオフセットを重みへ足し込む推論実装)
  16. [16] Joshua Ainslie, James Lee-Thorp, Michiel de Jong, Yury Zemlyanskiy, Federico Lebrón, Sumit Sanghai, "GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints," EMNLP, 2023. (Grouped-Query Attention)
  17. [17] Jianlin Su, Murtadha Ahmed, Yu Lu, Shengfeng Pan, Wen Bo, Yunfeng Liu, "RoFormer: Enhanced Transformer with Rotary Position Embedding," Neurocomputing 568, 2024. (RoPEの原典)
  18. [18] Bowen Peng, Jeffrey Quesnelle, Honglu Fan, Enrico Shippole, "YaRN: Efficient Context Window Extension of Large Language Models," ICLR, 2024. (YaRN)
  19. [19] Federico Barbero, Alex Vitvitskyi, Christos Perivolaropoulos, Razvan Pascanu, Petar Veličković, "Round and Round We Go! What makes Rotary Positional Encodings useful?," ICLR, 2025. (p-RoPE。低周波側の回転ペアから回転を取り除く提案)
  20. [20] Noam Shazeer, "GLU Variants Improve Transformer," arXiv:2002.05202, 2020. (SwiGLUを含むGLU系MLP)