深度學習:透過神經網絡的內在靈魂與柏拉圖的哲學理念

以神經網絡為基礎的深度學習,它最大的作用就是讓計算機能求解那些沒有明確規則或定義的問題,例如你根本無法制定出一系列明確的規則或步驟去讓計算機識別一幅圖像中的內容是什麼,人工智能最大的強項就是讓計算機能處理那些模糊不清,幾乎無法用明確的規則或步驟來描述的問題。

一個受過大量數據訓練的神經網絡,給定領域內的圖像表示什麼內容,此時它就像一個黑盒子,把數據從一端輸入,然後結果自動從另一端輸出,你根本不知道他內部的運行機制。如果我們只在乎得到正確的結果,那麼無論神經網絡的內部機理如何複雜,我們都無需關心。如果我們想知道神經網絡是如何學習輸入數據,並總結出某種特定規律來,那麼我們現在能知道的,就是網絡會不斷的調整鏈路上權值。

由於神經網絡模擬的是大腦思維方式,人的大腦有意識,那神經網絡是否也有自己的”意識“呢?原來神經網絡的工作流程如下:

數字圖片 -> 神經網絡 -> 數字

如果我們反其道而行之,給定一個數字,讓神經網絡把該數字對應的”圖片“給畫出來,那麼我們就可以窺探神經網絡是如何通過自己的”意識“來認識這個世界了,也就是我們把上面流程改為:

數字圖片

通過上面的逆流程,我們就可以大概瞭解世界在神經網絡中的樣子。

我們已經知道,神經網絡得出的結果是先把輸入信號與鏈路權重做乘機後求和,然後在對求和結果應用激活函數,所的到的結果就是神經節點輸出的信號。反過來,我們拿到最終輸出結果後,先做激活函數的逆運算,得到信號與權重的乘機和,然後再除以鏈路權重還原回各個信號分量,將這些信號分量合成一幅圖像,我們就可以得知,神經網絡是如何看待這個世界的。

我們知道網絡節點的激活函數如下:

要把裡面的x解出來,我們就需要下面的公式:

在python中,它對應的代碼為scipy.special.logit(),於是利用這個公式,我們用如下代碼把結果反解出對應的圖片,相應的實現代碼如下:

import scipy.specialimport numpyclass NeuralNetWork: def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate): #初始化網絡,設置輸入層,中間層,和輸出層節點數 self.inodes = inputnodes self.hnodes = hiddennodes self.onodes = outputnodes #設置學習率 self.lr = learningrate ''' 初始化權重矩陣,我們有兩個權重矩陣,一個是wih表示輸入層和中間層節點間鏈路權重形成的矩陣 一個是who,表示中間層和輸出層間鏈路權重形成的矩陣 ''' self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5 self.who = numpy.random.rand(self.onodes, self.hnodes) - 0.5 self.activation_function = lambda x:scipy.special.expit(x) #設置激活函數的反函數 self.inverse_activation_function = lambda x:scipy.special.logit(x) pass def train(self, inputs_list, targets_list): #根據輸入的訓練數據更新節點鏈路權重 ''' 把inputs_list, targets_list轉換成numpy支持的二維矩陣 .T表示做矩陣的轉置 ''' inputs = numpy.array(inputs_list, ndmin=2).T targets = numpy.array(targets_list, ndmin=2).T #計算信號經過輸入層後產生的信號量 hidden_inputs = numpy.dot(self.wih, inputs) #中間層神經元對輸入的信號做激活函數後得到輸出信號 hidden_outputs = self.activation_function(hidden_inputs) #輸出層接收來自中間層的信號量 final_inputs = numpy.dot(self.who, hidden_outputs) #輸出層對信號量進行激活函數後得到最終輸出信號 final_outputs = self.activation_function(final_inputs) #計算誤差 output_errors = targets - final_outputs hidden_errors = numpy.dot(self.who.T, output_errors) #根據誤差計算鏈路權重的更新量,然後把更新加到原來鏈路權重上 self.who += self.lr * numpy.dot((output_errors * final_outputs *(1 - final_outputs)), numpy.transpose(hidden_outputs)) self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)), numpy.transpose(inputs)) pass def query(self, inputs): #根據輸入數據計算並輸出答案 #計算中間層從輸入層接收到的信號量 hidden_inputs = numpy.dot(self.wih, inputs) #計算中間層經過激活函數後形成的輸出信號量 hidden_outputs = self.activation_function(hidden_inputs) #計算最外層接收到的信號量 final_inputs = numpy.dot(self.who, hidden_outputs) #計算最外層神經元經過激活函數後輸出的信號量 final_outputs = self.activation_function(final_inputs) return final_outputs def backQuery(self, targets_list): # 將結果向量轉置以便反解出輸入信號量 final_outputs = numpy.array(targets_list, ndmin=2).T # 通過激活函數的反函數得到輸出層的輸入信號 final_inputs = self.inverse_activation_function(final_outputs) # 獲取中間層的輸出信號 hidden_outputs = numpy.dot(self.who.T, final_inputs) # 將信號量圓整到0.01和0.98之間 hidden_outputs -= numpy.min(hidden_outputs) hidden_outputs /= numpy.max(hidden_outputs) hidden_outputs *= 0.98 hidden_outputs += 0.01 #通過激活函數的反函數計算中間層獲得的輸入信號量 hidden_inputs = self.inverse_activation_function(hidden_outputs) # 計算輸入層的輸出信號量 inputs = numpy.dot(self.wih.T, hidden_inputs) # 將信號量圓整到0.01和0.98之間 inputs -= numpy.min(inputs) inputs /= numpy.max(inputs) inputs *= 0.98 inputs += 0.01 #input對應的就是輸入神經網絡的圖片像素數組 return inputs

上面代碼跟以前不一樣之處就在於多了一個backQuery函數,它接收結果向量,讓後反解出向量對應的輸入圖片,有了上面神經網絡後,我們先對其進行訓練,得到相應的神經元鏈路權重:

#初始化網絡input_nodes = 784hidden_nodes = 100output_nodes = 10learning_rate = 0.3n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)#讀入訓練數據#open函數里的路徑根據數據存儲的路徑來設定training_data_file = open("/Users/chenyi/Documents/人工智能/mnist_train.csv")trainning_data_list = training_data_file.readlines()print(len(trainning_data_list))training_data_file.close()#加入epocs,設定網絡的訓練循環次數epochs = 7print("begin trainning")for e in range(epochs): #把數據依靠','區分,並分別讀入 for record in trainning_data_list: all_values = record.split(',') inputs = (numpy.asfarray(all_values[1:]))/255.0 * 0.99 + 0.01 #設置圖片與數值的對應關係 targets = numpy.zeros(output_nodes) + 0.01 targets[int(all_values[0])] = 0.99 n.train(inputs, targets)print("trainning complete")

有了合適的鏈路權值後,我們構造一個結果向量,將它輸入到網絡中,讓網絡把它認為的向量對應的圖像輸出來:

label = 8targets = numpy.zeros(output_nodes) + 0.01targets[label] = 0.99print(targets)image_data = n.backQuery(targets)#print(image_data.reshape(28,28))matplotlib.pyplot.imshow(image_data.reshape(28,28), cmap="Greys", interpolation="None")

上面代碼構造了一個數字8對應的結果向量,然後傳入神經網絡看看網絡眼中的數字8是什麼樣子的,上面代碼執行後結果如下:

沒錯,上面模糊一片的圖案就是神經網絡對數組8的理解,我們慢慢觀察黑色部分,發覺它確實跟數字8有點像。這個圖案讓我想起了兩千年前古希臘哲學家柏拉圖提出一個叫”理型“的哲學概念,它的意思是說,每一個種類的事物,無論其外形如何變幻,他們本身都包含著一種超越時空,永恆不變的抽象屬性。舉個例子,給一個孩子看一隻黑馬的圖片,然後讓他看一隻白馬的圖片,他很容易能認出那是馬,也就是說不管黑馬白馬的外形差異如何,兩隻馬都具備相同的理型叫做”馬“,人正是因為感受到馬的”理型“,所以不管馬的形態如何變幻,人總能認出它是馬來,也就是說所有的馬都具備同一種抽象的超越時空,永恆不變的性質,正是這個性質將它與其他事物區分開來。

道可道,非常道。理型的概念就是”道可道“中的第一個道,也就是永恆不變的規律,通常情況下,”道“是很難用語言或某種顯著方式進行表達的。神經網絡通過計算大量的手寫數字圖片,通過計算抽取出了這些同一類數字圖片的共性,這個共性其實就是柏拉圖所說的理型,也就是所有手寫的數字”8“,都蘊含著某種不變的性質,神經網絡就是把這個性質抽取出來的方式,上面那張模糊的圖案,可以認為是所有手寫數字8的理型,網絡讀取的圖片數量越大,其對理型的把握就越準確。

如此一來”非常道“似乎就不成立了,神經網絡的結構以及鏈路上的權重,就可以用來對”道“進行描述,或者說一切手寫數字的”理型“可以用現在我們訓練的神經網絡來描述。

以神經網絡為基礎的深度學習模仿人的意識,在一定程度上也跟人的思維哲學有了交互之處,這就是技術發展與人文思想的交相輝映吧。至此我們對神經網絡的基本原理說明就告一段落,從下一章開始,我們進入神經網絡的實際應用層面。