TensorFlow系列專題(六):實戰專案Mnist手寫資料集識別
歡迎大家關注我們的網站和系列教程:http://panchuang.net/ ,學習更多的機器學習、深度學習的知識!
目錄:
- 導讀
- MNIST資料集
- 資料處理
- 單層隱藏層神經網路的實現
- 多層隱藏層神經網路的實現
- 導讀
- MNIST資料集
我們將下載的壓縮檔案解壓後會發現數據都是以二進位制檔案的形式儲存的,以訓練集的影象資料為例:
表1 訓練集影象資料的檔案格式
[offset] | [type] | [value] | [description] |
0000 | 32 bit integer | 0x00000803(2051) | magic number |
0004 | 32 bit integer | 60000 | number of images |
0008 | 32 bit integer | 28 | number of rows |
0012 | 32 bit integer | 28 | number of columns |
0016 | unsigned byte | ?? | pixel |
017 | unsigned byte | ?? | pixel |
…… | |||
xxxx | unsigned byte | ?? | pixel |
訓練集類標檔案的格式如下:
表2 訓練集類標資料的檔案格式
[offset] | [type] | [value] | [description] |
0000 | 32 bit integer | 0x00000801(2049) | magic number(MSB first) |
0004 | 32 bit integer | 60000 | number of items |
0008 | unsigned byte | ?? | label |
0009 | unsigned byte | ?? | label |
…… | |||
xxxx | unsigned byte | ?? | label |
圖1 訓練集影象資料視覺化效果
- 資料處理
1 | import numpy as np |
2 | import struct |
3 | import random |
4 | |
5 | class MnistData: |
6 | def __init__(self, train_image_path, train_label_path, |
7 | test_image_path, test_label_path): |
8 | # 訓練集和測試集的檔案路徑 |
9 | self.train_image_path = train_image_path |
10 | self.train_label_path = train_label_path |
11 | self.test_image_path = test_image_path |
12 | self.test_label_path = test_label_path |
13 | |
14 | # 獲取訓練集和測試集資料 |
15 | # get_data()方法,引數為0獲取訓練集資料,引數為1獲取測試集 |
16 | self.train_images, self.train_labels = self.get_data(0) |
17 | self.test_images, self.test_labels = self.get_data(1) |
18 | |
19 | # 定義兩個輔助變數,用來判斷一個回合的訓練是否完成 |
20 | self.num_of_batch = 0 |
21 | self.got_batch = 0 |
接下來我們要實現“MnistData”類的另一個方法“get_data”,該方法實現了Mnist資料集的讀取以及資料的預處理。
22 | def get_data(self, data_type): |
23 | if data_type == 0: # 獲取訓練集資料 |
24 | image_path = self.train_image_path |
25 | label_path = self.train_label_path |
26 | else: # 獲取測試集資料 |
27 | image_path = self.test_image_path |
28 | label_path = self.test_label_path |
29 | |
30 | with open(image_path, 'rb') as file1: |
31 | image_file = file1.read() |
32 | with open(label_path, 'rb') as file2: |
33 | label_file = file2.read() |
34 | |
35 | label_index = 0 |
36 | image_index = 0 |
37 | labels = [] |
38 | images = [] |
39 | |
40 | # 讀取訓練集影象資料檔案的檔案資訊 |
41 | magic, num_of_datasets, rows, columns =\ |
42 | struct.unpack_from('>IIII', image_file, image_index) |
43 | image_index += struct.calcsize('>IIII') |
44 | |
45 | for i in range(num_of_datasets): |
46 | # 讀取784個unsigned byte,即一副影象的所有畫素值 |
47 | temp = struct.unpack_from('>784B', image_file, image_index) |
48 | # 將讀取的畫素資料轉換成28*28的矩陣 |
49 | temp = np.reshape(temp, (28, 28)) |
50 | # 歸一化處理 |
51 | temp = temp / 255 |
52 | images.append(temp) |
53 | image_index += struct.calcsize('>784B') # 每次增加784B |
54 | |
55 | # 跳過描述資訊 |
56 | label_index += struct.calcsize('>II') |
57 | labels = struct.unpack_from('>' + str(num_of_datasets) |
58 | + 'B', label_file, label_index) |
59 | |
60 | # one-hot |
61 | labels = np.eye(10)[np.array(labels)] |
62 | |
63 | return images, labels |
第51行程式碼中,我們對資料進行了歸一化處理,關於歸一化我們在第一章中有介紹。在後面兩節實現神經網路模型的時候,讀者可以嘗試註釋掉歸一化的這行程式碼,比較一下做了歸一化和不做歸一化,模型的效果有什麼差別。
最後,我們要實現一個“get_batch”方法。在訓練模型的時候,我們通常會用訓練集資料訓練多個回合(epoch),每個回合都會用且只用一次訓練集中的每一條資料。因為我們使用隨機梯度下降的方式來更新引數,所以每個回合中,我們會把訓練集資料分為多個批次(batch)送進模型中去訓練,每次送進模型的資料量的大小為“batch_size”。因此,我們需要將資料按“batch_size”進行劃分。
def get_batch(self, batch_size): |
# 剛開始訓練或當一輪訓練結束之後,打亂資料集資料的順序 |
if self.got_batch == self.num_of_batch: |
train_list = list(zip(self.train_images, self.train_labels)) |
random.shuffle(train_list) |
self.train_images, self.train_labels = zip(*train_list) |
# 重置兩個輔助變數 |
self.num_of_batch = 60000 / batch_size |
self.got_batch = 0 |
# 獲取一個batch size的訓練資料 |
train_images = self.train_images[ |
self.got_batch*batch_size:(self.got_batch+1)*batch_size] |
train_labels = self.train_labels[ |
self.got_batch*batch_size:(self.got_batch+1)*batch_size] |
self.got_batch += 1 |
return train_images, train_labels |
到這裡我們已經實現了Mnist資料的讀取和預處理,在後面兩小節的內容裡,我們會分別實現一個單層的神經網路和一個多層的前饋神經網路模型,實現Mnist手寫數字的識別問題。
四、單層隱藏層神經網路的實現
介紹完MNIST資料集之後,我們現在可以開始動手實現一個神經網路來解決手寫數字識別的問題了,我們先從一個簡單的兩層(一層隱藏層)神經網路開始。
本小節所實現的單層神經網路結構如圖3-16所示。每張圖片的大小為,我們將其轉為長度為784的向量作為網路的輸入。隱藏層有10個神經元,在這個簡單的神經網路中我們沒有在隱藏層中使用啟用函式。在隱藏層後面我們加了一個Softmax層,用來將隱藏層的輸出直接轉化為模型的預測結果。
圖2 實現Mnist手寫數字識別的兩層神經網路結構
接下來我們實現具體的程式碼,首先匯入上一小節中我們實現的資料處理的類以及TensorFlow的包:1 | from mnist_data import MnistData |
2 | import tensorflow as tf |
3 | # 建立Session會話 |
4 | sess = tf.InteractiveSession() |
5 | |
6 | # 訓練集、測試集的檔案路徑 |
7 | train_image_path = './data/train-images-idx3-ubyte' |
8 | train_label_path = './data/train-labels-idx1-ubyte' |
9 | test_image_path = './data/t10k-images-idx3-ubyte' |
10 | test_label_path = './data/t10k-labels-idx1-ubyte' |
11 | |
12 | epochs = 10 # 訓練的總輪數 |
13 | batch_size = 100 # 每個batch的大小 |
14 | learning_rate = 0.2 # 學習率 |
接下來我們定義模型的引數:
15 | # 建立樣本資料的placeholder |
16 | x = tf.placeholder(tf.float32, [None, 28, 28]) |
17 | # 定義權重矩陣和偏置項 |
18 | W = tf.Variable(tf.zeros([28*28, 10])) |
19 | b = tf.Variable(tf.zeros([10])) |
20 | |
21 | # 樣本的真實標籤 |
22 | y_ = tf.placeholder(tf.float32, [None, 10]) |
23 | # 使用softmax函式將單層網路的輸出轉換為預測結果 |
24 | y = tf.nn.softmax(tf.matmul(tf.reshape(x, [-1, 28*28]), W) + b) |
定義完網路的引數後我們還需要定義損失函式和優化器:
# 損失函式和優化器 |
# -tf.reduce_sum(y_ * tf.log(y) 計算這個batch中每個樣本的交叉熵 |
# reduce_mean方法對一個batch的樣本的交叉熵求平均值,作為最終的loss |
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y), axis=1)) |
train_step = \ |
tf.train.GradientDescentOptimizer(learning_rate).minimize(cross_entropy) |
第29和30行程式碼中,我們定義了一個梯度下降優化器“GradientDescentOptimizer”,並設定了學習率為“learning_rate”以及優化目標為“cross_entropy”。
接下來我們還需要實現模型的評估:
31 | # 比較預測結果和真實類標 |
32 | correct_prediction = tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)) |
33 | # 計算準確率 |
34 | accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) |
35 | # 初始化MnistData類 |
36 | data = MnistData(train_image_path, train_label_path, |
37 | test_image_path, test_label_path) |
38 | # 初始化模型引數 |
39 | init = tf.global_variables_initializer().run() |
40 | |
41 | # 開始訓練 |
42 | for i in range(epochs): |
43 | for j in range(600): |
44 | # 獲取一個batch的資料 |
45 | batch_x, batch_y = data.get_batch(batch_size) |
46 | # 優化引數 |
47 | train_step.run({x: batch_x, y_: batch_y}) |
48 | |
49 | # 對測試集進行預測並計算準確率 |
50 | print(accuracy.eval({x: data.test_images, y_: data.test_labels})) |
- 多層神經網路的實現
圖3 實現Mnist手寫數字識別的多層神經網路結構
如上圖所示,這裡我們增加了一層隱藏層,實現的是一個三層神經網路。與上一小節的兩層神經網路不同的是,除了增加了一層隱藏層,在第一層隱藏層中我們還是用了“Sigmoid”啟用函式。實現三層神經網路我們只需要在上一小節的程式碼基礎上對網路的引數做一些修改:
# 定義權重矩陣和偏置項 |
w_1 = tf.Variable(tf.truncated_normal([28*28, 200], stddev=0.1)) |
b_1 = tf.Variable(tf.zeros([200])) |
w_2 = tf.Variable(tf.truncated_normal([200, 10], stddev=0.1)) |
b_2 = tf.Variable(tf.zeros([10])) |
定義好模型引數之後,就可以實現網路的具體結構了:
# 定義一個兩層神經網路模型 |
y_1 = tf.nn.sigmoid(tf.matmul(tf.reshape(x, [-1, 28*28]), w_1) + b_1) |
y = tf.nn.softmax(tf.matmul(y_1, w_2) + b_2) |
到這裡我們已經介紹完基本的前饋神經網路的內容了,這一章的內容是深度神經網路的基礎,理解本章的內容對於後續內容的學習會很有幫助。從下一章開始,我們要正式開始深度神經網路的學習了。