世界を動かす技術を、日本語で。

微分、勾配、ヤコビ行列およびヘッセ行列

概要

  • 微分・勾配・ヤコビアン行列・ヘッセ行列 の関係性と用途の解説
  • 最適化問題 における微分と勾配降下法の利用
  • 多変数関数 に対する勾配・ヤコビアン・ヘッセ行列の計算例
  • グラフィックスや機械学習 への応用例
  • ヘッセ行列の性質 と最適化での役割

微分と最適化

  • 微分 は微積分で最も基本的な概念であり、関数の各点での変化率を示す指標

  • 例えば関数 f(x) があれば、その導関数 f'(x) は各点での増減の速さを表現

  • 微分は 最適化 (グラフの最小値や最大値を探すこと)に活用

  • ある点での導関数が なら右へ進むと値が下がり、 なら左へ進むと値が下がる

  • 微分値の 0 になる点が極値(最小値または最大値)であることを利用し、式を解いて極値を求める手法

    • 反復的な最適化手法(例: Gradient Descent)では、導関数の値を使って「坂を下る」ように最小値を探索
    • ステップサイズを調整しながら進むことで、真の最小値に近づく

高次関数・多次元関数での最適化

  • 二次関数 以外の高次関数では、最小値・最大値が複数存在し、どちらも導関数が0になる
  • 二次関数の係数が負 の場合、最小値ではなく最大値のみが存在
  • 多次元関数では、ある点が x方向で最小・y方向で最大 となる「鞍点」も存在
  • こうした点でも勾配がゼロとなり、単純なGradient Descentでは「谷底」以外にも到達する可能性

勾配(Gradient)

  • 勾配 は多変数関数における「各変数ごとの偏微分」をまとめたベクトル
  • 例:関数 w = f(x, y, z) の勾配は ∇w = [∂w/∂x, ∂w/∂y, ∂w/∂z]
  • 各要素は「他の変数を固定して一つの変数を変化させたときの変化率」
  • 勾配ベクトル は「関数が最も急激に増加する方向」を示す
  • 勾配降下法(Gradient Descent) では、勾配方向の逆向きに進むことで最小値を探索
  • レンダリング などでも勾配が利用され、アンチエイリアスや距離場表現などに応用

ヤコビアン行列(Jacobian Matrix)

  • ヤコビアン行列 は多変数入力・多変数出力関数の「各出力の勾配ベクトル」をまとめた行列
  • 例:関数 F(x, y) = [v(x, y), w(x, y)] のヤコビアンは、各出力ごとに偏微分した成分を行列に配置
  • ヤコビアン行列は、空間がその点でどのように「回転・伸縮・反転」されるかを示す
  • 行列式(determinant)が
    • 1より大きい :拡大
    • 0より大きく1未満 :縮小
    • :反転
    • 0 :少なくとも1次元が潰れて不可逆変換
  • 機械学習やグラフィックス における空間変換やアンチエイリアス処理で活用

ヘッセ行列(Hessian Matrix)

  • ヘッセ行列 は多変数関数の「全ての2階偏微分」を並べた正方行列
  • 例:関数 w = f(x, y, z) のヘッセ行列は、各偏微分成分の2階微分を全組み合わせで配置
  • 各行は「各変数での勾配」をさらに全変数で微分した値
  • ヘッセ行列 は「関数の曲率」を示し、最適化で関数の形状(上に凸か下に凸か、鞍点か)を判別可能
  • 2階微分 による局所的な二次関数近似で、最小値探索を高速化(ニュートン法など)
  • 固有値 で正定値かどうか(最小値か最大値か鞍点か)を判別
  • 大規模な機械学習 ではヘッセ行列の計算コストが高く、 準ニュートン法 などの近似手法が利用

まとめ

  • 微分・勾配・ヤコビアン行列・ヘッセ行列 は、関数の変化・曲率・空間変形を解析するための数学的ツール
  • 最適化・機械学習・グラフィックス など多様な分野で応用
  • 各概念の理解が、より高度なアルゴリズム設計や実装の基礎となる

Hackerたちの意見

周りを見渡せば、表面をなぞったり勾配を追ったりしなくても、何かの最小値を見つけられるよね。グローバルミニマもすぐに見分けられるし、ローカルミニマじゃなくて。2次元や3次元ではみんなできるけど、俺たちのアルゴリズムはそうできてない。2次元でもね。確かに、目隠しして表面を感じながら最小化の方向を探るのが正しい方法かもしれないけど、見えてるときはそんなことしなくてもいいんだよね。何か見落としてることがあるのかな?

あなたが考えてるのは、一度に全体を見渡せる物体のことだね。もし、全体が見えないくらい大きな物体を扱ってたら、どうやって探るか決めなきゃいけないよ。

2次元の表面を見ると、その表面上のすべての値を直接観察できるよね。損失関数の場合、各点での値を計算しなきゃいけない。全部計算して「表面を見る」ことができるし、直接一番低い値を選ぶこともできる。それがグリッドサーチって呼ばれるもの。高次元になると、計算する「点」が多すぎて大変なんだよね。

答えを調べずに(誰かがもう計算してくれてるから)、自分の国で一番高い地理的なポイント(標高の高いところ)をどうやって見つける?

密に配置されたグリッドを使って関数を評価してプロットするのは確かに効果的だよ。これは brute-force サーチだね。グリッドが十分に密であれば、あなたが説明した方法でグローバルミニマをすぐに見つけられるよ。ただ、コンピュータで関数を実装するときは、そんなに多くの点を評価するのに時間がかかるから、勾配のような情報を活用するもっと洗練された最適化アルゴリズムを使った方がほぼいつも速いんだ。物理的な現実では、すべての点がすでに存在してるから、安く観察できるなら brute force アプローチはうまくいくよ。編集:あなたの質問は良かったね。そんな表面的にナイーブな質問をすることは、解決が難しそうな問題に対する新しいトリックを考えるための有益な出発点になることが多いんだ。

見えないものの最小値を探しているとしたら、どうなるんだろう?それとも、目で見ても感じられないほど小さな違いがある場合は?

あなたの目は、局所的および全体的な最小値がどこにあるかを見積もるために、脳が行う大量の視覚処理の一部として勾配を計算してるんだ。でも完璧じゃないから、たくさんの錯覚があるよね。意識的であれ無意識的であれ、私たちは常に勾配を追ってる。例えば、すべての道が上に向かっているとき、あなたが穴の底にいることがわかるよね。

例えば、ボウルや卵のカートンマットレスみたいなものを見て、全体の最小値を探しているとき、あなたは2次元の表面を見ていて、1次元の外に出ているんだ。脳は数千のポイントを処理して「ボウルの底はここだ」と言うのは簡単だよね。コンピュータも同じことができる。x/y方向で100の値をチェックすれば、合計で10000の値を確認するだけで済む。コンピュータはそれを簡単にできる。だけど、深層ニューラルネットワークを使った機械学習では、2次元の入力と1次元の出力なんてない。何千から何百万の次元が入力されて、何千から何百万の次元が出力されるんだ。もし100000の入力があって、各入力に対して1000の値をチェックしたら、組み合わせの総数は1000^100000になる。さらに、100000の出力もあることを忘れないで。そんな計算はできないよ。だから、ヤコビ行列やバックトラッキングみたいな高度なものが必要なんだ。

ここにいる人たちは、あなたが求めている数学的な答えを出しているけど、あなたの直感に挑戦したいな。建設では、建物のためにサイトを整地するのは、測量を含む一連のプロセスなんだ。もし、整地されていないランダムな土地に人を落としたら、その人が道具なしで正しく地面を平らにするのはかなりの挑戦になるよ。つまり、「周りを見て何でも最小値を見つけられる」っていうあなたの直感は、他の誰も持っていない超能力がない限り、ほぼ確実に間違ってるってこと。

まず最初に...無理だよ。明らかに理由があるケースを挙げるのは簡単だし、特別なことでもない。君が想像してるのは、立体的なレイトレーシングみたいなもので、それは微分を計算するよりもずっと計算負荷が高いんだ。

多くの実用的な最適化問題は、「目の前に見える丘を登る」みたいなものじゃなくて、「可能なデザインの中で、ある目的を最大化する最適なデザインを見つける」って感じだよ。ここにもっと高次元で、空間的な次元じゃない例題がいくつかある。例えば、(a) あなたの目的は、キッチンの食材を使って、最大限に美味しい料理のレシピを見つけること。レシピ空間の1点をサンプリングするには、(1) レシピを考案し、(2) レシピに従って候補の料理を準備・調理し、(3) その料理を友達や家族に振る舞って評価する必要がある。これで1つのサンプルポイントが得られるかも。もしかしたら、作れる「レシピ」は1兆通りあるかもしれない。全てを試して料理して評価して、最高に美味しい料理を見つけるつもり?それとも、もっと効率的な方法があるの?(b) あなたの目的は、必要な荷重とストレスを支えつつ、建設コストを最小化する橋の最も効率的なデザインを見つけること。

ちょっとこの投稿より進んだ話だけど、ヤコビアンやヘッセ行列を計算するために、Juliaの人たちが最近、古典的な自動微分の研究を基にしたクールな仕事をしてるよ。https://iclr-blogposts.github.io/2025/blog/sparse-autodiff/

Enzyme(https://enzyme.mit.edu/)を使ってみたことある?LLVM IRで動くから、LLVMに変換できるどんな言語でも使えるよ(例えば、Juliaで、サーフェス勾配のために使ったことがある)。すごく最適化されたADコードを生成するんだ。めっちゃクールだよ。

うーん、ちょっと雑だね。関数 f::a -> b の導関数は Df::a -> a -o b で、2つ目の変な矢印は線形関数を示してる。つまり、導関数 Df はドメイン内の点を取り、その点での f の線形近似(ヤコビアン)を返すんだ。常にヤコビアンなんだけど、f が R -> R のときは、ヤコビアン(この場合は 1x1 行列)とその中の数を混同しちゃうんだよね。

もう少し良い見方をすると、基準点を明示的に持っておいて、Df:: a -> (b, a -o b) = (f(p), A(p))と言うことができる。ここで、f(p+v)≈f(p)+A(p)vとなる。そうすれば、合成の定義に必要な情報を保持できる。Dg∘Df=D(g∘f)=(Dg._1∘Df._1, Dg(Df.1).2∘Df._2)っていう、いわゆる連鎖律だね。[0] これはこのトークから学んだことだよ。 https://youtube.com/watch?v=17gfCTnw6uE

実際に言うけど、関数fが空間Aから空間Bへの点aでの導関数は、点aのAの接空間から点bのBの接空間への線形関数Df_aなんだ。空間がユークリッド空間の場合、接空間と空間自体を同一視できるから、接空間と空間は同じなんだ。ちなみに、これで1次元の連鎖律の公式を覚えやすくなるよ。任意の次元m、n、pの空間間での論理的な関係は、T_a AからT_f(a) B、T_g(f(a)) Cへの線形変換の合成だけだ。m = n = p = 1とすると、線形変換の合成は単に掛け算になる。 (半分冗談だけど)

素晴らしい投稿だね!必要なだけ短いのに、ポイントをしっかり伝えてる。数学の一般化レベルを上がっていくのが大好きだよ。

(...) wのxに関する導関数。別の言い方をすると、「xに1を足してから関数に入れたら、wはこれだけ変わる」ってこと。間違ってる!

うん、でも良いHNの投稿は正しいことを説明するべきだよ。知らない人が学べるようにね。

(...) もし関数が直線だったら 地元の線形化から導関数の完全な見方に行くにはもう少しあるけど、全然間違ってるわけじゃないよね。

この投稿にダイバージェンスとカールも追加してほしいな。

「私が説明したのは、勾配降下法に似た反復最適化手法です。勾配降下法は、ボールが坂を転がり落ちる様子をシミュレートして、できるだけ低い点を見つけるためにステップサイズを調整したり、真の最小値にハマらないようにモメンタムを加えたりします。」これは、ほとんどの説明よりもずっと理解しやすいね。オープニング全体がそうだった。

勾配や微分を理解するのに役立ったのは、矢印マップとして視覚化したときだった。小さなツールも作ったよ https://github.com/GistNoesis/VisualizeGradient 。この視覚化は最適化アルゴリズムを理解するのに役立つ。ヤコビアンは、出力の各座標を独立に考えるときの勾配の集まりとして理解できる。ヘッセ行列のイメージは、各点をその関数に最も合う放物線(または鞍)に関連付けること。ズームインしたときに見える形だって気づくと、視覚化が簡単になるよ。(技術的には、このメンタルイメージはヘッセ行列 + 勾配接平面の同時多変量テイラー展開だけど、傾きと曲率を分けて考えるのが難しいんだよね。)

ヤコビアンとヘッセ行列を比較するのがいつも混乱するのは、その性質が全然違うから。ヘッセ行列は行列と呼ばれるべきじゃなかった。ヤコビアンはベクトル値関数のすべての一次導関数を説明するけど、ヘッセ行列はスカラー値出力関数のすべての二次導関数を説明する。導関数の次数が増えるときに、配列の次元数が1増えないのはなぜ?実際には、複数の入力を持つベクトル値関数の二次導関数を完全に記述するオブジェクトは、3次元テンソルなんだ。一つは元のベクトル値出力用、もう一つは各導関数の次数用。数学者は何故か2次元以上のテンソルを恐れて、すべてを行列にしたがるんだよね。つまり、関数 R^n -> R^m の場合: 順序0: 出力値: 形状(m)の1次元配列(ベクトル) 順序1: 一次導関数: 形状(m, n)の2次元配列(ヤコビアン行列) 順序2: 二次導関数: 形状(m, n, n)の3次元配列(ヘッセ行列の配列) すべて理にかなってる!「ヤコビアンとヘッセ行列」を両方とも自然に行列だと言うのは、かなり誤解を招くよ。

確かに混乱するよね。記法から始めると余計に混乱するし。個人的には、部分導関数から始める定義はあまり好きじゃないな。ちょっと恣意的に感じるから。私が理解できたのは、導関数の定義(ある意味での最良の線形近似)から始めて、あとはそれをどう表現するかってこと。ベクトルや行列などは、適切なベクトル空間の中のベクトルで、導関数は常に関数的な形で同じ形なんだ。例えば、f(M)の導関数が欲しい?なら、f(M+h) - f(M)を書いて、h / h^2 / などの項を探せばいい。もっと複雑な場合は、連鎖律を適用すればいい。これが私にとっては、学ぶのにずっと良い方法だと思う。記法については、複雑な場合にはベクトル/クロネッカー積を使うよ: https://janmagnus.nl/papers/JRM093.pdf

プログラミングにはあまり関係ないけど、物理学では上付きインデックスと下付きインデックスを使うのが伝統的で、これで求めてる区別がすごく明確になるんだよね。もし入力xが成分xⁿを持って、出力f(x)の成分がfᵐなら、ヤコビアンは∂ₙfᵐになる。上に1つ、下に1つインデックスがある感じ。導関数は下にインデックスがあるのは…d/dxの分母にxがあるからかな?もしxの単位が秒だったら、d/dxの単位は「毎秒」になるよね。一方でg(x)が数値なら、勾配は∂ₙgで、ヘッセ行列は∂ₙ∂ₙ₂gで下付きインデックスが2つある。これを(0,2)テンソルって呼ぶかもしれないけど、ヤコビアンは(1,1)だよ。普通の線形代数の行列のほとんどは(1,1)テンソルだね。

少なくとも僕の学部の多変数実解析の授業では、教授がヘッセ行列は∇⊗∇として考えるべきだって強く示唆してたのを覚えてる。それが高次元のテイラー展開の2番目の項で、3階導関数の項は∇⊗∇⊗∇みたいになるんだよね。テンソル積や商空間みたいなことは前提知識としては扱われてなかったから、明示的にはカバーされてなかったけど、そのつながりは当時は明らかだと感じてた。で、入門的な微分幾何の授業では(n,m)テンソルについても触れたから、数学者たちはテンソルを扱うのは全然問題ないと思うよ。ただ、僕の経験では学部の工学数学は共変ベクトルすら避けようとするから、多変数微積分の一貫したイメージには遠いかも。例えば、僕の工学の教授はディラックのδを「無限のスパイク」みたいな、実際には存在しないものとして話してて、積分をうまく機能させるためのものだとか言ってた。僕の解析の教授はただ「δ(f) = f(0)は線形汎関数だよ」って言ってたけど。

約10年前、Appleの自動運転車プロジェクトの面接を受けたことがあって、そのプロジェクトの役員にこの4つのことを詳しく説明して、例を挙げるように言われたんだよね。うーん、どうしようもないけど。