linalg 設計
このページでは、linalg のドライバーがスカラーの双対数から勾配とヤコビ行列をどのように得るか、それぞれのコスト、そしてパッケージが linear-algebra への薄いブリッジとして構成されている理由を説明します。
設計目標
linear-algebra のイミュータブルなベクトルで書かれた関数 と を、Dual[T] をそのまま再利用し、結果が通常の行列の慣習に従う小さな API で微分します。
数学的背景
1 回のパスによる方向微分
が で微分可能であるとし、 とします。各入力を としてシードします。dual の設計 により、各出力は次のようになります
したがって双対数上での 1 回の評価でヤコビ行列ベクトル積 が得られます。これは多変数の連鎖律です。 に対して となります。
単位ベクトルのシードによる列
単位ベクトル をシードすると、ヤコビ行列の第 列 が返ります。ドライバーはこのようなパスを 回行います:
のとき、唯一の行は であり、gradient はそれをベクトルとして返します。
リバースモードとのコスト比較
上で を評価するコストを とします。双対数のパス 1 回のコストは高々 の小さな定数倍です(dual の設計 を参照)。したがって
リバースモードはベクトル・ヤコビ行列積 をパスごとに 1 行ずつ計算し、計算を記録するという代償と引き換えに、 によらない の定数倍で勾配を得ます。11 これは「安価な勾配の原理」です。A. Griewank and A. Walther, Evaluating Derivatives, 2nd ed., SIAM, 2008, section 4.6 を参照してください。 したがってフォワードモードは が小さいか の場合に適した手法であり、多変数関数の勾配には遅いほうの手法です。
設計上の判断
スカラーの接成分と 回のパス
問題。 勾配には 個の方向微分が必要です。
選択肢。 個の接成分のベクトルを持つ双対数型(1 回のパス、演算ごとに の処理)、またはスカラーの Dual[T] による 回のパス。
選択。 回のパスです。算術の総量は同じオーダー であり、スカラー型では演算ごとのメモリ確保も新しい数値型も不要です。ベクトルの接成分を持つ型は今後の課題です。
出力×入力の慣習
行列は で、成分は です。これは連鎖律 の慣習であり、結果を通常の行列積で合成できます。第 列はパス に対応します。実装は 個の出力ベクトルを集めてから、Matrix::make(m, n, (i, j) => columns[j][i].tangent()) で行列を構築します。
値と のための独立したパス
value_and_gradient と value_and_jacobian は、シードしたパスのいずれかから値を読み取るのではなく、すべての接成分を 0 にして f をもう 1 回評価します。射影準同型 によりどのパスも同じ値を持つので、これは評価 1 回分のコストがかかりますが、ドライバーを互いに独立に保ち、 でも正しく動作させます。jacobian も、行列を確保する前に を知るために同じ接成分 0 の呼び出しを使います。
形状の検証はまだない
問題。 関数が入力の範囲外を読んだり、出力の長さを変えたりすることがあります。
選択肢。 形状エラーを持つ Result を返す、中断する、事前条件として文書化する。
選択。 文書化された事前条件です。linear-algebra はまだこのリポジトリと形状エラーの型を共有しておらず、独自のエラー型は後で置き換えられることになります。ソースにはチェック付きの版のための TODO があります。
Dual[T] 上の環演算のみ
ドライバーが必要とするのはシードのための One + Zero だけです。f 内部のベクトル演算と行列演算は Dual[T] の Add、Mul、Neg インスタンスを使うので、環構造だけを必要とする linear-algebra の演算はすべて双対数のベクトル上で動作します。スカラーに Field、Inverse や順序を必要とするアルゴリズム(たとえばピボット選択)は Dual[T] でインスタンス化できません。これは意図的なものです。dual の設計 を参照してください。
正しさと不変条件
- 事前条件 を満たすプログラムについて、
gradient(f, x)[j]かつjacobian(f, x)[i][j]であり、各成分には dual の設計の丸めの上界が適用されます。 value_and_gradient(f, x)かつvalue_and_jacobian(f, x)です。値はfがT上で計算するものとちょうど一致します。- 評価回数:
gradientは 、value_and_gradientは 、jacobianは 、value_and_jacobianは 。 - メモリ:
jacobianは 行列を構築する前に、長さ の出力ベクトルを 個保持します。
却下した代替案
- 勾配のためのリバースモード。 が大きい場合は漸近的に安価ですが、計算の記録が必要であり、実装されていません。
- 公開のヤコビ行列ベクトル積。
Dual::new(x[j], v[j])による 1 回のパスで既に が得られるので(linalg チュートリアル を参照)、専用の関数を追加してもほとんど得るものはありません。 - ミュータブルな行列。 ドライバーは
immutの値を返し、これはリポジトリの他の部分の値の意味論と一致します。
境界
- フォワードモードのみです。リバースモードも、ヘッセ行列や高階のドライバーもありません。
- 形状チェックもチェック付きの版もありません。
- 密な
immutのベクトルと行列のみです。疎なコンテナやミュータブルなコンテナはありません。 Dual[T]上に体や順序を必要とするアルゴリズムはありません。
Footnotes
-
これは「安価な勾配の原理」です。A. Griewank and A. Walther, Evaluating Derivatives, 2nd ed., SIAM, 2008, section 4.6 を参照してください。 ↩