SciPy (lausutaan sai pie) on Numpy Python -laajennukseen perustuva matemaattinen sovelluspaketti. SciPyn avulla interaktiivisesta Python-istunnostasi tulee sama täydellinen datatiede ja monimutkainen järjestelmäprototyyppiympäristö kuin MATLAB, IDL, Octave, R-Lab ja SciLab. Tänään haluan puhua lyhyesti siitä, kuinka käyttää joitain tunnettuja optimointialgoritmeja scipy.optimize-paketissa. Tarkempia ja ajantasaisia ohjeita funktioiden käyttöön saa aina help()-komennolla tai Shift+Tab-näppäimillä.
Esittely
Voit säästää itseäsi ja lukijoita ensisijaisten lähteiden etsinnästä ja lukemisesta, linkit menetelmien kuvauksiin ovat pääasiassa Wikipediassa. Yleensä nämä tiedot riittävät menetelmien yleiseen ymmärtämiseen ja niiden soveltamisehtoihin. Ymmärtääksesi matemaattisten menetelmien olemuksen, seuraa linkkejä arvokkaampiin julkaisuihin, jotka löytyvät jokaisen artikkelin lopusta tai suosikkihakukoneestasi.
Joten scipy.optimize-moduuli sisältää seuraavat toimenpiteet:
Useiden muuttujien skalaarifunktioiden ehdollinen ja ehdoton minimointi (minimi) käyttämällä erilaisia algoritmeja (Nelder-Mead simplex, BFGS, Newtonin konjugaattigradientit, COBYLA и SLSQP)
Jäännösten minimoiminen MNC (pienimmän neliösumman) ja käyrän sovitusalgoritmit käyttäen epälineaarisia pienimmän neliösumman (curve_fit) avulla
Yhden muuttujan skalaarifunktioiden minimointi (minim_scalar) ja juurien etsiminen (root_scalar)
Yhtälöjärjestelmän (juuren) moniulotteiset ratkaisijat eri algoritmeilla (hybridi Powell, Levenberg-Marquardt tai suuren mittakaavan menetelmiä, kuten Newton-Krylov).
Tässä artikkelissa tarkastelemme vain ensimmäistä kohtaa koko luettelosta.
Useiden muuttujien skalaarifunktion ehdoton minimointi
Scipy.optimize-paketin minimifunktio tarjoaa yleisen käyttöliittymän useiden muuttujien skalaarifunktioiden ehdollisten ja ehdottomien minimointiongelmien ratkaisemiseen. Sen toiminnan osoittamiseksi tarvitsemme sopivan funktion useista muuttujista, jotka minimoidaan eri tavoin.
Näihin tarkoituksiin N muuttujan Rosenbrock-funktio on täydellinen, jonka muoto on:
Huolimatta siitä, että Rosenbrock-funktio ja sen Jacobi- ja Hessian-matriisit (ensimmäinen ja toinen derivaatta) on jo määritelty scipy.optimize-paketissa, määrittelemme sen itse.
Selvyyden vuoksi piirretään 3D:ssä kahden muuttujan Rosenbrock-funktion arvot.
Piirustuskoodi
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()
Tietäen etukäteen, että minimi on 0 at , katsotaanpa esimerkkejä siitä, kuinka määrittää Rosenbrock-funktion vähimmäisarvo käyttämällä erilaisia scipy.optimize-toimenpiteitä.
Nelder-Mead simplex -menetelmä
Olkoon 0-ulotteisessa avaruudessa alkupiste x5. Etsitään algoritmin avulla sitä lähinnä oleva Rosenbrock-funktion minimipiste Nelder-Mead simplex (algoritmi on määritetty menetelmäparametrin arvoksi):
Optimization terminated successfully.
Current function value: 0.000000
Iterations: 339
Function evaluations: 571
[1. 1. 1. 1. 1.]
Simpleksimenetelmä on yksinkertaisin tapa minimoida selkeästi määritelty ja melko tasainen funktio. Se ei edellytä funktion derivaattojen laskemista, riittää, että määritetään vain sen arvot. Nelder-Mead-menetelmä on hyvä valinta yksinkertaisiin minimointiongelmiin. Koska se ei kuitenkaan käytä gradienttiestimaatteja, minimin löytäminen voi kestää kauemmin.
Powellin menetelmä
Toinen optimointialgoritmi, jossa vain funktioarvot lasketaan, on Powellin menetelmä. Käyttääksesi sitä, sinun on asetettava menetelmä = 'powell' minimifunktiossa.
Saadaksesi nopeamman konvergenssin ratkaisuun, menettely BFGS käyttää tavoitefunktion gradienttia. Gradientti voidaan määrittää funktiona tai laskea käyttämällä ensimmäisen asteen eroja. Joka tapauksessa BFGS-menetelmä vaatii tyypillisesti vähemmän funktiokutsuja kuin simpleksimenetelmä.
Tämä lauseke pätee kaikkien muuttujien johdannaisiin paitsi ensimmäistä ja viimeistä, jotka määritellään seuraavasti:
Katsotaan Python-funktiota, joka laskee tämän gradientin:
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
Gradientin laskentafunktio on määritetty minimifunktion jac-parametrin arvoksi alla olevan kuvan mukaisesti.
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]
Konjugoitu gradienttialgoritmi (Newton)
Algoritmi Newtonin konjugaattigradientit on modifioitu Newtonin menetelmä.
Newtonin menetelmä perustuu funktion approksimoimiseen paikallisella alueella toisen asteen polynomilla:
missä on toisten derivaattojen matriisi (Hessian matriisi, Hessian).
Jos Hessian on positiivinen definiitti, niin tämän funktion paikallinen minimi voidaan löytää vertaamalla toisen asteen muodon nollagradientti nollaan. Tuloksena on lauseke:
Käänteis-Hessian lasketaan käyttämällä konjugaattigradienttimenetelmää. Alla on esimerkki tämän menetelmän käytöstä Rosenbrock-funktion minimoimiseksi. Jotta voit käyttää Newton-CG-menetelmää, sinun on määritettävä funktio, joka laskee Hessenin.
Rosenbrock-funktion Hessen-funktio analyyttisessä muodossa on yhtä suuri kuin:
missä и , määrittele matriisi .
Matriisin loput nollasta poikkeavat elementit ovat yhtä suuria kuin:
Esimerkiksi viisiulotteisessa avaruudessa N = 5 Rosenbrock-funktion Hessen-matriisi on nauhan muotoinen:
Koodi, joka laskee tämän Hessenin ja koodin Rosenbrock-funktion minimoimiseksi käyttämällä konjugaattigradientti (Newton) menetelmää:
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]
Esimerkki Hessenin tulofunktion ja mielivaltaisen vektorin määritelmästä
Reaalimaailman ongelmissa koko Hessenin matriisin laskeminen ja tallentaminen voi vaatia huomattavia aika- ja muistiresursseja. Tässä tapauksessa itse Hessen-matriisia ei tarvitse määrittää, koska minimointimenettely vaatii vain vektorin, joka on yhtä suuri kuin Hessenin tulo toisen mielivaltaisen vektorin kanssa. Laskennallisesti katsottuna on siis paljon parempi määrittää välittömästi funktio, joka palauttaa Hessenin tulon tuloksen mielivaltaisella vektorilla.
Tarkastellaan hess-funktiota, joka ottaa minimointivektorin ensimmäiseksi argumentiksi ja mielivaltaisen vektorin toiseksi argumentiksi (sekä muiden minimoitavan funktion argumenttien kanssa). Meidän tapauksessamme Rosenbrock-funktion Hessenin tulon laskeminen mielivaltaisella vektorilla ei ole kovin vaikeaa. Jos p on mielivaltainen vektori, sitten tulo on muoto:
Funktio, joka laskee Hessenin ja mielivaltaisen vektorin tulon, välitetään hessp-argumentin arvoksi minimointifunktiolle:
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
Hessin matriisin huono ehdollisuus ja väärät hakusuunnat voivat aiheuttaa Newtonin konjugaattigradienttialgoritmin tehottomuuden. Tällaisissa tapauksissa etusija annetaan luottamusalueen menetelmä (luottamusalue) konjugaatti Newtonin gradientit.
Optimization terminated successfully.
Current function value: 0.000000
Iterations: 20
Function evaluations: 21
Gradient evaluations: 20
Hessian evaluations: 0
[1. 1. 1. 1. 1.]
Krylov-tyyppiset menetelmät
Trust-ncg-menetelmän tavoin Krylov-tyyppiset menetelmät sopivat hyvin suurten ongelmien ratkaisemiseen, koska niissä käytetään vain matriisivektorituloja. Niiden ydin on ratkaista ongelma luottamusalueella, jota rajoittaa katkaistu Krylov-aliavaruus. Epävarmoihin ongelmiin on parempi käyttää tätä menetelmää, koska se käyttää vähemmän epälineaarisia iteraatioita, koska matriisivektorituloja on pienempi määrä aliongelmaa kohti verrattuna luottamus-ncg-menetelmään. Lisäksi neliöllisen aliongelman ratkaisu löydetään tarkemmin kuin käyttämällä trust-ncg-menetelmää.
Esimerkki Hessenin matriisin määritelmästä:
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.]
Esimerkki Hessenin tulofunktiosta ja mielivaltaisesta vektorista:
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.]
Kaikki menetelmät (Newton-CG, trust-ncg ja trust-krylov) soveltuvat hyvin suurten (tuhansien muuttujien) ongelmien ratkaisemiseen. Tämä johtuu siitä, että taustalla oleva konjugaattigradienttialgoritmi edellyttää käänteisen Hessin matriisin likimääräistä määritystä. Ratkaisu löydetään iteratiivisesti ilman Hessenin nimenomaista laajentamista. Koska sinun on määritettävä funktio vain Hessenin ja mielivaltaisen vektorin tulolle, tämä algoritmi on erityisen hyvä harvoille (kaistalävistäjä) matriiseille. Tämä tarjoaa alhaiset muistikustannukset ja huomattavat ajansäästöt.
Keskikokoisissa ongelmissa Hessianin varastointi- ja suhdekustannukset eivät ole kriittisiä. Tämä tarkoittaa, että on mahdollista saada ratkaisu harvemmalla iteraatiolla, mikä ratkaisee luottamusalueen aliongelmat lähes tarkasti. Tätä varten jotkin epälineaariset yhtälöt ratkaistaan iteratiivisesti jokaiselle toisen asteen alitehtävälle. Tällainen ratkaisu vaatii yleensä 3 tai 4 Cholesky-hajotelmaa Hessen-matriisista. Tämän seurauksena menetelmä konvergoi harvemmissa iteraatioissa ja vaatii vähemmän tavoitefunktiolaskelmia kuin muut toteutetut luottamusalueen menetelmät. Tämä algoritmi sisältää vain täydellisen Hessin matriisin määrittämisen, eikä se tue mahdollisuutta käyttää Hessenin ja mielivaltaisen vektorin tulofunktiota.
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.])
Todennäköisesti lopetamme siihen. Seuraavassa artikkelissa yritän kertoa mielenkiintoisimmista asioista ehdollisen minimoinnin, minimoinnin soveltamisesta approksimaatioongelmien ratkaisemiseen, yhden muuttujan funktion minimoimisesta, mielivaltaisista minimointityökaluista ja yhtälöjärjestelmän juurien löytämisestä scipy.optimize-tiedoston avulla. paketti.