摘要:理論基礎什么是神經網絡我們知道深度學習是機器學習的一個分支,是一種以人工神經網絡為架構,對數據進行表征學習的算法。深度神經網絡中的深度指的是一系列連續的表示層,數據模型中包含了多少層,這就被稱為模型的深度。
理論基礎 什么是神經網絡
我們知道深度學習是機器學習的一個分支,是一種以人工神經網絡為架構,對數據進行表征學習的算法。而深度神經網絡又是深度學習的一個分支,它在 wikipedia 上的解釋如下:
深度神經網絡(Deep Neural Networks, DNN)是一種判別模型,具備至少一個隱層的神經網絡,可以使用反向傳播算法進行訓練。權重更新可以使用下式進行隨機梯度下降法求解。
首先我們可以知道,深度神經網絡是一種判別模型。意思就是已知變量 x ,通過判別模型可以推算出 y。比如機器學習中常用到的案例,通過手寫數字,模型推斷出手寫的是數字幾。
深度神經網絡中的“深度”指的是一系列連續的表示層,數據模型中包含了多少層,這就被稱為模型的“深度”。通過這些層我們可以對數據進行高層的抽象。如下圖所示,深度神級網絡由一個輸入層,多個(至少一個)隱層,以及一個輸出層構成,而且輸入層與輸出層的數量不一定是對等的。每一層都有若干個神經元,神經元之間有連接權重。
還是上面的案例,識別手寫數字,手寫的數字要怎么轉成輸入呢?既然是手寫,那么肯定是一張圖片,圖片由多個像素點組成,這些像素點可以構成一個輸入,經過多層神經網絡,輸出10個數字,這個10個數字就代表了數字 0 ~ 9 的概率。
神經元如何輸入輸出神經網絡中的每個神經元都可以看成是一個簡單的線性函數,下面我們構造一個簡單的三層的神經網絡來看看。
如上圖所示,n1 可以表示為:
$$ n_1 = w_{1,1}x_1 + w_{2,1}x_2 + w_{3,1}x_3 + b $$
其中 w_{1,1} 表示神經元之間的權重,b 為一個常量,作為函數的偏移量。較小的權重可以弱化某個神經元對下一個神經元造成的影響,而較大的權重將放大信號。假設 w_{1,1} 為 0.1,w_{3,1} 為 0.7,那么 x3 對 n1 的影響要大于 x1。你可能會問,為什么每個神經元要與其他所有層的神經元相互連接?
這里主要由兩個原因:
完全連接的形式相對容易的編寫成計算機指令。
在神經網絡訓練的過程中會弱化實際上不需要的連接(也就是某些連接權重會慢慢趨近于 0)。
實際上通過計算得到 n1 后,其實不能立馬用于后面的計算,還需要經過一個激活函數(一般為 sigmod 函數)。
其作用主要是引入非線性因素。如果神級網絡中只有上面那種線性函數,無論有多少層,結果始終是線性的。
實際案例為了方便計算,我們構造一個只有兩層的神經網絡,演示一下具體的計算過程。
先通過線性函數求得一個 x 值,再把 x 值帶入激活函數,得到 y1 的值。
$$ x = w_{1,1}x_1 + w_{2,1}x_2 = (1.0 * 0.9) + (0.5 * 0.3) = 1.05 $$
$$ y_1 = 1 / (1 + e ^{-x}) = 1 / (1 + 0.3499) = 0.7408 $$
矩陣乘法其實上面的計算過程,很容易通過矩陣乘法的方式表示。矩陣這個東西,說簡單點就是一個表格,或者一個二維數組。如下圖所示,就是一個典型的矩陣。
那么矩陣的乘法可以表示為:
矩陣的乘法通常被成為點乘或者內積。如果我們將矩陣內的數字換成我們神經網絡的輸入和權重,你會發現原來前面的計算如此簡單。
獲得點積后,只需要代入到激活函數,就能獲得輸出了。
通過矩陣計算過程可以表示為:
$$ X_{hidden} = W_{input\_hidden} · I_{input} O_{hidden} = sigmoid(X_{hidden}) $$
實際案例下面通過矩陣來表示一個三層神經網絡的計算過程。
上圖只給出了輸入層到隱層的計算過程,感興趣可以自己手動計算下,隱層到輸出層的計算過程。隱層到輸出層的權重矩陣如下:
反向傳播進過一輪神經網絡計算得到輸出值,通常與我們實際想要的值是不一致的,這個時候我們會得到一個誤差值(誤差值就是訓練數據給出的正確答案與實際輸出值之間的差值)。但是這個誤差是多個節點共同作用的結果,我們到底該用何種方式來更新各個連接的權重呢?這個時候我們就需要通過反向傳播的方式,求出各個節點的誤差值。
下面我們代入具體值,進行一次計算。
上圖中可以看到 e_1 的誤差值主要由 w_{1,1} 和 w_{2,1} 造成,那么其誤差應當分散到兩個連接上,可以按照兩個連接的權重對誤差 e_1 進行分割。
$$ e_1 * frac{w_{1,1}}{w_{1,1} + w_{2,1}} = 0.8 * frac{2}{2 + 3} = 0.32 e_1 * frac{w_{2,1}}{w_{1,1} + w_{2,1}} = 0.8 * frac{3}{2 + 3} = 0.48 $$
同理對誤差 e_2 進行分割,然后把兩個連接處的誤差值相加,就能得到輸出點的前饋節點的誤差值。
然后在按照之前的方法將這個誤差傳播到前面的層,直到所有節點都能得到自己的誤差值,這種方式被成為反向傳播。
使用矩陣乘法進行反向傳播誤差上面如此繁瑣的操作,我們也可以通過矩陣的方式進行簡化。
這個矩陣中還是有麻煩的分數需要處理,那么我們能不能大膽一點,將分母直接做歸一化的處理。這么做我們僅僅只是改變了反饋誤差的大小,其誤差依舊是按照比例來計算的。
仔細觀察會發現,與我們之前計算每層的輸出值的矩陣點擊很像,只是權重矩陣進行翻轉,右上方的元素變成了左下方的元素,我們可以稱其為轉置矩陣,記為 w^T 。
反向傳播誤差的矩陣可以簡單表示為:
$$ error_{hidden} = W^{T}_{hidden\_output} · error_{output} $$
梯度下降在每個點都得到誤差后,我們該按照何種方式來更新權重呢?
這個時候就要使用到機器學習中常用的方式:梯度下級。
更多細節可以參考我之前寫的博客:梯度下降與線性回歸
通過不停的訓練,我們就能改進神經網絡,其本質就是不斷地改變權重的大小,減小神經網絡輸出的誤差值。
最后就能夠得到一個多層神經網絡的模型,通過輸入進行有效的預測。
首先需要安裝 python3 ,直接去 python 官網安裝,盡量安裝最新版,不推薦安裝 python2 。安裝好 python 環境之后,然后安裝 virtualenv 以及相關依賴。
# 升級 pip 到最新版本 pip3 install --upgrade pip # 安裝 virtualenv ,用于配置虛擬環境 pip3 install --user --upgrade virtualenv
正常情況下,當我們在使用 pip 進行包安裝的時候,都是安裝的全局包,相當于npm install -g。假如現在有兩個項目,項目 A 依賴 simplejson@2 ,項目 B 依賴 simplejson@3,這樣我們在一臺機器上開發顯得有些手足無措。這個時候 virtualenv 就能大展身手了,virtualenv 可以創建一個獨立的 python 運行環境,也就是一個沙箱,你甚至可以在 virtualenv 創建的虛擬環境中使用與當前系統不同的 python 版本。
# 配置虛擬環境 cd ~/ml virtualenv env # 啟動虛擬環境 # linux source env/bin/activate # windows ./env/Scripts/activate
啟動后,如下
(env) λ
在虛擬環境下安裝所有模塊依賴。
# 安裝模塊和依賴 (env) λ pip3 install --upgrade jupyter matplotlib numpy scipy
jupyter:基于網頁的用于交互計算的應用程序。其可被應用于全過程計算:開發、文檔編寫、運行代碼和展示結果。
numpy:數組計算擴展的包,支持高維度數組與矩陣運算,此外也針對數組運算提供大量的數學函數庫。
scipy:基于numpy的擴展包,它增加的功能包括數值積分、最優化、統計和一些專用函數。
matplotlib:基于numpy的擴展包,提供了豐富的數據繪圖工具,主要用于繪制一些統計圖形。
scikit-learn:開源的Python機器學習庫,它基于Numpy和Scipy,提供了大量用于數據挖掘和分析的工具,包括數據預處理、交叉驗證、算法與可視化算法等一系列接口。
啟動 jupyterjupyter notebook
jupyter 會在8888端口起一個服務,并自動打開瀏覽器。
通過右上角的new,你就能創建一個項目了。創建項目后,我們很方便的在該頁面上進行 python 代碼的運行與輸出。
準備數據MNIST 是由美國的高中生和美國人口調查局的職員手寫數字(0 ~ 9)圖片。接下來要做的事情就是讓我們的程序學習這些圖片的信息,能夠識別出輸入的圖片所代表的數字含義,這聽上去好像有點難度,不著急,我們一步步來。
這里準備了 MNIST 的訓練數據,其中 train_100 為訓練數據集,test_10 為測試數據集。在機器學習的過程中,我們一般會將數據集切分成兩個,分別為訓練集合測試集,一般 80% 的數據進行訓練,保留 20% 用于測試。這里因為是 hello world 操作,我們只用 100 個數據進行訓練,真實情況下,這種數據量是遠遠不夠的。
mnist_train_100.csv
mnist_test_10.csv
如果想用完整的數據進行訓練,可以下載這個 csv 文件。
https://pjreddie.com/media/files/mnist_train.csv
觀察數據下載數據后,將 csv (逗號分隔值文件格式)文件放入到 datasets 文件夾,然后使用 python 進行文件的讀取。
data_file = open("datasets/mnist_train_100.csv", "r") data_list = data_file.readlines() # readlines方法用于讀取文件的所有行,并返回一個數組 data_file.close() len(data_list) # 數組長度為100
打印第一行文本,看看數據的格式是怎么樣的
print(data_list[0]) len(data_list[0].split(",")) # 使用 , 進行分割,將字符串轉換為數組
可以看到一行數據一共有 785 個數據,第一列表示這個手寫數的真實值(這個值在機器學習中稱為標簽),后面的 784 個數據表示一個 28 * 28 的尺寸的像素值,流行的圖像處理軟件通常用8位表示一個像素,這樣總共有256個灰度等級(像素值在0~255 間),每個等級代表不同的亮度。
下面我們導入 numpy 庫,對數據進行處理,values[1:] 取出數組的第一位到最后并生成一個新的數組,使用 numpy.asfarray 將數組轉為一個浮點類型的 ndarray,然后每一項除以 255 在乘以 9,將每個數字轉為 0 ~ 9 的個位數,使用 astype(int) 把每個數再轉為 int 類型,最后 reshape((28,28) 可以把數組轉為 28 * 28 的二維數組。
如果想了解更多 numpy 的資料,可以查看它的文檔。
import numpy as np values = data_list[3].split(",") image_array = (np.asfarray(values[1:]) / 255 * 9).astype(int).reshape(28,28)
這樣看不夠直觀,接下來使用 matplotlib ,將像素點一個個畫出來。
import matplotlib.pyplot %matplotlib inline matplotlib.pyplot.imshow( np.asfarray(values[1:]).reshape(28,28), cmap="Greys", interpolation="None" )搭建神經網絡
我們簡單勾勒出神經網絡的大概樣子,至少需要三個函數:
初始化函數——設定輸入層、隱藏層、輸出層節點的數量,隨機生成的權重。
訓練——學習給定的訓練樣本,調整權重。
查詢——給定輸入,獲取預測結果。
框架代碼如下:
# 引入依賴庫 import numpy as np import scipy.special import matplotlib.pyplot # 神經網絡類定義 class neuralNetwork: # 初始化神經網絡 def __init__(): pass # 訓練神經網絡 def train(): pass # 查詢神經網絡 def query(): pass初始化神經網絡
接下來讓我們進行第一步操作,初始化一個神經網絡。
# 初始化神經網絡 def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate): # 設置輸入層、隱藏層、輸出層節點的數量 self.inodes = inputnodes self.hnodes = hiddennodes self.onodes = outputnodes # 連接權重,隨機生成輸入層到隱藏層和隱藏層到輸出層的權重 self.wih = np.random.rand(self.hnodes, self.inodes) - 0.5 self.who = np.random.rand(self.onodes, self.hnodes) - 0.5 # 學習率 self.lr = learningrate # 將激活函數設置為 sigmoid 函數 self.activation_function = lambda x: scipy.special.expit(x) pass
生成權重
生成連接權重使用 numpy 函數庫,該庫支持大維度數組以及矩陣的運算,通過numpy.random.rand(x, y)可以快速生成一個 x * y 的矩陣,每個數字都是一個 0 ~ 1 的隨機數。因為導入庫的時候使用了 import numpy as np 命令,所有代碼中可以用 np 來代替 numpy。
上面就是通過 numpy.random.rand 方法生成一個 3 * 3 矩陣的案例。減去0.5是為了保證生成的權重所有權重都能維持在 -0.5 ~ 0.5 之間的一個隨機值。
激活函數
scipy.special 模塊中包含了大量的函數庫,利用 scipy.special 庫可以很方便快捷的構造出一個激活函數:
activation_function = lambda x: scipy.special.expit(x)查詢神經網絡
# 查詢神經網絡 def query(self, inputs_list): # 將輸入的數組轉化為一個二維數組 inputs = np.array(inputs_list, ndmin=2).T # 計算輸入數據與權重的點積 hidden_inputs = np.dot(self.wih, inputs) # 經過激活函數的到隱藏層數據 hidden_outputs = self.activation_function(hidden_inputs) # 計算隱藏層數據與權重的點積 final_inputs = np.dot(self.who, hidden_outputs) # 最終到達輸出層的數據 final_outputs = self.activation_function(final_inputs) return final_outputs
查詢神經網絡的操作很簡單,只需要使用 numpy 的 dot 方法對兩個矩陣求點積即可。
這里有一個知識點,就是關于 numpy 的數據類型,通過 numpy.array 方法能夠將 python 中的數組轉為一個 N 維數組對象 Ndarray,該方法第二個參數就是表示轉化后的維度。
上圖是一個普通數組 [1, 2, 3] 使用該方法轉變成二維數組,返回 [[1, 2, 3]]。該方法還有個屬性 T,本質是調用 numpy 的 transpose 方法,對數組進行軸對換,如下圖所示。
通過轉置我們就能得到一個合適的輸入矩陣了。
訓練神經網絡# 訓練神經網絡 def train(self, inputs_list, targets_list): # 將輸入數據與目標數據轉為二維數組 inputs = np.array(inputs_list, ndmin=2).T targets = np.array(targets_list, ndmin=2).T # 通過矩陣點積和激活函數得到隱藏層的輸出 hidden_inputs = np.dot(self.wih, inputs) hidden_outputs = self.activation_function(hidden_inputs) # 通過矩陣點積和激活函數得到最終輸出 final_inputs = np.dot(self.who, hidden_outputs) final_outputs = self.activation_function(final_inputs) # 獲取目標值與實際值的差值 output_errors = targets - final_outputs # 反向傳播差值 hidden_errors = np.dot(self.who.T, output_errors) # 通過梯度下降法更新隱藏層到輸出層的權重 self.who += self.lr * np.dot( (output_errors * final_outputs * (1.0 - final_outputs)), np.transpose(hidden_outputs) ) # 通過梯度下降法更新輸入層到隱藏層的權重 self.wih += self.lr * np.dot( (hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs) ) pass
訓練神經網絡前半部分與查詢類似,中間會將得到的差值通過求矩陣點積的方式進行反向傳播,最后就是使用梯度下級的方法修正權重。其中 self.lr 為梯度下降的學習率,這個值是限制梯度方向的速率,我們需要經常調整這個值來達到模型的最優解。
進行訓練# 設置每一層的節點數量 input_nodes = 784 hidden_nodes = 100 output_nodes = 10 # 學習率 learning_rate = 0.1 # 創建神經網絡模型 n = neuralNetwork(input_nodes,hidden_nodes,output_nodes, learning_rate) # 加載訓練數據 training_data_file = open("datasets/mnist_train_100.csv", "r") training_data_list = training_data_file.readlines() training_data_file.close() # 訓練神經網絡 # epochs 表示訓練次數 epochs = 10 for e in range(epochs): # 遍歷所有數據進行訓練 for record in training_data_list: # 數據通過 "," 分割,變成一個數組 all_values = record.split(",") # 分離出圖片的像素點到一個多帶帶數組 inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 # 創建目標輸出值(數字 0~9 出現的概率,默認全部為 0.01) targets = np.zeros(output_nodes) + 0.01 # all_values[0] 表示手寫數字的真實值,將該數字的概率設為 0.99 targets[int(all_values[0])] = 0.99 n.train(inputs, targets) pass pass # 訓練完畢 print("done")驗證訓練結果
# 加載測試數據 test_data_file = open("datasets/mnist_test_10.csv", "r") test_data_list = test_data_file.readlines() test_data_file.close() # 測試神經網絡 # 記錄所有的訓練值,正確存 1 ,錯誤存 0 。 scorecard = [] # 遍歷所有數據進行測試 for record in test_data_list: # 數據通過 "," 分割,變成一個數組 all_values = record.split(",") # 第一個數字為正確答案 correct_label = int(all_values[0]) # 取出測試的輸入數據 inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 # 查詢神經網絡 outputs = n.query(inputs) # 取出概率最大的數字,表示輸出 label = np.argmax(outputs) # 打印出真實值與查詢值 print("act: ", label, " pre: ", correct_label) if (label == correct_label): # 神經網絡查詢結果與真實值匹配,記錄數組存入 1 scorecard.append(1) else: # 神經網絡查詢結果與真實值不匹配,記錄數組存入 0 scorecard.append(0) pass pass # 計算訓練的成功率 scorecard_array = np.asarray(scorecard) print("performance = ", scorecard_array.sum() / scorecard_array.size)完整代碼
要查看完整代碼可以訪問我的 github: deep_neural_network
總結到這里整個深度神級網絡的模型原理與實踐已經全部進行完畢了,雖然有些部分概念講解并不是那么仔細,但是你還可以通過搜索其他資料了解更多。感謝《Python神經網絡編程》這本書,因為它才有了這個博客,如果感興趣你也可以買來看看,這本書真的用很簡單的語言描述了復雜的數學計算。
人工智能現在確實是一個非常火熱的階段,希望感興趣的同學們多多嘗試,但是也不要一昧的追新,忘記了自己本來的優勢。
最后附上原文鏈接:深度神經網絡原理與實踐
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43398.html
閱讀 2342·2021-11-24 09:39
閱讀 3788·2021-11-19 09:40
閱讀 2157·2021-09-27 13:36
閱讀 1902·2019-08-30 15:44
閱讀 397·2019-08-30 13:52
閱讀 2716·2019-08-30 11:13
閱讀 2189·2019-08-29 16:18
閱讀 1763·2019-08-29 15:43