mnist手寫數字識別——深度學習入門項目(tensorflow+keras+Sequential模型)


前言

今天記錄一下深度學習的另外一個入門項目——《mnist數據集手寫數字識別》,這是一個入門必備的學習案例,主要使用了tensorflow下的keras網絡結構的Sequential模型,常用層的Dense全連接層、Activation激活層和Reshape層。還有其他方法訓練手寫數字識別模型,可以基於pytorch實現的,《Pytorch實現基於卷積神經網絡的面部表情識別(詳細步驟)》 這篇就是基於pytorch實現,pytorch里也封裝了mnist的數據集,實現方法應該類似,正在學習中……
這一篇記錄則是基於keras的Sequential模型實現的。

 

1、mnist手寫數字真面目

我們使用離線下載的數據集進行導入,一定程度上解決了從遠程加載數據緩慢的問題,這里有兩種數據集提供給大家,分別是:

  1. mnist.npz數據集
    它是把手寫數字的圖像數據和對應標簽集成在一起,而且訓練集與測試集也在里面,使用的時候無需拆分文件,只需要簡單代碼划分數據,可直接下載本地 mnist手寫數字識別數據集npz文件.zip
  2. 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()

效果如下:
圖1
也可以花樣輸出:
代碼:

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()

圖像顯示:
圖2

  1.2、mnist數據集(訓練測試數據與標簽分離)

這里介紹第二中方法,也就是數據集是分離的,下載好mnist訓練數據+測試數據(手寫數字識別).zip之后,解壓得到文件如圖:
圖3
進去解壓得到:
文件
可以看到分別是訓練集和測試集,包括數據和標簽。
這種方法比較麻煩,沒想到吧!^_ ^ ,大家可以選擇第一種步驟簡單
最后得到:
在這里插入圖片描述
導入時候需要用到的是這些.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()

輸出結果:
結果1

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,增加參數更新以及訓練速度。
以上參數以及選擇訓練效果如下:
結果1
使用優化器為adagrad效果:
結果2
大家也可以自行各種嘗試,優化器和損失函數選擇,參數調優等,進一步提高正確率。

這里提供另一種寫法,模型構建類似。

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()

最后正確率比上面好一點,如圖:
圖3

寫在后面

經過這次學習,感覺收獲了許多,之前只是在理論知識上的理解,現在配合代碼實踐,模型訓練,理解更加深刻,還存在不足,歡迎大家指正交流,這個過程的詳細步驟,希望能幫助跟我一樣入門需要的伙伴,記錄學習過程,感覺總結一下很好,繼續加油!

我的CSDN博客:mnist手寫數字識別深度學習入門項目(tensorflow+keras+Sequential模型)
我的博客園:mnist手寫數字識別——深度學習入門項目(tensorflow+keras+Sequential模型)

版權聲明:本文為博主原創文章,遵循  CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接: https://blog.csdn.net/Charzous/article/details/107748508


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM