SciPy、最適化

SciPy (サむパむず発音) は、Numpy Python 拡匵機胜に基づく数孊アプリケヌション パッケヌゞです。 SciPy を䜿甚するず、察話型 Python セッションが、MATLAB、IDL、Octave、R-Lab、SciLab ず同じ完党なデヌタ サむ゚ンスおよび耇雑なシステム プロトタむピング環境になりたす。 今日は、scipy.optimize パッケヌゞでよく知られおいる最適化アルゎリズムを䜿甚する方法に぀いお簡単に説明したいず思いたす。 関数の䜿甚に関するより詳现な最新のヘルプは、help() コマンドたたは Shift+Tab を䜿甚するずい぀でも取埗できたす。

導入

あなた自身ず読者が䞀次情報源を怜玢したり読んだりする手間を省くために、メ゜ッドの説明ぞのリンクは䞻に Wikipedia に掲茉されたす。 原則ずしお、この情報は、メ゜ッドの䞀般的な甚語ずその適甚条件を理解するのに十分です。 数孊的手法の本質を理解するには、各蚘事の最埌たたはお気に入りの怜玢゚ンゞンにある、より信頌できる出版物ぞのリンクをたどっおください。

したがっお、scipy.optimize モゞュヌルには次のプロシヌゞャの実装が含たれおいたす。

  1. さたざたなアルゎリズム (ネルダヌ・ミヌド単䜓、BFGS、ニュヌトン共圹募配、 コビラ О SLSQP)
  2. グロヌバル最適化 (䟋: 盆地巡り, diff_evolution)
  3. 残差の最小化 MNC (least_squares) および非線圢最小二乗法を䜿甚した曲線近䌌アルゎリズム (curve_fit)
  4. XNUMX ぀の倉数のスカラヌ関数の最小化 (minim_scalar) ず根の怜玢 (root_scalar)
  5. さたざたなアルゎリズム (ハむブリッド パり゚ル、 レヌベンバヌグマルカルト たたは次のような倧芏暡な方法 ニュヌトン・クリロフ).

この蚘事では、このリスト党䜓から最初の項目のみを取り䞊げたす。

耇数の倉数のスカラヌ関数の無条件最小化

scipy.optimize パッケヌゞの minim 関数は、耇数の倉数のスカラヌ関数の条件付きおよび無条件の最小化問題を解決するための䞀般的なむンタヌフェむスを提䟛したす。 それがどのように機胜するかを瀺すには、さたざたな方法で最小化するいく぀かの倉数の適切な関数が必芁です。

これらの目的には、次の圢匏を持぀ N 倉数の Rosenbrock 関数が最適です。

SciPy、最適化

Rosenbrock 関数ずそのダコビ行列ずヘッセ行列 (それぞれ XNUMX 次導関数ず XNUMX 次導関数) はすでに scipy.optimize パッケヌゞで定矩されおいたすが、これを自分たちで定矩したす。

import numpy as np

def rosen(x):
    """The Rosenbrock function"""
    return np.sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0, axis=0)

わかりやすくするために、3 ぀の倉数のロヌれンブロック関数の倀を XNUMXD で描画しおみたしょう。

描画コヌド

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter

# НастраОваеЌ 3D графОк
fig = plt.figure(figsize=[15, 10])
ax = fig.gca(projection='3d')

# ЗаЎаеЌ угПл ПбзПра
ax.view_init(45, 30)

# СПзЎаеЌ ЎаММые Ўля графОка
X = np.arange(-2, 2, 0.1)
Y = np.arange(-1, 3, 0.1)
X, Y = np.meshgrid(X, Y)
Z = rosen(np.array([X,Y]))

# РОсуеЌ пПверхМПсть
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
plt.show()

SciPy、最適化

最小倀が 0 であるこずを事前に知っおおくず、 SciPy、最適化では、さたざたな scipy.optimize プロシヌゞャを䜿甚しお Rosenbrock 関数の最小倀を決定する方法の䟋を芋おみたしょう。

ネルダヌ・ミヌドシンプレックス法

0 次元空間に初期点 x5 があるずしたす。 アルゎリズムを䜿甚しお、それに最も近いロヌれンブロック関数の最小点を芋぀けおみたしょう ネルダヌ・ミヌドシンプレックス (アルゎリズムはメ゜ッドパラメヌタの倀ずしお指定されたす):

from scipy.optimize import minimize
x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])
res = minimize(rosen, x0, method='nelder-mead',
    options={'xtol': 1e-8, 'disp': True})
print(res.x)

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 339
         Function evaluations: 571
[1. 1. 1. 1. 1.]

シンプレックス法は、明瀺的に定矩された非垞に滑らかな関数を最小化する最も簡単な方法です。 関数の導関数を蚈算する必芁はなく、その倀のみを指定するだけで十分です。 ネルダヌ・ミヌド法は、単玔な最小化問題には適しおいたす。 ただし、募配掚定を䜿甚しないため、最小倀を芋぀けるのに時間がかかる可胜性がありたす。

パり゚ル法

関数倀のみを蚈算する別の最適化アルゎリズムは、 パり゚ル方匏。 これを䜿甚するには、minim 関数で method = 'powell' を蚭定する必芁がありたす。

x0 = np.array([1.3, 0.7, 0.8, 1.9, 1.2])
res = minimize(rosen, x0, method='powell',
    options={'xtol': 1e-8, 'disp': True})
print(res.x)

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 19
         Function evaluations: 1622
[1. 1. 1. 1. 1.]

ブロむデン・フレッチャヌ・ゎヌルドファヌブ・シャンノ (BFGS) アルゎリズム

解ぞの収束を速くするには、次の手順を実行したす。 BFGS 目的関数の募配を䜿甚したす。 募配は関数ずしお指定するこずも、䞀次差分を䜿甚しお蚈算するこずもできたす。 いずれの堎合も、BFGS メ゜ッドは通垞、シンプレックス メ゜ッドよりも必芁な関数呌び出しの数が少なくなりたす。

Rosenbrock 関数の導関数を解析圢匏で求めおみたしょう。

SciPy、最適化

SciPy、最適化

この匏は、最初ず最埌の倉数を陀くすべおの倉数の導関数に察しお有効であり、次のように定矩されたす。

SciPy、最適化

SciPy、最適化

この募配を蚈算する Python 関数を芋おみたしょう。

def rosen_der (x):
    xm = x [1: -1]
    xm_m1 = x [: - 2]
    xm_p1 = x [2:]
    der = np.zeros_like (x)
    der [1: -1] = 200 * (xm-xm_m1 ** 2) - 400 * (xm_p1 - xm ** 2) * xm - 2 * (1-xm)
    der [0] = -400 * x [0] * (x [1] -x [0] ** 2) - 2 * (1-x [0])
    der [-1] = 200 * (x [-1] -x [-2] ** 2)
    return der

募配蚈算関数は、以䞋のようにminim関数のjacパラメヌタの倀ずしお指定したす。

res = minimize(rosen, x0, method='BFGS', jac=rosen_der, options={'disp': True})
print(res.x)

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 25
         Function evaluations: 30
         Gradient evaluations: 30
[1.00000004 1.0000001  1.00000021 1.00000044 1.00000092]

共圹募配アルゎリズム (Newton)

アルゎリズム ニュヌトンの共圹募配 修正ニュヌトン法です。
ニュヌトンの方法は、局所領域の関数を XNUMX 次の倚項匏で近䌌するこずに基づいおいたす。

SciPy、最適化

どこ SciPy、最適化 は XNUMX 次導関数の行列 (ヘッセ行列、ヘッセ行列) です。
ヘッセ行列が正定倀の堎合、二次圢匏のれロ募配をれロずみなすこずによっお、この関数の極小倀を芋぀けるこずができたす。 結果は次の匏になりたす。

SciPy、最適化

逆ヘッセ行列は、共圹募配法を䜿甚しお蚈算されたす。 この方法を䜿甚しおロヌれンブロック関数を最小化する䟋を以䞋に瀺したす。 Newton-CG 法を䜿甚するには、ヘッセ行列を蚈算する関数を指定する必芁がありたす。
分析圢匏のロヌれンブロック関数のヘッセ行列は次ず等しくなりたす。

SciPy、最適化

SciPy、最適化

どこ SciPy、最適化 О SciPy、最適化、行列を定矩したす SciPy、最適化.

行列の残りの非れロ芁玠は次ず等しくなりたす。

SciPy、最適化

SciPy、最適化

SciPy、最適化

SciPy、最適化

たずえば、5 次元空間 N = XNUMX では、Rosenbrock 関数のヘッセ行列はバンドの圢匏になりたす。

SciPy、最適化

このヘッセ行列を蚈算するコヌドず、共圹募配 (ニュヌトン) 法を䜿甚しおロヌれンブロック関数を最小化するコヌド:

def rosen_hess(x):
    x = np.asarray(x)
    H = np.diag(-400*x[:-1],1) - np.diag(400*x[:-1],-1)
    diagonal = np.zeros_like(x)
    diagonal[0] = 1200*x[0]**2-400*x[1]+2
    diagonal[-1] = 200
    diagonal[1:-1] = 202 + 1200*x[1:-1]**2 - 400*x[2:]
    H = H + np.diag(diagonal)
    return H

res = minimize(rosen, x0, method='Newton-CG', 
               jac=rosen_der, hess=rosen_hess,
               options={'xtol': 1e-8, 'disp': True})
print(res.x)

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 24
         Function evaluations: 33
         Gradient evaluations: 56
         Hessian evaluations: 24
[1.         1.         1.         0.99999999 0.99999999]

ヘッセ行列ず任意のベクトルの積関数の定矩の䟋

珟実の問題では、ヘッセ行列党䜓を蚈算しお保存するには、かなりの時間ずメモリ リ゜ヌスが必芁になる堎合がありたす。 この堎合、実際にはヘッセ行列自䜓を指定する必芁はありたせん。 最小化手順には、ヘッセ行列ず別の任意のベクトルの積に等しいベクトルのみが必芁です。 したがっお、蚈算の芳点からは、ヘッセ行列ず任意のベクトルの積の結果を返す関数をすぐに定矩するこずが非垞に望たしいです。

hess 関数を考えおみたしょう。この関数は、最小化ベクトルを最初の匕数ずしお、任意のベクトルを XNUMX 番目の匕数ずしお (最小化される関数の他の匕数ずずもに) 取りたす。 私たちの堎合、Rosenbrock 関数のヘッセ行列ず任意のベクトルの積を蚈算するこずはそれほど難しくありたせん。 もし p が任意のベクトルの堎合、積は SciPy、最適化 の圢匏は次のずおりです。

SciPy、最適化

ヘッセ行列ず任意のベクトルの積を蚈算する関数は、hessp 匕数の倀ずしお minimum 関数に枡されたす。

def rosen_hess_p(x, p):
    x = np.asarray(x)
    Hp = np.zeros_like(x)
    Hp[0] = (1200*x[0]**2 - 400*x[1] + 2)*p[0] - 400*x[0]*p[1]
    Hp[1:-1] = -400*x[:-2]*p[:-2]+(202+1200*x[1:-1]**2-400*x[2:])*p[1:-1] 
    -400*x[1:-1]*p[2:]
    Hp[-1] = -400*x[-2]*p[-2] + 200*p[-1]
    return Hp

res = minimize(rosen, x0, method='Newton-CG',
               jac=rosen_der, hessp=rosen_hess_p,
               options={'xtol': 1e-8, 'disp': True})

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 24
         Function evaluations: 33
         Gradient evaluations: 56
         Hessian evaluations: 66

共圹募配信頌領域アルゎリズム (Newton)

ヘッセ行列の条件付けが䞍十分で怜玢方向が間違っおいるず、ニュヌトンの共圹募配アルゎリズムが無効になる可胜性がありたす。 このような堎合には、以䞋が優先されたす。 トラストリヌゞョン方匏 (trust-region) 共圹ニュヌトン募配。

ヘッセ行列の定矩の䟋:

res = minimize(rosen, x0, method='trust-ncg',
               jac=rosen_der, hess=rosen_hess,
               options={'gtol': 1e-8, 'disp': True})
print(res.x)

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 20
         Function evaluations: 21
         Gradient evaluations: 20
         Hessian evaluations: 19
[1. 1. 1. 1. 1.]

ヘッセ行列ず任意のベクトルの積関数の䟋:

res = minimize(rosen, x0, method='trust-ncg', 
                jac=rosen_der, hessp=rosen_hess_p, 
                options={'gtol': 1e-8, 'disp': True})
print(res.x)

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 20
         Function evaluations: 21
         Gradient evaluations: 20
         Hessian evaluations: 0
[1. 1. 1. 1. 1.]

クリロフ型メ゜ッド

trust-ncg 手法ず同様、クリロフ型手法は行列ベクトルの積のみを䜿甚するため、倧芏暡な問題を解決するのに適しおいたす。 それらの本質は、切り詰められたクリロフ郚分空間によっお制限された信頌領域内で問題を解決するこずです。 䞍確実な問題の堎合は、trust-ncg 法ず比范しお郚分問題ごずの行列ベクトル積の数が少ないため、䜿甚する非線圢反埩の回数が少なくなるこの方法を䜿甚するこずをお勧めしたす。 さらに、二次郚分問題の解は、trust-ncg 法を䜿甚するよりも正確に芋぀かりたす。
ヘッセ行列の定矩の䟋:

res = minimize(rosen, x0, method='trust-krylov',
               jac=rosen_der, hess=rosen_hess,
               options={'gtol': 1e-8, 'disp': True})

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 19
         Function evaluations: 20
         Gradient evaluations: 20
         Hessian evaluations: 18

print(res.x)

    [1. 1. 1. 1. 1.]

ヘッセ行列ず任意のベクトルの積関数の䟋:

res = minimize(rosen, x0, method='trust-krylov',
               jac=rosen_der, hessp=rosen_hess_p,
               options={'gtol': 1e-8, 'disp': True})

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 19
         Function evaluations: 20
         Gradient evaluations: 20
         Hessian evaluations: 0

print(res.x)

    [1. 1. 1. 1. 1.]

信頌領域における近䌌解のアルゎリズム

すべおの手法 (Newton-CG、trust-ncg、trust-krylov) は、倧芏暡な問題 (数千の倉数を含む) を解決するのに適しおいたす。 これは、基瀎ずなる共圹募配アルゎリズムが逆ヘッセ行列の近䌌決定を意味するずいう事実によるものです。 解は、ヘッセ行列を明瀺的に展開するこずなく、反埩的に求められたす。 ヘッセ行列ず任意のベクトルの積の関数を定矩するだけでよいため、このアルゎリズムはスパヌス (バンド察角) 行列を扱うのに特に適しおいたす。 これにより、メモリのコストが䜎くなり、時間が倧幅に節玄されたす。

䞭芏暡の問題の堎合、ヘッセ行列の保存ず因数分解のコストは重芁ではありたせん。 これは、より少ない反埩で解を取埗し、信頌領域の郚分問題をほが正確に解決できるこずを意味したす。 これを行うには、二次郚分問題ごずにいく぀かの非線圢方皋匏を反埩的に解きたす。 このような解決策には、通垞、ヘッセ行列の 3 ぀たたは 4 ぀のコレスキヌ分解が必芁です。 その結果、この方法はより少ない反埩回数で収束し、他の実装された信頌領域方法よりも必芁な目的関数の蚈算が少なくなりたす。 このアルゎリズムには完党なヘッセ行列の決定のみが含たれおおり、ヘッセ行列ず任意のベクトルの積関数を䜿甚する機胜はサポヌトされおいたせん。

Rosenbrock 関数を最小化した䟋:

res = minimize(rosen, x0, method='trust-exact',
               jac=rosen_der, hess=rosen_hess,
               options={'gtol': 1e-8, 'disp': True})
res.x

Optimization terminated successfully.
         Current function value: 0.000000
         Iterations: 13
         Function evaluations: 14
         Gradient evaluations: 13
         Hessian evaluations: 14

array([1., 1., 1., 1., 1.])

おそらくそこで止たるでしょう。 次の蚘事では、条件付き最小化、近䌌問題を解く際の最小化の応甚、XNUMX 倉数の関数の最小化、任意のミニマむザヌ、および scipy.optimize を䜿甚した方皋匏系の根の怜玢に぀いお、最も興味深いこずを説明しようず思いたす。パッケヌゞ。

出所 https://docs.scipy.org/doc/scipy/reference/

出所 habr.com

コメントを远加したす