ディープラーニングのあちこちにヤコビアンが現れる理由をわかりやすく解説

ディープラーニングを勉強していると、まるで合言葉のように「ヤコビアン」という言葉が出てきます。逆伝播の説明でも、正規化フローの説明でも、敵対的サンプルの話でも、Neural ODE でも——分野はバラバラなのに、なぜか同じ「ヤコビアン」が顔を出す。「また出てきた。これ結局なんなの?」と思った人は多いはずです。

実は、これは偶然ではありません。ニューラルネットワークが「たくさんの数を入れて、たくさんの数を出す関数」である以上、その微分は必然的に行列=ヤコビアンになるからです。そして一度ヤコビアンという行列で物事を捉えると、学習・確率・安定性という一見バラバラな話が、すべて「この行列をどう料理するか」という1つの視点に統一されます。

ヤコビアンの3つの顔

この記事のゴールは、ヤコビアンを「3つの顔を持つ1人の主役」として紹介することです。同じヤコビアン $J$ が、(1) 掛け算すると学習(逆伝播)(2) 行列式を取ると確率(生成モデル)(3) 大きさ(ノルム)を測ると感度・安定性 という3つの顔を見せます。なぜそうなるのかが腹落ちすれば、これから出会う新しい手法でも「ああ、またヤコビアンのこの顔か」と見通しが立つようになります。難しい証明はせず、図と直感、そして PyTorch の自動微分で実際に手を動かしながら進めます。

本記事の内容

  • なぜニューラルネットの微分が「行列」になるのか(ヤコビアンの正体)
  • 顔その1:連鎖律と逆伝播は「ヤコビアンの積」
  • 顔その2:確率密度の変換は「ヤコビアンの行列式」
  • 顔その3:入力感度・安定性は「ヤコビアンのノルム・特異値」
  • PyTorch の自動微分でヤコビアンを計算して確かめる

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。最初の2セクションだけなら、高校〜大学初年級の微分の知識でも追えます。

なぜ「行列」が出てくるのか — ヤコビアンの正体

まず、いちばん根っこの話から始めます。なぜディープラーニングの微分は、ただの数(スカラー)ではなく行列になるのでしょうか。

高校で習った微分を思い出してください。1つの入力 $x$ を受けて1つの出力 $y$ を返す関数 $y = f(x)$ の微分は、$f'(x)$ という1つの数でした。これは「$x$ を少し動かしたら $y$ がどれだけ動くか」という傾きです。

ところがニューラルネットワークは違います。入力も出力もベクトル、つまりたくさんの数の組です。例えば入力が $(x_1, x_2, x_3)$ の3つ、出力も $(y_1, y_2, y_3)$ の3つだとしましょう。このとき「微分」は何を意味するのでしょうか。

スカラー微分とヤコビアン行列の対比

考えるべき傾きが一気に増えます。「$x_1$ を動かすと $y_1$ はどれだけ動く?」「$x_1$ を動かすと $y_2$ は?」……入力3つ × 出力3つで、全部で9通りの傾きがあります。この9個の傾きを表にして並べたものがヤコビアン行列です。

ヤコビアン行列の定義

行列の $(i, j)$ 成分は「$j$ 番目の入力で $i$ 番目の出力を微分したもの」、つまり偏微分 $\dfrac{\partial y_i}{\partial x_j}$ です。

$$ \begin{equation} J = \begin{pmatrix} \dfrac{\partial y_1}{\partial x_1} & \dfrac{\partial y_1}{\partial x_2} & \cdots \\[2mm] \dfrac{\partial y_2}{\partial x_1} & \dfrac{\partial y_2}{\partial x_2} & \cdots \\[2mm] \vdots & \vdots & \ddots \end{pmatrix} \end{equation} $$

行は「どの出力を見ているか」、列は「どの入力で微分したか」を表します。たったこれだけです。ヤコビアンとは、多入力多出力の関数における「傾きの全パターンを並べた表」 にすぎません。スカラーの微分 $f'(x)$ をそのまま多次元に一般化したもの、と思えば十分です。

ニューラルネットはまさにベクトルからベクトルへの関数なので、その微分は必ずこの行列の形になります。ここがすべての出発点です。では、この行列がどんなふうに「学習」「確率」「安定性」という3つの顔を見せるのか、1つずつ見ていきましょう。

顔その1:連鎖律と逆伝播 ——「ヤコビアンの積」

最初の顔は、ディープラーニングの心臓部である学習です。

ニューラルネットは「層」を何枚も重ねた構造をしています。数学的に言えば、これは関数の合成です。入力 $\bm{x}$ にまず $f_1$ を適用し、その結果に $f_2$ を適用し……と繰り返して出力 $\bm{y}$ を得ます。

$$ \bm{y} = f_2(f_1(\bm{x})) $$

学習するには「出力(最終的には損失)が、各層のパラメータでどう変わるか」を知る必要があります。つまり全体の微分が欲しい。ここで連鎖律が登場します。合成関数の微分は、各段階の微分の掛け算でした。スカラーなら $\dfrac{dy}{dx} = \dfrac{dy}{dh}\dfrac{dh}{dx}$ です。

多次元でもまったく同じ形が成り立ちます。ただし「掛け算」が行列の積になります。

$$ \begin{equation} \frac{\partial \bm{y}}{\partial \bm{x}} = J_2 \, J_1 \end{equation} $$

ここで $J_1$ は1層目のヤコビアン、$J_2$ は2層目のヤコビアンです。

連鎖律はヤコビアンの積

層が $L$ 枚あれば、全体のヤコビアンは $J_L J_{L-1} \cdots J_1$ という長い行列積になります。「深いネットワークの微分」とは、各層のヤコビアンを順番に掛け合わせたものなのです。連鎖律が、ディープラーニングの「ディープ(深い)」を支えています。

逆伝播はヤコビアンを「賢く」掛ける

ここで実装上の大事な工夫があります。素直に $J_L \cdots J_1$ を計算しようとすると、巨大な行列どうしの積を何度もやることになり、計算もメモリも爆発します。

救いは、学習で本当に欲しいのは損失 $L$(スカラー1個)の勾配だという点です。出力が1個のスカラーなら、いちばん左のヤコビアン $J_L$ は「1 × たくさん」の横長ベクトルになります。だったら、行列どうしを掛ける前に、まずこのベクトルを左に置いて、右側のヤコビアンへ順に掛けていけばよいのです。

逆伝播はヤコビアン-ベクトル積

$$ \big( ( \bm{v}^\top J_L ) J_{L-1} \big) \cdots J_1 $$

こうすると、計算の途中でずっと「ベクトル × 行列」だけが続きます。巨大な行列を陽に作らずに済むので、速くて省メモリです。この「ヤコビアンを陽に作らず、ベクトルに掛けながら右から左へ運ぶ」操作をヤコビアン-ベクトル積(VJP, vector-Jacobian product) と呼び、これこそが逆伝播(バックプロパゲーション)の正体です。

つまり、逆伝播は魔法でも特別なアルゴリズムでもありません。「ヤコビアンの積を、損失側から効率よく計算する手順」 にすぎないのです。PyTorch や TensorFlow が裏でやっているのは、まさにこのヤコビアン-ベクトル積の連鎖です。

学習がヤコビアンの「積」の顔だと分かったところで、まったく違う分野——確率と生成モデル——に目を移します。そこでは同じヤコビアンが「行列式」という別の顔で現れます。

顔その2:確率密度の変換 ——「ヤコビアンの行列式」

2つ目の顔は確率です。VAE や正規化フローのような生成モデルを学ぶと、必ず「ヤコビアンの行列式(の絶対値)」$|\det J|$ が出てきます。なぜでしょうか。

直感をつかむために、ゴム膜に描いた絵を想像してください。膜を引き伸ばすと、絵の混み具合(密度)は変わります。引き伸ばした場所は薄くなり、縮めた場所は濃くなる。でも絵の具の総量は変わりません。確率密度もこれと同じで、変数を変換すると「伸び縮みした分だけ」密度が補正されます。

問題は「2次元・3次元……でどれだけ伸び縮みしたか」をどう測るかです。これを測るのがヤコビアンの行列式です。下の図を見てください。

変数変換と行列式による体積変化

左は変換前の等間隔の格子、右はある非線形変換を通した後の格子です。各セルの面積が場所によって伸び縮みしています。この「変換後の面積 ÷ 変換前の面積」が、ちょうどその点でのヤコビアン行列式 $|\det J|$ に等しいのです。行列式は「面積(体積)の拡大率」を表す量だ、というのが線形代数の基本でした。それがそのまま使われています。

数式で書くと、変数 $\bm{z}$ を可逆変換 $\bm{x} = g(\bm{z})$ で移したとき、確率密度は次のように補正されます。

$$ \begin{equation} p_X(\bm{x}) = p_Z(\bm{z}) \, \left| \det \frac{\partial \bm{z}}{\partial \bm{x}} \right| \end{equation} $$

右辺の行列式が「体積の伸び縮み」を打ち消す補正項です。生成モデルでは、単純な分布(標準正規分布など)をニューラルネットで複雑な分布に変換します。その「変換」がまさに多入力多出力の関数なので、密度を正しく計算するにはヤコビアン行列式が要る——というわけです。

この $|\det J|$ をどう軽く計算するかが正規化フロー設計の肝で、RealNVP のアフィンカップリング層は行列式が対角成分の積だけで済むよう工夫しています。詳しくは別記事で扱っていますが、ポイントは「確率を扱う場面では、ヤコビアンは行列式という顔で現れる」ことです。

連続版:トレースという顔

ついでに、もう少し進んだ話にも触れておきます。Neural ODE や連続正規化フローでは、変換を「一気に」ではなく「時間をかけて少しずつ」行います。このとき、密度の変化率を決めるのは行列式ではなく、ヤコビアンのトレース(対角成分の和) になります。

ヤコビアンのトレースと発散

図は、点が流れていくベクトル場と、その「発散(広がり具合)」を色で示したものです。発散とは、まさにヤコビアンのトレース $\mathrm{tr}(J)$ のこと。トレースが正の場所では点が広がって密度が薄まり、負の場所では集まって濃くなります。連続的な変換では、この瞬間ごとの伸び縮み(トレース)を時間で積み上げると、全体の伸び縮み(行列式の対数)になります。

$$ \log |\det J| = \int \mathrm{tr}(J)\, dt $$

行列式もトレースも、結局は「ヤコビアンが体積をどれだけ変えるか」を測る量です。離散的に変換するなら行列式、連続的に変換するならトレース、と使い分けているだけ。確率の顔は、このように形を変えながらディープラーニングの随所に現れます。

確率の話が一段落したので、3つ目の顔——モデルの「壊れやすさ」を測る視点に進みます。

顔その3:感度と安定性 ——「ヤコビアンのノルム・特異値」

3つ目の顔は、モデルの感度・安定性です。敵対的サンプル(adversarial example)やロバスト性、正則化の話で、ヤコビアンの「大きさ」が問題になります。

ヤコビアンは「入力を少し動かしたら出力がどれだけ動くか」を表す行列でした。だとすれば、その行列が大きいほど、わずかな入力の変化で出力が激しく動く、つまり敏感で壊れやすいということになります。逆に小さければ、入力が多少ぶれても出力は安定します。

これを2次元の分類器で見てみましょう。two moons というデータを分類するニューラルネットを学習し、各点での「出力の入力に対するヤコビアンの大きさ(ノルム)」を色で塗りました。

入力感度とヤコビアンのノルム

左が分類器の出力確率と決定境界、右がヤコビアンのノルム $\|J\|$ です。決定境界のすぐ近くで $\|J\|$ が明るく(大きく)なっているのが分かります。これは直感に合います。境界の近くでは「どちらのクラスか」が拮抗しているので、入力をほんの少し動かすだけで予測が反対側に飛んでしまう。まさに $\|J\|$ が大きい=敏感、という状態です。敵対的サンプルが、この境界近くのわずかな摂動で作られるのも納得できます。

だからこそ、モデルを頑健にしたいときは「ヤコビアンを小さく保つ」工夫をします。例えば contractive autoencoder は学習時にヤコビアンのノルムを罰則として加え、入力のノイズに鈍感な(滑らかな)表現を学ばせます。spectral normalization は層のヤコビアンの最大の伸び率を抑えて、ネットワーク全体の「暴れにくさ(リプシッツ性)」を保証します。いずれも「ヤコビアンの大きさをコントロールする」という共通の発想です。

特異値で「方向ごとの伸び率」を読む

ヤコビアンの「大きさ」をもう一歩細かく見ると、さらに役立ちます。行列は方向によって伸び率が違います。ある方向には大きく拡大し、別の方向にはほとんど拡大しない、ということが起こります。この「方向ごとの伸び率」を取り出すのが特異値分解(SVD) です。

ヤコビアンの特異値分解

入力側で半径1の円(あらゆる方向への同じ大きさのゆらぎ)を考え、ヤコビアンを通すと、出力側では楕円になります。楕円の軸の長さが、ヤコビアンの特異値です。長い軸の方向には大きく拡大され(敏感)、短い軸の方向にはあまり拡大されません(鈍感)。最大特異値が、その点での「最悪方向の伸び率」、すなわちモデルがいちばん壊れやすい方向の感度を表します。

  • 最大特異値が大きい → どこかの方向にすごく敏感 → 敵対的に脆い・学習が不安定
  • すべての特異値が穏やか → 全方向に安定 → 頑健

このように、確率では行列式(=全特異値の積=体積拡大率)を見ましたが、安定性では最大特異値(=最悪方向の伸び率)を見ます。同じヤコビアンを、積で見るか、行列式で見るか、特異値で見るか——見る角度の違いが、そのまま学習・確率・安定性という3つの応用に対応しているのです。

3つの顔がそろいました。最後に、これらが机上の空論でないことを、PyTorch で実際にヤコビアンを計算して確かめましょう。

PyTorch でヤコビアンを計算してみる

ここまで「ヤコビアンが大事」と言ってきましたが、実際に手元で計算できると一気に身近になります。PyTorch には自動微分でヤコビアンを直接求める関数 torch.autograd.functional.jacobian があります。

まず、適当な多入力多出力の関数(2入力3出力)でヤコビアンを計算してみます。

import torch
from torch.autograd.functional import jacobian

# 2入力 → 3出力 の関数
def f(v):
    return torch.stack([
        torch.sin(v[0]) + v[1]**2,   # y1
        v[0] * v[1],                 # y2
        torch.exp(0.5 * v[0]) - v[1] # y3
    ])

x0 = torch.tensor([0.7, -0.4], dtype=torch.float64)
J = jacobian(f, x0)
print(J.shape)   # torch.Size([3, 2]) … 出力3 × 入力2 の行列
print(J)

出力は $3 \times 2$ の行列で、まさに「出力3個 × 入力2個」のヤコビアンです。行が出力、列が入力に対応しています。自動微分は、この各成分 $\partial y_i / \partial x_j$ を解析的な正確さで計算してくれます。

本当に正しいのか、有限差分(入力をほんの少し動かして出力の変化を見る、素朴な数値微分)と比べて検証しましょう。

import numpy as np

eps = 1e-6
J_fd = np.zeros((3, 2))
for j in range(2):
    dp = x0.clone(); dp[j] += eps
    dm = x0.clone(); dm[j] -= eps
    J_fd[:, j] = ((f(dp) - f(dm)) / (2 * eps)).numpy()   # 中心差分

err = np.abs(J.numpy() - J_fd).max()
print("自動微分と有限差分の最大誤差:", err)   # → 約 1e-10

自動微分のヤコビアン検証

横軸が有限差分、縦軸が自動微分で求めた各成分です。すべての点が対角線(一致を表す点線)にぴったり乗り、最大誤差は約 $10^{-10}$ ——浮動小数点の精度レベルで完全に一致しました。自動微分が正しくヤコビアンを計算していることが確認できます。普段は逆伝播の裏に隠れているヤコビアンを、こうして表に取り出して眺められるわけです。

ニューラルネットの入力感度を測る

最後に、3つ目の顔(感度)を実際に測ってみます。学習済みの分類器に対して、出力の入力に関するヤコビアンのノルムを求めれば、それが先ほどの感度マップになります。

# net は学習済みの分類器(2入力 → 1出力のロジット)とする
pts = torch.tensor(grid_points, dtype=torch.float32, requires_grad=True)
logit = net(pts).squeeze(1)
# 出力の和を入力で微分すると、各点のヤコビアン(∂logit/∂x)が得られる
grad = torch.autograd.grad(logit.sum(), pts)[0]
jacobian_norm = grad.norm(dim=1)   # 各点での ||J||

出力が1次元なので、ヤコビアンは各点で「1 × 2」の横ベクトル(=勾配)になります。そのノルムが大きい点ほど、入力の微小変化に敏感な点です。これを格子全体で計算して色塗りすれば、決定境界の近くが明るく光る——あの感度マップが得られます。torch.autograd.grad が内部でやっているのも、結局はヤコビアン-ベクトル積です。逆伝播も感度の計算も、同じ仕組みの上に乗っているのだと実感できます。

まとめ

本記事では、ディープラーニングのあちこちにヤコビアンが現れる理由を、3つの顔として整理しました。

  • 出発点:ニューラルネットは多入力多出力の関数。その微分は必ず「傾きの表」=ヤコビアン行列になる。
  • 顔その1(積):層の合成の微分は連鎖律でヤコビアンの積になる。逆伝播は、その積を損失側から効率よく計算するヤコビアン-ベクトル積。
  • 顔その2(行列式):変数変換で密度を補正するのが行列式 $|\det J|$(体積の伸び縮み)。連続版ではトレース $\mathrm{tr}(J)$ が密度変化率を決める。
  • 顔その3(ノルム・特異値):入力感度・安定性はヤコビアンの大きさで測る。最大特異値が大きいほど壊れやすく、それを抑えるのがロバスト化・正則化。
  • 検証:PyTorch の自動微分でヤコビアンを取り出し、有限差分と $10^{-10}$ で一致することを確認した。

バラバラに見えた手法たちが、「ヤコビアンという1つの行列を、積・行列式・ノルムのどれで見るか」という視点で串刺しになります。次に新しい論文で「ヤコビアン」が出てきたら、「これは3つの顔のどれだろう?」と問うてみてください。たいていは学習(積)・確率(行列式)・安定性(ノルム)のどれかに収まり、見通しよく読めるはずです。

次のステップとして、以下の記事も参考にしてください。