天天看點

深度學習入門筆記(四):神經網絡

推薦文章

  • 深度學習入門筆記(一):機器學習基礎
  • 深度學習入門筆記(二):神經網絡基礎
  • 深度學習入門筆記(三):感覺機
  • 深度學習入門筆記(四):神經網絡
  • 深度學習入門筆記(五):神經網絡的學習
  • 未完待續。。。

文章目錄

  • 推薦文章
  • 1. 從感覺機到神經網絡
  • 1.1 神經網絡示例
  • 1.2 回憶感覺機
  • 3.1 激活函數
  • 2. 激活函數
  • 2.1 sigmoid函數
  • 2.2 階躍函數的代碼實作
  • 2.3 階躍函數的圖形
  • 2.4 sigmoid函數的代碼實作
  • 2.5 sigmoid函數與階躍函數的比較
  • 2.6 ReLU函數
  • 3. 多元數組運算
  • 3.1 多元數組
  • 3.2 矩陣乘法
  • 3.3 神經網絡的内積
  • 4. 3層神經網絡的實作
  • 4.1 符号含義
  • 4.2 各層間信号傳遞的實作
  • 4.3 代碼小結
  • 5.輸出層的設計
  • 5.1 恒等函數和softmax函數
  • 5.2 softmax函數注意事項
  • 5.3 softmax函數的特征
  • 6. 手寫數字識别
  • 6.1 MNIST資料集
  • 6.2 神經網絡的推理處理
  • 6.3 批處理
  • 7. 小結

上一個筆記我們學習了感覺機。對于複雜的函數,感覺機也隐含着能夠表示它的可能性。上一章已經介紹過,即便是計算機進行的複雜處理,感覺機(理論上)也可以将其表示出來。壞消息是,設定權重的工作,即确定合适的、能符合預期的輸人與輸出的權重,現在還是由 人工進行的。上一章中,我們結合與門、或門的真值表人工決定了合适的權重。

神經網絡的出現就是為了解決剛才的壞消息。具體地講,神經網絡的一個重要性質是它可以自動地從資料中學習到合适的權重參數。本章中,我們會先介紹神經網絡的概要,然後重點關注神經網絡進行識别時的處理。在下一章中,我們将了解如何從資料中學習權重參數。

深度學習入門筆記(四):神經網絡

如果需要擷取到【人工智能-深度學習入門筆記】的話幫忙轉發、轉發、轉發一下然後再關注我私信回複“ 1 ”得到擷取方式吧!

1. 從感覺機到神經網絡

神經網絡和上一章介紹的感覺機有很多共同點。這裡,我們主要以兩者的差異為中心,來介紹神經網絡的結構。

1.1 神經網絡示例

用圖來表示神經網絡的話,如圖3-1所示。我們把最左邊的一列稱為輸入層,最右邊的一列稱為輸出層,中間的一列稱為中間層。中間層有時也稱為隐藏層。“隐藏”一詞的意思是,隐藏層的神經元(和輸人層、輸出層不同)肉眼看不見。另外,我們把輸人層到輸出層依次稱為第0層、第1層、第2層(層号之是以從0開始,是為了友善後面基于Python進行實作)。圖3-1中,第0層對應輸入層,第1層對應中間層,第2層對應輸出層。

隻看圖3-1的話,神經網絡的形狀類似上一節的感覺機。實際上,就神經元的連接配接方式而言,與上一章的感覺機并沒有任何差異。那麼,神經網絡中信号是如何傳遞的呢?

深度學習入門筆記(四):神經網絡

1.2 回憶感覺機

深度學習入門筆記(四):神經網絡

如上圖,感覺機接收x 1和x 2兩個輸入信号,輸出y,數學公式如下(式3.1):

深度學習入門筆記(四):神經網絡

b是被稱為 偏置的參數,用于控制神經元被激活的容易程度;而w 1 和w 2 是表示各個信号的權重的參數,用于控制各個信号的重要性。

順便提一下,在圖3-2的網絡中,偏置b并沒有被畫出來。如果要明确地表示出b,可以像圖3-3那樣做。圖3-3中添加了權重為b的輸人信号1。這個感覺機将x 1 、 x 2 、 1 三個信号作為神經元的輸人,将其和各自的權重相乘後,傳送至下一個神經元。在下一個神經元中,計算這些權重信号的總和。如果這個總和超過0,則輸出1,否則輸出0。另外,由于偏置的輸人信号一直是1,是以為了差別于其他神經元,我們在圖中把這個神經元整個塗成灰色。

深度學習入門筆記(四):神經網絡

現在将式(3.1)改寫成更加簡潔的形式。為了簡化式(3.1),我們用一個函數來表示這種分情況的動作(超過0則輸出1,否則輸出0)。引人新函數h(x),将式(3.1)改寫成下面的式(3.2)和式(3.3)。

深度學習入門筆記(四):神經網絡

3.1 激活函數

剛才登場的h(x)函數會将 輸人信号的總和轉換為輸出信号,這種函數一般稱為 激活函數(activation function)。 如“激活”一詞所示,激活函數的作用在于決定如何來激活輸入信号的總和。

現在來進一步改寫式(3.2)。式(3.2) 分兩個階段進行處理,先計算輸入信号的權重總和,然後用激活函數轉換這一總和。是以,如果将式(3.2)寫得詳細一點,則可以分成下面兩個式子。

深度學習入門筆記(四):神經網絡

首先,式(3.4)計算權重輸入信号和偏置的總和,記為a。然後,式(3.5)用 h() 函數将 a 轉換為輸出 y。

之前的神經元都是用一個O表示的,如果要在圖中明确表示出式(3.4)和式(3.5),則可以像圖3-4這樣做。

深度學習入門筆記(四):神經網絡

如圖3-4所示,表示神經元的O中明确顯示了激活函數的計算過程,即信号的權重總和為節點a,然後節點 a 被激活函數 h() 轉換成節點 y。注意,“神經元"和“節點"兩個術語的含義相同。這裡,我們稱 a 和 y 為“節點",其實它和之前所說的“神經元”含義相同。

下面,我們将仔細介紹激活函數。激活函數是連接配接感覺機和神經網絡的橋梁。

2. 激活函數

式(3.3)表示的激活函數以門檻值為界,一旦輸入超過門檻值,就切換輸出。這樣的函數稱為 “階躍函數”。是以,可以說感覺機中使用了階躍函數作為激活函數。也就是說,在激活函數的衆多候選函數中,感覺機使用了階躍函數。那麼,如果感覺機使用其他函數作為激活函數的話會怎麼樣呢?實際上,如果将激活函數從階躍函數換成其他函數,就可以進入神經網絡的世界了。下面我們就來介紹一下神經網絡使用的激活函數。

2.1 sigmoid函數

神經網絡中經常使用的一個激活函數就是式(3.6)表示的 sigmoid函數(sigmoid function)。

深度學習入門筆記(四):神經網絡

式(3.6)中的 e x p ( − x ) 表示 e − x e^的意思。式(3.6) 表示的sigmoid函數看上去有些複雜,但它也僅僅是個函數而已。而函數就是給定某個輸人後,會傳回某個輸出的轉換器。比如,向sigmoid函數輸人1.0或2.0後,就會有某個值被輸出,類似h(1.0) = 0.731… h(2.0) = 0.88…這樣。神經網絡中用sigmoid函數作為激活函數,進行信号的轉換,轉換後的信号被傳送給下一個神經兀。實際上,上一章介紹的感覺機和接下來要介紹的神經網絡的主要差別就在于這個激活函數。其他方面,比如神經元的多層連接配接的構造、信号的傳遞方法等,基本上和感覺機是一樣的。下面,讓我們通過和階躍函數的比較來詳細學習作為激活函數的sigmoid函數。

2.2 階躍函數的代碼實作

這裡我們試着用Python畫出階躍函數的圖(從視覺上确認函數的形狀對了解函數而言很重要)。階躍函數如式(3.3)所示,當輸人超過0時,輸出1,否則輸出0。可以像下面這樣簡單地實作階躍函數。

def step_ function(x):
if x > 0:
return 1
else:
return 0           

這個實作簡單、易于了解,但是參數 x 隻能接受實數(浮點數)。也就是說,允許形如step_ function(3.0) 的調用,但不允許參數取NumPy數組,例如step. function(np.array([1.0, 2.0])。 為了便于後面的操作,我們把它修改為支援NumPy數組的實作。為此,可以考慮下述實作。

def step_ function(x):
y = x > θ
return y.astype (np.int)           

對NumPy數組進行不等号運算後,數組的各個元系都會進行不等号運算,生成一個布爾型數組。這裡,數組x中大于0的元素被轉換為True,小于等于0的元素被轉換為False,進而生成一個新的數組y。數組 y 是一個布爾型數組,但是我們想要的階躍函數是會輸出int型的0或1的函數。是以,需要把數組 y 的元素類型從布爾型轉換為int型。astype()方法會将數值轉化為指定的資料類型。

2.3 階躍函數的圖形

下面我們]就用圖來表示上面定義的階躍麗數,為此需要使用matplotlib庫。

import numpy as np
import matplotlib.pylab as plt
def step_ function(x):
return np.array(x > 0,dtype=np.int)

x = np.arange(-5.0, 5.0, 0.1)
y = step_ function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) #指定y軸的範圍
plt.show()           

np.arange(−5.0,5.0,0.1) 在 -5.0 到 5.0 的範圍内,以 0.1 為機關,生成NumPy數組([-5.0, -4.9, … 4.9])。step_ function() 以該NumPy數組為參數,對數組的各個元素執行階躍函數運算,并以數組形式傳回運算結果。對數組x、y進行繪圖,結果如圖3-6所示。

深度學習入門筆記(四):神經網絡

如圖3-6所示,階躍函數以0為界,輸出從0切換為1(或者從1切換為0)。它的值呈階梯式變化,是以稱為階躍函數。

2.4 sigmoid函數的代碼實作

python實作sigmoid:

def sigmoid(x):
return 1 / (1 + np.exp(-x))           

下面我們把sigmoid函數畫在圖上。畫圖的代碼和剛才的階躍函數的代碼幾乎是一樣的,唯一不同的地方是把輸出 y 的函數換成了sigmoid函數。

x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) #指定y軸的範圍
plt.show()           

運作上面的代碼,可以得到圖3-7。

深度學習入門筆記(四):神經網絡

2.5 sigmoid函數與階躍函數的比較

sigmoid 函數是一條平滑的曲線,輸出随着輸人發生連續性的變化。而階躍函數以0為界,輸出發生急劇性的變化。sigmoid 函數的連續性對神經網絡的學習具有重要意義。

另一個不同點是,相對于階躍兩數隻能傳回 0 或1, sigmoid兩數可以傳回0.731…0.880… 等實數(這一點和剛才的平滑性有關)。也就是說,感覺機中神經元之間流動的是0或1的二進制信号,而神經網絡中流動的是連續的實數值信号。

接着說一下階躍麗數和sigmoid函數的共同性質。階躍麗數和sigmoid函數雖然在平滑性上有差異,但是它們具有相似的形狀。實際上,兩者的結構均是“輸人小時,輸出接近0(為0);随着輸人增大,輸出向1靠近(變成1)”。也就是說,當輸入信号為重要資訊時,階躍函數和sigmoid麗數都會輸出較大的值;當輸入信号為不重要的資訊時,兩者都輸出較小的值。還有一個共同點是,不管輸入信号有多小,或者有多大,輸出信号的值都在0到1之間。

2.6 ReLU函數

到目前為止,我們介紹了作為激活函數的階躍函數和sigmoid函數。在神經網絡發展的曆史上,sigmoid函數很早就開始被使用了,而最近則主要使用 ReLU( Rectified Linear Unit )函數。ReLU函數在輸入大于0時,直接輸出該值;在輸人小于等于0時,輸出0(圖3-9)。

深度學習入門筆記(四):神經網絡

ReLU函數可以表示為下面的式(3.7)。

深度學習入門筆記(四):神經網絡

ReLU函數代碼實作:

def relu(x):
return np.maximum(0,x)           

這裡使用了NumPy的maximum函數。maximum函數會從輸入的數值中選擇較大的那個值進行輸出。

3. 多元數組運算

如果掌握了NumPy多元數組的運算,就可以高效地實作神經網絡。是以,本節将介紹NumPy多元數組的運算,然後再進行神經網絡的實作。

3.1 多元數組

簡單地講,多元數組就是 “數字的集合”,數字排成一列的集合、排成長方形的集合、排成三維狀或者(更加一般化的) N維狀的集合都稱為多元數組。下面我們就用NumPy來生成多元數組,先從前面介紹過的一維數組開始。

>>> import numpy as np
>>> A = np.array([1, 2, 3, 4])
>>> print(A)
[1234]
>>> np. ndim(A)
>>> A.shape
(4,)
>>> A.shape[0]
4           

如上所示, 數組的維數可以通過np.ndim()函數獲得。此外,數組的形狀可以通過執行個體變量shape獲得。在上面的例子中,A是一維數組,由4個元素構成。注意,這裡的A.shape的結果是個元組(tuple)。這是因為一維數組的情況下也要傳回和多元數組的情況下一緻的結果。例如,二維數組時傳回的是元組(4,3),三維數組時傳回的是元組(4,3,2),是以一維數組時也同樣以元組的形式傳回結果。下 面我們來生成一個二維數組。

>>> B = np.array([[1,2], [3,4], [5,6]])
>>> print(B)
[[1 2]
[3 4]
[5 6]]
>>> np.ndim(B)
>>> B.shape
(3,2)           

這裡生成了一個3 x 2的數組B。3 x 2的數組表示第一個次元有3 個元素,第二個次元有2個元素。另外,第一個次元對應第0維,第二個次元對應第1維( Python的索引從0開始)。二維數組也稱為矩陣(matrix)。如圖3- 10所示,數組的橫向排列稱為行(row),縱向排列稱為列(column)。

3.2 矩陣乘法

深度學習入門筆記(四):神經網絡

如本例所示,矩陣的乘積是通過 左邊矩陣的行(橫向)和右邊矩陣的列(縱向)以對應元素的方式相乘後再求和而得到的。并且,運算的結果儲存為新的多元數組的元素。比如,A的第1行和B的第1列的乘積結果是新數組的第1行第1列的元素,A的第2行和B的第1列的結果是新數組的第2行第1列的元素。另外,在本書的數學标記中,矩陣将用黑斜體表示(比如,矩陣A),以差別于單個元素的标量(比如,a或b)。這個運算在Python中可以用如下代碼實作。

>> A = np.array([[1,2], [3,4]])
>>> A.shape
(2, 2)
>>> B = np.array([[5,6], [7,8]])
>>> B.shape
(2, 2)
>>> np.dot(A, B)
array([[19,22],
[43,50]])           

這裡,A和B都是 2 x 2 的矩陣,它們的乘積可以通過 NumPy 的 np. dot() 函數計算(乘積也稱為點積)。np. dot()接收兩個NumPy數組作為參數,并傳回數組的乘積。這裡要注意的是,np.dot(A, B) 和np.dot(B, A) 的值可能不一樣。和一般的運算(+或*等)不同,矩陣的乘積運算中,操作數(A,B)的順序不同,結果也會不同。

這裡介紹的是計算2 x 2形狀的矩陣的乘積的例子,其他形狀的矩陣的乘積也可以用相同的方法來計算。比如,2 x 3的矩陣和3 x 2的矩陣的乘積.可按如下形式用Python來實作。

>>> A = np.array([[1,2,3], [4,5,6]])
>>> A.shape
(2, 3)
>>> B = np.array([[1,2], [3,4], [5,6]])
>>> B.shape
(3,2)
>>> np.dot(A, B)
array([[22,28],
[49, 64]])           

矩陣A的第1維的元素個數(列數)必須和矩陣B的第0維的元素個數(行數)相等。在上面的例子中,矩陣A的形狀是2x 3,矩陣B的形狀是3x2,矩陣A的第1維的元素個數(3)和矩陣B的第0維的元素個數(3)相等。如果這兩個值不相等,則無法計算矩陣的乘積。

3.3 神經網絡的内積

下面我們使用NumPy矩陣來實作神經網絡。這裡我們以圖3- 14中的簡單神經網絡為對象。這個神經網絡省略了偏置和激活函數,隻有權重。

深度學習入門筆記(四):神經網絡

實作該神經網絡時,要注意X , W , Y X,W,YX,W,Y的形狀,特别是 X 和 W 的對應次元的元素個數是否一緻,這一點很重要。

>>> X = np.array([1, 2])
>>> X.shape
(2,)
>> W = np.array([[1, 3, 5], [2, 4, 6])
>>> print(W)
[[1 3 5]
[2 4 6]]
>>> W.shape
>(2, 3)
>>> Y = np.dot(X, W)
>>> print(Y)
[5 11 17]           

如上所示,使用np.dot(多元數組的點積),可以一次性計算出Y的結果。這意味着,即便Y的元素個數為100或1000,也可以通過一次運算就計算出結果!如果不使用np.dot,就必須單獨計算Y的每一個元素(或者說必須使用for語句),非常麻煩。是以,通過矩陣的乘積一次性完成計算的技巧,在實作的層面上可以說是非常重要的。

4. 3層神經網絡的實作

現在我們來進行神經網絡的實作。這裡我們以圖3- 15的3層神經網絡為對象,實作從輸入到輸出的(前向)處理。在代碼實作方面,使用上一節介紹的NumPy多元數組。巧妙地使用NumPy數組,可以用很少的代碼完成神經網絡的前向處理。

深度學習入門筆記(四):神經網絡

4.1 符号含義

深度學習入門筆記(四):神經網絡

4.2 各層間信号傳遞的實作

深度學習入門筆記(四):神經網絡
深度學習入門筆記(四):神經網絡

下面我們用NumPy多元數組來實作式(3.9),這裡将輸入信号、權重、偏置設定成任意值。

X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2,0.3])
print (Wl.shape) # (2, 3)
print(X. shape) # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X,W1) + B1           

如圖3-18所示,隐藏層的權重和(權重信号和偏置的總和)用a表示,被激活函數轉換後的信号用z表示。此外,圖中h()表示激活函數,這裡我們使用的是sigmoid函數。用Python來實作,代碼如下所示。

Z1 = sigmoid(A1)
print(A1) # [0.3, 0.7, 1.1]
print(Z1) # [0.57444252, 0. 66818777, 0. 75026011]           
深度學習入門筆記(四):神經網絡

這個sigmoid()函數就是之前定義的那個函數。它會接收NumPy數組,并傳回元素個數相同的NumPy數組。

下面,我們來實作第1層到第2層的信号傳遞(圖3-19)。

W2 = np.array([[0.1, 0.4],[0.2, 0.5],[0.3, 0.6]1)
B2 = np.array([0.1, 0.2])
print(Z1.shape) # (3,)
print (W2.shape) # (3, 2)
print (B2.shape) # (2,)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)           

最後是第2層到輸出層的信号傳遞(圖3-20)。輸出層的實作也和之前的實作基本相同。不過,最後的激活麗數和之前的隐藏層有所不同。

def identity_ function(x):
return X
W3 = np.array([[0.1, 0.3],[0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_ _function(A3) #或者Y = A3           

這裡我們定義了identity_ function()函數(也稱為 “恒等函數”),并将其作為輸出層的激活函數。恒等函數會将數人按原樣輸出,是以,這個例子中沒有必要特意定義identity_ function()。這裡這樣實作隻是為了和之前的流程保持統一。另外,圖3-20中,輸出層的激活函數用σ()表示,不同于隐藏層的激活函數h()(σ讀作sigma)。

深度學習入門筆記(四):神經網絡

4.3 代碼小結

至此,我們已經介紹完了3層神經網絡的實作。現在我們把之前的代碼實作全部整理一下。這裡,我們按照神經網絡的實作慣例,隻把權重記為大寫字母 W1,其他的(偏置或中間結果等)都用小寫字母表示。

def init_ network():
network = {}
network['W1'] = np.array([[0.1, 0.3,0.5],[0.2, 0.4,0.6]])
network['bl'] = np.array([0.1, 0.2,0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2,0.5], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3],[0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
def forward(network, x):
W1, W2, W3 = network[ 'Wl'],network['W2'], network[ 'W3']
bl, b2, b3 = network['b1'], network['b2'], network['b3']
al = np.dot(x, W1) + bl
z1 = sigmoid(a1)
a2 = np.dot(zl, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = identity_ function(a3)
return y
network = init_ network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [ 0.31682708 0.69627909 ]           

這裡定義了init_ network() 和forward()函數。init_ network() 函數會進行權重和偏置的初始化,并将它們儲存在字典變量network中。這個字典變量network中儲存了每一層所需的參數(權重和偏置)。forward() 函數中則封裝了将輸入信号轉換為輸出信号的處理過程。

另外,這裡出現了forward(前向一詞,它表示的是從輸入到輸出方向的傳遞處理。後面在進行神經網絡的訓練時,我們将介紹後向(backward,從輸出到輸入方向)的處理。

至此,神經網絡的前向處理的實作就完成了。通過巧妙地使用NumPy多元數組,我們高效地實作了神經網絡。

5.輸出層的設計

神經網絡可以用在分類問題和回歸問題上,不過需要根據情況改變輸出層的激活函數。一般而言,回問題用恒等函數,分類問題用softmax函數。

5.1 恒等函數和softmax函數

恒等函數會将輸人按原樣輸出,對于輸人的資訊,不加以任何改動地直接輸出。是以,在輸出層使用恒等函數時,輸人信号會原封不動地被輸出。另外,将恒等函數的處理過程用之前的神經網絡圖來表示的話,則如圖3-21所示。和前面介紹的隐藏層的激活函數一樣,恒等函數進行的轉換處理可以用一根箭頭來表示。

深度學習入門筆記(四):神經網絡

分類問題中使用的softmax函數可以用下面的式(3.10)表示。

深度學習入門筆記(四):神經網絡

式(3.10) 假設輸出層共有n個神經元,計算第k個神經元的輸出 y k 。如式(3.10)所示,softmax函數的分子是輸人信号 a k的指數函數,分母是所有輸入信号的指數函數的和。

用圖表示softmax麗數的話,如圖3-22 所示。圖3-22中,softmax函數的輸出通過箭頭與所有的輸人信号相連。這是因為,從式(3.10)可以看出,輸出層的各個神經元都受到所有輸入信号的影響。

深度學習入門筆記(四):神經網絡

現在我們來實作softmax函數。在這個過程中,我們将使用Python解釋器逐一确認結果。

>>> a = np.array([0.3, 2.9, 4.0])
>>>
>>> exp_a = np.exp(a) #指數函數
>>> print(exp_a)
[ 1. 34985881 18. 17414537 54. 59815003]
>>>
>>> sum_exp_a = np.sum(exp_a) #指數函數的和
>>> print(sum_exp_a)
74.1221542102
>>>
>>> y = exp_a/sum_exp_a
>>> print(y)
[ 0.01821127 0.24519181 0.73659691]           

我們把它定義為函數:

def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum exp_a
return y           

5.2 softmax函數注意事項

深度學習入門筆記(四):神經網絡

首先,式(3.11)在分子和分母上都乘上C這個任意的常數(因為同時對分母和分子乘以相同的常數,是以計算結果不變)。然後,把這個C移動到指數函數(exp)中,記為logC。最後,把logC 替換為另一個符号C’。

式(3.11)說明,在進行softmax的指數函數的運算時,加上(或者減去)某個常數并不會改變運算的結果。這裡的C’可以使用任何值,但是為了防止溢出,一般會使用輸入信号中的最大值。我們來看一個 具體的例子。

>>> a = np.array([1010, 1000, 990] )
>>> np.exp(a) / np.sum(np.exp(a)) # softmax 麗數的運算
array([ nan, nan, nan] )
#沒有被正确計算
>>>
>>> C = np.max(a) # 1010.
>>> a-C
array([0, -10, -20])
>>>
>>> np.exp(a - c) / np.sum(np.exp(a - c))
array([ 9.99954600e-01, 4.53978686e - 05, 2.06106005e - 09])           

如該例所示,通過減去輸入信号中的最大值(上例中的c),我們發現原本為nan(not a number,不确定)的地方,現在被正确計算了。綜上,我們可以像下面這樣實作softmax函數。

def softmax(a):
C = np.max(a)
exp_a = np.exp(a - c) #溢出對策
sum_exp_a = np.sum(exp_a)
y=exp_a / sum_exp_a
return y           

5.3 softmax函數的特征

使用softmax()函數,可以按如下方式計算神經網絡的輸出。
>>> a = np.array([0.3, 2.9, 4.0])
>>> y = softmax(a )
>>> print(y)
[ 0.01821127 0.24519181 0.73659691 ]
>>> np.sum(y)
1.0           

如_上所示,softmax 函數的輸出是 0.0 到 1.0 之間的實數。并且,softmax函數的輸出值的總和是1。輸出總和為1是softmax函數的一個重要性質。正因為有了這個性質,我們才可以把softmax函數的輸出解釋為 “機率”。

這裡需要注意的是,即便使用了softmax函數,各個元素之間的大小關系也不會改變。這是因為指數函數(y=exp(x))是單調遞增函數。實際上,上例中a的各元素的大小關系和y的各元素的大小關系并沒有改變。比如,a的最大值是第2個元素,y的最大值也仍是第2個元素。

一般而言, 神經網絡隻把輸出值最大的神經元所對應的類别作為識别結果。并且,即便使用softmax函數,輸出值最大的神經元的位置也不會變。是以,神經網絡在進行分類時,輸出層的softmax函數可以省略。在實際的問題中,由于指數函數的運算需要一定的計算機運算量,是以輸出層的softmax函數一般會被省略。

深度學習入門筆記(四):神經網絡

6. 手寫數字識别

介紹完神經網絡的結構之後,現在我們來試着解決實際問題。這裡我們來進行手寫數字圖像的分類。假設學習已經全部結束,我們使用學習到的參數,先實作神經網絡的“推理處理”。這個推理處理也稱為神經網絡的 前向傳播( forward propagation)。

6.1 MNIST資料集

這裡使用的資料集是MNIST手寫數字圖像集。MNIST是機器學習領域最有名的資料集之一,被應用于從簡單的實驗到發表的論文研究等各種場合。實際上,在閱讀圖像識别或機器學習的論文時,MNIST資料集經常作為實驗用的資料出現。

MNIST資料集是由0到9的數字圖像構成的(圖3-24)。訓練圖像有6萬張,測試圖像有1萬張,這些圖像可以用于學習和推理。MNIST資料集的一般使用方法是,先用訓練圖像進行學習,再用學習到的模型度量能在多大程度上對測試圖像進行正确的分類。

深度學習入門筆記(四):神經網絡

MNIST的圖像資料是28像素x 28像素的灰階圖像(1通道),各個像素的取值在0到255之間。每個圖像資料都相應地标有“7” “2" “1"等标簽。

現在我們擷取MNIST資料,通過調用dataset檔案下的mnist.py,使用其中的load_mnist()函數就可以輕松讀入MNIST資料。

import sys, os
sys.path.append(os.pardir) #為了導人父目錄中的檔案而進行的設定
from dataset.mnist import load_mnist
#第一次調用會花費幾分鐘..... .
(x_train, t_train), (x_test, t_test) = load_ mnist(flatten=True ,normalize=False)
#輸出各個資料的形狀
print(x_train.shape) # (60000,784)
print(t_train.shape) # (60000, )
print(x_test.shape) # (10000,784)
print(t_test.shape ) # (10000,)           

首先,為了導人父目錄中的檔案,進行相應的設定”。然後,導人dataset/mnist. py中的load mnist 函數。最後,使用 load_mnist函數,讀人MNIST資料集。第一次調用load mnist函數時,因為要下載下傳MNIST資料集,是以需要接入網絡。第2次及以後的調用隻需讀人儲存在本地的檔案(pickle檔案)即可,是以處理所需的時間非常短。

load_ mnist 函數以“(訓練圖像,訓練标簽),( 測試圖像,測試标簽)”的形式傳回讀入的MNIST資料。此外,還可以像 load mnist (normalize=True,flatten=True,one_ hot_ label=False) 這 樣,設定3個參數。第1個參數normalize設定 是否将輸人圖像正規化為0.0 ~ 10的值。如果将該參數設定為False,則輸人圖像的像素會保持原來的0~255。第2個參數flatten設定是否展開輸入圖像(變成一維數組)。如果将該參數設定為False,則輸人圖像為1 x 28x 28的三維數組;若設定為True,則輸人圖像會儲存為由784個元素構成的一維數組。

第3個參數one_hot_label設定是否将标簽儲存為one-hot表示(one-hot representation)。one-hot 表示是僅正确解标簽為1,其餘皆為0的數組,就像 [0,0,1,0,0,0,0,0,0,0] 這樣。當one_ hot_ label 為False時,隻是像7、2這樣簡單儲存正确解标簽;當one_hot_label為True時,标簽則儲存為one-hot表示。

現在,我們試着顯示MNIST圖像,同時也确認一下資料。圖像的顯示使用PIL(Python Image Library)子產品。執行下述代碼後,訓練圖像的第一張就會顯示出來,儲存檔案為mnist_ show.py。

# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 為了導入父目錄的檔案而進行的設定
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image
def img_show(img):
pil_img = Image.fromarray(np.uint8(img))
pil_img.show()
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
img = x_train[0]
label = t_train[0]
print(label) # 5
print(img.shape) # (784,)
img = img.reshape(28, 28) # 把圖像的形狀變為原來的尺寸
print(img.shape) # (28, 28)
img_show(img)           

這裡需要注意的是,flatten=True時讀 入的圖像是以一列(一維) NumPy數組的形式儲存的。是以,顯示圖像時,需要把它變為原來的28像素x 28像素的形狀。可以通過reshape()方法的參數指定期望的形狀,更改NumPy數組的形狀。此外,還需要把儲存為NumPy數組的圖像資料轉換為PIL用的資料對象,這個轉換處理由Image. fromarray()來完成。

6.2 神經網絡的推理處理

下面,我們對這個MNIST資料集實作神經網絡的推理處理。神經網絡的輸入層有784個神經元,輸出層有10個神經元。輸入層的784這個數字來源于圖像大小的28 x 28= 784,輸出層的10這個數字來源于10類别分類(數字0到9,共10類别)。此外,這個神經網絡有2個隐藏層,第1個隐藏層有50個神經元,第2個隐藏層有100個神經元。這個50和100可以設定為任何值。下面我們先定義get_ data()、 init_ network()、 predict() 這3個函數(代碼儲存在neuralnet_ mnist .py中)。

# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 為了導入父目錄的檔案而進行的設定
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
def init_network():
with open("sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
def predict(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = softmax(a3)
return y           

init_ network()會讀人儲存在pickle檔案sample_ weight .pkl中的學習到的 權重參數”。這個檔案中以字典變量的形式儲存了權重和偏置參數。剩餘的2個函數,和前面介紹的代碼實作基本相同,無需再解釋。現在,我們用這3個函數來實作神經網絡的推理處理。然後,評價它的識别精度(accuracy),即能在多大程度上正确分類。

x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
y = predict(network, x[i])
p= np.argmax(y) # 擷取機率最高的元素的索引
if p == t[i]:
accuracy_cnt += 1
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))           

首先獲得MNIST資料集,生成網絡。接着,用for語句逐一取出儲存在 x 中的圖像資料,用predict()函數進行分類。predict() 函數以NumPy數組的形式輸出各個标簽對應的機率。比如輸出[0.1, 0.3, 0.2, … 0.04] 的數組,該數組表示“0”的機率為0.1,“1” 的機率為0.3,等等。然後,我們取出這個機率清單中的最大值的索引(第幾個元素的機率最高),作為預測結果。可以用np.argmax(x)函數取出數組中的最大值的索引,np.argmax(x) 将擷取被賦給參數x的數組中的最大值元素的索引。最後,比較神經網絡所預測的答案和正确解标簽,将回答正确的機率作為識别精度。

執行上面的代碼後,會顯示“Accuracy:0.9352”。這表示有93.52 %的資料被正确分類了。目前我們的目标是運作學習到的神經網絡,是以不讨論識别精度本身,不過以後我們會花精力在神經網絡的結構和學習方法上,思考如何進一步提高這個精度。實際上,我們打算把精度提高到99 %以上。另外,在這個例子中,我們把load_ mnist 函數的參數normalize設定成了True。将normalize設定成True後,函數内部會進行轉換,将圖像的各個像素值除以255,使得資料的值在0.0~1.0的範圍内。像這樣把資料限定到某個範圍内的處理稱為正規化(normalization)。 此外,對神經網絡的輸入資料進行某種既定的轉換稱為預處理( pre processing)。 這裡,作為對輸入圖像的種預處理,我們進行了正規化。

深度學習入門筆記(四):神經網絡

6.3 批處理

下面我們使用Python解釋器,輸出剛才的神經網絡的各層的權重的形狀。

>>> x, _ = get_ data()
>>> network = init_network()
>>> W1, W2, W3 = network['W1'], network[ 'W2'], network[ 'W3']
>>>
>>> X.shape
(10000,784)
>>> x[0].shape
(784,)
>>> W1.shape
>(784,50)
>>> W2.shape
(50,100)
>>> W3.shape
(100, 10)           

我們通過上述結果來确認一下多元數組的對應次元的元素個數是否一緻(省略了偏置)。用圖表示的話,如圖3-26所示。可以發現,多元數組的對應次元的元素個數确實是一緻的。此外,我們還可以确認最終的結果是輸出了元素個數為10的一維數組。

深度學習入門筆記(四):神經網絡

從整體的處理流程來看,圖3-26中,輸人一共由784個元素(原本是一個28 x 28的二維數組)構成的一維數組後,輸出一個有10個元素的一維數組。這是隻輸入一張圖像資料時的處理流程。

現在我們來考慮打包輸人多張圖像的情形。比如,我們想用predict()函數一次性打包處理100張圖像。為此,可以把x的形狀改為100x 784,将100張圖像打包作為輸人資料。用圖表示的話,如圖3-27所示。

深度學習入門筆記(四):神經網絡

如圖3-27所示,輸人資料的形狀為100x 784,輸出資料的形狀為100x10。這表示輸人的100張圖像的結果被–次性輸出了。比如,x[0]和y[0]中儲存了第0張圖像及其推理結果,x[1] 和y[1]中儲存了第1張圖像及其推理結果,等等。

這種打包式的輸人資料稱為 批(batch)。批有“捆”的意思,圖像就如同紙币一樣紮成一捆。

下面我們進行基于批處理的代碼實作。這裡用粗體顯示與之前的實作的不同之外。

x, t = get_data()
network = init_ network()
batch_size = 100 #批數量
accuracy_ cnt = 0
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_ batch, axis=1)
accuracy_cnt += np.sum(p == t[i:i+batch_size])
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))           

我們來逐個解釋粗體的代碼部分。首先是range()函數。range() 函數若指定為range(start, end), 則會生成一個由start到end-1之間的整數構成的清單。若像range(start,end,step)這樣指定3個整數,則生成的清單中的下一個元素會增加step指定的值。

在range()函數生成的清單的基礎上,通過 x[i : i+batch_ size] 從輸人資料中抽出批資料。x[i : i+batch n] 會取出從第i個到第 i+batch_ n 個之間的資料。本例中是像 x[0 : 100]、x[100 : 200]…這樣,從頭開始以100為機關将資料提取為批資料。

然後,通過argmax()擷取值最大的元素的索引。不過這裡需要注意的是,我們給定了參數axis=1。這指定了在100 x 10的數組中,沿着第1維方向(以第1維為軸)找到值最大的元素的索引(第0維對應第1個次元)”。我們來看一個例子:

>>> x = np.array([[0.1, 0.8, 0.1],[0.3, 0.1, 0.6],
>>> [0.2, 0.5,0.3], [0.8, 0.1, 0.1]])
>>> y = np.argmax(x, axis=1)
>>> print(y)
[1 2 1 0]           

最後,我們比較一下以批為機關進行分類的結果相實際的答案。為此,需要在NumPy數組之間使用比較運算符(==)生成由True/False構成的布爾型數組,并計算True的個數。我們]通過下面的例子進行确認。

>>> y = np,array([1, 2, 1, 0])
>>> t = np.array([1, 2,0,0])
>>> print(y==t)
[True True False True ]
>>> np. sum(y==t)           

7. 小結

  • 本章所學的内容
  • 神經網絡中的激活函數使用平滑變化的sigmoid函數或ReLU函數。
  • 通過巧妙地使用NumPy多元數組,可以高效地實作神經網絡。
  • 機器學習的問題大體.上可以分為回歸問題和分類問題
  • 關于輸出層的激活函數,回歸問題中一般用恒等函數,分類問題中一般用softmax函數。
  • 分類問題中,輸出層的神經元的數量設定為要分類的類别數。
  • 輸入資料的集合稱為批。通過以批為機關進行推理處理,能夠實作高速的運算
深度學習入門筆記(四):神經網絡

繼續閱讀