Tensorflow快餐教程(7) - 梯度降低

摘要: 梯度降低git

梯度降低

學習完基礎知識和矩陣運算以後,咱們再回頭看下第一節講的線性迴歸的代碼:算法

import tensorflow as tf
import numpy as np

trX = np.linspace(-1, 1, 101)
trY = 2 * trX + np.random.randn(*trX.shape) * 0.33 # 建立一些線性值附近的隨機值

X = tf.placeholder("float") 
Y = tf.placeholder("float")

def model(X, w):
    return tf.multiply(X, w) # X*w線性求值,很是簡單

w = tf.Variable(0.0, name="weights") 
y_model = model(X, w)

cost = tf.square(Y - y_model) # 用平方偏差作爲優化目標

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost) # 梯度降低優化

# 開始建立Session幹活!
with tf.Session() as sess:
    # 首先須要初始化全局變量,這是Tensorflow的要求
    tf.global_variables_initializer().run()

    for i in range(100):
        for (x, y) in zip(trX, trY):
            sess.run(train_op, feed_dict={X: x, Y: y})

    print(sess.run(w))

除了這一句網絡

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost)

應該均可以看懂了。
咱們這一節就來說看不懂的這一行,梯度降低優化函數。dom

從畫函數圖形提及

所謂梯度降低,其實沒有什麼神祕的,就是求個函數極值問題而己。
函數比矩陣強的一點是能夠畫圖啊。函數

因此咱們先學習一下如何畫函數的圖形:學習

import matplotlib.pyplot as plt
import numpy as np

    x = np.linspace(-10,10,1000)
    y = x ** 2 - 2 * x+ 1
    plt.plot(x, y)
    plt.title("matplotlib")
    plt.xlabel("height")
    plt.ylabel("width")
    # 設置圖例
    plt.legend(["X","Y"], loc="upper right")
    plt.grid(True)
    plt.show()

上面咱們用np.linspace來生成若干點組成的向量,而後取y=x2−2x+1的值。
畫出的圖像是這樣的:優化

clipboard.png

求函數的最小值

如今咱們想要求這條曲線上的最小值點。由於這個函數的定義域是無限的,咱們不可能從負無窮到正無窮挨個試那個最小。
可是,咱們能夠隨便找一個點爲起點,而後比較一下它左邊相鄰一點的點和右邊和它相鄰一點的點看看哪一個更小。而後取較小的那個點,繼續這個過程。spa

假設咱們從x=-5這個座標開始,y=(-5)(-5)-2(-5)+1=36。因此這個點是(-5,36).
咱們取0.1爲步長,看看-5.1和-4.9的y值是多少,發現同(-5.1, 37.21)和(-4.9,34.81)。-4.9的值更小,因而-4.9就成爲新一輪迭代的值。而後從(-4.9,34.81)到(-4.8,33.64),以此類推。一直到(1.0, 0)達到最小值,左右都比它大,這個極值就找到了。
求極值的這個函數咱們稱爲損失函數loss function,或代價函數cost function,或者偏差函數error function。這幾個名稱能夠互換使用。
求得的極小值,咱們稱爲x∗=argminf(x)
這種方法能夠解決問題。可是它的問題在於慢。在函數降低速度很快的時候能夠多移動一點加快速度,降低速度變慢了以後少移一點防止跑過了。3d

那麼這個降低速度如何度量呢?咱們都學過,用導數-derivative,也就是切線的斜率。有了導數以後,咱們能夠沿着導數的反方向,也就是切換降低的方向去尋找便可。
這種沿着x的導數的反方向移動一小步來減小f(x)值的技術,就被稱爲梯度降低 - gradient descent.
第n+1的值爲第n次的值減去導數乘以一個能夠調整的係數。
也就是
xn+1=xn−ηdf(x)dx
其中,這個可調整的係數η,咱們稱爲學習率。學習率越大降低速度越快,可是也可能錯過最小值而產生振盪。
選擇學習率的方法通常是取一個小的常數。也能夠經過計算導數消失的步長。還能夠多取幾個學習率值,而後取效果最好的一個。code

f(x)=x2−2x+1的導函數爲f′(x)=2x−2
咱們仍是選擇從(-5,36)這個點爲起點,學習率咱們取0.1。
則下一個點$x_2=-5 - 0.1 (2(-5)-2)= -3.8$
則第2個點降低到(-3.8,23.04)。比起第一種算法咱們右移0.1,此次一會兒就右移了1.2,效率提高12倍。

晉階三維世界

咱們看下咱們在作線性迴歸時使用的損失函數:

cost = tf.square(Y - y_model)

這是個平方函數fLoss=Σni=1(yi−(wxi+b))2
其中yi是已知的標註好的結果,xi是已知的輸入值。未知數只有兩個,w和b。
因此下一步求梯度降低的問題變成求二元二次函數極小值的問題。

咱們仍是先看圖,二元二次函數的圖是什麼樣子的。咱們取個f(x,y)=x2+y2+x+y+1爲例吧。

先學畫圖:

from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt

# 建立 3D 圖形對象
fig = plt.figure()
ax = Axes3D(fig)

X3 = np.linspace(-10,10,100)
Y3 = np.linspace(-10,10,100)
X3, Y3 = np.meshgrid(X3, Y3)
Z3 = X3*X3 + Y3*Y3 + X3 + Y3 + 1
ax.plot_surface(X3, Y3, Z3, cmap=plt.cm.winter)

# 顯示圖
plt.show()

圖繪製出來是這樣子的:

clipboard.png

從一條曲線變成了一個碗同樣的曲面。可是仍然是有最小值點的。

如今不僅是有x一個方向,而是有x和y兩個方向。兩個方向也好辦,x方向和y方向分別找梯度就是了。分別取x和y方向的偏微分。
xn+1=xn−η∂f(x,y)∂x,yn+1=yn−η∂f(x,y)∂y
偏導數雖然從d換成了∂,可是求微分的公式仍是同樣的。只不過在求x的時候把y當成常數就是了。

咱們能夠用梯度符號來簡單表示∇=(∂f(x,y)∂x,∂f(x,y)∂y),這個倒三角符號讀做nabla.

在Tensorflow裏,梯度降低這樣基本的操做天然是不須要咱們操心的,咱們只須要調用tf.train.GradientDescentOptimizer就好。

例子咱們在前面已經看過了,咱們複習一下:

cost = tf.square(Y - y_model) # 用平方偏差作爲優化目標

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(cost) # 梯度降低優化

賦給GradientDescentOptimizer的0.01就是咱們的η,學習率。

自適應梯度演化史

若是您的邏輯思惟比較強的話,必定會發現從二維導數升級到三維梯度時,有一個參數咱們沒有跟着一塊兒擴容,那就是學習率η.

咱們仍是看一個圖,將上面的方程變成f(x,y)=8x2+y2+x+y+1

Z3 = 8*X3*X3 + Y3*Y3 + X3 + Y3 + 1

圖像變成下面這樣子:

clipboard.png

你們能夠直觀地感覺到,∂f(x,y)∂x和∂f(x,y)∂y方向的降低速度是明顯不同的。
若是對於更高維的狀況,問題可能更嚴重,衆口難調。

最簡單的方法多是對於每一個維度都用一個專門的學習率。不過這樣也太麻煩了,這時Adaptive Gradient自適應梯度降低就被當時在UC伯克利讀博士的John C. Duchi提出了,簡寫爲AdaGrad.
在Tensorflow中,咱們能夠經過tf.train.AdagradOptimizer來使用AdaGrad.

AdaGrad的公式爲(xt+1)i=(xt)i−ηΣtτ=1(∇f(xτ))2i√(∇f(xt))i
可是,AdaGrad也有本身的問題。雖然初始指定一個η值以後能夠自適應,可是若是這個η太大的話仍然會致使優化不穩定。可是若是過小了,隨着優化,這個學習率會愈來愈小,可能就到不了極小值就停下來了。

因而當時在Google作實習生的Mathew Zeiler作出了兩點改進:一是引入時間窗口概念,二是不用手動指定學習率。改進以後的算法叫作AdaDelta.
公式中用到的RMS是指Root Mean Square,均方根.
AdaDelta的公式爲(∇xi)i=−(RMS[Δx]t−1)i(RMS[g]t)i(∇f(xi))i
具體過程請看Zeiler同窗寫的paper: http://arxiv.org/pdf/1212.570...

在Tensorflow中,咱們只要使用tf.train.AdadeltaOptimizer這個封裝就好。

AdaDelta並非惟一的改進方案,相似的方案還有RMSProp, Adam等。咱們可使用tf.train.RMSPropOptimizer和tf.train.AdamOptimizer來調用就行了。

還記得第1節中咱們講卷積神經網絡的例子嗎?咱們用的再也不是梯度降低,而是RMSProp:

cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=py_x, labels=Y))
train_op = tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
predict_op = tf.argmax(py_x, 1)

物理學的啓示
雖然各類自適應梯度降低方法洋洋大觀,可是複雜的未必就是最好的。導數的物理意義能夠認爲是速度。速度的變化咱們能夠參考物理學的動量的概念,同時引入慣性的概念。若是遇到坑,靠動量比較容易衝出去,而其它方法就要迭代好久。
在Tensorflow中,能夠經過tf.train.MomentumOptimizer來使用動量方法。

咱們能夠看一下Mathew Zeiler的論文中對於幾種方法的對比:

做者:lusing

原文連接

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索