SciPy, optimizim

SciPy, optimizim

SciPy (të shqiptuar si sai pai) është një paketë e procedurave matematikore të aplikueshme, e ndërtuar mbi zgjerimin Numpy Python. Me SciPy, seanca interaktive Python shndërrohet në një mjedis të plotë për përpunimin e të dhënave dhe prototipizimin e sistemeve të komplikuara, si MATLAB, IDL, Octave, R-Lab dhe SciLab. Sot, dua të flas shkurtimisht për aplikimin e disa algoritmeve të njohura të optimizimit në paketën scipy.optimize. Për një informacion më të detajuar dhe të azhurnuar mbi përdorimin e funksioneve, gjithmonë mund të merrni ndihmë me komandën help() ose me Shift+Tab.

Hyrje

Për të kursyer veten dhe lexuesit nga kërkimi dhe leximi i burimeve origjinale, lidhjet për përshkrimet e metodave do të jenë kryesisht në Wikipedia. Në përgjithësi, këto informacione janë të mjaftueshme për të kuptuar metodat në përgjithësi dhe kushtet e përdorimit të tyre. Për të kuptuar thelbin e metodave matematikore, ne ndjekim lidhjet për botime më autoritative, të cilat mund të gjenden në fund të çdo artikulli ose në motorin tuaj të preferuar të kërkimit.

Pra, moduli scipy.optimize përfshin implementimin e procedurave të mëposhtme:

  1. Minimizimi i kushtëzuar dhe i pa kushtëzuar i funksioneve skalarë me disa variabla (minim) përmes algoritmeve të ndryshme (simpelksi i Nelder-Mead, BFGS, gradientet e lidhura të Newtonit, COBYLA и SLSQP)
  2. Optimizimi global (p.sh: basinhopping, diff_evolution)
  3. Minimizimi i mbetjeve MNP (least_squares) dhe algoritmet për përshtatjen e kurbave me MNP jo-linear (curve_fit)
  4. Minimizimi i funksioneve skalarë me një variabël (minim_scalar) dhe kërkimi i rrënjëve (root_scalar)
  5. Zgjidhësit shumë-dimensionalë të sistemit të ekuacioneve (root) duke përdorur algoritmo të ndryshme (hibride të Powellit, Levenberg-Marquardt ose metoda me shkallë të madhe, si Newton-Krylov).

Në këtë artikull ne do të shqyrtojmë vetëm pikën e parë nga e gjithë kjo listë.

Minimizimi i pa kushtëzuar i funksioneve skalarë me disa variabla

Funksioni minim nga paketa scipy.optimize ofron një ndërfaqe të përgjithshme për zgjidhjen e problemeve të minimizimit të kushtëzuar dhe të pa kushtëzuar të funksioneve skalarë me disa variabla. Për të demonstruar funksionimin e tij, na nevojitet një funksion i përshtatshëm me disa variabla, që do ta minimizojmë në mënyra të ndryshme.

Për këto qëllime, funksioni i Rosenbrockut me N variabla është i përshtatshëm dhe ka formën:

SciPy, optimizim

Megjithëse funksioni i Rosenbrockut dhe matricat e tij Jacobian dhe Hessian (derivatet e para dhe të dyta përkatësisht) janë tashmë të përcaktuara në paketën scipy.optimize, le të e përcaktojmë atë vetë.

import numpy as np

def rosen(x):
    """Funksioni i Rosenbrockut"""
    return np.sum(100.0*(x[1:]-x[:-1]**2.0)**2.0 + (1-x[:-1])**2.0, axis=0)

Për qartësi do të vizatojmë në 3D vlerat e funksionit të Rosenbrockut nga dy variabla.

Kodi për vizatimin

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

# Konfigurojmë grafikun 3D
fig = plt.figure(figsize=[15, 10])
ax = fig.gca(projection='3d')

# Caktojmë këndin e shikimit
ax.view_init(45, 30)

# Krijojmë të dhëna për grafik
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]))

# Vizatojmë sipërfaqen
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm)
plt.show()

SciPy, optimizim

Duke e ditur paraprakisht se minimumi është 0 kur SciPy, optimizim, le të shqyrtojmë shembuj se si mund të përcaktojmë vlerën minimale të funksionit të Rosenbrockut përmes procedurave të ndryshme scipy.optimize.

Metoda Simplex e Nelder-Mead

Supozoni se kemi një pikë fillestare x0 në hapësirën 5-dimensionale. Le të gjejmë pikën më të afërt të minimumit të funksionit të Rosenbrockut duke përdorur algoritmin simplex Nelder-Mead (algoritmi u përmend si vlerë e parametrave method):

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)

Optimizimi përfundoi me sukses.
         Vlera aktuale e funksionit: 0.000000
         Iteracionet: 339
         Vlerësimet e funksionit: 571
[1. 1. 1. 1. 1.]

Metoda Simplex është mënyra më e thjeshtë për të minimizuar një funksion të përcaktuar qartë dhe mjaft të qetë. Ajo nuk kërkon llogaritjen e derivatave të funksionit, mjafton të jepen vetëm vlerat e tij. Metoda Nelder-Mead është një zgjedhje e mirë për detyrat e thjeshta të minimizimit. Megjithatë, pasi ajo nuk përdor vlerësime të gradientit, mund të kërkohet më shumë kohë për të gjetur minimumin.

Metoda Powell

Një tjetër algoritëm optimizimi, në të cilin llogariten vetëm vlerat e funksioneve, është metoda Powell. Për ta përdorur atë, duhet të vendosni method = ‘powell’ në funksionin minim.

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)

Optimizimi përfundoi me sukses.
         Vlera aktuale e funksionit: 0.000000
         Iteracionet: 19
         Vlerësimet e funksionit: 1622
[1. 1. 1. 1. 1.]

Algoritmi Broyden-Fletcher-Goldfarb-Shanno (BFGS)

Për të arritur një konvergjencë më të shpejtë në zgjidhje, procedura BFGS përdor gradientin e funksionit qëllimor. Gradianti mund të jepet në formën e një funksioni ose llogaritet përmes ndryshimeve të rendit të parë. Në çdo rast, zakonisht metoda BFGS kërkon më pak thirrje funksionesh sesa metoda simplex.

Të gjejmë derivatën e funksionit Rosenbrock në formë analitike:

SciPy, optimizim

SciPy, optimizim

Ky shprehje është e drejtë për derivatat e të gjitha variablave, përveç të parës dhe të fundit, të cilat përcaktohen si:

SciPy, optimizim

SciPy, optimizim

Le të shohim funksionin në Python që llogarit këtë gradient:

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

Funksioni për llogaritjen e gradientit është specifikuar si vlerë e parametrave jac të funksionit minim, siç përshkruhet më poshtë.

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

Optimizimi përfundoi me sukses.
         Vlera aktuale e funksionit: 0.000000
         Iteracionet: 25
         Vlerësimet e funksionit: 30
         Vlerësimet e gradientit: 30
[1.00000004 1.0000001  1.00000021 1.00000044 1.00000092]

Algoritmi i gradientëve të çiftëzuar (Newton)

Algoritmi gradientëve të çiftëzuar të Newtonit është një metodë e modifikuar Newtonit.
Metoda e Newtonit bazohet në aproximimin e funksionit në një zonë lokale me një polinom të rendit të dytë:

SciPy, optimizim

ku SciPy, optimizim është matrica e të dytave derivatave (matrica Hessiane, hessian).
Nëse hessian është pozitivisht i përcaktuar, atëherë mund të gjendet minimumi lokal i këtij funksioni duke e barazuar gradientin zero të formës katrore me zero. Si rezultat do të kemi shprehjen:

SciPy, optimizim

Hessiani i funksionit Rosenbrock llogaritet me metodën e gradientëve të çiftëzuar. Një shembull i përdorimit të kësaj metode për minimizimin e funksionit Rosenbrock jepet më poshtë. Përdorimi i metodës Newton-CG kërkon përcaktimin e funksionit që llogarit hessian-in.
Hessiani i funksionit Rosenbrock në formë analitike është:

SciPy, optimizim

SciPy, optimizim

ku SciPy, optimizim и SciPy, optimizim, përcaktojnë matricën SciPy, optimizim.

Elementet e tjera jo zero të matricës janë:

SciPy, optimizim

SciPy, optimizim

SciPy, optimizim

SciPy, optimizim

Për shembull, në hapësirën pesëdimensional N = 5, matrica Hessiane për funksionin Rosenbrock ka një formë bande:

SciPy, optimizim

Kodi që llogarit këtë hessian së bashku me kodin për minimizimin e funksionit Rosenbrock me metodën e gradientëve të çiftëzuar (Newton):

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)

Optimizimi u përfundua me sukses.
         Vlera aktuale e funksionit: 0.000000
         Iteracionet: 24
         Vlerësimet e funksionit: 33
         Vlerësimet e gradientes: 56
         Vlerësimet e Hessianit: 24
[1.         1.         1.         0.99999999 0.99999999]

Shembulli me caktimin e funksionit të prodhimit të Hessianit dhe një vektori të rastshëm

Në problemet reale, llogaritja dhe ruajtja e të gjithë matricës së Hessianit mund të kërkojë burime të konsiderueshme kohe dhe memorie. Në të njëjtën kohë, në thelb, nuk ka nevojë për të caktuar vetë matricën e Hessianit, pasi për procedurën e minimizimit ne kemi nevojë vetëm për një vektor, i barabartë me produktin e Hessianit me një vektor tjetër të rastshëm. Prandaj, nga një këndvështrim llogaritës, është shumë më e preferueshme të caktojmë menjëherë një funksion që kthen rezultatin e produktit të Hessianit me një vektor të rastshëm.

Le të shqyrtojmë funksionin hess, i cili merr si argument të parë vektorin e minimizimit dhe si argument të dytë një vektor të rastshëm (përveç argumenteve të tjera të funksionit të minimizuar). Në rastin tonë, llogaritja e produktit të Hessianit të funksionit të Rosenbrock me një vektor të rastshëm nuk është shumë e vështirë. Nëse p — është një vektor i rastshëm, atëherë produkti SciPy, optimizim ka formën:

SciPy, optimizim

Funksioni që llogarit produktin e Hessianit dhe një vektor të rastshëm kalon si vlerë e argumentit hessp të funksionit minimize:

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})

Optimizimi u përfundua me sukses.
         Vlera aktuale e funksionit: 0.000000
         Iteracionet: 24
         Vlerësimet e funksionit: 33
         Vlerësimet e gradientes: 56
         Vlerësimet e Hessianit: 66

Algoritmi i rajonit të besimit (trust region) të gradientëve të lidhur (Newton)

Keq-kondicioni i matricës së Hessianit dhe drejtimet e gabuara të kërkimit mund të bëjnë që algoritmi i gradientëve të lidhur të Newtonit të jetë joefektiv. Në këto raste, preferohet metoda e rajonit të besimit (trust-region) të gradientëve të lidhur të Newtonit.

Shembulli me caktimin e matricës së Hessianit:

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

Optimizimi u përfundua me sukses.
         Vlera e tanishme e funksionit: 0.000000
         Iteracionet: 20
         Vlerësime për funksionin: 21
         Vlerësime për gradienin: 20
         Vlerësime për hessianin: 19
[1. 1. 1. 1. 1.]

Shembulli me funksionin e produktit të hessianit dhe një vektori të rastësishëm:

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

Optimizimi u përfundua me sukses.
         Vlera e tanishme e funksionit: 0.000000
         Iteracionet: 20
         Vlerësime për funksionin: 21
         Vlerësime për gradienin: 20
         Vlerësime për hessianin: 0
[1. 1. 1. 1. 1.]

Metodat e tipit Krylov

Si metodat trust-ncg, metodat e tipit Krylov janë të përshtatshme për zgjidhjen e problemeve të mëdha, pasi ato përdorin vetëm produkte matricore-vektoriale. Thelbi i tyre është zgjidhja e problemeve në një zonë besimi të kufizuar nga një nën-hapsirë e prerë Krylov. Për problemet e pa-përcaktuara, ky metod është më i përshtatshëm, pasi përdor një numër më të vogël iteracionesh jo-lineare për shkak të numrit më të vogël të produkteve matricore-vektoriale për një detyrë të vogël, krahasuar me metodën trust-ncg. Për më tepër, zgjidhja e nën-problemit katror është më e sakta se me metodën trust-ncg.
Shembulli me caktimin e matricës së Hessianit:

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

Optimizimi u përfundua me sukses.
         Vlera e tanishme e funksionit: 0.000000
         Iteracionet: 19
         Vlerësime për funksionin: 20
         Vlerësime për gradienin: 20
         Vlerësime për hessianin: 18

print(res.x)

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

Shembulli me funksionin e produktit të hessianit dhe një vektori të rastësishëm:

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

Optimizimi u përfundua me sukses.
         Vlera e tanishme e funksionit: 0.000000
         Iteracionet: 19
         Vlerësime për funksionin: 20
         Vlerësime për gradienin: 20
         Vlerësime për hessianin: 0

print(res.x)

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

Algoritmi i zgjidhjes afatgjatë në zonën e besimit

Të gjitha metodat (Newton-CG, trust-ncg dhe trust-krylov) janë të përshtatshme për zgjidhjen e problemeve të mëdha (me mijëra variabla). Kjo lidhet me faktin se algoritmi i bazuar në to, i gradienteve të ndërlidhura, nënkupton gjetjen afatgjatë të matricës së inversë të Hessianit. Zgjidhja gjendet në mënyrë iteruese, pa shprehje të qarta Hessiane. Duke qenë se kërkohet të përcaktohet vetëm funksioni për produktin e Hessianit dhe një vektor të rastësishëm, ky algoritëm është veçanërisht i mirë për punën me matrix të hollë (diagona të shtrënguara). Kjo siguron kosto të ulët memorie dhe një kursim të rëndësishëm të kohës.

Në problemet me përmasat e mesme, kostot e ruajtjes dhe faktorizimi i hessianit nuk kanë rëndësi vendimtare. Kjo do të thotë se mund të arrihet një zgjidhje me një numër më të vogël iteracionesh, duke lejuar nën-problemet e zonës së besimit të zgjidhen pothuajse saktësisht. Për këtë, disa ekuacione jo-lineare zgjidhen iterativ për secilën nën-problem katror. Kjo zgjidhje zakonisht kërkon 3 ose 4 shpërbërje të matricës së Hessianit. Si rezultat, metoda konvergon në një numër më të vogël iteracionesh dhe kërkon më pak llogaritje të funksionit objektiv se metodat e tjera të zbatuara në zonën e besimit. Ky algoritëm nënkupton vetëm përcaktimin e matricës së plotë Hessiane dhe nuk mbështet përdorimin e funksionit të produktit të hessianit dhe një vektori të rastësishëm.

Shembulli me minimizimin e funksionit Rosenbrock:

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

Optimizimi përfundoi me sukses.
         Vlera aktuale e funksionit: 0.000000
         Iteracionet: 13
         Vlerësimet e funksionit: 14
         Vlerësimet e gradientit: 13
         Vlerësimet e hessianit: 14

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

Këtu, ndoshta, do të ndalojmë. Në artikullin e ardhshëm do të përpiqem të flas për gjëra më interesante rreth minimizimit të kushtëzuar, aplikacionit të minimizimit në zgjidhjen e problemeve të aproksimimit, minimizimit të funksioneve me një variabël, minimizatorëve të rastësishëm dhe gjetjes së rrënjëve të sistemit të ekuacioneve me ndihmën e paketës scipy.optimize.

Burimi: https://docs.scipy.org/doc/scipy/reference/

Burimi: habr.com

Купить надежный хостинг для сайтов с защитой от DDoS, VPS VDS серверы 🔥 Купить надежный хостинг для сайтов с защитой от DDoS, VPS VDS серверы | ProHoster