目錄

Deep Learning 基本功:Gradient Descent 介紹

在〈Deep Learning 原理:Neural Network 如何分類圖像〉一文中,我們以「手寫數字圖像」的分類問題為例,站在 Neural Network 的角度感受它如何理解一張圖像。接著我們也知道,Neural Network 要靠以下 3 個元素來調整內部的參數 (weight 與 bias),讓輸出愈來愈接近正確答案:

  • 訓練資料集 (Training Dataset)
  • 損失函數 (Cost Function)
  • 最佳化演算法 (Optimizer)

前兩個元素在Deep Learning 基本功:認識 MNIST 資料集與損失函數一文中已經介紹過,本文要談的是第三個元素:最佳化演算法 (Optimizer),而且是其中最基本、也最重要的一種——Gradient Descent。讀完本文,你會知道 Gradient Descent 到底在做什麼、它背後的數學長什麼樣子,以及 Learning Rate 在裡面扮演什麼角色。

重點整理 (TL;DR)
  • 訓練 Neural Network 的本質:調整參數 (w 與 b),使得 Cost Function 的數值變小。
  • Gradient Descent 的比喻:我們是一顆停在山壁上的球,看不到谷底,只能感受附近哪個方向是下坡,然後往那走一步、再走一步。
  • C \nabla C (Gradient Vector) 決定每個參數的「移動方向」,η \eta (Learning Rate) 決定「移動的大小」,兩者缺一不可。
  • Learning Rate 是 Hyperparameter:太大會跨過頭讓 Cost 反而變大,太小則要走非常多步才會到谷底。

在進入演算法之前,先把「訓練」這件事講清楚。訓練 Neural Network,說白了就是把網路裡的參數 (weight 與 bias) 調整到一組好的數值,讓網路的輸出愈接近正確答案愈好。

那「好」與「不好」怎麼衡量?靠的就是 Cost Function。

Cost Function C(w, b) 的數學算式,用來衡量目前這組 weight 與 bias 的好壞。
Cost Function 的算式

Cost Function 會把「目前這組參數表現得如何」濃縮成一個數字:值愈小,代表網路的輸出愈接近正確答案。所以訓練 Neural Network 的過程,其實就是調整 w (weight) 與 b (bias),使得 C (Cost Function) 的值最小。

一句話總結:訓練 Neural Network ⇒ 調整參數 (w 與 b),使得 Cost Function 變小

問題來了,該怎麼調整 w 與 b,才能讓 Cost Function 愈來愈小?這就要靠 Gradient Descent 這個演算法。

為了把概念講清楚,我們先把問題簡化。暫時忘掉 Neural Network、Cost Function、weight 與 bias 這些名詞。

現在手上只有一個函數 C(v) C(v) ,其中 v=v1,v2, v = v_1, v_2, \dots ,表示 C 這個函數可以接受任意數量的參數。目標很單純:透過 Gradient Descent 調整每一個 v,讓 C(v) C(v) 的值愈來愈小。

為了方便視覺化,我們假設 C 只接受兩個參數 v1 v_1 v2 v_2 ,也就是 C(v1,v2) C(v_1, v_2) 。輸入不同的 v1 v_1 v2 v_2 會得到不同的數值,如果把所有 (v1,v2)C(v1,v2) (v_1, v_2) \Rightarrow C(v_1, v_2) 的情況都畫出來,這些點會在 3 維空間中形成一個面:

C(v1, v2) 在三維空間中形成的曲面,起伏像一座山谷,谷底即為函數的最小值所在。
C(v1, v2) 在三維空間中形成的面 [source: Neural Networks and Deep Learning]

單看上面這張圖,你大概一眼就能看出最小值在哪裡。但實務上一個 Neural Network 動輒好幾千萬個參數,不可能這樣用眼睛看出來。這正是 Gradient Descent 派上用場的地方。

在講數學之前,先換個更貼近真實世界的角度:把 C(v) C(v) 函數想成一座「山谷」,而我們自己是山谷上的一顆「球」。球會怎麼移動?當然是沿著山壁往下滾,直到滾進谷底才停下來。

地勢起伏錯綜複雜的山谷地形示意圖,有多處高低起伏,並以箭頭標示出一條往谷底下降的路徑。
在一座地勢錯綜複雜的山谷上,我們是一顆球 [source: sciencesprings.wordpress.com]

這裡的眉角是:我們是一顆停在山壁上的球,而這座山谷地勢錯綜複雜(如上圖),我們看不到谷底在哪裡,只知道自己「目前所在位置」附近的地勢怎麼變化。

既然如此,要抵達谷底就只能靠一個土法煉鋼的辦法:感受一下四面八方哪個方向是「下坡」,朝那個方向走一步;到了新位置後,再感受一次哪個方向是下坡,再走一步……如此反覆,直到四面八方都是平地、找不到下坡方向為止。那時我們就在谷底了。

有了「山谷與球」的比喻,接著把數學補上。這裡不會深入到數學系的程度,只需要一點偏微分的概念就夠了。

回到剛剛的 C(v)=C(v1,v2) C(v) = C(v_1, v_2) 。球的起始位置落在哪裡,取決於 v1 v_1 v2 v_2 的初始數值。因為我們把自己當成球,看不到整座山谷的全貌,也就看不到 C(v1,v2) C(v_1, v_2) 的最低點在哪,只能看到所處位置的狀態。

假設我們已經決定要往某個方向前進(用向量 Δv \Delta v 表示),可以把這一步拆成先朝 v1 v_1 方向移動 Δv1 \Delta v_1 、再朝 v2 v_2 方向移動 Δv2 \Delta v_2 ,也就是 Δv(Δv1,Δv2)T \Delta v \equiv ( \Delta v_1, \Delta v_2 )^T (T 是 Transpose 的意思)。

Δv \Delta v 走一步之後,C(v1,v2) C(v_1, v_2) 的變化量稱為 ΔC \Delta C ,可以寫成下面這個算式:

ΔC 的算式:C 對 v1 的偏微分乘上 Δv1,加上 C 對 v2 的偏微分乘上 Δv2。
往 Δv 前進一步,對 ΔC 造成的影響

偏微分的概念在Perceptron 的改良版:了解什麼是 Sigmoid Neuron一文中介紹過,這裡可以簡單把它視為:一個多變數函數對於某一個獨立變數的「改變率」。舉例來說,當 v1 v_1 加上 5 時 (Δv1=5 \Delta v_1 = 5 ),對 C 的影響不一定就是加上 5 (ΔC \Delta C 不一定等於 5),而是要再乘上 C 對 v1 v_1 的改變率(v1 v_1 變動 1 時,C 變動多少)。

接著,我們把 ΔC \Delta C 算式中偏微分的部分抽出來,給它一個新符號 C \nabla C ,稱為「Gradient of C」:

∇C 的定義:由 C 對各個參數的偏微分所組成的向量。
將偏微分部分取出,並給他一個新的定義

第一次看到 \nabla 這個符號不用緊張。 \nabla 通常用來表示 Gradient Vector,而 Gradient Vector 也不是什麼了不起的東西,就是一個向量,裡頭每一個元素都是一個偏微分(函數對某個特定參數的微分)。

有了 Δv \Delta v C \nabla C ,原來的 ΔC \Delta C 算式就可以改寫成:

ΔC 約等於 ∇C 與 Δv 相乘的算式。
將 ΔC 改寫

從這個算式可以看得更清楚:ΔC \Delta C (函數的變化量)取決於 Δv \Delta v (每一個參數的變動量)與 C \nabla C (每一個參數變動一單位時對 C 造成的影響)兩者相乘。

別忘了我們的目的。我們是一顆球,希望移動 Δv \Delta v 之後 C(v1,v2) C(v_1, v_2) 的數值變小,也就是 ΔC0 \Delta C \leq 0 。那要怎麼挑 Δv \Delta v ?只要設定:

Δv=ηC \Delta v = -\eta \nabla C

代入後會得到 ΔCηCC=ηC2 \Delta C \approx -\eta \nabla C \cdot \nabla C = -\eta \|\nabla C\|^2 。因為 C2 \|\nabla C\|^2 一定大於等於 0,所以 ΔC \Delta C 一定小於等於 0——這一步保證不會走上坡。

到這裡,我們已經知道每一步該怎麼走了。當球目前位於 v,要移動到新位置 v’:

參數更新規則:新位置 v’ 等於舊位置 v 減去 η 乘上 ∇C。
我們知道每一步應該怎麼走

依照這個規則,每要移動一步就重新計算一次 C \nabla C (Gradient Vector),每移動一次 C(v1,v2) C(v_1, v_2) 就變小一點。一步一步走下去,直到抵達谷底,也就是 C(v1,v2) C(v_1, v_2) 的最小值。這整個過程就叫做 Gradient Descent。

上面的算式裡還有一個符號沒交代:η \eta η \eta 在機器學習中代表 Learning Rate。

要理解 Learning Rate 的意義,先回頭看 C \nabla C (也就是前面提到的 Gradient Vector)在做什麼。C \nabla C 裡記錄的是函數對各參數的偏微分;在 C 只有一個參數 v 的情況下,其實就是在算切線斜率,而斜率告訴我們每個參數該往哪個方向移動。如果 C 對 v 的偏微分是「負值」,代表 v 變大時 C 會「減少」;如果偏微分是「正值」,代表 v 變大時 C 會「增加」。

所以分工很清楚:C \nabla C 決定每一個參數的「移動方向」,η \eta (Learning Rate) 決定「移動的大小」。

η \eta 在 Deep Learning 中屬於一種超參數 (Hyperparameter),意思是它必須由我們自己設定、自己調整,沒辦法透過 Neural Network 的學習過程自動調出來。

而設定 Learning Rate 的數值也是一門學問,兩個方向都會出問題:

  • Learning Rate 太大:v 一次改變太多,某些情況下反而會讓 ΔC \Delta C 大於 0,C 函數的數值愈跑愈大。用山谷的比喻來說,就是一步跨太大,直接從這側山壁跨到對面山壁上去了。
  • Learning Rate 太小:v 每次只改變一點點,雖然方向沒錯,但要花非常多步、非常多時間才走得到最小值。

前面都是用兩個變數的 C(v1,v2) C(v_1, v_2) 在說明。實際的 Neural Network 當然沒這麼單純,但好消息是:如果上面的內容你都懂了,那 Gradient Descent 如何應用在 Deep Learning 上,你其實也已經懂了。

差別只在於變數變多:兩個變數的 Cost Function C(v1,v2) C(v_1, v_2) 換成多個變數的 C(v1,v2,v3,v4,) C(v_1, v_2, v_3, v_4, \dots) 。當我們移動 Δv \Delta v 後,一樣會產生 ΔC \Delta C 的改變,兩者的關係也跟前面完全相同:

多變數情況下 ΔC 與 Δv 的關係算式,形式與兩個變數時相同。
在多變數的情況下,ΔC 與 Δv 的關係仍舊相同

只不過現在的 Δv \Delta v 包含了更多元素:Δv(Δv1,Δv2,Δv3,Δv4,)T \Delta v \equiv ( \Delta v_1, \Delta v_2, \Delta v_3, \Delta v_4, \dots )^T C \nabla C 也同樣從兩個元素變成了多個:

多變數情況下的 ∇C,向量中包含 Cost Function 對每一個變數的偏微分。
Gradient Vector 中包含了 Cost Function 對所有變數的偏微分

在兩個變數的例子中,我們每一步都選 Δv=ηC \Delta v = -\eta \nabla C ;在多個變數的情況下,一樣這樣選,也一樣用前面那條規則更新 v,讓 Cost Function 的數值愈來愈小。整套邏輯完全沒變,只是向量變長了而已。

本文介紹了 Gradient Descent 的觀念。說穿了,Gradient Descent 就是透過計算一個函數的 Gradient Vector,得知參數的更新方向,讓函數的數值愈來愈小

我們也談到 Learning Rate:它是一種必須事先設定好的超參數 (Hyperparameter)。計算 Gradient 讓我們掌握參數的「更新方向」,Learning Rate 則讓我們決定參數的「更新大小」,兩者缺一不可。

理解了 Gradient Descent 之後,下一篇文章會接著說明它在實務上的變形:Stochastic Gradient Descent