1. 程式人生 > >梯度下降----各種方式總結比較

梯度下降----各種方式總結比較

梯度下降方法總結

完整的notebook上傳到了github上:https://github.com/NewQJX/DeepLearning/tree/master/梯度下降總結

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

深度學習中,我們最常用的優化演算法就是基於梯度的方式了。所以在這裡總結一下我目前所能理解的方法。(PS:在寫這個的過程中,我發現指數平均無處不在啊。這個在視覺化模型的Loss值隨迭代次數的改變,為了使影象看起來更加平滑,用的也是指數平均。看來是該瞭解一下了!)

梯度下降示例(迴歸問題)

示例中的資料是自己生成的,真實資料符合: y = 3 x + 5 y = 3x + 5

構建 y = w x + b y = wx + b

模型擬合數據, 通過梯度下降的方法更新引數w,b。比較幾種梯度下降更新方式的優缺點。
目標函式為均方誤差: L o s s = 1 N i = 0 N 1 2 ( y ^ ( i ) y ( i ) ) 2 Loss = \frac{1}{N}\sum_{i=0}^{N}\frac{1}{2}(\hat{y}^{(i)} - y^{(i)})^2

x = np.arange(0, 20)
y = 3 * x + 5 
plt.scatter(x, y)
plt.xlabel('w')
plt.ylabel('b')
plt.title('Data')

fig = plt.figure()
ax = Axes3D(fig)
w = np.linspace(-5, 5)
b = np.linspace(-5, 5)
w, b = np.meshgrid(w, b)
loss = []
for w_i, b_i in zip(w.flatten(), b.flatten()):
    y_hat = w_i * x + b_i
    loss.append(np.mean((y_hat - y) ** 2) / 2)
    
    
ax.scatter(3, 5, 0, c='r',s=30**2, marker='+')                     #最優解
ax.plot_surface(w, b, np.array(loss).reshape(50, 50), cmap='rainbow',alpha=1)
ax.set_xlabel('w')
ax.set_ylabel('b')
ax.set_zlabel('Loss')
Text(0.5,0,'Loss')

在這裡插入圖片描述
在這裡插入圖片描述

梯度下降:

深度學習中給一個目標函式 L ( w ) L(w) ,我們的目標是找到令其最小化的一組引數 w w 。目前最常用的就是梯度下降了,找到一個方向,令當前的 w w 向該方向移動從而減小目標函式的值。這個方向就是梯度的負方向(具體證明)。所以 w w 的更新公式為: w t + 1 = w t η g t \large w^{t+1} = w^{t} - \eta g^t
其中 η \eta 為步長或者學習率, g t = L w g^t = \frac{\partial L}{\partial w} 即第t次迭代, L L w w 的梯度。
但上面這種更新引數的方式存在一個問題,就是 η \eta 如何取值,如果取值過大就很有可能跳過最優點,取值過小那麼更新的就會很慢。我們在更新引數的過程中,一開始離最優解比較遠,我們希望步伐邁的可以大一點,當離最優解越來越近的時候,我們希望步伐要小一點,因此有人就提出一種隨著迭代次數的增加步長逐漸減小的更新方式: w t + 1 = w t η t g t \large w^{t+1} = w^t - \eta^t g^t
其中 η t = η t + 1 \eta^t = \frac{\eta}{\sqrt{ t+1 }} , η \eta 為 步長或者學習率。

下面是計算引數w,b梯度以及視覺化更新過程程式碼。

# 計算w, b的梯度
def compute_grad(paramters, w, b):
    y_hat = paramters['y_hat']
    y =  paramters['y']
    x = paramters['x']
   
    grad_w = np.sum((y_hat - y) * x)
    grad_b = np.sum((y_hat - y))
    return grad_w, grad_b
# 視覺化每次引數的更新
def plot_history(w_history, b_history, title=None):
    plt.scatter(3., 5., c = 'r' ,s=50**2, marker='+')
    plt.scatter(w_history, b_history, s=20, marker='o')
    plt.plot(w_history, b_history)
    plt.xlabel('w')
    plt.ylabel('b')
    plt.title(title)
    fig = plt.figure()
    ax = Axes3D(fig)
    w = np.linspace(-5, 5)
    b = np.linspace(-5, 5)
    w, b = np.meshgrid(w, b)
    loss = []
    for w_i, b_i in zip(w.flatten(), b.flatten()):
        y_hat = w_i * x + b_i
        loss.append(np.mean((y_hat - y) ** 2) / 2)

    w_history, b_history = np.array(w_history),np.array(b_history)
    history = []
    for w_i, b_i in zip(w_history, b_history):
        y_hat = w_i * x + b_i
        history.append(np.mean((y_hat - y) ** 2) / 2)

    ax.scatter(3, 5, 0, 'k+',s=20**2)                     #最優解
    ax.plot(w_history, b_history, history, c='k',alpha=1)
    ax.scatter(w_history, b_history, history, c='k',s=10**2, marker='o')
    ax.plot_surface(w, b, np.array(loss).reshape(50, 50), cmap='rainbow',alpha=1)
    ax.set_xlabel('w')
    ax.set_ylabel('b')
    ax.set_zlabel('Loss')

Gradient Desent with leraning rate decay

iteration = 100000
w = b = 0
lr = 0.001
parameters = {}
parameters['y'] = y
parameters['x'] = x

w_history = [w]
b_history = [b]
loss_history = []
for t in range(iteration):
    y_hat = w * x + b
    parameters['y_hat'] = y_hat
    loss_t = np.mean((y_hat - y) ** 2) / 2             # mse loss
    grad_w, grad_b = compute_grad(parameters, w, b)
    w = w - lr / np.sqrt(t + 1) * grad_w
    b = b - lr / np.sqrt(t + 1) * grad_b
    w_history.append(w)
    b_history.append(b)
    loss_history.append(loss_t)
print("After iteration 1000000 w = ", w)
print("After iteration 1000000 b = ", b)
After iteration 1000000 w =  3.0124698201218907
After iteration 1000000 b =  4.838243650840835
plot_history(w_history, b_history, title='Gradient desent with learning rate decay')

在這裡插入圖片描述
在這裡插入圖片描述

上圖可以看出普通的梯度更新方式,在迭代了10萬次後才收斂到最優值(紅色十字),並且有震盪的現象。

上述引數更新方法,學習率隨著迭代次數的增加就會越來越小。但是這麼做還是有些不足,因為所有引數的學習率都是一樣的,這是很不科學的,因為對於L的不同維度而言。它距離最優點的距離是不一樣的,如下圖:

Image(filename='./1.png')

在這裡插入圖片描述
圖中在 (水平)方向上loss的變化明顯要比 (垂直)方向上的變化要明顯 。我們如果在更新 w 1 w_1 w 2 w_2 時使用相同的學習率顯然是不合適的。
我們從上面的的結果中,也可以很清楚的看出傳統梯度下降更新引數的缺點,引數w很快就收斂到了最優,但是引數b離最優值還很遠。造成這種現象就是因為兩個引數每次迭代步長都一樣。對於不同的引數我們希望能夠有屬於自己的學習率,因此提出了一種叫做Adagrad的更新方式。

更新公式:

w t + 1 = w t η i = 0 t ( g i ) 2 g t \large w^{t+1} = w^t - \frac{\eta}{\sqrt{\sum_{i=0}^{t}(g^i)^2}}g^t