前言
今天記錄一下深度學習的另外一個入門專案——《mnist資料集手寫數字識別》,這是一個入門必備的學習案例,主要使用了tensorflow下的keras網路結構的Sequential模型,常用層的Dense全連線層、Activation啟用層和Reshape層。還有其他方法訓練手寫數字識別模型,可以基於pytorch實現的,《Pytorch實現基於卷積神經網路的面部表情識別(詳細步驟)》 這篇就是基於pytorch實現,pytorch裡也封裝了mnist的資料集,實現方法應該類似,正在學習中……
這一篇記錄則是基於keras的Sequential模型實現的。
1、mnist手寫數字真面目
我們使用離線下載的資料集進行匯入,一定程度上解決了從遠端載入資料緩慢的問題,這裡有兩種資料集提供給大家,分別是:
- mnist.npz資料集
它是把手寫數字的影像資料和對應標籤整合在一起,而且訓練集與測試集也在裡面,使用的時候無需拆分檔案,只需要簡單程式碼劃分資料,可直接下載本地 mnist手寫數字識別資料集npz檔案.zip - mnist.zip資料集
它包含了兩個壓縮包,分別是訓練集和測試集(檔名:mnist_traint_data.zip和mnist_test_data.zip),每個資料集解壓后里面分別是資料和對應的標籤,所以最後由4個檔案,可直接下載本地 mnist訓練資料+測試資料(手寫數字識別).zip
1.1、mnist.npz(整合)資料集
下載好mnist手寫數字識別資料集npz檔案.zip之後,解壓得到mnist.npz之後,我們這裡開始寫程式碼看看手寫數字影像的真面目。
顯示影像程式碼:
import numpy as np
import matplotlib.pyplot as plt
def load_mnist(): # 自定義載入資料
path = r'D:\mnist_data\mnist.npz' # 放置mnist.npz的目錄。注意斜槓
f = np.load(path)
x_train, y_train = f['x_train'], f['y_train'] # 程式碼實現分離資料集裡面的訓練集和測試集以及對應標籤
x_test, y_test = f['x_test'], f['y_test'] # x_train為訓練資料,y_train為對應標籤
f.close() # 關閉檔案
return (x_train, y_train), (x_test, y_test)
def main():
(X_train, y_train_label), (test_image, test_label) = load_mnist() #後續可以顯示訓練資料的數字或者測試資料的
fig, ax = plt.subplots(nrows=5, ncols=5, sharex=True, sharey=True) # 顯示影像
ax = ax.flatten()
for i in range(25):
img = X_train[i].reshape(28, 28)
# img = X_train[y_train_label == 8][i].reshape(28, 28) # 顯示標籤為8的數字影像
ax[i].set_title(y_train_label[i])
ax[i].imshow(img, cmap='Greys', interpolation='nearest')
ax[0].set_xticks([])
ax[0].set_yticks([])
plt.tight_layout()
plt.show()
if __name__ == '__main__':
main()
效果如下:
也可以花樣輸出:
程式碼:
import numpy as np
import matplotlib.pyplot as plt
def load_mnist():
path = r'D:\mnist_data\mnist.npz' # 放置mnist.npz的目錄。注意斜槓
f = np.load(path)
x_train, y_train = f['x_train'], f['y_train'] # 程式碼實現分離資料集裡面的訓練集和測試集以及對應標籤
x_test, y_test = f['x_test'], f['y_test'] # x_train為訓練資料,y_train為對應標籤
f.close() # 關閉檔案
return (x_train, y_train), (x_test, y_test)
def main():
(X_train, y_train_label), (test_image, test_label) = load_mnist()
plt.subplot(221)#顯示影像
plt.imshow(X_train[0], cmap=plt.get_cmap('Accent'))
plt.subplot(222)
plt.imshow(X_train[1], cmap=plt.get_cmap('gray'))
plt.subplot(223)
plt.imshow(X_train[2], cmap=plt.get_cmap('Blues'))
plt.subplot(224)
plt.imshow(X_train[3], cmap=plt.get_cmap('Oranges'))
plt.show()
if __name__ == '__main__':
main()
影像顯示:
1.2、mnist資料集(訓練測試資料與標籤分離)
這裡介紹第二中方法,也就是資料集是分離的,下載好mnist訓練資料+測試資料(手寫數字識別).zip之後,解壓得到檔案如圖:
進去解壓得到:
可以看到分別是訓練集和測試集,包括資料和標籤。
這種方法比較麻煩,沒想到吧!^_ ^ ,大家可以選擇第一種步驟簡單
最後得到:
匯入時候需要用到的是這些.gz檔案。
顯示影像程式碼:
import gzip
import os
import numpy as np
import matplotlib.pyplot as plt
local_file = 'D:\mnist_data'
files = ['train-images-idx3-ubyte.gz', 'train-labels-idx1-ubyte.gz',
't10k-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz']
def load_local_mnist(filename):# 載入檔案
paths = []
file_read = []
for file in files:
paths.append(os.path.join(filename, file))
for path in paths:
file_read.append(gzip.open(path, 'rb'))
# print(file_read)
train_labels = np.frombuffer(file_read[1].read(), np.uint8, offset=8)#檔案讀取以及格式轉換
train_images = np.frombuffer(file_read[0].read(), np.uint8, offset=16) \
.reshape(len(train_labels), 28, 28)
test_labels = np.frombuffer(file_read[3].read(), np.uint8, offset=8)
test_images = np.frombuffer(file_read[2].read(), np.uint8, offset=16) \
.reshape(len(test_labels), 28, 28)
return (train_images, train_labels), (test_images, test_labels)
def main():
(x_train, y_train), (x_test, y_test) = load_local_mnist(local_file)
fig, ax = plt.subplots(nrows=6, ncols=6, sharex=True, sharey=True)#顯示影像
ax = ax.flatten()
for i in range(36):
img=x_test[i].reshape(28,28)
# img = x_train[y_train == 8][i].reshape(28, 28) # 顯示標籤為8的數字影像
ax[i].set_title(y_train[i])
ax[i].imshow(img, cmap='Greys', interpolation='nearest')
ax[0].set_xticks([])
ax[0].set_yticks([])
plt.tight_layout()
plt.show()
if __name__ == '__main__':
main()
輸出結果:
2、Sequential模型訓練
這裡實現主要使用了tensorflow下的keras網路結構的Sequential模型,常用層的Dense全連線層、Activation啟用層和Reshape層。tensorflow安裝有問題可參考《初入機器學習,安裝tensorflow包等問題總結》
模型比較簡單,網路搭建以及模型選擇的損失函式、優化器可見程式碼。
import numpy as np
import os
import gzip
from tensorflow import keras
from tensorflow.keras.optimizers import SGD
from tensorflow_core.python.keras.utils import np_utils
from tensorflow.keras.layers import Dense, Dropout, Activation
local_file = 'D:\mnist_data'
files = ['train-images-idx3-ubyte.gz', 'train-labels-idx1-ubyte.gz',
't10k-images-idx3-ubyte.gz', 't10k-labels-idx1-ubyte.gz']
def load_local_mnist(filename): # 載入檔案
paths = []
file_read = []
for file in files:
paths.append(os.path.join(filename, file))
for path in paths:
file_read.append(gzip.open(path, 'rb'))
# print(file_read)
train_labels = np.frombuffer(file_read[1].read(), np.uint8, offset=8) # 檔案讀取以及格式轉換
train_images = np.frombuffer(file_read[0].read(), np.uint8, offset=16) \
.reshape(len(train_labels), 28, 28)
test_labels = np.frombuffer(file_read[3].read(), np.uint8, offset=8)
test_images = np.frombuffer(file_read[2].read(), np.uint8, offset=16) \
.reshape(len(test_labels), 28, 28)
return (train_images, train_labels), (test_images, test_labels)
def load_data():# 載入模型需要的資料
(x_train, y_train), (x_test, y_test) = load_local_mnist(local_file)
number = 10000
x_train = x_train[0:number]
y_train = y_train[0:number]
x_train = x_train.reshape(number, 28 * 28)
x_test = x_test.reshape(x_test.shape[0], 28 * 28)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
y_train = np_utils.to_categorical(y_train, 10)
y_test = np_utils.to_categorical(y_test, 10)
x_train = x_train
x_test = x_test
x_train = x_train / 255
x_test = x_test / 255
return (x_train, y_train), (x_test, y_test)
(X_train, Y_train), (X_test, Y_test) = load_data()
model = keras.Sequential()# 模型選擇
model.add(Dense(input_dim=28 * 28, units=690,
activation='relu')) # tanh activation:Sigmoid、tanh、ReLU、LeakyReLU、pReLU、ELU、maxout
model.add(Dense(units=690, activation='relu'))
model.add(Dense(units=690, activation='relu')) # tanh
model.add(Dense(units=10, activation='relu'))
model.compile(loss='mse', optimizer=SGD(lr=0.1),
metrics=['accuracy']) # loss:mse,categorical_crossentropy,optimizer: rmsprop 或 adagrad、SGD(此處推薦)
model.fit(X_train, Y_train, batch_size=100, epochs=20)
result = model.evaluate(X_test, Y_test)
print('TEST ACC:', result[1])
經過稍微調優,發現輸入層啟用函式使用relu和tanh效果好,其他網路層使用relu。另外,損失函式使用了MSE(均方誤差),優化器使用 SGD(隨即梯度下降),學習率learning rate調到0.1,度量常用正確率。
引數batch_size=100, epochs=20,增加引數更新以及訓練速度。
以上引數以及選擇訓練效果如下:
使用優化器為adagrad效果:
大家也可以自行各種嘗試,優化器和損失函式選擇,引數調優等,進一步提高正確率。
這裡提供另一種寫法,模型構建類似。
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, models, metrics
from tensorflow.keras.optimizers import SGD
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2' # 忽略tensorflow版本警告
(xs, ys), _ = datasets.mnist.load_data()
print('datasets:', xs.shape, ys.shape, xs.min(), xs.max())
# tf.compat.v1.enable_eager_execution()
tf.enable_eager_execution()
xs = tf.convert_to_tensor(xs, dtype=tf.float32) / 255.
db = tf.data.Dataset.from_tensor_slices((xs, ys))
db = db.batch(100).repeat(20)
network = models.Sequential([layers.Dense(256, activation='relu'),
layers.Dense(256, activation='relu'),
layers.Dense(256, activation='relu'),
layers.Dense(10)])
network.build(input_shape=(None, 28 * 28))
network.summary()
optimizer = optimizers.SGD(lr=0.01)
acc_meter = metrics.Accuracy()# 度量正確率
for step, (x, y) in enumerate(db):
with tf.GradientTape() as tape:
# [b, 28, 28] => [b, 784] 784維=24*24
x = tf.reshape(x, (-1, 28 * 28))#-1的含義,陣列新的shape屬性應該要與原來的配套,根據剩下的維度計算出陣列的另外一個shape屬性值。
# [b, 784] => [b, 10]
out = network(x)
# [b] => [b, 10]
y_onehot = tf.one_hot(y, depth=10) # 獨熱編碼,y = 0 對應的輸出是[1,0,0,0,0,0,0,0,0,0],範圍0-9,depth深度10層表示10個數字
# [b, 10]
loss = tf.square(out - y_onehot)# 計算模型預測與實際的損失
# [b]
loss = tf.reduce_sum(loss) / 32
acc_meter.update_state(tf.argmax(out, axis=1), y)
grads = tape.gradient(loss, network.trainable_variables)# 計算梯度
optimizer.apply_gradients(zip(grads, network.trainable_variables))
if step % 200 == 0:
print(step, 'loss:', float(loss), 'acc:', acc_meter.result().numpy())
acc_meter.reset_states()
最後正確率比上面好一點,如圖:
寫在後面
經過這次學習,感覺收穫了許多,之前只是在理論知識上的理解,現在配合程式碼實踐,模型訓練,理解更加深刻,還存在不足,歡迎大家指正交流,這個過程的詳細步驟,希望能幫助跟我一樣入門需要的夥伴,記錄學習過程,感覺總結一下很好,繼續加油!
我的CSDN部落格:mnist手寫數字識別深度學習入門專案(tensorflow+keras+Sequential模型)
我的部落格園:mnist手寫數字識別——深度學習入門專案(tensorflow+keras+Sequential模型)