← 一覧へ
連載 Agentic OS:技術スタックを下から読む の一部です ―― 目次を見る →

Agentic OS 技術スタックを下から読む 第3回:一枚に載らないモデルを、どう分けて配るか

この記事の読み方
前回までは、モデルが一枚の GPU に収まっているものとして話を進めてきた。

前回までは、モデルが一枚の GPU に収まっているものとして話を進めてきた。

一枚の GPU に重みが載る。推論中に必要な状態も載る。その前提なら、考えるべきことは比較的見通しがよい。どれだけ計算するか。どれだけ待たせずに処理するか。どれだけ無駄なくバッチを組むか。

しかし、最前線の大きなモデルでは、この前提が崩れる。

まず、重みが大きすぎる。一枚の GPU のメモリには載らない。さらに、推論中には重みだけでなく、途中まで読んだ文脈の状態も持ち続ける必要がある。これも小さくない。長い文脈を扱うほど、こちらの負担は増える。

だから、大きなモデルは複数の GPU に分けて置く。

ここで大事なのは、ただ適当に分けるわけではないということだ。分け方には型がある。そして、その型が、どこまで大きなモデルを動かせるか、どこまで安く速く出せるかを決める。

分け方は、モデルの形に沿う

大きなものを分けるときは、どこで切るかが問題になる。

モデルも同じである。重みのかたまりを、どの方向に切るか。計算の流れを、どの単位で切るか。ここを間違えると、GPU は計算するより待つ時間が長くなる。

よい分け方は、モデルが大きくなっている方向に沿っている。切ったあとも、それぞれの GPU が自分の分を計算できる。必要な通信も、どこで何を渡すのかがはっきりする。

代表的な切り方は二つある。

一つは、専門家ごとに分けること。もう一つは、層ごとに分けることだ。

この回では、テンソルをどう割るかという細かい配線には入らない。見たいのは、もっと手前にある制約である。どの単位でモデルを配ると、何が楽になり、何が残るのか。その因果を追う。

専門家で分ける

いまの大きなモデルの多くは、すべての重みを毎回使う形ではない。

全体としては巨大な重みを持っている。だが、ひとつのトークンを処理するときに実際に動くのは、その一部だけである。入力に応じて、使う部品を選ぶ。この部品をここでは「専門家」と呼ぶ。

たとえば、モデル全体では非常に大きな重みを持っていても、あるトークンでは数個の専門家だけを使う。別のトークンでは、また別の専門家を使う。こうすると、モデル全体の知識や表現力は大きくできる一方で、毎回の計算量はある程度に抑えられる。

この形なら、専門家を別々の GPU に置くのは自然である。

GPU A には専門家 1 から 4 を置く。GPU B には専門家 5 から 8 を置く。こうして分ければ、一枚に載らない重みを複数枚に配れる。しかも、各トークンが使う専門家だけを動かせばよい。

ただし、ここで通信が出てくる。

どのトークンがどの専門家に行くかは、入力ごとに変わる。あるトークンを最初に持っていた GPU が、そのトークンに必要な専門家を持っているとは限らない。必要な専門家が別の GPU にあるなら、トークンをそちらへ送らなければならない。

しかも、これは一方向の単純な受け渡しではない。

多くの GPU が、それぞれ別のトークンを持っている。各トークンは、それぞれ別の専門家へ向かう。結果として、GPU 同士が互いにデータを送り合う形になる。全員が全員に近い通信である。

この通信は重い。

計算そのものは各 GPU で並べて進められる。だが、トークンを専門家へ配り、結果をまた戻すには、GPU 間のつながりが速くなければならない。ここが遅いと、専門家を増やしても、計算に入る前後で待たされる。

だから、専門家で分ける方式は、密につながった GPU の範囲に強く縛られる。

たとえば、一つのノード、あるいは一つのラックの中では、GPU 同士が太い帯域でつながっているとする。この範囲では、トークンを送り合ってもまだ耐えられる。しかし、その外へ出ると通信は急に高くつく。遠くの GPU に専門家を置けば置くほど、専門家を選ぶたびに通信が足を引っ張る。

つまり、専門家をどこまで増やせるかは、ソフトウェアだけでは決まらない。密につながった GPU のかたまりが、どれだけ大きいかで決まる。

規模には二つの天井がある

専門家で分ける話から、モデルの大きさには二種類の天井があることが見えてくる。

一つ目は、各トークンで実際に動かす量の天井である。

これは計算に縛られる。トークンごとに使う専門家を増やせば、そのぶん計算が増える。層を厚くし、隠れ層を広げ、動く部分を増やせば、当然ながら一トークンあたりの処理は重くなる。

この「毎回実際に動く部分」を、活性化されるパラメータと考えると分かりやすい。モデル全体がどれだけ大きくても、毎回動く量が増えれば計算時間と計算コストに跳ね返る。

二つ目は、モデル全体の大きさの天井である。

これは、密につながった GPU のかたまりに縛られる。専門家を増やすには、それらの重みをどこかに置かなければならない。だが、専門家同士がトークンを受け渡すなら、遠くに置けばよいとは言えない。速く通信できる範囲に置く必要がある。

このため、総パラメータ数を増やすことと、毎回動かす量を増やすことは、同じ問題ではない。

総量を増やすと、置き場所と通信の制約にぶつかる。毎回動かす量を増やすと、計算の制約にぶつかる。どちらも「モデルを大きくする」話に見えるが、ぶつかる壁が違う。

ここを混ぜると、大きなモデルの見方を誤る。

巨大な総量を持つモデルでも、毎回動く部分が小さければ、一トークンあたりの計算は抑えられる。反対に、総量がそこまで極端でなくても、毎回動く部分が大きければ処理は重くなる。

そして総量を増やす側では、密につながった GPU の範囲が物理的な上限として現れる。

層で分ける

もう一つの自然な切り方は、層で分けることだ。

多くの言語モデルは、同じような処理の層を何段も積み重ねている。入力は一層目を通り、二層目を通り、最後の層へ進む。この縦方向の流れに沿って分ける。

たとえば、前半の層をある GPU 群に置く。中盤の層を別の GPU 群に置く。後半の層をさらに別の GPU 群に置く。こうすれば、一か所に載せる重みの量は減る。

これは、容量の問題に効く。

一枚の GPU に全層の重みが載らない。あるいは、一つの密な GPU かたまりにも載りきらない。そのとき、層を区切って別々の場所に置けば、モデル全体としてはもっと大きな重みを持てる。

層で分ける場合、データの流れも分かりやすい。前の区画が処理した結果を、次の区画へ渡す。専門家分割のように、入力ごとに行き先が大きく変わるわけではない。基本的には前から後ろへ流れる。

その意味で、層分割は大きなモデルを容量の面で逃がすための、素直な方法である。

ただし、これで全部が解けるわけではない。

層で分けても、KV キャッシュは薄くならない

見落とされやすいのは、層で分けても推論中の状態は軽くならないという点である。

言語モデルは、文章を左から右へ読みながら次のトークンを出す。そのとき、過去に読んだトークンの情報を毎回最初から計算し直すと無駄が大きい。そこで、各層が過去トークンについて持っている中間状態を保存しておく。

この保存された状態が KV キャッシュである。

名前だけを見ると分かりにくいが、要するに「これまで読んだ文脈を、次のトークン生成で再利用するための層ごとの記録」である。長い文脈を扱うほど、この記録は増える。並行して処理するリクエストが増えても増える。

層でモデルを分けると、各区画は流れ作業の一部になる。

前半の区画が処理し、中盤が受け取り、後半が出力する。このとき、一つのリクエストだけを流していると、どこかの区画が働いている間、別の区画が待つことになる。せっかく複数の GPU に分けても、遊んでいる時間が増える。

そこで、複数のバッチを続けて流し込む。前半が次のバッチを処理している間に、中盤は前のバッチを処理する。後半はさらにその前を処理する。こうして全体を詰まらせずに動かす。

しかし、流すバッチが増えれば、その分だけ推論中の状態も増える。

各リクエストには文脈がある。各文脈には KV キャッシュがある。バッチを増やすということは、同時に抱える文脈を増やすということでもある。

つまり、層で分けることは、重みの容量問題を逃がす。だが、KV キャッシュの問題は逃がさない。

ここが重要である。

重みは層で分ければ薄くできる。一つの GPU、あるいは一つの区画が持つ重みは減る。だが、長い文脈をたくさん同時に抱えると、KV キャッシュは各層に残る。層を分けたからといって、文脈の記録そのものが消えるわけではない。

そして KV キャッシュは、メモリ容量だけでなく帯域にも関わる。次のトークンを出すたびに、過去の文脈を参照する必要があるからだ。長い文脈では、読むべき状態が増える。たくさんのリクエストを並べれば、同時に触る状態も増える。

容量の問題と、帯域や状態の問題は別である。

層で分ければ、重みは配れる。だが、KV キャッシュの重さは残る。ここでも、密につながった GPU のかたまりが効いてくる。どの範囲で状態を持ち、どの範囲で速く読めるかが、長い文脈や高い並列性の上限を決める。

賢さの前に、配り方の天井がある

大きなモデルを動かすとき、効いてくる天井は一つではない。

毎回どれだけの部分を動かせるかは、計算に縛られる。トークンごとに動く専門家や層の量が増えれば、計算は重くなる。

モデル全体をどこまで大きくできるかは、重みの置き場所に縛られる。専門家を増やすなら、速く通信できる範囲に置かなければならない。層で分ければ容量は逃がせるが、そのぶん流れ作業として動かす必要が出る。

長い文脈をどこまで抱えられるかは、KV キャッシュに縛られる。これは重みとは別の負担である。文章が長くなるほど増え、同時に処理するリクエストが増えるほど増える。

だから、「大きいモデルを動かす」と言うとき、実際にはいくつもの問いが重なっている。

総量としてどれだけの重みを持てるのか。各トークンでどれだけの計算を使えるのか。どれだけ長い文脈を持てるのか。どれだけ多くのリクエストを同時に流せるのか。応答をどれだけ速く返せるのか。

これらは、モデルの賢さだけでは決まらない。

かなりの部分は、モデルをどう分け、どの GPU の範囲に置き、どこで通信し、どこに状態を持つかで先に決まっている。

Agentic OS への含意

Agentic OS の上の層から見ると、モデル選択は単に「どのモデルが賢いか」を選ぶように見える。

しかし、実際にはそれだけではない。

あるモデルを選ぶということは、その裏にある配り方の制約も一緒に選ぶということだ。長い文脈を安く扱えるか。大量のリクエストを並列にさばけるか。最初の応答をどれだけ速く返せるか。混雑したときにどこで詰まるか。

それらは、L0 から L1 の土台でかなり決まる。

専門家で分ければ、総量を大きくしやすいが、専門家をまたぐ通信が問題になる。層で分ければ、重みの容量は逃がせるが、流れを詰めるために多くの状態を抱える。KV キャッシュは消えない。長い文脈を扱うほど、状態の重さが前に出る。

上の層が「このタスクには長い文脈が必要だ」と判断しても、下の層がその文脈を安く保持できなければ、選択肢は狭くなる。上の層が「多数のエージェントを同時に走らせたい」と考えても、下の層が同時状態を持てなければ、速度かコストのどちらかに跳ね返る。

ここまでで、Agentic OS の土台を見るための道具は一通りそろった。

一枚の GPU の中で何が起きるか。バッチと待ち時間がどう絡むか。そして、一枚に載らないモデルをどう分けて配るか。

次回からは、一つ上の層に入る。モデルそのものの構造を見る。モデルは、この帯域、容量、KV キャッシュの制約に対して、どのように折り合いをつけているのか。L2 では、そこを読んでいく。

← 一覧へ