◀ Previous | TOC |
現在, 最適化で最もよく用いられているのは, おそらく, Newton 法と勾配降下法だろう. 前者は効率よく最適値を求めるのに適しており, 後者は 1 階微分のみを使うという計算負荷上の利点から機械学習で用いられる(実際には派生法である確率的勾配降下法がよく用いられる).
最適化の難しさは, 過学習や, 初期値の設定方法, などアルゴリズムとは違ったところにあることが多いが, それでも, アルゴリズムを理解していれば, 問題に対してより適切な対処ができる可能性が高まる. そのような観点から, この章では, Newton 法と勾配降下法を学ぶ.
Newton 法(Newton Raphson 法)は, 極小値を求めるというよりは, 方程式
図 22 1 変数の場合の Newton 法による方程式
まず, 適当な初期値
Newton 法で極大・極小点を求めるには, 停留点を求める.
求めた停留点が極大点か極小点かについては, 第 1
章に述べた方法で別に調べる必要がある. 図 23
に停留点を Newton 法で求めるときの
図 23 1 変数の場合の Newton 法による停留点の求め方の例, 実線
図 24 Newton 法により停留点を求めるアルゴリズム
勾配降下法では関数の勾配(が最大になる)方向に勾配に比例するだけ移動する.
関数の谷を降りていき, 谷底(極小点)に到達すれば, 勾配が 0 になるので,
移動が止まる. 計算には, 学習率(learning
rate)と呼ばれるパラメーターを用いるが,
この値により収束速度が大きく異なり, 場合によっては収束しないこともある.
図 25
に勾配降下法での極小点を求めるまでの
図 25 勾配降下法による極小点の求め方の例
図 26 に, 勾配降下法のアルゴリズムを示す.
図 26 勾配降下法のアルゴリズム
Newton 法では,
アルゴリズムから分かるが, Newton 法は停留点に収束する. つまり極大値にも収束する. 勾配降下法は極小値のみに収束し, 極大値に収束することはない.
この問題はできれば関数電卓で解いてみよう.
解答例題 4.1
以上の操作で, Newton 法と勾配降下法で
一般に, Newton 法は近似解への収束が速いことが知られている.
これに対し, 勾配降下法は収束速度が遅いが, 1
階微分を計算するだけでよいのが大きなメリットとなる.
特に機械学習など変数の数
非線形の最適化問題は, 初期値の選び方が重要である. 領域内の「最小点」を選ぶ問題の場合, 初期値の選び方によっては, 最小点とは別の極小点が解として得られてしまう場合もあれば, 全く解が得られない(発散する)場合もある.
大まかな傾向とすれば,
目的とする極小値(例えば最小値)の近くに初期値を持ってくると,
その点に収束する場合が多い. しかし,
勾配降下法などでは適切な学習率を設定しないと, 初期値が解の近くにあっても
また, そもそもの問題として, 解が未知なのだから, 初期値をその近くに設定するというのは無理がある. このため, 往々にして様々な初期値を試すことになる. このあたりに非線形最適化問題の難しさがある.
まずは Newton 法で初期値依存性をみよう.
の次の関数の停留点を求めるのに Newton 法を用いる.
初期値を
解答例題 4.2
実際に Newton 法で計算してみると( 表 2 Newton 法の初期値依存性 )
Newton 法は, 停留点
勾配降下法では, 初期値
第 1 章で見たように, 2 変数以上の多変数関数の極大・極小問題はすべて同じように扱える. よって, ここでは 2 変数関数のみ考える. 次の関数を考えよう.
この関数のゼロ点は
の解なので,
1 変数の場合と, アルゴリズムは同じになる. ただし,
ここで,
式 (31)
の
これだけでは分かりにくいだろうから, 簡単な計算例をみていこう.
の停留点は
さて,
あとは,
勾配降下法の場合も,
例 4.5
でみたように, 勾配は,
あとは,
多変数の場合の収束判定は, 1 変数の場合と違って, ベクトル
は, 変数の数が多くなると, 計算コストが高い. そのためだろうが, 多くの場合, 次の最大値ノルムが用いられる.
整理の意味もこめて, 多変数の Newton 法と勾配降下法のアルゴリズムを載せる.
図 27 多変数関数で Newton 法により停留点を求めるアルゴリズム
図 28 多変数関数で勾配降下法により極小点を求めるアルゴリズム
Newton 法にせよ勾配降下法にせよ, 1 変数の場合はともかく, 多変数の場合はコンピューターを使って計算すべきものである. このとき, 勾配などを計算する必要があるが, 特に Newton 法の場合, Hesse 行列, 及びその逆行列を計算する必要があり, これを一々入力するのは大変である. このようなとき, 自動微分関連のパッケージを用いればよい.
ここでは, Google 製の JAX(https://github.com/google/jax)を使う. JAX
は自動微分と高速化線形代数をひとつにしたパッケージであり,
色々なことができるのだが, ここでは, numpy の機能を代替する
ここでは, 次の関数の停留点を初期値
{language=Python}
import jax # jax パッケージを使う
import jax.numpy as jnp # numpy の代わりに jax.numpy を使う.
from jax import config # この行と次の行は jax での 64-bit 浮動小数点計算のため
config.update("jax_enable_x64", True)
def func(x):
return - jnp.cos(2 * x[0]) * jnp.sin(x[1])
# jax では, Hesse 行列を計算する関数がないので作る.
def jax_hess(f):
return jax.jacfwd(jax.jacrev(f))
def main():
eps_0 = 1.e-15 # 収束条件の設定
x = jnp.array([.2, 2.2]) # 初期値の設定
# 最大値ノルムの計算
nrm_grad = jnp.linalg.norm(jax.grad(func)(x), ord=jnp.inf)
while nrm_grad > eps_0:
grad = jax.grad(func)(x)
hess = jax_hess(func)(x)
hess_inv = jnp.linalg.inv(hess) # Hesse 行列の逆行列
dltx = jnp.matmul(hess_inv, grad) # H_f^-1 と grad f の積
x = x - dltx
print(x)
nrm_grad = jnp.linalg.norm(grad, ord=jnp.inf)
# 極大・極小判定のため Hessian matrix の 固有値を出力
print(jnp.linalg.eigvals(hess))
if __name__ == '__main__':
main()
出力は以下の通り.
{language=Python}
[-0.15723497 1.25222653]
[0.02518525 1.62116685]
[-8.55469635e-05 1.57062523e+00]
[3.33897023e-12 1.57079633e+00]
[0. 1.57079633]
[0. 1.57079633]
[4.+0.j 1.+0.j]
出力から, 停留点として
次の関数の極小点を
次の関数の停留点を Newton 法で求める. 1) 関数
ちなみに, 停留点は,
の解なので,
の解であるから,
(この問題は Python を使うほうがよい.) 上記の式 (32)
の関数の極小点を勾配降下法で求めることにする. グラフの形から,
初期値を正の数にとると, 極小値
◀ Previous | TOC |