mnist手寫數字識別——深度學習入門專案(tensorflow+keras+Sequential模型)
前言
今天記錄一下深度學習的另外一個入門專案——《mnist資料集手寫數字識別》,這是一個入門必備的學習案例,主要使用了tensorflow下的keras網路結構的Sequential模型,常用層的Dense全連線層、Activation啟用層和Reshape層。還有其他方法訓練手寫數字識別模型,可以基於pytorch實現的,《Pytorch實現基於卷積神經網路的面部表情識別(詳細步驟)》 這篇就是基於pytorch實現,pytorch裡也封裝了mnist的資料集,實現方法應該類似,正在學習中……
這一篇記錄則是基於keras的Sequential模型實現的。
專案步驟
前言
1、mnist手寫數字真面目
1.1、mnist(整合)資料集
1.2、mnist資料集(訓練測試資料與標籤分離)
2、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模型)