咱們知道深度學習是機器學習的一個分支,是一種以人工神經網絡爲架構,對數據進行表徵學習的算法。而深度神經網絡又是深度學習的一個分支,它在 wikipedia 上的解釋以下:html
深度神經網絡(Deep Neural Networks, DNN)是一種判別模型,具有至少一個隱層的神經網絡,可使用反向傳播算法進行訓練。權重更新可使用下式進行隨機梯度降低法求解。node
首先咱們能夠知道,深度神經網絡是一種判別模型。意思就是已知變量 x ,經過判別模型能夠推算出 y。好比機器學習中經常使用到的案例,經過手寫數字,模型推斷出手寫的是數字幾。python
深度神經網絡中的「深度」指的是一系列連續的表示層,數據模型中包含了多少層,這就被稱爲模型的「深度」。經過這些層咱們能夠對數據進行高層的抽象。以下圖所示,深度神級網絡由一個輸入層,多個(至少一個)隱層,以及一個輸出層構成,並且輸入層與輸出層的數量不必定是對等的。每一層都有若干個神經元,神經元之間有鏈接權重。linux
仍是上面的案例,識別手寫數字,手寫的數字要怎麼轉成輸入呢?既然是手寫,那麼確定是一張圖片,圖片由多個像素點組成,這些像素點能夠構成一個輸入,通過多層神經網絡,輸出10個數字,這個10個數字就表明了數字 0 ~ 9 的機率。git
神經網絡中的每一個神經元均可以當作是一個簡單的線性函數,下面咱們構造一個簡單的三層的神經網絡來看看。github
如上圖所示,n1 能夠表示爲:算法
其中 表示神經元之間的權重,b 爲一個常量,做爲函數的偏移量。較小的權重能夠弱化某個神經元對下一個神經元形成的影響,而較大的權重將放大信號。假設
爲 0.1,
爲 0.7,那麼 x3 對 n1 的影響要大於 x1。你可能會問,爲何每一個神經元要與其餘全部層的神經元相互鏈接?npm
這裏主要由兩個緣由:編程
實際上經過計算獲得 n1 後,其實不能立馬用於後面的計算,還須要通過一個激活函數(通常爲 sigmod 函數)。json
其做用主要是引入非線性因素。若是神級網絡中只有上面那種線性函數,不管有多少層,結果始終是線性的。
爲了方便計算,咱們構造一個只有兩層的神經網絡,演示一下具體的計算過程。
先經過線性函數求得一個 x 值,再把 x 值帶入激活函數,獲得 y1 的值。
其實上面的計算過程,很容易經過矩陣乘法的方式表示。矩陣這個東西,說簡單點就是一個表格,或者一個二維數組。以下圖所示,就是一個典型的矩陣。
那麼矩陣的乘法能夠表示爲:
矩陣的乘法一般被成爲點乘或者內積。若是咱們將矩陣內的數字換成咱們神經網絡的輸入和權重,你會發現原來前面的計算如此簡單。
得到點積後,只須要代入到激活函數,就能得到輸出了。
經過矩陣計算過程能夠表示爲:
下面經過矩陣來表示一個三層神經網絡的計算過程。
上圖只給出了輸入層到隱層的計算過程,感興趣能夠本身手動計算下,隱層到輸出層的計算過程。隱層到輸出層的權重矩陣以下:
進過一輪神經網絡計算獲得輸出值,一般與咱們實際想要的值是不一致的,這個時候咱們會獲得一個偏差值(偏差值就是訓練數據給出的正確答案與實際輸出值之間的差值)。可是這個偏差是多個節點共同做用的結果,咱們到底該用何種方式來更新各個鏈接的權重呢?這個時候咱們就須要經過反向傳播的方式,求出各個節點的偏差值。
下面咱們代入具體值,進行一次計算。
上圖中能夠看到 的偏差值主要由
和
形成,那麼其偏差應當分散到兩個鏈接上,能夠按照兩個鏈接的權重對偏差
進行分割。
同理對偏差 進行分割,而後把兩個鏈接處的偏差值相加,就能獲得輸出點的前饋節點的偏差值。
而後在按照以前的方法將這個偏差傳播到前面的層,直到全部節點都能獲得本身的偏差值,這種方式被成爲反向傳播。
上面如此繁瑣的操做,咱們也能夠經過矩陣的方式進行簡化。
這個矩陣中仍是有麻煩的分數須要處理,那麼咱們能不能大膽一點,將分母直接作歸一化的處理。這麼作咱們僅僅只是改變了反饋偏差的大小,其偏差依舊是按照比例來計算的。
仔細觀察會發現,與咱們以前計算每層的輸出值的矩陣點擊很像,只是權重矩陣進行翻轉,右上方的元素變成了左下方的元素,咱們能夠稱其爲轉置矩陣,記爲 。
反向傳播偏差的矩陣能夠簡單表示爲:
在每一個點都獲得偏差後,咱們該按照何種方式來更新權重呢?
這個時候就要使用到機器學習中經常使用的方式:梯度下級。
更多細節能夠參考我以前寫的博客:梯度降低與線性迴歸
經過不停的訓練,咱們就能改進神經網絡,其本質就是不斷地改變權重的大小,減少神經網絡輸出的偏差值。 最後就可以獲得一個多層神經網絡的模型,經過輸入進行有效的預測。
首先須要安裝 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,提供了大量用於數據挖掘和分析的工具,包括數據預處理、交叉驗證、算法與可視化算法等一系列接口。
jupyter notebook
複製代碼
jupyter 會在8888端口起一個服務,並自動打開瀏覽器。
經過右上角的new,你就能建立一個項目了。建立項目後,咱們很方便的在該頁面上進行 python 代碼的運行與輸出。
MNIST 是由美國的高中生和美國人口調查局的職員手寫數字(0 ~ 9)圖片。接下來要作的事情就是讓咱們的程序學習這些圖片的信息,可以識別出輸入的圖片所表明的數字含義,這聽上去好像有點難度,不着急,咱們一步步來。
這裏準備了 MNIST 的訓練數據,其中 train_100
爲訓練數據集,test_10
爲測試數據集。在機器學習的過程當中,咱們通常會將數據集切分紅兩個,分別爲訓練集合測試集,通常 80% 的數據進行訓練,保留 20% 用於測試。這裏由於是 hello world 操做,咱們只用 100 個數據進行訓練,真實狀況下,這種數據量是遠遠不夠的。
若是想用完整的數據進行訓練,能夠下載這個 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神經網絡編程》這本書,由於它纔有了這個博客,若是感興趣你也能夠買來看看,這本書真的用很簡單的語言描述了複雜的數學計算。
人工智能如今確實是一個很是火熱的階段,但願感興趣的同窗們多多嘗試,可是也不要一昧的追新,忘記了本身原本的優點。
最後附上原文連接:深度神經網絡原理與實踐