Python中的函數(shù)最優(yōu)化 (scipy)

最優(yōu)化處理尋找一個函數(shù)的最小值(最大值或零)的問題。在這種情況下,這個函數(shù)被目標(biāo)函數(shù)。本文中,我們使用 scipy.optimize 來進(jìn)行黑盒優(yōu)化。 我們不依賴于我們優(yōu)化的函數(shù)的算術(shù)表達(dá)式。注意這個表達(dá)式通??梢赃M(jìn)行優(yōu)化的。

1. 梯度下降法

梯度下降本身是沿梯度方向變化,對于一些友好的函數(shù)我們可以很容易找到最低點(diǎn),但是對于一些復(fù)雜的函數(shù),我們通過梯度下降不一定可以找到最低點(diǎn)。
在scipy中基于共軛梯度下降方法名稱帶有‘cg’。最小化函數(shù)的簡單梯度下降方法是scipy.optimize.fmin_cg():

from scipy import optimize
# 這里的f 可以自定義,這里為了更加直觀的演示,使用的是較為簡單的函數(shù)。
def f(x):
    return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
#  梯度下降,這里我們要給出函數(shù)變量的初始值。
optimize.fmin_cg(f,[0,0])

在這里我們也可以主動傳入函數(shù)的梯度(基于每個自變量求偏導(dǎo))

def fprime(x):
    return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin_cg(f, [0, 0], fprime=fprime)

在我們傳入梯度之后,求解的性能會得到提升。


image.png

2. 牛頓法

牛頓法的基本思路是,在現(xiàn)有極小點(diǎn)估計值的附近對目標(biāo)函數(shù)做二階展開,進(jìn)而找到極小點(diǎn)的下一個估計值。

def f(x):
    return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一階求導(dǎo)
def fprime(x):
    return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
# 二階求導(dǎo)
def hessian(x):
    return np.array(-2*.5*x[0], 2*x[1])
# 最少需要傳入一階導(dǎo)
optimize.fmin_ncg(f,[200,800],fprime=fprime)
optimize.fmin_ncg(f,[200,800],fprime=fprime, fhess=hessian)
image.png

3. 擬牛頓法

BFGS: BFGS (Broyden-Fletcher-Goldfarb-Shanno算法) 改進(jìn)了每一步對Hessian的近似。在一些正常的函數(shù)中,BFGS 雖然不如牛頓法快,但是還是比較快的。而且對于一些情況復(fù)雜的函數(shù),BFGS 要比牛頓法好,因為它對 Hessian 進(jìn)行了改進(jìn)。

def f(x):
    return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一階求導(dǎo)
def fprime(x):
    return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin_bfgs(f,[200,800],fprime=fprime)
image.png

L-BFGS: 限制內(nèi)存的 BFGS 介于 BFGS 和梯度之間: 在非常高的維度 (> 250) 計算和翻轉(zhuǎn)的Hessian矩陣的成本非常高。L-BFGS保留了低秩的版本。并且在這種情況下可以不為 L-BFGS 求解器傳入梯度,添加 approx_grad = 1 即可。


image.png

4. Powell 算法

和梯度下降類似的方法

def f(x):
    return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一階求導(dǎo)
def fprime(x):
    return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin_powell(f,[0,0])
image.png

5. Nelder-Mead 算法

對噪音很強(qiáng)壯,他不依賴于計算梯度。因此,它可以在局部光滑的函數(shù)上工作。但是,它在光滑、非噪音函數(shù)上比基于梯度的方法慢。

def f(x):
    return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一階求導(dǎo)
def fprime(x):
    return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
optimize.fmin(f, [2, 2])
image.png

6. 帶有限制條件的優(yōu)化

更多的情況,我們需要關(guān)注的不僅僅是目標(biāo)函數(shù),同時還會對變量有一定的限制條件,下面分別介紹了三種限制條件:邊界限制、等于限制、大于(小于)限制。

6.1 邊界限制

最優(yōu)解不會出現(xiàn)在指定邊界以外的位置,即在指定邊界內(nèi)尋找最優(yōu)解

def f(x):
    return 0.5*(1 - x[0])**2 + (x[1] - 7)**2
# 一階求導(dǎo)
def fprime(x):
    return np.array((-2*.5*(1 - x[0]), 2*(x[1] - 7)))
# 這里限制了 x[0] 的取值在(1,2), x[1] 的取值在(9,19)
optimize.fmin_l_bfgs_b(f, [0,0], approx_grad=1, bounds=((1,2),(9,19)))
image.png

6.2 等于限制

最優(yōu)解會滿足等于限制條件,即在指定等于限制條件內(nèi)尋找最優(yōu)解。

def f(x):
    return np.sqrt((x[0] - 3)**2 + (x[1] - 2)**2)
# 自定義限制條件
def constraint(x):
    return x[0]+x[1] - 4
#   eqcons == 0.0 
optimize.fmin_slsqp(f, np.array([0, 0]), eqcons=[constraint,])
image.png

6.3 大于限制

最優(yōu)解會滿足大于限制條件,即在大于限制條件內(nèi)尋找最優(yōu)解。

def f(x):
    return np.sqrt((x[0] - 3)**2 + (x[1] - 2)**2)
# 自定義限制條件
def constraint(x):
    return x[0]+x[1] - 4
#   eqcons >= 0.0 
optimize.fmin_slsqp(f, np.array([0, 0]), ieqcons=[constraint,])
image.png

如果你對解決優(yōu)化問題有更多的了解,你會知道許多條件優(yōu)化問題可以被轉(zhuǎn)化為非條件優(yōu)化問題,使用被稱為拉格朗日乘子法的數(shù)學(xué)技巧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容