2017年4月24日 星期一

繼續對TensorFlow的Gradient Descent做逆向工程

上一篇,TensorFlow & Gradient Descent,我用一個簡單的例子
y = w * x

解釋了TensorFlow的Gradient Descent的運作原理,並且連數學公式都推導出來了。

本篇,承自上一篇,討探Gradient Descent如何處理更通用的直線方程式:
y = w * x + b

本篇標題,本來想取名為「TensorFlow & Gradient Descent Part 2」,後來決定把標題改成「…逆向工程」,這是因為後來發現,我做的事情,其實就是在對TensorFlow做逆向工程(Reverse Engineering)。當然,TensorFlow是Open Source,直接看程式碼不就好了,有必要做逆向工程嗎?原因是:樂趣。看程式碼比較無趣,我比較喜歡用數學的角度去推敲程式背後的原理,也順便加強一下自己數學的功力。

我喜歡畫一些圖,所以先將問題表達成下面這張x-y平面圖:



圖畫好了之後,開始用數學式子描述我們的問題:


我們希望調整w和b,直到找到最小的E為止。

下面的Python程式,呼叫TensorFlow的Gradient Descent,解這個問題:

import tensorflow as tf

w = tf.Variable ([0.3], tf.float32)
b = tf.Variable ([-0.3], tf.float32)
x = tf.placeholder (tf.float32)
y = w * x + b

t = tf.placeholder (tf.float32)
squared_deltas = tf.square (y-t)
E = tf.reduce_sum (squared_deltas)

optimizer = tf.train.GradientDescentOptimizer (0.01)
train = optimizer.minimize (E)

sess = tf.Session ()
init = tf.global_variables_initializer()
sess.run (init)

[[w_value], [b_value]] = sess.run ([w, b])
print ("w = %s, b = %s" % (w_value, b_value))

x_train = [1, 4]
t_train = [0, -3]

for i in range (10):
    sess.run (train, {x: x_train, t: t_train})
    [[w_value], [b_value]] = sess.run ([w, b])
    print ("w = %s, b = %s" % (w_value, b_value))

結果如下:


然後,根據這個結果,開始做逆向工程。先將此問題,轉換成一個Neural Node:


和上一篇不同的是,多一個b要考慮。這次,我們要根據E的值,調整w和b。

程序員在處理複雜問題,常採用各個擊破法。於以我們將此問題「根據E的值,調整w和b」拆解成兩個問題:「根據E的值,調整w」和「根據E的值,調整b」

問題一:根據E的值,調整w

和上一篇類似。調整w,找到最小的E為止。所以對E做w的微分:



求調整後的w,下面不解釋:
△w = -2 * (-3-0.9) * 4 = 31.2
△w = -L * △w
△w = -0.01 * 31.2 = -0.312
w = w + △w = 0.3 - 0.312 = -0.012 

問題二:根據E的值,調整b

調整b,找到最小的E為止。所以對E做b的微分:


求調整後的b,下面不解釋:
△b = -2 * (-3-0.9) * 1 = 7.8
△b = -L * △b
△b = -0.01 * 7.8 = -0.078
b = b + △b = -0.3 - 0.078 = -0.378 

結論

我們對E分別做w和b的微分,分別求得△w和△b的變化量,然後根據變化量,調整w和b,使得E的值變小。這就是在跑這支 TensorFlow Python 程式時,所看到第二行 w = -0.012, b = -0.378 的由來。

-Count


2017年4月23日 星期日

TensorFlow & Gradient Descent

TensorFlow的官網,有教我們Gradient Descent的用法。
https://www.tensorflow.org/get_started/get_started

Gradient Descent的好處是,用比暴力破解更好的方法,去猜w和b的值。暴力破解法,請參考:
TensorFlow & Linear Regression

本文探討TensorFlow採用Gradient Descent背後的原理。當然,首先,我們簡化官網的問題。簡化成下圖:


原來官網的問題:
y = w*x + b

簡化後的問題:
y = w*x

而且只考慮兩組(x, y, t):(0, 0, 0)和(3, 0.9, -3)。

用數學式子描述我們的問題:


我們希望調整w,直到找到最小的E為止。

將上面的數學式子,寫成下面的Python程式,並且使用TensorFlow的Gradient Descent,去解這個問題:

import tensorflow as tf

w = tf.Variable ([0.3], tf.float32)
x = tf.placeholder (tf.float32)
y = w * x

sess = tf.Session ()
init = tf.global_variables_initializer()
sess.run (init)

t = tf.placeholder (tf.float32)
squared_deltas = tf.square (y-t)
E = tf.reduce_sum (squared_deltas)

optimizer = tf.train.GradientDescentOptimizer (0.01)
train = optimizer.minimize (E)

x_train = [0, 3]
t_train = [0, -3]

[[w_value]] = sess.run ([w])
print ("w = %s" % w_value)

sess.run (init)
for i in range (10):
    sess.run (train, {x: x_train, t: t_train})
    [[w_value]] = sess.run ([w])
    print ("w = %s" % w_value)

結果如下:


問題是,到底TensorFlow的Gradient Descent做了什麼事情?

我們將這個問題,轉換成一個Neural Node,比較好理解:


這個問題,和上一篇類似,如何計算Neural Network的Error Function的斜率?。但更簡單,因為沒有sigmoid function。

比較上一篇:
y = sigmoid (w * x)

本篇:
y = w * x

回到我們的問題:調整w,找到最小的E為止。所以對E做w的微分:



這個公式,比上一篇簡單。我們用它來計算:
△w = -2 * (-3-0.9) * 3 = 23.4

還記得Learning Rate嗎?
Training A Simple Classifier - Learning Rate
Learning Rate在Neural Network的作用

我們不希望△w

  1. 走錯方向
  2. 衝過頭
所以要調整它。負號是給它正確的方向,L=0.01是叫它慢慢學習:
△w = -L * △w

(各位可以試者把L=0.5,會發現它永遠學不會)

於是:
△w = -0.01 * 23.4 = -0.234

調整後的w:
w = w + △w = 0.3 - 0.234 = 0.066

這就是我們在跑TensorFlow Python這支程式時,所看到的第二行 w = 0.066 的由來。

本篇,理論結合實作,希望能幫助各位了解Gradient Descent的原理。

-Count

TensorFlow & Linear Regression

TensorFlow最近很紅。是Google力推的人工智慧架構。缺點是無法在Windows下安裝,但這不成為拋棄它的理由。據我所知,類似框架很多種,選擇TensorFlow的好處是:

  1. Google的產品,業界普及率高
  2. 支持Python
  3. 支持CUDA 3.0

我最近將它安裝TensorFlow在HP Gen8。不過這台沒有NVIDIA顯卡,無法發揮CUDA的效能,但還是可以用。

TensorFlow安裝完成後,官網會教你寫一個Hello TensorFlow的Python程式,用來驗證是否安裝成功。個人覺得,應該要進一步學會寫一支TensorFlow Python程式,了解Machine Learning的最簡單的原理,這樣我們就很容易地跨過這一道心理門檻。

這支簡單的程式,就是Linear Regression。

這支程式官網有說明。
https://www.tensorflow.org/get_started/get_started

其實大家去看官網就能了解TensorFlow的使用了。個人習慣是自己動手作,而且喜歡將範例程式的變數名稱改一下,讓它們和之前寫的文章裡的符號一致,如此一來,前後對照容易,會有一種觸類旁通的感覺。

現在開始介紹這個範例程式:將TensorFlow用在Linear Regression上。這裡,我們採用的是暴力破解的方式。

問題描述如下圖,座標上有4個紅點,分別是
(1, 0), (2, -1), (3, -2), (4, -3)


要找一條線,可以代表這4個點。這就是Linear Regression問題。當然,大家很容易找到這條線,這是因為剛好這4個點是在同一條線上。實務上,我們面對的是,4個點不見得會在同一條線上,要如何畫出適當的線出來呢?

這條線是
y = w * x + b

一開始,我們指定
w = 0.3
b = -0.3

於是
y = 0.3 * x - 0.3

輸入
x = [1, 2, 3, 4]

輸出
y = [0, 0.3, 0.6, 0.9]

我們的目標(Target)是輸出
t = [0, -1, -2, -3]

如何得知y和t的差距(Error)?定義Error Function:
E = Sum ((t-y)^2)
E = 0^2 + 1.3^2 + 2.6^2 + 3.9^2 = 23.66

目標是,調整w和b的值,使得E為最小,這就是Linear Regression。

最直覺的方法,是暴力破解,就是去試每個可能的值。這有點像是破解密碼。講到這裡,我感覺Machine Learning和Cryptography有類似之處,將來深入了解後,再為各位道來。

暴力破解,就是嘗式w和b的各種組合。理論上,組合是無線多個。實務上,我們要去找或去猜,可以在我們計算能力範圍之內的,w-b組合。
w = [-0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4]
b = [-0.4, -0.3, -0.2, -0.1, 0, 0.1, 0.2, 0.3, 0.4]

這樣看來,w-b組合就有9 * 9 = 81,我們可以一個一個去試。

若運氣不好,無法在81種組合裡找到答案,這時候,就要根據之前的結果,去推論下一個w-b組合該是什麼樣子。這種推論的過程,我認為就是Machine Learning的演算法。

數學稍為好一點的人,會把這個問題,再做一個簡化。就是看看能否把w和b,削去其中一個,如此一來,就只要試9個就好了,如下圖。


國中數學教過的y = w * x + b,如果線是通過圓點,則b = 0,於是y = w * x。這樣一來,我們只要考慮w就好了。從y = w * x + b轉換成y = wx,就是座標轉換,亦即線性代數的Linear Transformation。

這就是所謂的降低問題的維度,我相信此類方法常用在Machine Learning上。不然面對10幾個以上的變數,就算有再好的公式,再快的電腦,答案可能在有生之年也求不到。

不過,本篇的重點是,暴力破解,而且用TensorFlow。我們開始Coding吧。

我們把上面的數學式子,集中起來,寫在一起。最後一行算出E的值,然後不斷調整w和b,直到找到最小的E。我們誇稱為Machine Learning的暴力演算法,用數學式子表示。


先猜出一條線

y = w * x + b
w = 0.3
b = -0.3

Python:

import tensorflow as tf
w = tf.Variable ([0.3], tf.float32)
b = tf.Variable ([-0.3], tf.float32)
x = tf.placeholder (tf.float32)

y = w * x + b


sess = tf.Session()
init = tf.global_variables_initializer()
sess.run (init) 

輸入
x = [1, 2, 3, 4]
得到
y = [0, 0.3, 0.6, 0.9]

Python:

res = sess.run (y, {x:[1, 2, 3, 4]})
print (res)

[ 0.          0.30000001  0.60000002  0.90000004]

輸入
t = [0, -1 -2, -3]
計算 
Error = Sum ((t-y)^2) = 23.66

Python:


t = tf.placeholder (tf.float32)
squared_deltas = tf.square (y - t)
E = tf.reduce_sum (squared_deltas)

res = sess.run (E, {x:[1, 2, 3, 4], t:[0, -1, -2, -3]})
print (res)
23.66

我們運氣很好,挑選
w = -1
b = 1
剛好
Error = 0

Python:


w1 = tf.assign (w, [-1])
b1 = tf.assign (b, [1])
sess.run ([w1, b1])
res = sess.run (E, {x:[1, 2, 3, 4], t:[0, -1, -2, -3]})
print (res)
0.0 

我想,這是一個,除了Hello TensorFlow之外,最簡單的一個TensorFlow的例子吧!

-Count

2017年4月18日 星期二

如何計算Neural Network的Error Function的斜率?

上一篇,我們解釋了,為何Neural Network要用到微積分,的理由。本篇探討,如何求出Error Function的斜率。

下面是Error Function斜率公式,出自於「Make Your Own Neural Network」這本書。對於非數學科班出身的我們程序員來說,有看沒有懂。所以我習慣將先將問題簡化,再去了解公式怎麼來。如何簡化?


這個公式,是表達3-Layer Neural Network的中間層的所有Node的Error Function的斜率公式,是不是有點繞口?我不管3-Layer,把問題簡化成最簡單的問題,就是在只有一個Node的情況下,求Error Function的斜率公式。此問題以下圖表示


Error Function的斜率,依weight變化而變化:


各個擊破。先處理左邊紅框的,不解釋了:


再處理右邊綠框的,這有點麻煩。


我用S代表sigmoid function。目的是求S (w*x)的斜率公式。S(x)的斜率公式,好辦。至於為何會長這樣子,我也不知道,大家可以自己想想看看。


可是S(w*x)的斜率公式呢?用微分的鏈式法則求得。鏈式法則,請參考「数学指南-实用数学手册」。推薦買一本,當作數學字典來用。


最後,把紅框和綠框合併起來,就成為Error Function的斜率公式了。通常會將2去掉,因為最後會結合Learning Rate使用,請參考前篇,Learning Rate在Neural Network的作用


為何要費那麼大的勁求Error Function的斜率公式?這樣一來,我們的程式,就可以套用這個簡單的公式去找Error的最小值。如何找?也請參考前篇,Learning Rate在Neural Network的作用

為何此斜率公式看起來那麼簡單?這是因為我們採用sigmoid function的關係。大家知道
y = e^x,微分後,還是e^x。

這就是為何數學家喜歡用e來組合各式各樣的function,就是為了方便。數學家的思維,在愉懶這一方面,是不是我們程序員一樣呢?

請大家把我推導的斜率公式,和本篇第一行從書裡抄來的斜率公式,好好做一個比較,會發掘許多有趣的東西出來。

-Count

2017年4月17日 星期一

為何Neural Network要用到微積分?

為何Neural Network要用到微積分?這要從上一篇的分類問題,Training Simple Classifier,講起。上一篇的分類問題,我們以x-y軸的平面座標來解釋,x代表width,y代表length。於是,我們用國中數學的知識,就足以了解分類問題的原理。


現在,我把分類問題,從 x-y平面座標,轉換成最簡單的Neural Network問題,就是一個Neural Node。

也就是說,當我們將分類問題描述成x-y平面座標時,我們關注的是從原點出來的直線,怎樣畫比較洽當。

現在我們把分類問題描述成一個Neural Node,我們關注的是,如何根據Output,調整Weight,讓下一次Output的誤差Error為最小。定義Error Function為,E = t - y。

用國中數學就可以解決簡單的分類問題了,何為要將此問題轉成Neural Network?這樣是否把問題複雜化?理由是:

  1. 透過最簡單的例子,理解Neural Network的基本原理,為何要用微積分的原因
  2. Neural Network模型可以解決更多的問題,包含分類問題。通用性,是我們程序員的最愛
現在,從一個Neural Node的角度來看待分類問題。我們關注的是計算下一個Weight值,讓Error最小。


x是Input,y是Output。Neural Node只是做簡單的y = w * x 運算。在x值固定為x1,w值設為w1的情況下,我們得到y1值。

y1 = w1 * x1

發現y1值和我們預期的t有一段差距

E1 = t - y1。

然後我們根據E1的值,修改w1為w2,使得

y2 = x1 * w2 = t2,這時候的E2 = 0

因為E和w會變動,我們把它們之間的關係 ,畫成平面座標如下。


那麼,這和微積分有什麼關係呢?回答這個問題之前,我們要先看看Error的定義是否恰當。對於只有一個Neural Node而言,E = t - y,這是可以接受的。但對於兩個Neural Node而言,這樣定義就有問題了。


E = (t1 - y1) + (t2 - y2)

若t2 - y2 < 0,為負數,就有可能和t1 - y1 > 0為正數抵消。導致E = 0,誤判,以為找到最佳的weight。但就兩個各別的Node的來說,Output的離期望還是很遠。所以E = t - y,不是一個恰當的Error Function。那麼,什麼才是恰當的Error Function呢?

E = | t - y |

如此一來,E-w 曲線就會長這樣子。快要接近問題的核心了。


因為我們的Node進行的是線性運算,線性的意思是,Input和Output可以用直線方程式表達,即

y = ax + b

但,實際上,Neural Network是模仿生物神經細胞,神經細胞的運算,幾乎是非線性的。數學家用sigmoid function,來模擬神經細胞的運算,它是非線性的。之前有提過,sigmoid的樣子,Neural Network如何選用Sigmoid Function。在這裡,我們就簡單地,把直線畫成稍為曲線,來觀察看看。


下面三張圖,由左至右,選用不同的Error Function,而有不同的E-w曲線。


E = t - y,會導致在w2之後,E為負數,這不是我們要的。
E = | t - y |,會讓原來w2之後的負數,轉正,但我們發現,最低點很尖銳,並不平滑。不利於我們用微分取最小值。
E = (t - y)^2,可以讓最低點變成平滑,這樣有利於微分取最小值。為何此Error Function可以製造平滑曲線?交給數學家證明。但我們可以拿直線的例子來觀察,的確是如此,如下面三張圖。


E = t - y,情況下,E-w是直線。
E = |t - y|,情況下,E-w是折線。
E = (t-y)^2,E-w是曲線。

各位不相信,可以拿下面這張表,自己畫畫看,就可以相信我說的。


最後,各位有沒有觀察到,為何上面舉的例子,最低點都在E = 0的位置?實際情況是,不見得會出現E = 0的情況,如下圖。


這是什麼意思?這個意思是,大部份情況,我們無法訓練Neural Network,能達到100%的精確,Error始終會存在,只是盡善盡美而已。這就是為什麼,Neural Network要用到微積分的微分,就是為了要找在E-w曲線上,找到Error的最小值,如此而己。

搞不好,數學家,在發明Neural Network時,沒有想到要用微積分這件事,而是東拼西湊,發現這樣做,效果不錯,而己。若是這樣,和我們程序員,有什麼不同?

繞了一大圈,希望不要為大家造成困擾,甚至愈看愈糊塗。本人做學問,習慣按步就班,常常遇到問題,想清楚後,才敢會往下走。過了一段時間,容易忘,又要重頭來。寫部落格,主要幫助自己,能比較快回想起來當時的理解。若也能幫助大家對於某些問題的了解,那也不錯。

-Count

從A.I.談程序員的未來

理工相關科系畢業的大學生,應該有修過微積分。尤其是對於資訊工程系的學生而言,在修微積分的時候,覺得很納悶,只要快快樂樂地學程式設計就好了,學這個有什麼用?的確,對於90年代的資工系學生而言,只要會寫程式,畢業不愁沒工作。可是現在,人工智慧當道,現在資工系學生,若想找到待遇更好的工作,只會寫程式是不夠的。這是我在業界這幾年的觀察。

撇開工作不談,其實人工智慧對於程序員來說,就是終極目標。我們寫程式的目的,不就是希望自己的程式,夠聰明,能幫人類解決許多問題嗎?我當初接觸程式設計,也是抱持這樣的信念。只是當時還是桌上型電腦,甚至要撥接上網,許多應用才剛起步,那個時候,大約是1996年,Web在台灣開始興起。研究所畢業之後,從事的工作,主要是幫企業產生報表,和自己接一些Case,因為那時候的App不多,能有這樣的成就,就算不錯了,更本用不到人工智慧,於是忘了當初寫程式的信念。

直到最近這幾年,手機App的興起,能想到的應用都有了。於是,我最近思考,如果該發明的App都發明了,那麼程序員還有什麼可貴的利用價值嗎?本人比較有先見之明,很早就拋棄很多人都會寫的報表程式,和Windows程式,改走冷門的門檻較高的BIOS。後來因緣際會,走更難的Security,這需有懂基本的Cryptography理論,所以維持了一些優勢。

我不是說報表程式不好,反而因為前幾年大數據興起,之前寫報表程式的程序員,搞不好搭上這班列車,而混得不錯也不一定。而Windows程式,也是因為會的人愈來愈少,反而愈來愈吃香。這是題外話。

現在AI當道,本人也順勢而為,加上當時的初衷,擁抱AI。根據以往的經驗,程式不是問題,理論才是重點。比如在開發Security的時候,基本加密解密的演算法,OpenSSL都幫你寫好了,不需要重頭再寫一遍。問題是,你怎麼知道,什麼時候?要用那一種加密,才會比較安全?理由何在?這就需要理論基礎了。所以,對於AI的領域,我的學習方法是,不用急者寫程式,先搞好理論比較重要。

抱怨完了,開始問自己兩個問題:
  1. 為何Neural Network要用到微積分?
  2. 為何Neural Network要用到線性代數?
-Count

2017年4月15日 星期六

Training A Simple Classifier - Learning Rate

承續上一篇,Training A Simple Classifier - Algorithm。我們的問題是,想修改程式,讓Finale Refined Line不要太偏向於Caterpillar。本篇,採用Learning Rate,改善這種情況。

下面步驟,會先產生Initial Line,然後用Learning Rate修正Refined Line與Final Refined Line。其過程,和上篇,Training A Simple Classifier - Algorithm,類似,細節就不多說了。這裡,著重在Learning Rate的使用方式

Initial Line

y = A * x
A = 0.25

Refined Line for Ladybird (3, 1)

Desired Target = 1.1
1.1 = (0.25 + △A) * 3
△A = (1.1 / 3) - 0.25 = 0.1167

我們不希望△A變化太大,故設定Learning Rate,L = 0.5,來修正△A。
△A = L * △A = 0.5 * 0.1167 = 0.0583

A = A + △A = 0.25 + 0.0583 = 0.3083
Refined Line: y = 0.3083 x

Final Refined Line for Caterpillar (1, 3)

Desired Target = 2.9
2.9 = (0.3083 + △A) * 1
△A = 2.9 - 0.3083 = 2.5917

△A = L * △A = 0.5 * 2.5917 = 1.2958
A = A + △A = 0.3083 + 1.2958 = 1.6042
Final Refined Line: y = 1.6042 x

然後,把Initial Line、Refine Line、Final Refined Line的演化過程,表達為下圖:



我們發現,因為Learning Rate設為0.5,導致Refined Line,還是在Ladybird下面。但對於Final Refined Line而言,此線畫在Caterpillar與Ladybird的中間,比較不偏向任何一方。這就是Learning Rate的作用。

-Count

Training A Simple Classifier - Algorithm

承續上一篇,Training A Simple Classifier,我們的問題是,想寫一支程式,該程式,經由透過指導員的訓練之後,可以學會區分Ladybird和Caterpillar這兩種生物。

我們把訓練電腦的次數限制為兩個,先是訓練Ladybird (3, 1),再來是訓練Caterpillar (1, 3)。當然,實際上,只訓練兩次是絕對不夠的,這裡只是為了說明方便而已。

訓練之前,程式隨機從原點畫出一條線叫Initial Line:
y = A * x
A = 0.25

進行第一次訓練:指導員告訴程式,位在(3, 1)的是Ladybird

程式判斷這Initial Line適不適合:
Actual Output = 0.25 * 3 = 0.75

0.75 表示Initial Line,在Ladybird的下面,不是我們要的線,要修正。

如何修正?

我們希望,線要在Ladybird的上面,而且一次不要修正太多。
我們決定:
Desired Target = 1.1
(在 y = 1 的上面)

目前的線是
y = (0.25) x

我們想調整它的斜率,使得
1.1 = (0.25 + △A) * 3

於是
△A = (1.1 / 3) - 0.25 = 0.1167

程序員,想把它寫程通用的公式 
△A = (Target / x) - A

在「Make Your Own Neural Network」這本書,公式是長這個樣子
△A = Error / x

其實是這麼來的
△A = (Target - A * x) / x = (Target - Output) / x = Error / x

這兩個公式的意思是一樣的。作為程序員的我,比較偏好前者。因為可以一目了然△A和A之間的關係。

於是調整後的A就是
A = 0.25 + 0.1167 = 0.3667

當然,我們寫把它寫成一行程式
A = A + △A

請注意,這是程式,不是數學式。若數學式這樣寫,可能會被數學家罵,請多包涵。另外,若變數名稱取△A,這對大部份程式語言而言,是有困難。我的習慣是,把它取名為DiffA。

至此,我們修正的線為:
Refined Line: y = (0.3667) x

接下來,進行第二次訓練:指導員告訴程式,位在(1, 3)的是Caterpillar
我們發現,Refined Line雖然在Caterpillar與Ladybird的中間,但離Caterpillar太遠,我們希望,它不要離Caterpillar太遠,但不要一次修正太多。

我們決定
Desired Target = 2.9 
(在y = 3的下面)

我們想調整線的斜率,使得:
2.9 = (0.3667 + △A) * 1

那就直接套用剛才寫那兩行程式吧!
△A = (Target / x) - A = (2.9 / 1) - 0.3667 = 2.5333
A = A + △A = 0.3667 + 2.5333 = 2.9

最後,修正的線為:
Final Refined Line: y = (2.9) x

我們把Initial Line、Refined Line、Final Refined Line的演化過程,表達為下圖:



Final Refined Line不是我們要的,因為離Caterpillar太近,而且離Ladybird太遠。我們期望的線應該要居中。是不是有Bug?

具我了解,大部份的程序員,不見得數學非常好,甚至不喜歡數學。其實,程序員遇到一些Bug,在解Bug的過程,所用到的思考方式,其實和數學家差不多,只是程序員沒有意識到這一點而已。我的意思是說,程式要寫的好,數學很重要。數學家,不見得想成為好的程序員。可是程序員,進級到某一階段,數學能力是必備的。

我的心路歷程是,非常喜歡寫程式,不排斥數學。但因為年輕的時候,時間都花在寫程式上,導致數學的底子不夠。後來,工作的關係,從參與Secure Flash與UEFI Secure Boot的開始,發現程式的核心部份和密碼學有關,而密碼學本質上就是一套數學。我才意識到,原來數學對程序員的修養,非常重要。彌補的方法就是,先把要用到的數學理論學好,而不用急者寫程式。如何做到?可以用程序員的思維來學習數學,我不敢說,這樣做最有效,但心理感覺比較踏實。

這種方法的好處就是,一旦了解之後,就不一定要花時間把程式寫出來,才為認自己懂了,因為程式已經Run在心裡面。同樣的學習方法,也可以從人工智慧的領域,去攻線性代數和微積分。當然,這個方法,謹適用在實作經驗很豐富的程式員身上。

這是題外話,該回歸正題了。如何解決直線太偏的Bug?程序員解這個問題?不外乎是改一改程式,看一看結果。若運氣好找到不錯的結果,再去找Root Cause,然後想辦法對自己的Solution自圓其說。若運氣不好,一直找不到,才去思考Root Cause。程序員通常都是這樣工作的。

下一篇,我會介紹如何用Learning Rate,來改善直線太偏的Bug。

Training A Simple Classifier

「學如逆水行舟,不進則退。」這句話是真的。

有一段時間沒碰Neural Network,今天想要繼續深入研究時,感覺心裡有一點心虛,好像有一些地方沒有真正搞懂,然後就是一點害怕,因為竟然不知道那些地方不懂。於是,重新復習「Make Your Own Neural Network」,從Trainig A Simple Classifier這一章節開始,再讀一遍。於是,才算真正了解為何需要Learning Rate,以及如何用它。為了怕自己將來再次忘記,故寫此文,這樣以後就不用花時間去回想,而阻礙到學習的進步。

以上是記錄本人的心路歷程,大家可以略過。本文,主要的目的是:
  1. 設計一個演算法,可以簡單區分兩種蟲
  2. 如何用Learning Rate改善此演算法
會分三篇說明:
在「Make Your own Neural Network」這本書,拿兩種蟲作為一個例子。一個叫Caterpillar,長得像毛毛蟲,身體長長的,另外一個叫Ladybird,瓢蟲,身體圓滾滾的。3歲小孩就能區分這兩種蟲的不同了。電腦如何區分呢?我們要「教」電腦。可是,若電腦沒有學習能力,如何教之?所以,首先,我們要讓電腦具備簡單的學習能力。也就是,在教電腦之前,要先把具有學習能力演算法的程式寫好,放到電腦裡執行。

這種程式要如何寫?先從簡單的開始。

Caterpillar和Ladybird這兩種蟲,我們可以單從形狀,就可以簡單區分他們的不同。形狀來看,一個是長長的,另一個是圓滾滾的。如何用數學來描述呢?根據經驗,可以用Width和Length,可以用這兩個值,區分這兩種蟲。如,抓到一隻Caterpillar,(Width, Length) = (1, 3)。一隻Ladybird,(Width, Length) = (3, 1),為了舉例方便,這是一隻很寬的瓢蟲。

會不會有例外,抓到一隻圓滾滾的Caterpillar?如果真的有這樣的情況出現,那麼用Width和Length,這兩個值,來區分Caterpillar和Ladybird。就行不通了。可以現像一下,如果真的看到一隻圓滾滾的Caterpillar,我們如何判斷牠不是Ladybird呢?其實還有其它特徵可以使用,如顏色、翅膀、腳的長度、移動方式、……等等,如下表所示。


為了說明方便,本文就假設,光靠Width和Length就能區分這兩個蟲。

程式要如何寫?

我們先把這個問題,轉換成數學問題。因為只有考慮Width和Length,就把這兩個視為平面的兩個座標軸,Width為x軸,Length為y軸。假設,我們在花園裡面,只抓到Caterpillar和Ladybird各一,量牠們的Width和Length,然後標示在平面座標上。Caterpillar = (1, 3)、Ladybird = (3, 1)。

接下來,我們要思考,如何讓電腦學習,去判斷這兩種蟲?我覺得學習的方法,可能有無限個。如下圖:


把Caterpillar和Ladybird周遭的範圍,圈起來,這兩個圈的中心點及半徑,就是所謂的學習結果,以後就可以拿這個結果,去區分Caterpillar和Ladybird。

或者,我們在 y = 2 之處畫一個橫線,來區分Caterpillar和Ladybird。於是 y = 2 就是學習的結果

當然 x = 2 之處,畫一條直線,來區分Caterpillar和Ladybird。這樣也行。

「Make Your Own Neural Network」這本書,採用的方法是,從原點畫一條線,來區分Caterpillar和Ladybird。這條線要怎麼畫,是一個需要指導員參與的學習過程。也就是說,當抓到一個蟲的時候,指導員會和電腦說,牠是Caterpillar還是Ladybird,電腦會根據結果,來調整這條線。隨者電腦看過的蟲愈多,它的線就會愈能區分Caterpillar和Ladybird。

至此,分類的問題,大家是不是清楚了解了,而且也找到許多不錯的Idea去解這個問題。我會在下一篇,介紹如何用程式,去表達這個Idea。

Training A Simple Classifier - Algorithm

-Count