如對博文有任何疑問,請留言。
1. 方向導數
方向導數:類比於函數的偏導數是函數沿座標軸方向的變化率,方向導數是函數沿某一射線方向的變化率。
定理:如果函數 在點 可微分,那麼函數在該點沿任一方向 的方向導數存在,且有
其中 和 是方向 的方向餘弦。
2. 梯度
梯度:梯度是一個向量(矢量),表示某一函數在該點處的方向導數沿着該方向取得最大值,即函數在該點處沿着該方向(梯度的方向)變化最快,變化率最大(爲梯度的模)。
另一種理解:在微積分裏,對多元函數的參數求偏導,把求得的各個參數的偏導以向量的形式寫出來,就是梯度。
定義:設二元函數 在平面區域D上具有一階連續偏導數,則對與於每一個點 P(x,y) 都可定出一個向量 ,該函數就稱爲函數 在點 P(x,y) 的梯度,記作 或 ,既有:
注意,梯度是在多元函數中的,如果要拓展到一元函數,則要這樣理解:它是一個標量,並且在某點的梯度等於這一點的導數。
3. 梯度下降算法數學推導
梯度下降算法針對的是最小優化問題(即求最小值問題),目的是使目標函數沿最快路徑下降到最小值。
通俗的解釋,是模擬下山,每次沿着當前位置最陡峭最易下山的方向前進一小步,然後繼續沿下一個位置最陡方向前進一小步。這樣一步一步走下去,一直走到覺得我們已經到了山腳。
雖然這樣很好理解,但這只是給不懂梯度下降算法的小白講的形象比喻,最終要落實到算法上代碼上,具體的過程是怎麼樣的呢?還得靠數學推導。
算法作用於損失函數(也稱目標函數、代價函數、誤差函數),是爲了找到使損失函數取最小值的權重()和偏置()。
設損失函數爲 ,要尋找最優的 和 ,爲便於計算,抽象出函數描述 ,這裏 ,只是描述形式不同。同時設 , 爲轉置符號,此時 是一個二維列向量。
則損失函數爲 ,將其進行一階泰勒展開,得:
爲什麼要一階泰勒展開呢,因爲這樣可以“以直代曲”,數學術語叫“局部線性近似”,就是在很小的區間內,直線與曲線近似重合,對曲線不易做的計算,可以對直線計算作爲代替。
就是這個很小的區間,可以表示爲 ,但它仍然是一個矢量,將其分解爲模和單位向量的形式,即長度和方向的形式:
這裏的 就是前面下山比喻中每次走的一小步的距離(步長), 就是走的方向,這裏注意 是距離(長度),所以 > 0。
則損失函數 的一階泰勒展開式可描述爲:
其中 是現在的損失函數的值, 是即將要更新的損失函數的值,前面說過我們的目的是爲了找到使損失函數取最小值的權重()和偏置(),所以我們每次更新要保證 ,即:
又因爲 > 0,所以有:
這裏注意, 是一個向量, 是函數 在點 處的梯度,它也是一個向量。要兩個向量的乘積小於0,則需要他們的夾角大於90°。再次想到我們的目的,使目標函數沿 最快 路徑下降到最小值,既然要最快, 與 $ f(\theta_0)$ 的距離(即 )就要越大越好,根據公式 ,就是結果越小越好(因爲結果是負值)。再回到 和 兩個向量的乘積上,就是他們的夾角爲180°時, 的結果最小(這裏也是爲什麼要沿着梯度反方向更新自變量的原因),此時結果爲:
則 描述爲:
帶入 得:
因爲 和 都是標量,所以可以設 ,則上式(梯度下降算法中 的更新表達式)可描述爲:
這裏的 就是我們常說的學習速率。
另外,這個公式還有一種比較專業的描述方法:
這個公式就是最終往代碼裏寫的形式,只不過要結合你的 (損失函數)的具體形式進一步計算偏導,再落實到代碼。
實際使用該算法時,有時將公式寫成分量的形式,即將 代入上式得:
即:
它們的意思是一樣的,只是表示方法不同。但分量形式不常用,多用向量形式。
以上是梯度下降算法自變量更新的數學推導,那麼什麼時候停止更新呢,你肯定會說當然是找到使損失函數取最小值的權重()和偏置()時,沒錯這也是我們前面算法目的中的描述。但是怎樣知道找到了損失函數的最小值呢,實際應用中不會真的一直迭代到損失函數的最小值,而是在精度和訓練時間都可接受的範圍內,儘可能的接近最小值,在資源消耗和精度要求間權衡。具體結束條件通常爲:
- 的值足夠小。(也可以說是損失函數不再明顯的減小,但同時也要兼顧損失函數的值,否則就要檢查初始參數和訓練數據等),實際編程時,考慮到程序性能,不一定以直接判斷損失函數的值爲依據,也可以間接判斷(比如誤差值)。
- 迭代次數達到預定值。
這裏講的是梯度下降算法的核心思想,最後實際應用還要落實到具體算法,梯度下降算法家族包括批量梯度下降法(Batch Gradient Descent,BGD,也叫最速梯度下降法)、隨機梯度下降法(Stochastic Gradient Descent,SGD)、小批量梯度下降法(Mini-Batch Gradient Descent,MBGD)三種。它們的算法原理相同,只是在輸入數據時採取不同的策略。
4. 批量梯度下降法代碼實現
4.1 選擇損失函數與模型
損失函數選擇均方差損失函數(MSE),其表達式爲:
其中:
— — 預測函數,
— — 分別是訓練數據的輸入值與標籤值,
— — 是 和 組成的向量,
— — 是訓練數據個數 。
預測模型(函數)選擇線性迴歸模型,表達式爲:
其中:
— — 是預測輸入數據點,
— — 是學習得到的權重()和偏置(b)。
— — 是輸入數據的維數。
4.2 從公式到代碼
首先確定目的,是爲了找到使損失函數取最小值的權重()和偏置()。我們找到了嗎?我們找到了。他們的計算式就是公式 ,所以我們的代碼核心部分就是實現公式 ,對於這個公式,重要的部分是 ,損失函數對 的偏導數,我們的損失函數已由公式 給出,即 ,公式 對 求偏導得:
將公式 代入公式 得:
其中 是預測函數, 代表當前值, 是下一次的更新值, 是訓練數據的輸入值與標籤值, 是學習率,由於 是常數, 是標量,可以將 併入 ,則實際上的 爲 ,可見學習率可以表達梯度下降迭代步長的變化,實際應用中常常人爲賦值或使用特定策略賦值,而不是使用原公式。
至此,公式 可描述爲:
這是最後寫入程序的公式。
爲了編程方便,將公式分解,令:
關於 gradient 的計算,這裏說明一下,梯度下降算法家族的三個算法的不同之處就在這裏。
批量梯度下降法(BGD):每次迭代計算梯度,使用整個數據集(),也就是每次計算 gradient 都用上所有數據點,然後求均值。
隨機梯度下降法(SGD):每次迭代計算梯度,從整個數據集中隨機選取一個數據點()。
小批量梯度下降法(MBGD):每次迭代計算梯度,從整個數據集中選取一個小批量數據()。
以下根據公式 編寫代碼,實現批量梯度下降法:
def batchGradientDescent(x, y, theta, alpha, m, maxInteration):
'''批梯度下降算法簡單實現
x: 輸入
y: 輸出
theta: w 和 b 組成的向量
alpha: 學習率
m: 批數據數量
maxInteration:最大迭代次數
'''
x_train = x.transpose() # 轉置
for i in range(0, maxInteration):
# 預測值
hypothesis = np.dot(x, theta)
# 預測誤差
error = hypothesis - y
# 下降梯度
gradient = np.dot(x_train, error) / m
# 更新theta
theta = theta - alpha * gradient
return theta
同理,隨機梯度下降代碼爲:
def stochasticGradientDescent(x, y, theta, alpha, maxInteration):
'''批梯度下降算法簡單實現
x: 輸入
y: 輸出
theta: w 和 b 組成的向量
alpha: 學習率
m: 批數據數量
maxInteration:最大迭代次數
'''
data = []
for i in range(4):
data.append(i)
for i in range(0, maxInteration):
hypothesis = np.dot(x, theta)
# 預測誤差
error = hypothesis - y
# 選取一個隨機數
index = random.sample(data, 1) # 從列表data中隨機選取一個數據
index1 = index[0]
# 下降梯度
gradient = error[index1] * x[index1]
# 求導之後得到theta
theta = theta - alpha * gradient
return theta
小批量梯度下降:
def miniBatchGradientDescent(x, y, theta, alpha, m, batch_size, epochs):
'''
x: 輸入
y: 輸出
theta: w 和 b 組成的向量
alpha: 學習率
m: 數據集的數據量
batch_size:一個批次的數據量
epochs:數據集最大迭代次數
'''
for epoch in range(epochs):
# 生成索引列表
indexs_list = np.arange(m)
# 按批次迭代
for batch in range(m // batch_size):
# 生成批次數據索引
index_list = indexs_list[batch*batch_size : batch*batch_size+batch_size]
# 獲取批次數據
x_batch = x[index_list]
y_batch = y[index_list]
# 預測值
hypothesis = np.dot(x_batch, theta)
# 預測誤差
error = hypothesis - y_batch
# 下降梯度
gradient = np.dot(x_batch.T, error) / m
# 更新theta
theta = theta - alpha * gradient
return theta
1.5 總結及其他的一些說明
梯度下降運行步驟:
-
用隨機值初始化權重和偏差
-
把輸入傳入網絡,得到輸出值(預測值)
-
計算預測值和真實值(標籤值)之間的誤差
-
對每一個產生誤差的神經元,調整相應的(權重和偏差)值以減小誤差
-
重複迭代,直至得到網絡權重和偏差的最佳值
批量梯度下降法(BGD):每次迭代計算梯度,使用整個數據集。每次更新都會朝着正確的方向進行,最後能夠保證收斂於極值點,凸函數收斂於全局極值點,非凸函數可能會收斂於局部極值點,缺陷就是學習時間太長,消耗大量內存。
隨機梯度下降法(SGD):每次迭代計算梯度,從整個數據集中隨機選取一個數據,所以每次迭代的時間非常快。但收斂時震盪,不穩定,在最優解附近波動,難以判斷是否已經收斂。
小批量梯度下降法(MBGD):這個是 BGD 和 SGD 的折中方法, BGD 每次使用整體數據,收斂太慢, SGD 每次只使用一條數據,雖然收斂快但震盪厲害,所以出現了折中的 MBGD,每次使用 n 條數據,如果 n(batch size) 選擇的合適,不僅收斂速度比SGD更快、更穩定,而且在最優解附近的震盪也不會很大,甚至得到比 BGD 更好的解。
batch size 的選擇,一般取2的冪次時能充分利用矩陣運算操作,因此可以在2的冪次中挑選最優取值。例如16、32、64、128、256等等。
另外,還有一種小批量隨機梯度下降法,即在小批量梯度下降法中,獲取批次數據時,不是按原有輸入順序選取數據,而是先把原有輸入數據打亂,再選取批次數據,代碼如下:
def mini_batch_stochastic_gradient_descent(x, y, theta, alpha, m, batch_size, epochs):
'''
x: 輸入
y: 輸出
theta: w 和 b 組成的向量
alpha: 學習率
m: 數據集的數據量
batch_size:一個批次的數據量
epochs:數據集最大迭代次數
'''
for epoch in range(epochs):
# 生成索引列表
data_index = np.arange(m)
# 打亂樣本順序
np.random.shuffle(data_index)
# 按批次迭代
for batch in range(m // batch_size):
# 生成批次數據索引
batch_index = data_index[batch*batch_size : batch*batch_size+batch_size]
# 獲取批次數據
x_batch = x[batch_index]
y_batch = y[batch_index]
# 預測值
hypothesis = np.dot(x_batch, theta)
# 預測誤差
error = hypothesis - y_batch
# 下降梯度
gradient = np.dot(x_batch.T, error) / m
# 更新theta
theta = theta - alpha * gradient
return theta