Scipyでの実践的な最適化のためのガイド（Scipy lecture notes 日本語訳）

# 2.7.5. Scipyでの実践的な最適化のためのガイド

## 2.7.5.1. 手法の選択

すべての手法は[scipy.optimize.minimize()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html#scipy.optimize.minimize)の`method`引数として利用できます。

![最適化手法の比較](https://qiita-image-store.s3.amazonaws.com/0/37701/161c9c5c-f094-b9f7-708a-7690fcf87d1e.png)

### 勾配が未知の場合：

* 一般的には、数値的に勾配を近似する必要がある場合でも、**BFGS**や**L-BFGS** が好ましいです。
これらはどちらも`method`引数を省略した場合のデフォルトで、問題に拘束条件や境界があるかによって選ばれます。
* 良条件下では**Powell**や**Nelder-Mead**の両者は勾配を必要とせず高次元でもうまく動きますが悪条件の問題ではこれらは挫折します。

### 勾配が既知の場合：

* **BFGS**または**L-BFGS**。
* BFGSの計算オーバーヘッドはL-BFGSより大きく、L-BFGSは共役勾配法よりも大きくなります。一方で BFGSはたいていの場合でCGと比べて少ない関数評価で済みます。なので共役勾配法は関数の計算コストが低い場合にはBFGSよりよい方法といえます。

### ヘッセ行列がある場合：

* ヘッセ行列を計算できるなら、ニュートン法（**Newton-CG**または**TCG**）が好ましいです。

### ノイズのある測定値の場合：

* **Nelder-Mead**または**Powell**を使います。

## 2.7.5.2. 最適化を高速化する

* 正しい手法を選択して（上記参照）、可能なら勾配やヘッセ行列を解析的に計算します。
* 可能なら[前処理](https://en.wikipedia.org/wiki/Preconditioner)を利用します。
* 初期値を賢く選択します。例えば、多くの似たような最適化をするなら、別の結果でウォームアップした点から始めます。
* 精度が必要なければ`tol`引数を使って許容誤差を緩めます。

## 2.7.5.3. 勾配の計算

勾配の計算、さらにはヘッセ行列の計算は、うんざりする作業ですが苦労する分の価値があります。[Sympy](http://www.turbare.net/transl/scipy-lecture-notes/packages/sympy.html#sympy)による記号計算は役に立つかもしれません。
非常によくある最適化が収束しない原因は、勾配の計算でのヒューマンエラーです。[scipy.optimize.check_grad()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.check_grad.html#scipy.optimize.check_grad)を利用して勾配が正しいかを確認できます。この関数は与えた勾配と数値的に計算した勾配の差のノルムを返します：

```python
>>> optimize.check_grad(f, jacobian, [2, -1])
2.384185791015625e-07
```

間違いを見つけるために[scipy.optimize.approx_fprime()](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.approx_fprime.html#scipy.optimize.approx_fprime)も参照してください。

## 2.7.5.4. 総合演習

### 演習問題: 単純（？）な二次関数

以下の関数を K[0] を開始点として最適化します：
![グラフ](https://qiita-image-store.s3.amazonaws.com/0/37701/e3e61b79-0f39-49be-ba53-f9dc0d149d32.png)

```python
np.random.seed(0)
K = np.random.normal(size=(100, 100))

def f(x):
    return np.sum((np.dot(K, x - 1))**2) + np.sum(x**2)**2
```

時間計測してみましょう。最も高速な方法は？BFGSはどうしてうまく動作しないのでしょうか？

#### ソースコード

[2.7.4.10. Alternating optimization — Scipy lecture notes](https://www.scipy-lectures.org/advanced/mathematical_optimization/auto_examples/plot_exercise_ill_conditioned.html)

### 演習問題: 局所的に平らな最小値

関数`exp(-1/(.1*x**2 + y**2)`を考えます。この関数は (0, 0) で最小になります。(1, 1) の初期値から始めて、この最小値から 1e-8 以内になるまで試しましょう。
![グラフ](https://qiita-image-store.s3.amazonaws.com/0/37701/0bb0ae50-2a82-98a6-36e8-a438581f210b.png)
![グラフ](https://qiita-image-store.s3.amazonaws.com/0/37701/7c328d1b-eacc-78e2-2e8b-9606c1492ef8.png)


#### ソースコード

[2.7.4.5. Finding a minimum in a flat neighborhood — Scipy lecture notes](https://www.scipy-lectures.org/advanced/mathematical_optimization/auto_examples/plot_exercise_flat_minimum.html)

---
## 関連リンク

### 元記事

[2.7.5. Practical guide to optimization with scipy — Scipy lecture notes](https://www.scipy-lectures.org/advanced/mathematical_optimization/index.html#practical-guide-to-optimization-with-scipy)

### 旧バージョンの日本語訳

[2.7.3. scipy での実践的な最適化のためのガイド — Scipy lecture notes](http://www.turbare.net/transl/scipy-lecture-notes/advanced/mathematical_optimization/index.html#practical-guide-to-optimization-with-scipy)