目錄
一、《python神經網絡編程》
二、一些粗淺的認識
1) 神經網絡也是一種擬合
2)神經網絡不是真的大腦
3)網絡構建需要反復迭代
三、數字圖像識別的實現思路
1)建立一個神經網絡類
?2)權重更新的具體實現
3)網絡測試
四、總結
一、《python神經網絡編程》
最近學習了一本書《python神經網絡編程》,該書通過對一個數字識別案例的深入詳細講解,可以讓讀者對神經網絡的思想有更加清晰的理解,明白計算機神經網絡是如何工作的。在沒有真正接觸神經網絡之前,總以為這是非常深奧的理論,也不明白神經網絡是如何模擬人的大腦進行學習和判斷的,難以理解計算機能夠模擬人的大腦。《python神經網絡編程》確實是一本很好的入門教材,它讓讀者能夠真正踏入人工智能的門檻,奠定深入研究的基礎。
二、一些粗淺的認識
1) 神經網絡也是一種擬合
在數學中有很多擬合方法,比如線性擬合、多項式擬合等等。在這些擬合中,我們同樣需要有確定的已知數據,然后通過這些擬合方法我們可以得到一個確定的數學解析表達式,并通過這個表達式來預測未知的結果。而神經網絡的核心思想,我想也是一樣的,只是這個實現過程相對復雜。我們最終能得到的是一個預測網絡,也稱模型,而不是一個精確的數學方程表達式。我們使用已知輸入和輸出的大量數據來訓練神經網絡,就是在讓這個網絡的輸出結果逐漸逼近已知的輸出結果。這個網絡在訓練的過程中,最終建立起了輸入與輸出之間的對應關系。但是這個對應關系我們無法用一個清晰的數學表達式來描述,可是我們可以使用這個網絡來進行預測。正如對于圖片中的數字而言,我們人看到圖片中的數字就可以知道圖中的數字是多少,因為在我們認識數字之前經過了學習,很多次有人告訴我們這個圖形對應的數字就是這個數字。就像神經網絡不能用一個準確的數學公式來描述輸入與輸出之間的關系一樣,我們人類其實至今也還是不知道我們大腦學習的本質是什么。所以神經網絡得到的擬合結果是一個黑盒子,我們針對特定的問題建立了一個黑盒子,然后訓練了這個黑盒子,我們只知道給這個黑盒子一個輸入,他能給出一個可信的結果。
2)神經網絡不是真的大腦
神經網絡只是借用了人類大腦神經元的形式。計算機神經網絡是否真的能實現動物大腦的能力,我想還是難以斷定。我們以前總是認為計算機的工作都是確定的,給它一個輸入,我們必然能夠準確預測它的輸出結果。其實現在的神經網絡也是一樣的,雖然它是個黑盒子,但是給它相同的輸入,它必然能夠得到相同的結果,即使我們不知道它具體的計算邏輯,因為在這個網絡中所有的參數經過訓練后已經確定,計算機計算的每一步都是確定的。但計算機神經網絡這種模糊擬合的實現,讓我們認識到,當大量簡單計算累計到一定數量時,在龐大的信息傳遞中是否就存在不可預測性。我們不理解自己為什么會思考,或許在不久的將來我們也無法確信龐大計算機系統的運行的真是我們人類設計的程序。
3)網絡構建需要反復迭代
在構建神經網絡過程中,輸入與輸出的節點數量需要根據實際問題進行分析。我們需要根據解決的實際問題,分析如何將問題的輸入數字化,并設計相應的輸入節點。而對于輸出也是一樣,我們需要分析如何用輸出的數字信息來表達我們想要的結果形式,并設計相應的輸出節點。我們常見的簡單的神經網絡通常是3層,包括輸入、輸出和中間的隱藏層,而隱藏層的層數以及每層的節點數該如何設計,這需要通過不斷的嘗試。較少的層數和節點數能夠獲得較高的計算效率,但是性能較低,誤差較大,較多的層數和節點數能夠獲得較高的精度,但是計算效率低。因此,針對具體的問題需要綜合考慮,反復迭代后確定網絡構建形式。
三、數字圖像識別的實現思路
1)建立一個神經網絡類
《python神經網絡編程》書中給出了一個實現數字識別的例子。在Python中首先定義一個3層神經網絡類neuralNetwork。在類的初始化函數中確定網絡的輸入層節點數、隱藏層節點數、輸出層節點數和學習率4個參數。并確定使用的激活函數。輸入層與隱藏層、隱藏層和輸出層之間的初始權重矩陣隨機生成。
訓練神經網絡時,先根據訓練數據的輸入,正向逐層計算,最后得到網絡的輸出結果。然后計算網絡輸出結果與實際結果的誤差值,然后再將誤差進行反向傳播,更新權重矩陣。這樣,一組數據的訓練就完成。
網絡輸出就是利用測試數據對網絡進行一次正向輸出的過程,最后根據輸出數據給出網絡的圖像識別結果,具體實現后面再敘述。
class neuralNetwork:# 初始化函數def __int__(self, inputnodes, hiddennodes, outputnodes, learningrate):self.inodes = inputnodes # 輸入節點數self.hnodes = hiddennodes # 隱藏節點數self.onodes = outputnodes # 輸出節點數self.wih = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes)) # 隨機生成初始的輸入層到隱藏層的權重矩陣self.who = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.onodes, self.hnodes)) # 隨機生成初始的隱藏層到輸出層的權重矩陣self.learn = learningrate # 權重更新率self.active_function = lambda x: scipy.special.expit(x) # 選擇需要使用的激活函數pass# 定義訓練函數def train(self, input_list, target_list): # 提供已知的輸入和輸出結果inputs = numpy.array(input_list, ndmin=2).T # 將輸入轉換為二維矩陣形式targets = numpy.array(target_list, ndmin=2).Thidden_inputs = numpy.dot(self.wih, inputs) # 計算隱藏層的輸入值,輸入層到隱藏層的權重矩陣乘以輸入hidden_outputs = self.active_function(hidden_inputs) # 計算隱藏層的輸出值,將隱藏層的輸入值帶入激活函數final_inputs = numpy.dot(self.who, hidden_outputs) # 計算輸出層的輸入值,隱藏層到輸出層的權重矩陣乘以隱藏層的輸出值final_outputs = self.active_function(final_inputs) # 計算輸出層的輸出結果,將輸出層的輸入值帶入激活函數output_errors = targets - final_outputs # 計算誤差值,已知的輸出結果-輸出層的輸出值hidden_errors = numpy.dot(self.who.T, output_errors) # 傳遞誤差# 更新權重矩陣self.who += self.learn * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)),numpy.transpose(hidden_outputs))self.wih += self.learn * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),numpy.transpose(inputs))pass# 定義輸出函數def query(self, input_list):inputs = numpy.array(input_list, ndmin=2).Thidden_inputs = numpy.dot(self.wih, inputs)hidden_outputs = self.active_function(hidden_inputs)final_inputs = numpy.dot(self.who, hidden_outputs)final_outputs = self.active_function(final_inputs)return final_outputs
?2)權重更新的具體實現
權重矩陣反復更新,就是神經網絡不斷學習,獲得正確擬合結果的過程。權重更新根據嚴格的數學推導公式,程序實現過程不能直觀反映具體的實現思想。它的核心就是利用每次訓練的計算誤差來修正權重矩陣。
(1)誤差計算
在訓練神經網絡過程中計算誤差時,程序中我們直接將實際結果與計算結果相減(output_errors = targets - final_outputs),得到一個誤差向量。但是理論分析時,直接相減計算誤差的方法并不合適,我們通常使用差的平方來表示,所有節點的總誤差如下:
?(2)誤差傳遞
我們可以直接計算出輸出層的誤差,但是最終的誤差是由前面的計算逐層、逐節點計算累計得到的,該如何將最終的誤差反向分配到每個節點上呢?通常我們認為節點之間連接權重越大的鏈路,計算引入的誤差也越大,因此,我們也同樣通過權重來分配誤差。針對本例而言,我們已經得到了輸出層的誤差,還需要計算隱藏層的誤差。輸入層不需要計算,因為默認輸入層輸入與輸出是相同的,不需要使用激活函數。那么隱藏層的輸出誤差計算如下:
此處需要注意的是,誤差反向傳播計算的矩陣正好是正向計算時隱藏層到輸出層權重矩陣的轉置,至于為什么是轉置,我們手動簡單計算一下就知道了。同時需要注意的是,通過上式計算得到的總誤差并不是輸出層的總誤差()?。因為,更新權重矩陣時,我們其實并不關心過程中的誤差大小,而是更關心是哪個鏈路導致的誤差更大,只要能體現出各鏈路誤差的相對大小就可以。
(3)更新權重
在此例中使用跟新權重的方法是梯度下降法,這也是使用非常廣泛的一種方法。我們需要建立起權重與誤差之間的關系。我們以最后的輸出層為例,輸出層的輸入是隱藏層的輸出乘以隱藏層和輸出層之間的權重矩陣:
將此結果代入激活函數,則輸出層的結果為:
sigmoid函數為激活函數,形式為:
則每個節點的輸出誤差為:
?
將上述公式聯合,然后對權重w求導,就可以得到誤差相對于權重的導數(斜率):
權重更新公式為:
上述公式中,我們實際使用時并不關心常系數2,因此在代碼實現時省略了。
3)網絡測試
網絡測試的具體實現可參見代碼。
# 測試網絡
scorecard = []
i = 0
for record in test_data_list:i += 1if(i != 10): # 可調整數字,逐張識別continuepassall_values = record.split(',')correct_label = int(all_values[0])inputs = (numpy.asarray(all_values[1:], numpy.float32) / 255.0 * 0.99) + 0.01image_array = numpy.asarray(all_values[1:], numpy.int32).reshape((28, 28))fig = matplotlib.pyplot.figure(figsize=(5, 5))matplotlib.pyplot.imshow(image_array, cmap='Greys', interpolation='None')matplotlib.pyplot.show()outputs = n.query(inputs) # 網絡識別的結果label = numpy.argmax(outputs) # 輸出結果中的最大值print("輸入數字:", all_values[0], "識別結果:", label) # 打印出實際值if(label == correct_label):scorecard.append(1)else:scorecard.append(0)passpassscorecard_array = numpy.asarray(scorecard)
print("performance = ", scorecard_array.sum() / scorecard_array.size)
四、總結
《python神經網絡編程》與書中的示例簡明闡述了神經網絡的核心思想和基本架構,任何復雜的神經網絡均可基于此構建。我們可以通過使用不同的網絡層數、節點數、激活函數以及鏈路連接方式等,構建不同的適用于各種應用的神經網絡模型。或許正式因為神經網絡內部的不可描述性,對網絡結構的各種優化,為研究神經網絡開辟了廣闊空間。