機器學習第二課:無約束優化問題(局部極小值的幾種解法)(梯度下降法與擬牛頓法)


上一篇講了一些微積分的概括。(http://blog.csdn.net/dajiabudongdao/article/details/52397343) 並引出了梯度下降法與擬牛頓法。當計算機求函數的局部極小值時,我們選用這兩種方法(或衍生方法)來解。求極值的問題很複雜,我麼、們大致分爲兩種:無約束極值問題,與有約束極值問題。這一章主要討論無約束極值問題。有約束的極值在後面會提到。(http://blog.csdn.net/dajiabudongdao/article/details/52462942

一、無約束優化問題

無約束優化問題,就是我們中學常見的求函數極值的方法。一個函數求極值無需外部控制。一旦有外部條件控制了,我們可以用拉格朗日乘數法求解,那就是條件極值的事情了。但是問題出現了,極值不一定是最值。我們求極值容易,最值很難。這TM就令人尷尬了。

我們工業界的同志發明了“局部最小值”一詞的含義就是想要說“我的極值運算就是某種意義上的最值運算”。

1.局部極小值的解法

局部極小值的通用解法就是迭代。

例如當前自變量Xk,對應F(X)k 。那麼下次迭代的Xk+1  = Xk   + a*dk其中a代表挪動步長,dk表示挪動方向。迭代是個趨近過程,若使結果收斂需要遵循一個基本要求:下一步的結果要比這一步好,即Xk+1比Xk更接近值極值。當求解極小值時,我們自然希望Xk+1的結果比Xk小。按照泰勒級數展開,我們可以寫成

F(Xk+1 ) =F( Xk )  + dkF'(Xk)+...         (其中d = Xk+1-Xk)

並且Xk+1 < Xk。忽略後面的部分可得dkF'(Xk) <0。那麼我們可知每次迭代,d的方向爲負。。但d的值是什麼呢?

dk = -F'(Xk)

因爲一點的梯度的本質是一種方向。當兩個方向相同時,相乘正結果最大,相反時,相乘負結果最大。

下面是我用python模擬的,深度下降的程序

# coding=utf-8

import numpy
import theano
import theano.tensor as T


###多項式的梯度下降
###
###
# y
# x
# theta:容忍的誤差,因爲編程有誤,這裏的誤差僅控制循環次數
# alpha:步長
# x:用來標識對誰求導的標量,x=T.dvector('x')如'x'標識對x求導
# y:帶上面標量的公式
# dimen:變量的維數
# y=x**2
# 使用:steepest(x,y,3,theta=0.1,alpha=0.1)


def steepest(x, y, dimen, theta=0.1, alpha=0.1):
    theta = theta * theta
    J, updates = theano.scan(lambda i, y, x: T.grad(y[i], x), sequences=T.arange(y.shape[0]), non_sequences=[y, x])
    Gradientfunction = theano.function([x], J, updates=updates)
    x_0 = numpy.array([0.] * dimen)
    x_k = x_0
    wuchav = numpy.diag(numpy.array(Gradientfunction(x_k)))
    wucha = numpy.dot(wuchav, wuchav)
    while (wucha > theta):
        x_k_1 = x_k - alpha * numpy.diag(Gradientfunction(x_k))
        wuchav = x_k_1 - x_k
        wucha = numpy.dot(wuchav, wuchav)
        x_k = x_k_1
    return x_k


if __name__ == '__main__':
    x = T.dvector('x')
    y = x ** 2 + 2 * x + 1
    res = steepest(x, y, 1, theta=0.01, alpha=0.1)
    print "極小值的參數位置爲"
    print res

運行結果如下:



2.進一步探討局部極小值的解法

有個問題,上面的泰勒級數展開只到了一級。如果展開到二級就變成牛頓法。

F(Xk+1 ) =F( Xk )  + dF'(Xk)+1/2 dTH(X)d + ...      (其中d = Xk+1-Xk)

其中H爲Hassion矩陣,求二階導使用。因爲當X趨近與極值時,F(Xk+1)與F(Xk)很接近。F對d(d = Xk+1-Xk)求導爲0,得的

dk = -H-1(Xk)F'(Xk)

後面的算法與上面一樣。但說實話,矩陣求逆是件很難的事情。很多情況,還無法求逆,這就造成牛頓法不太常用。

  所以研究者提出了很多使用方法來近似Hessian矩陣,,這些方法都稱作準牛頓算法,,BFGS就是其中的一種,以其發明者Broyden, Fletcher, Goldfarb和Shanno命名。

我們用BFGS方法直接構造出Hassion矩陣的逆。具體步驟很複雜這裏僅 帖公式,步驟如下:

Sk = H-1(Xk),

rk=F'(Xk+1)-F'(Xk)

δk = Xk+1-Xk




同樣的我用python模擬,BFGS的程序如下:


# coding=utf-8
import numpy as np
import theano
import theano.tensor as T


###多項式的BFGS
###
###
# theta:容忍的誤差
# alpha:步長
# x:用來標識對誰求導的標量,x=T.dvector('x')如'x'標識對x求導
# y:帶上面標量的公式
# dimen:變量的維數
# y=x**2
# 使用:BFGS(x,y,3,theta=0.1,alpha=0.1)


def BFGS(x, y, dimen, theta=0.1, alpha=0.1):
    theta = theta * theta
    S_0 = np.eye(dimen)
    S_k = S_0
    J, updates = theano.scan(lambda i, y, x: T.grad(y[i], x), sequences=T.arange(y.shape[0]), non_sequences=[y, x])
    Gradientfunction = theano.function([x], J, updates=updates)
    x_0 = np.array([0.] * dimen)
    x_k = np.mat(x_0)

    wuchav = np.diag(np.array(Gradientfunction(x_0)))
    wucha = np.dot(wuchav, wuchav)
    while (wucha > theta):
        x_k_1 = x_k - alpha * np.mat(np.diag(Gradientfunction(np.array(x_k)[0]))) * S_k.T
        theta_k = x_k_1 - x_k
        gamma_k = np.mat(np.diag(Gradientfunction(np.array(x_k_1)[0])) - np.diag(Gradientfunction(np.array(x_k)[0])))
        S_k_1 = S_k + (1 + (gamma_k * S_k.T * gamma_k.T) / (theta_k * gamma_k.T)) * (
        (theta_k.T * theta_k) / (theta_k * gamma_k.T))
        S_k_1 = S_k_1 - (theta_k.T * gamma_k * S_k + S_k * gamma_k.T * theta_k) / (theta_k * gamma_k.T)
        S_k = S_k_1
        wuchav = np.array(theta_k)[0]
        wucha = np.dot(wuchav, wuchav)
        x_k = x_k_1
    return x_k


if __name__ == '__main__':
    x = T.dvector('x')
    y = x ** 2 + 2 * x + 1
    res = BFGS(x, y, 1, theta=0.001, alpha=0.1)
    print "極小值的參數位置爲"
    print res






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章