咱們知道深度學習是機器學習的一個分支,是一種以人工神經網絡爲架構,對數據進行表徵學習的算法。而深度神經網絡又是深度學習的一個分支,它在 wikipedia 上的解釋以下:html
深度神經網絡(Deep Neural Networks, DNN)是一種判別模型,具有至少一個隱層的神經網絡,可使用反向傳播算法進行訓練。權重更新可使用下式進行隨機梯度降低法求解。
首先咱們能夠知道,深度神經網絡是一種判別模型。意思就是已知變量 x ,經過判別模型能夠推算出 y。好比機器學習中經常使用到的案例,經過手寫數字,模型推斷出手寫的是數字幾。node
深度神經網絡中的「深度」指的是一系列連續的表示層,數據模型中包含了多少層,這就被稱爲模型的「深度」。經過這些層咱們能夠對數據進行高層的抽象。以下圖所示,深度神級網絡由一個輸入層,多個(至少一個)隱層,以及一個輸出層構成,並且輸入層與輸出層的數量不必定是對等的。每一層都有若干個神經元,神經元之間有鏈接權重。python
仍是上面的案例,識別手寫數字,手寫的數字要怎麼轉成輸入呢?既然是手寫,那麼確定是一張圖片,圖片由多個像素點組成,這些像素點能夠構成一個輸入,通過多層神經網絡,輸出10個數字,這個10個數字就表明了數字 0 ~ 9 的機率。linux
神經網絡中的每一個神經元均可以當作是一個簡單的線性函數,下面咱們構造一個簡單的三層的神經網絡來看看。git
如上圖所示,n1 能夠表示爲:github
$$ 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。你可能會問,爲何每一個神經元要與其餘全部層的神經元相互鏈接?npm
這裏主要由兩個緣由:編程
實際上經過計算獲得 n1 後,其實不能立馬用於後面的計算,還須要通過一個激活函數(通常爲 sigmod 函數)。 json
其做用主要是引入非線性因素。若是神級網絡中只有上面那種線性函數,不管有多少層,結果始終是線性的。
爲了方便計算,咱們構造一個只有兩層的神經網絡,演示一下具體的計算過程。
先經過線性函數求得一個 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 notebook
jupyter 會在8888端口起一個服務,並自動打開瀏覽器。
經過右上角的new,你就能建立一個項目了。建立項目後,咱們很方便的在該頁面上進行 python 代碼的運行與輸出。
MNIST 是由美國的高中生和美國人口調查局的職員手寫數字(0 ~ 9)圖片。接下來要作的事情就是讓咱們的程序學習這些圖片的信息,可以識別出輸入的圖片所表明的數字含義,這聽上去好像有點難度,不着急,咱們一步步來。
這裏準備了 MNIST 的訓練數據,其中 train_100
爲訓練數據集,test_10
爲測試數據集。在機器學習的過程當中,咱們通常會將數據集切分紅兩個,分別爲訓練集合測試集,通常 80% 的數據進行訓練,保留 20% 用於測試。這裏由於是 hello world 操做,咱們只用 100 個數據進行訓練,真實狀況下,這種數據量是遠遠不夠的。
若是想用完整的數據進行訓練,能夠下載這個 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神經網絡編程》這本書,由於它纔有了這個博客,若是感興趣你也能夠買來看看,這本書真的用很簡單的語言描述了複雜的數學計算。
人工智能如今確實是一個很是火熱的階段,但願感興趣的同窗們多多嘗試,可是也不要一昧的追新,忘記了本身原本的優點。
最後附上原文連接:深度神經網絡原理與實踐