テンソルは数・ベクトル・行列の梯子だ
「テンソル」という言葉は難しく聞こえますが、実はすでに知っているものを一本の梯子に通した名前にすぎません。一マスはただの数(スカラー)。一列に並べればベクトル。格子に広げれば行列。格子を何層も積めばキューブ。インデックスがいくつ必要かが、そのまま階数(rank)です。0個、1個、2個、3個。二つのベクトルを外に掛ける外積 u⊗v = u vᵀ は最も単純な rank-1 行列を作り、どんな行列もこうした片の和として積み上げられます(それが SVD でした)。行列は二つのベクトルを食べて数を一つ(uᵀ M v)返すこともできます。一枚の画像も、学習バッチひと束も、すべてテンソルです。梯子を一段ずつ登ってみましょう。
ボタンで階数を一段ずつ上げてみてください。rank 0 はマス一つ、ただの数で、場所を指すインデックスは要りません。rank 1 はマスを一列に伸ばしてベクトルになり、どのマスか選ぶにはインデックスが一つ要ります。rank 2 は列を格子に広げて行列になり、「何行何列」なのでインデックスは二つ。rank 3 はその格子を何層も積んだキューブなのでインデックスは三つです。階数がそのままインデックス(軸)の数だと一目で分かります。形も一緒に育ちます。点 → 線 → 面 → 体積。テンソルは新しい怪物ではなく、知っているものを同じ梯子に立てた名前です。
今度は二つのベクトルを外に掛けて行列を作ります。スライダーで u = (u1, u2) を左の縦に、v = (v1, v2) を上の横に置くと、格子のマス (i, j) には ui · vj が入ります。つまり u⊗v = u vᵀ です。左の値と上の値を掛けたものがその場所の数で、マスは値に応じて濃く薄く塗られます。u を二倍にすれば行が丸ごと二倍になり、v を上げれば列が丸ごと上がります。面白いのは、どうダイヤルを回しても、この行列のランクはつねに 1 ということ。すべての行が u の倍数、すべての列が v の倍数で、独立な方向が一つしかないからです。外積は最も単純な行列、rank-1 のれんがです。
では普通の行列はどう作るのでしょう? rank-1 のれんがを何個か積めばよいのです。トグルで層を一つずつ足してみてください。層 1 は最初の片 σ1·u1·v1ᵀ だけなので、目標行列の大きな輪郭だけを粗くまねます。層 2 を足すと σ2·u2·v2ᵀ が残りの差を埋め、和が目標行列とぴったり一致します。画面は現在の再構成を目標と並べて出し、二つの差も数で示します。これがまさに SVD の核心です。どんな行列も A = Σ σk uk vkᵀ、すなわち rank-1 片の和で、大きい σ から数個だけ残しても形がほとんど残ります。圧縮はここから生まれます。
行列を「変換」ではなく「二つのベクトルを食べる機械」として見ることもできます。固定した行列 M に左から u を、右から v を差し込むと数が一つ出ます。uᵀ M v です。これが rank 2 テンソル(行列)が二つのベクトルを受け取りスカラーを返す双線形形式です。座標平面で u と v の矢印を動かしてみてください。下の数字板に uᵀ M v の値がリアルタイムで出ます。片方を固定してもう片方だけ二倍にすると、結果もちょうど二倍になります。二つの引数それぞれについて線形なので「双線形」です。長さを測る(内積)、エネルギー(½ xᵀ K x)、曲率、応力。二つの方向を食べて数を一つ与える量は、すべてこの型です。
最後に、なぜテンソルが機械学習の母国語なのかを見ます。トグルで次元を一軸ずつ足してみてください。白黒画像一枚はピクセルの明るさの格子、すなわち [H, W] 行列(rank 2)です。カラーにすると赤・緑・青の三チャンネルが積まれて [H, W, 3] キューブ(rank 3)になります。多くの画像を一度に学習させると、そのキューブを N 個重ねて [N, H, W, 3](rank 4)になります。画面は軸を足すたびに、形のラベルと格子がどう積まれるかを並べて見せます。テンソルは抽象数学の飾りではなく、データの複数の軸を一つの器に収める実用の器です。深層学習フレームワークの名前に「Tensor」が入っているのはそのためです。