到目前爲止,我們已經介紹完了神經網絡的基本結構,現在用一個圖像識別示例對前面的知識作整體的總結。本專題知識點如下:
- MNIST數據集
- 圖像數據轉圖像
- 神經網絡的推理處理
- 批處理
- MNIST數據集
MNIST數據集由0到9的數字圖像構成。像素取值在0到255之間。每個圖像數據都相應地標有“7”、“2”、“1”等數字標籤。MNIST數據集中,訓練數據有6萬張,測試圖像有1萬張。一般先用訓練數據進行學習,再用學習到的模型(參數)對測試圖像進行識別分類。MNIST數據集可以從官網下載,這裏我們用python獲取已經下載好並做過處理的MNIST數據集的相關信息:
#數據文件:mnist.pkl,大小約54M。
#文件讀取,位置
import pandas as pd
network=pd.read_pickle('F:/deep-learning with python/dataset/mnist.pkl')
type(network) #類型:字典
network.keys() #關鍵字['train_label', 'train_img', 'test_img', 'test_label']
#訓練數據形狀,(60000, 784),6萬個樣本,每個樣本由784個數據組成(1·28·28)
network['train_img'].shape
network['train_img'][0,:].max() #第一個樣本的最大值255
network['train_img'][0,:].min() #第一個樣本的最小值0,
network['train_label'].shape #訓練標籤形狀(60000,),由0至9組成的6萬個數據
network['train_label'].max() #最大值9
network['train_label'].min() #最小值0
network['train_label'] #訓練標籤:0~9
network['test_img'].shape #測試數據形狀(10000, 784),1萬個樣本
network['test_label'].shape#測試數據標籤形狀(10000,)
network['test_label'] #測試標籤:0~9
MNIST數據集保存在mnist.pkl,讀者可點擊:mnist數據集及權重參數下載 進行下載,mnist數據集下載的源碼如下:
# coding: utf-8
try:
import urllib.request
except ImportError:
raise ImportError('You should use Python 3.x')
import os.path
import gzip
import pickle
import os
import numpy as np
url_base = 'http://yann.lecun.com/exdb/mnist/'
key_file = {
'train_img':'train-images-idx3-ubyte.gz',
'train_label':'train-labels-idx1-ubyte.gz',
'test_img':'t10k-images-idx3-ubyte.gz',
'test_label':'t10k-labels-idx1-ubyte.gz'
}
dataset_dir = os.path.dirname(os.path.abspath(__file__))
save_file = dataset_dir + "/mnist.pkl"
train_num = 60000
test_num = 10000
img_dim = (1, 28, 28)
img_size = 784
def _download(file_name):
file_path = dataset_dir + "/" + file_name
if os.path.exists(file_path):
return
print("Downloading " + file_name + " ... ")
urllib.request.urlretrieve(url_base + file_name, file_path)
print("Done")
def download_mnist():
for v in key_file.values():
_download(v)
def _load_label(file_name):
file_path = dataset_dir + "/" + file_name
print("Converting " + file_name + " to NumPy Array ...")
with gzip.open(file_path, 'rb') as f:
labels = np.frombuffer(f.read(), np.uint8, offset=8)
print("Done")
return labels
def _load_img(file_name):
file_path = dataset_dir + "/" + file_name
print("Converting " + file_name + " to NumPy Array ...")
with gzip.open(file_path, 'rb') as f:
data = np.frombuffer(f.read(), np.uint8, offset=16)
data = data.reshape(-1, img_size)
print("Done")
return data
def _convert_numpy():
dataset = {}
dataset['train_img'] = _load_img(key_file['train_img'])
dataset['train_label'] = _load_label(key_file['train_label'])
dataset['test_img'] = _load_img(key_file['test_img'])
dataset['test_label'] = _load_label(key_file['test_label'])
return dataset
def init_mnist():
download_mnist()
dataset = _convert_numpy()
print("Creating pickle file ...")
with open(save_file, 'wb') as f:
pickle.dump(dataset, f, -1)
print("Done!")
def _change_one_hot_label(X):
T = np.zeros((X.size, 10))
for idx, row in enumerate(T):
row[X[idx]] = 1
return T
def load_mnist(normalize=True, flatten=True, one_hot_label=False):
"""讀入MNIST數據集
Parameters
----------
normalize : 將圖像的像素值正規化爲0.0~1.0
one_hot_label :
one_hot_label爲True的情況下,標籤作爲one-hot數組返回
one-hot數組是指[0,0,1,0,0,0,0,0,0,0]這樣的數組
flatten : 是否將圖像展開爲一維數組
Returns
-------
(訓練圖像, 訓練標籤), (測試圖像, 測試標籤)
"""
if not os.path.exists(save_file):
init_mnist()
with open(save_file, 'rb') as f:
dataset = pickle.load(f)
if normalize:
for key in ('train_img', 'test_img'):
dataset[key] = dataset[key].astype(np.float32)
dataset[key] /= 255.0
if one_hot_label:
dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
if not flatten:
for key in ('train_img', 'test_img'):
dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label'])
if __name__ == '__main__':
init_mnist()
爲方便訓練和預測,一般以(訓練數據,訓練標籤),(測試數據,測試標籤)的形式修改數據格式。
- 圖像數據轉圖像
MNIST數據集是圖像數據,每一張圖的大小爲28像素×28像素,像素值在0至255之間。在MNIST數據集中的形狀是以一列數組的形式保存的(784個像素),因此要顯示爲圖像時需要對數據進行相關轉換。PIL是Python的圖像庫,可用於顯示圖像,這裏我們用它來將MNIST圖像數據轉換爲圖像進行顯示,方便大家對本主題知識的理解 。
import numpy as np
from PIL import Image
img_data=network['train_img'][1] #訓練數據中第二個樣本的圖像數據,(784,)
img_label=network['train_label'][1] #訓練數據中第2個樣本的標籤爲
print(img_label) # 0
img=img_data.reshape(28,28)#轉爲圖像尺寸
print(img.shape) #(28, 28)
show_img=Image.fromarray(np.uint8(img)) #轉換爲PIL顯示圖像的數據格式
show_img.show() #圖像顯示,0
- 神經網絡的推理處理
前面介紹的PIL庫顯示的訓練數據中的第二個樣本的圖形是“0”,實際標籤也是“0”。所謂神經網絡的推理,即利用神經網絡對訓練數據進行學習(這裏我們先直接使用學到的參數,保存在sample_weight.pkl文件中),利用學到的參數(w,b)對測試數據(test_img)進行識別,然後將識別結果與實際標籤(test_label)進行比較,判斷推理是否正確。原理如下圖:
具體的推理處理過程爲:
圖像數據有784個像素(28×28),即輸入層有784個神經元,推理的結果是識別數字0到9,因此輸出層有10個神經元。此外,爲和前面知識點相連,我們在這個神經網絡中添加了2個隱藏層,第一個隱藏層有50個神經元,第二個隱藏層有100個神經元,隱藏層中神經元的個數可自己設置。由於本主題不涉及參數的學習,因此我們直接使用已學習到的參數,它保存在文件sample_weight.pkl中,根據我們設計的神經網絡的結構,我們應該知道參數的結構。下面是神經網絡推理的代碼:
parms=pd.read_pickle('F:/deep-learning with python/ch03/sample_weight.pkl')
type(parms) #字典
parms.keys() #['b1', 'W1', 'b2', 'W2', 'b3', 'W3']
parms['b1'].shape #(50,)
parms['W1'].shape #(784,50)
parms['b2'].shape #(100,)
parms['W2'].shape #(50,100)
parms['b3'].shape #(10,)
parms['W3'].shape #(100,10)
def predict(parms,x):
"""
代碼同三層神經網絡的實現一樣,只是將隨機參數改爲實際學到的參數
6激活函數在前面專題已給出
"""
W1,W2,W3=parms['W1'],parms['W2'],parms['W3']
b1,b2,b3=parms['b1'],parms['b2'],parms['b3']
a1=np.dot(x,W1)+b1
z1=sigmoid(a1) #激活函數可在以前的文章中找到
a2=np.dot(z1,W2)+b2
z2=sigmoid(a2)
a3=np.dot(z2,W3)+b3
y=softmax(a3) #激活函數可在以前的文章中找到
return y
test_img=network['test_img'] #測試數據
test_label=network['test_label'] #測試標籤
accuracy_cnt=0
for i in range(len(test_img)):
y=predict(parms,test_img[i])
p=np.argmax(y)#獲取概率最高的元素的索引
if p==test_label[i]:
accuracy_cnt+=1
print("識別精度:"+str(float(accuracy_cnt)/len(test_img))) #0.9352
下面我們對代碼做簡單介紹,首先提取測試數據和測試標籤。接着用for循環逐一取出測試數據中的圖像數據,然後用predict()函數進行分類,該函數輸出各個標籤對應的概率,比如輸出[0.1,0.2,0.4…,0.03],表示“0”的概率爲0.1,1的概率爲0.2,9的概率爲0.03。然後我們取出這個概率列表中的最大值的索引即爲分類結果。最後比較神經網絡預測的結果和正確解標籤(test_label),對1萬張圖預測正確的概率作爲識別精度(93.52%)。
在機器學習領域中,一般需要考慮數據預處理,這裏我們可將像素0至255可縮小到0至1的範圍內(即對所有數據均除以255),然後再輸入至神經網絡中,這種將數據限制在某個範圍內的處理稱爲正則化,一般情況下,預處理會改善機器學習模型。讀者可比較一下圖像數據正則化後神經網絡的識別精度。
- 批處理
上面只介紹了輸入一張圖像數據時的處理流程。即每次向神經網絡中輸入一個由784個元素(原本是一個28·28的二維數組)構成的一維數組後,輸出一個有10個元素的一維數組。其數據形狀如下:
現在我們想predict()函數一次性打包處理100張圖像。爲此可把X的形狀改爲100×784,將100張圖片打包作爲輸入數據。數據形狀如下:
可見,輸入數據的形狀爲100×784,輸出數據的形狀爲100×100,這說明了輸入的100張圖像的推理結果被一次性輸出了。例如x[0]、x[1]....x[99]和y[0]、y[1]....y[99]保存了第1、2....到100張圖像的圖像數據及其推理結果。這種被打包的輸入數據被稱爲批(batch),批處理主要集中在數據計算上,而不是數據讀入,因此批處理可縮短時間開銷。下面用代碼實現如下:
test_img=network['test_img'] #測試數據
test_label=network['test_label'] #測試標籤
batch_size=100 #批數量
accuracy_cnt=0 #初始識別精度
for i in range(0,len(test_img),batch_size):
x_batch=test_img[i:i+batch_size]
y_batch=predict(parms,x_batch)
p=np.argmax(y_batch,axis=1)#取每列最大值
accuracy_cnt+=np.sum(p==t[i:i+batch_size])
print("識別精度:"+str(float(accuracy_cnt)/len(test_img)))
批處理代碼核心在於for循環語句添加了步數batch_size,輸入predict()函數的參數x由以前的單條數據變爲x_batch表示的100條數據,尋找二維數組中每行的最大值所在的列位置使用了參數axis=1。
至此,神經網絡的基本知識已經講解完了,後面的內容主要講解權重參數的學習!歡迎關注微信公衆號“Python生態智聯”,學知識,享生活!