未來將是人工智能和大數據的時代,是各行各業使用人工智能在雲上處理大數據的時代,深度學習將是新時代的一大利器,在此我將從零開始記錄深度學習的學習歷程。
我希望在學習過程中做到以下幾點:
- 了解各種神經網絡設計原理。
- 掌握各種深度學習算法的python編程實現。
- 運用深度學習解決實際問題。
讓我們開始踏上深度度學習的征程。
一、感知器原型
想要了解“神經網絡”,我們需要了解一種叫做“感知器”的⼈⼯神經元。感知器在 20 世紀五、六⼗年代由科學家 Frank Rosenblatt 發明,⼀個感知器接受⼏個輸⼊,並產⽣⼀個輸出。
下圖是一個感知器:
⽰例中的感知器有三個輸⼊x1、x2、x3(1*w0作為偏置,后面會講到)。通常可以有更多或更少輸⼊。 Rosenblatt 提議⼀個簡單的規則來計算輸出。他引⼊權重w1、w2、w3..表⽰相應輸⼊對於輸出重要性的實數(權重)。神經元的輸出為0 或者 1,則由計算權重后的總和 ∑jwjxj ⼩於或者⼤於⼀些閾值決定。和權重⼀樣,
閾值是⼀個實數,⼀個神經元的參數。⽤更精確的代數形式:
這就是⼀個感知器所要做的所有事情!
而我們把闔值移動到不等式左邊,並用感知器的偏置b=-threshold代替,用偏置而不用闔值。其中實現偏置的一種方法就是如前圖所示在輸入中引入一個偏置神經元x0=1,則b=x0*w0,那么感知器的規則可以重寫為:
此時就可以使用階躍函數來作為感知器的激勵函數。
到此我們可以發現,一個感知器由以下幾部分組成
-
輸入權值 一個感知器可以接收多個輸入
(x1,x2,...,xn∣xi∈R),每個輸入上有一個權值wi∈R,此外還有一個偏置項b∈R,就是上圖中的w0。 -
激活函數 感知器的激活函數可以有很多選擇在此我們選擇下面這個階躍函數來作為激活函數f:
f(z)={1z>00otherwise -
輸出 感知器的輸出由下面這個公式來計算
y=f(w∙x+b)公式(1)
接下去我們將會用一個例子來理解感知器的模型。
模型的建立是運用深度學習方法解決問題的基礎。
二、感知器的運用
1、感知器實現邏輯運算
我們設計一個感知器,讓它來實現and運算。程序員都知道,and是一個二元函數(帶有兩個參數和),下面是它的真值表:
x1 | x2 | y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
為了計算方便,我們用0表示false,用1表示true。
我們令w1=0.5;w2=0.5;b=−0.8,而激活函數f就是前面寫出來的階躍函數,這時,感知器就相當於and函數。我們驗算一下:
輸入上面真值表的第一行,即,那么根據公式(1),計算輸出:
也就是當x1x2都為0的時候,y為0,這就是真值表的第一行。讀者可以自行驗證上述真值表的第二、三、四行。
可以看到感知器本身是一個線性分類器,它通過求考慮了權重的各輸入之和與闔值的大小關系,對事物進行分類。
所以任何線性分類或線性回歸問題都可以用感知器來解決。前面的布爾運算可以看作是二分類問題,即給定一個輸入,輸出0(屬於分類0)或1(屬於分類1)。
如下面所示,and運算是一個線性分類問題,即可以用一條直線把分類0(false,紅叉表示)和分類1(true,綠點表示)分開。
然而,感知器卻不能實現異或運算,如下圖所示,異或運算不是線性的,你無法用一條直線把分類0和分類1分開。
2、感知器的訓練
現在,你可能困惑前面的權重項和偏置項的值是如何獲得的呢?這就要用到感知器訓練算法:將權重項和偏置項初始化為0,然后,利用下面的感知器規則迭代的修改wi和b,直到訓練完成。
其中:
wi是與輸入xi對應的權重項,b是偏置項。事實上,可以把b看作是值永遠為1的輸入所對應的權重w0。t是訓練樣本的實際值,一般稱之為label。而y是感知器的輸出值,它是根據公式(1)計算得出。η是一個稱為學習速率的常數,其作用是控制每一步調整權的幅度。
每次從訓練數據中取出一個樣本的輸入向量x,使用感知器計算其輸出y,再根據上面的規則來調整權重。每處理一個樣本就調整一次權重。經過多輪迭代后(即全部的訓練數據被反復處理多輪),就可以訓練出感知器的權重,使之實現目標函數。
三、python實現感知器
class Perceptron(object): def __init__(self, input_num, activator): ''' 初始化感知器,設置輸入參數的個數,以及激活函數。 激活函數的類型為double -> double ''' self.activator = activator # 權重向量初始化為0 self.weights = [0.0 for _ in range(input_num)] # 偏置項初始化為0 self.bias = 0.0 def __str__(self): ''' 打印學習到的權重、偏置項 ''' return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias) def predict(self, input_vec): ''' 輸入向量,輸出感知器的計算結果 ''' # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起 # 變成[(x1,w1),(x2,w2),(x3,w3),...] # 然后利用map函數計算[x1*w1, x2*w2, x3*w3] # 最后利用reduce求和 return self.activator( reduce(lambda a, b: a + b, map(lambda (x, w): x * w, zip(input_vec, self.weights)) , 0.0) + self.bias) def train(self, input_vecs, labels, iteration, rate): ''' 輸入訓練數據:一組向量、與每個向量對應的label;以及訓練輪數、學習率 ''' for i in range(iteration): self._one_iteration(input_vecs, labels, rate) def _one_iteration(self, input_vecs, labels, rate): ''' 一次迭代,把所有的訓練數據過一遍 ''' # 把輸入和輸出打包在一起,成為樣本的列表[(input_vec, label), ...] # 而每個訓練樣本是(input_vec, label) samples = zip(input_vecs, labels) # 對每個樣本,按照感知器規則更新權重 for (input_vec, label) in samples: # 計算感知器在當前權重下的輸出 output = self.predict(input_vec) # 更新權重 self._update_weights(input_vec, output, label, rate) def _update_weights(self, input_vec, output, label, rate): ''' 按照感知器規則更新權重 ''' # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起 # 變成[(x1,w1),(x2,w2),(x3,w3),...] # 然后利用感知器規則更新權重 delta = label - output self.weights = map( lambda (x, w): w + rate * delta * x, zip(input_vec, self.weights)) # 更新bias self.bias += rate * delta def f(x): ''' 定義激活函數f ''' return 1 if x > 0 else 0 def get_training_dataset(): ''' 基於and真值表構建訓練數據 ''' # 構建訓練數據 # 輸入向量列表 input_vecs = [[1,1], [0,0], [1,0], [0,1]] # 期望的輸出列表,注意要與輸入一一對應 # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0 labels = [1, 0, 0, 0] return input_vecs, labels def train_and_perceptron(): ''' 使用and真值表訓練感知器 ''' # 創建感知器,輸入參數個數為2(因為and是二元函數),激活函數為f p = Perceptron(2, f) # 訓練,迭代10輪, 學習速率為0.1 input_vecs, labels = get_training_dataset() p.train(input_vecs, labels, 10, 0.1) #返回訓練好的感知器 return p if __name__ == '__main__': # 訓練and感知器 and_perception = train_and_perceptron() # 打印訓練獲得的權重 print and_perception # 測試 print '1 and 1 = %d' % and_perception.predict([1, 1]) print '0 and 0 = %d' % and_perception.predict([0, 0]) print '1 and 0 = %d' % and_perception.predict([1, 0]) print '0 and 1 = %d' % and_perception.predict([0, 1])