ChatGPT に何かを頼むと、たいてい数秒で返ってきます。あれだけの文章を、なぜあんなに速く作れるのか。答えとしてよく聞くのが「AI は GPU で並列処理をしているから速い」という説明です。これは速さの重要な理由のひとつで、おおむねそのとおりなのですが、自分はここで一つ立ち止まりたくなります。
そもそも、どうして並列に「できる」のでしょう。
「速くしたいから並列にする」と言うと、まるで願えば手に入るもののように聞こえます。でも実際はそうではありません。並列にできるかどうかは、こちらの都合ではなく、計算そのものの性質で決まっています。今回はその性質、行列のかけ算が持つ「独立」という構造を、式まで開けて見てみます。GPU が AI の計算で速さを出せる重要な理由のひとつが、ここにあります。
並列にできるかは「独立」で決まる
まず、二つの計算を同時にやれるかどうかを考えてみます。
たとえば料理で、玉ねぎを炒める工程と、その炒めた玉ねぎを使って煮込む工程があるとします。煮込みは炒めたものが出来上がってからでないと始められません。前の結果が来るまで、次は待つしかない。こういう関係を「依存がある」と言います。依存があると、どんなに手が空いていても順番に進めるしかありません。
逆に、玉ねぎを炒める人とサラダを切る人は、お互いの出来上がりを必要としません。だから同時に進められます。こちらは「依存がない」、つまり独立な関係です。
ここが今日いちばん大事なところです。同時にやれるかどうかは、速くしたいという願望ではなく、計算どうしに依存があるかないかだけで決まります。独立でありさえすれば、いくつあっても同時にできます。独立でなければ、いくら計算機を並べても順番待ちは消えません。

行列のかけ算を開くと、独立な計算の山だった
では、AI 計算の中はどうなっているのでしょうか。
AI の中身の多くは、行列どうしのかけ算でできています。行列というのは、数を縦横に並べた表のことです。そして行列のかけ算 $C = AB$ は、表 $A$ を別の表 $C$ に変換する操作だと思ってください。
問題は、この $C$ の中身がどう決まるかです。$C$ の $i$ 行 $j$ 列にあるマス、これを $C_{ij}$ と書きますが、その値は次の式で計算されます。
$$C_{ij} = \sum_{k} A_{ik} B_{kj}$$
記号が並ぶと身構えてしまいますが、言っていることは単純です。$C$ のあるマスは、$A$ の第 $i$ 行と $B$ の第 $j$ 列だけを取り出して、掛けて足したものです。ここで指を差したいのは、この式に $C$ の他のマスがいっさい出てこないことです。$C_{ij}$ を求めるのに、隣の $C_{i+1,j}$ の答えも、どこか別の $C$ のマスの答えも要りません。
具体的に見た方が早いので、小さい例を一つ。
$$A = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix}, \quad B = \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix}$$
このとき $C = AB$ は次のようになります。
$$C = \begin{bmatrix} 19 & 22 \\ 43 & 50 \end{bmatrix}$$
たとえば左上の $19$ は $1 \times 5 + 2 \times 7$、右上の $22$ は $1 \times 6 + 2 \times 8$ です。この四つのマスを計算するとき、どのマスも他のマスの答えを使っていません。つまり四つは互いに独立です。前の節の言葉でいえば、独立だから同時にやれる、の条件をちょうど満たしています。

一つだけ補足しておくと、一つのマスの中には $\sum$、つまり足し合わせる段が残ります。$A_{ik} B_{kj}$ という掛け算たちはバラバラに計算できますが、最後にそれらを一つに足す作業は必要です。「足し算すらいらない」わけではない、という点は正直に置いておきます。独立なのは、あくまでマスとマスの間の話です。それに、結果どうしの依存はない一方で、多くのマスが同じ入力行列を共有するので、実際に速く動かすにはメモリやデータの運び方の工夫も重要になります。
だから数千個の計算機に配れる
独立なマスが四つではなく、何千何万個あるとします。すると、こういう配り方ができます。「君は左上のマス、君はその隣」と、一マスずつ別々の小さな計算機に割り当てる。どのマスも他の答えを待たないので、全部が同時に走り出して、いっせいに終わります。
ここで CPU と GPU の設計の違いが効いてきます。CPU は少数の速いコアで、難しい仕事を順番に片づける設計です。対して GPU は、一つひとつは非力でも、大量のコアで独立な仕事をいっぺんにさばく設計になっています。この「小さな計算機を大量に並べる」という形は、思いつきで選ばれたものではありません。行列のかけ算が独立なマスの集まりだからこそ、その形が必要になったわけです。

裏返すと、逐次な計算は並列化しにくい
ここまで来ると、逆側も見えてきます。独立だから並列にできた。では、独立でない計算はどうなるのでしょう。
身近な例が、AI が文章を作るときの動きです。AI は文章を一語ずつ生成していきます。次の一語を選ぶには、ここまで書いた文を見てから決める必要があります。前が決まらないと次に進めない。これはまさに、さっきの煮込みと同じ「依存がある」関係です。
こういう逐次な処理は、コアが何千あっても順番待ちが消えません。だから、同じ GPU を使っていても、理想的な並列に比べて逐次構造がボトルネックになり、並列化で稼げる部分が限られます。この一語ずつ流れてくる感じについては、別の記事で確率の側から書きました。
大事なのは、GPU の速さは万能の馬力ではない、ということです。速さは、行列のかけ算がたまたま独立な構造を持っていたことに乗っています。計算の形が変われば、同じハードでも速くはなりません。
なお「絶対に速くならない」とまでは言えません。実際にはまとめて処理する工夫などで、順番待ちをある程度ゆるめることはできます。ただ、素朴にはそのまま並列化できない、順番待ちが本質的に残る、という骨格は変わりません。
独立な積和だけを速くする専用チップ
中身が「独立な積和の山」だと分かると、次の発想は自然に出てきます。だったら、その積和だけを大量にこなす専用の回路を作ればいいのではないか、と。
実際にそういうものがあります。NVIDIA の GPU に載っている Tensor Core(テンソルコア、行列のかけ算を専門にする計算ユニット)や、Google の TPU(Tensor Processing Unit、同じく行列演算に特化したチップ)がそれです。中身の構造が分かれば、それに合わせて道具の方を尖らせる、というのは理にかなっています。
おまけの符合を一つ。GPU はもともと Graphics、つまり画面を描くために生まれたものです。画面に並ぶ大量のピクセルの色も、多くの場合は互いにほぼ独立に計算できます。だから GPU のような並列計算機と、もともと相性がよかった。大量のほぼ独立な計算をいっぺんにさばく道具、という一点で、画面描画と行列のかけ算はもとから近い形をしていました。だから AI の計算にそのまま転用できたわけです。
速さの正体は、行列のかけ算の独立性でした。ハードの物語に見えて、じつは「問題の形」の物語だったのです。では、次に AI が遅く感じる場面は、いったいどんな形の計算をしているのでしょうね。
あわせて読みたい関連記事。


