自動微分の基礎

自動微分(Automatic Differentiationあるいは Algorithmic Differentiationともいわれ、ADと略される場合が多い)とは、コンピュータープログラムで表現された関数を効率的かつ正確に計算する技術です。

もともとは流体力学、原子核工学、気象科学などで使用されていた手法ですが、近年機械学習や金融への応用が注目されています。そこでここでは、自動微分の基礎について紹介します。

1. 数値微分

関数の微分係数を求めたい場合、数式がわかっていれば、数学的にはその関数式を微分すれば求まります。しかし、コンピュータープログラムで使用される関数は、何段階にも入れ子になっていたり、ループや条件分岐を含むコードにより表現されているため、数学的に微分することは必ずしも簡単ではありません。

しかし、そもそも微分の定義を考えると、

ですから、この式を離散化して近似すれば、関数がどのように定義されているかにかかわらず、微分の近似値を求めることができます。

1.1. 計算方法

1.2. 計算例

数値微分の精度

hを小さくしていくと10−8程度までは精度が向上し(横軸)、誤差も10−8以下になります(縦軸)が、それ以後は逆に誤差が大きくなることが分かります。

2. 自動微分

前述のように、数値微分は考え方が非常に単純で実装も簡単な手法ですが、打切り誤差、丸め誤差が生ずるため、他の手法と比較して精度が低いという問題点があります。また、微分したい関数の計算負荷が高いと、数値微分の計算負荷も高くなります。

これらの問題をある程度克服する手法として、自動微分(AD)と呼ばれる手法があります。コンピューター上に実装された関数は基本的には四則演算や指数関数、対数関数等の初等関数に帰着されます。そして、それらの関数一つ一つの微分は別の初等関数によって表現できます。自動微分では、関数の実装をたどって、各初等関数をその微分に置き換え、合成関数の微分公式を適用していくことにより自動的に微分を行うことができます。このように計算された微分は、基本的には数式による表現を解析的に微分したものと同等になりますが、自動微分の場合には、上記のロジックにより自動的に微分を計算できるため、ループや条件分岐が含まれる関数など、数式を解析的に微分するには複雑すぎる場合でも、少ない労力で微分を行うことができます。

このように自動微分は数値微分と比較して、精度が高い(通常の関数値を計算するのと同じ精度で計算可能)というメリットがあります。また、次節以降で見るように、関数値を計算するのと同様に計算可能であり、計算負荷も低いというメリットがあります。

このようなメリットがある一方で実装が複雑になるというデメリットがあります。しかし、実装の複雑性については、主要なプログラミング言語においてはすでにライブラリが存在するため、それらを活用することも可能です。

2.1. 前進法

自動微分の計算方法には大きく分けて前進法(forward mode)と後進法(reverse mode もしくは adjoint mode とも呼ばれる)の2つの計算方法があります。前進法は関数の入力から出力の方向に向かって計算を進める方法であり、後進法は関数の出力から入力の方向に向かって計算を進める方法です。

例として、以下の関数を考えます※2

とします。

同様に各中間変数についても微分を行うと以下のとおりです:

2.2. 後進法

この記号を使い、出力方向から順番に計算をすると以下のようになります:

前進法では同様の計算を3回たどる必要がありましたが、後進法では1回ですべての入力変数に対して微分を求めることができます。この例では後進法の方が効率的な計算ロジックを与えますが、これは、この例が入力変数出力変数1個に対して、独立変数3個であったことによります。

2.3. 実装

上記のロジックをコンピューター上で実装するためには、各段階で微分を計算しておく必要があります。

これを2回用いると合成関数について以下のように計算できます:

例えば、具体的な初等関数に適用すると以下のようになります:

通常の数のかわりに上記のデュアル数を導入すると※4、関数の値を計算すると同時に微分も保持できるので、前進法、後進法を効率的に実装することができます。

2.4. 計算例

2.4.1 初等関数

(コード中の "D x0" が 2.3. のデュアル数を表します。)コードのコメントにあるように、全桁(10−15の精度)一致しています。数値微分の精度は10−8程度であったことを考えると、精度が大きく向上しています。

2.4.2 より複雑な例

より複雑な例として、2.1.、2.2.で使用した例を考えます。比較のため、まず数式を数学的に微分するアプローチを示します。

同じことを自動微分で表すと以下のようになります。

入力変数が3つあるので、各変数に対する微分(勾配)を求めました。計算の途中では、値はデュアル数として保持されており、必要に応じて関数値も取り出すことができます。ここでは関数値(デュアル数のプライマル部分)も同時に表示できる“grad´”という関数を使用しています。(ダッシュが付かない関数の場合は微分(タンジェント部分)のみ出力されます。)

3. まとめ

本稿では、具体的な事例を交えて、自動微分の基礎について紹介しました。数値計算において、微分が登場する場面では多くの場合、計算精度と計算負荷の問題が付きまといますが、自動微分はこれらの問題をある程度軽減するものであり、多くの場面で活用できる手法です。

自動微分を実装したライブラリも充実してきており、高度なプログラミングを行わなくても、比較的気軽にそのメリットを享受できます。

4. 参考文献

[1] Homescu, Cristian. "Adjoints and automatic (algorithmic) differentiation in computational finance." (2011).

[2] Baydin, Atilim Gunes, et al. "Automatic differentiation in machine learning: a survey." arXiv preprint arXiv: 1502.05767 (2015).

[3] 伊理正夫・藤野和建. "数値計算の常識" 共立出版 (1985)

[4] 久保田光一・伊理正夫. "アルゴリズムの自動微分と応用" コロナ社 (1998)

※1 通常は浮動小数点の精度の半分程度の値を固定で設定する場合が多いです。

※2 例は参考文献[1]より引用しました。

※4 コンピュータープログラム上は関数値と微分を保持するようなデータ型を定義することにより実装できます。

※5 簡易な記述が可能なことから、コードはF#を用いて記述し、自動微分のライブラリはDiffSharpを使用しています。

小澤 栄作

ディレクター, PwC Japan有限責任監査法人

Email

島田 理恵子

シニアマネージャー, PwC Japan有限責任監査法人

Email

{{filterContent.facetedTitle}}

{{contentList.dataService.numberHits}} {{contentList.dataService.numberHits == 1 ? 'result' : 'results'}}
{{contentList.loadingText}}

{{filterContent.facetedTitle}}

{{contentList.dataService.numberHits}} {{contentList.dataService.numberHits == 1 ? 'result' : 'results'}}
{{contentList.loadingText}}