深度篇—— CNN 卷积神经网络(四) 使用 tf cnn 进行 mnist 手写数字 代码演示项目
返回主目錄
返回 CNN 卷積神經網絡目錄
上一章:深度篇—— CNN 卷積神經網絡(三)?關于 ROI pooling 和 ROI Align?與 插值
?
本小節,細說 使用 tf cnn 進行 mnist 手寫數字 代碼演示項目
?
本項目 github 代碼:https://github.com/wandaoyi/tf_cnn_mnist_pro
?
五. TF_CNN_MNIST 手寫數字代碼演示
?
(1). 前言
之前在?深度篇——神經網絡?我們學了 ANN 和 DNN,現在,我們又學了 CNN,對于學以致用來說,我們將使用 CNN 來構建 卷積神經網絡。
?
(2). 明確需求
項目需求,就是想把 手寫 的 0 ~ 9 的阿拉伯數字圖片識別出來。比方說,發票上面的數字(第一個做手寫數字識別的,是1989年,美國的一家銀行 聘請大佬寫的,當時是用卷積神經網絡技術 LeNet-5 寫的。前面 深度篇——神經網絡(七) 細說 DNN神經網絡手寫數字代碼演示?已經使用 DNN 案例講解了;現在,我們就使用 LeNet-5 做一個案例講解。當時這個項目是用來識別支票上面簽約的數字?)。訓練網絡,當然離不開數據,所以,我們先下載數據,數據為已經為大家上傳到百度云盤:鏈接:https://pan.baidu.com/s/13OokGc0h3F5rGrxuSLYj9Q ??提取碼:qfj6 ?。
?
?
(3). 構建項目
項目結構如下:
上面的模型,是我隨意訓練 10 個 epoch 得到的精度:0.984000。之前我們使用 DNN 的時候,10 個 epoch 才 0.96+ 的精度。這樣,我們看到,精度上升了 2 個百分點,這樣說,也許有人會不以為意,覺得并不怎么樣。但是,如果我們反過來看,可以看成,錯誤率減少了一半,這樣,效果就會非常可觀了。在公司做項目的時候,會做事很重要,但是,語言表達能力,也很重要。
?
(4). 環境依賴
環境依賴:
pip install numpy==1.16 pip install easydict conda install tensorflow-gpu==1.13.1 # 建議不要用 2.0 版本的 tf,坑多tensorflow 的安裝,我前面的博客有詳細解說:碎點篇——tensorflow gpu 版本安裝??如果不會安裝的,可以查看如何安裝。
README.md 文件:
# tf_cnn_mnist_pro tf_cnn 手寫數字預測 2020-02-09 - 項目下載地址:https://github.com/wandaoyi/tf_cnn_mnist_pro - 請到百度云盤下載項目所需要的訓練數據: - 鏈接:https://pan.baidu.com/s/13OokGc0h3F5rGrxuSLYj9Q ??提取碼:qfj6 ## 參數設置 - 在訓練或預測之前,我們要先進行參數設置 - 打開 config.py 文件,對其中的參數或路徑進行設置。## 模型 - 模型代碼 model_net.py - 在這里,使用了 lenet-5 網絡模型來提取特征## 訓練模型 - 運行 cnn_mnist_train.py ,簡單操作,右鍵直接 run - 訓練效果如下: - acc_train: 1.0 - epoch: 10, acc_test: 0.984000 - 下面是隨意訓練的效果,如果想效果好,可以多訓練多點epoch - 也可以自己添加 early-stopping 進去,不麻煩的## 預測 - 運行 cnn_mnist_test.py ,簡單操作,右鍵直接 run - 運行后,部分預測結果會打印在控制臺上 - 預測效果如下: - 預測值: [7 2 1 0 4] - 真實值: [7 2 1 0 4]## tensorboard 日志 - 使用 tensorboard 的好處是,這個日志是實時的,可以一邊訓練一邊看效果圖。 - 在 cmd 命令窗口,輸入下面命令: - tensorboard --logdir=G:\work_space\python_space\pro2018_space\wandao\mnist_pro\logs\mnist_log_train --host=localhost - 在 --logdir= 后面是日志的文件夾路徑, - 在 --host= 是用來指定 ip 的,如果不寫,則只能電腦的地址,而不能使用 localhost - 在 谷歌瀏覽器 上打開 tensorboard 日志: http://localhost:6006/- 模型 acc  - 模型結構 ?
下面的文件或代碼,里面,都有注釋
(5). 配置文件?config.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # ============================================ # @Time : 2020/02/08 19:23 # @Author : WanDaoYi # @FileName : config.py # ============================================from easydict import EasyDict as edict import os__C = edict()cfg = __C# common options 公共配置文件 __C.COMMON = edict() # windows 獲取文件絕對路徑, 方便 windows 在黑窗口 運行項目 __C.COMMON.BASE_PATH = os.path.abspath(os.path.dirname(__file__)) # # 獲取當前窗口的路徑, 當用 Linux 的時候切用這個,不然會報錯。(windows也可以用這個) # __C.COMMON.BASE_PATH = os.getcwd()__C.COMMON.DATA_PATH = os.path.join(__C.COMMON.BASE_PATH, "dataset")# 圖像的形狀 __C.COMMON.DATA_RESHAPE = [-1, 28, 28, 1] # 圖像 rezise 的形狀 __C.COMMON.DATA_RESIZE = (32, 32)# 訓練配置 __C.TRAIN = edict()# 學習率 __C.TRAIN.LEARNING_RATE = 0.01 # batch_size __C.TRAIN.BATCH_SIZE = 32 # 迭代次數 __C.TRAIN.N_EPOCH = 10# 模型保存路徑, 使用相對路徑,方便移植 __C.TRAIN.MODEL_SAVE_PATH = "./checkpoint/model_" # dropout 的持有量,0.7 表示持有 70% 的節點。 __C.TRAIN.KEEP_PROB_DROPOUT = 0.7# 測試配置 __C.TEST = edict()# 測試模型保存路徑 __C.TEST.CKPT_MODEL_SAVE_PATH = "./checkpoint/model_acc=0.984000.ckpt-10"# 日志配置 __C.LOG = edict() # 日志保存路徑,后面會接上 train 或 test: 如 mnist_log_train __C.LOG.LOG_SAVE_PATH = "./logs/mnist_log_"?
(6). 公共代碼?common.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # ============================================ # @Time : 2020/02/08 19:26 # @Author : WanDaoYi # @FileName : common.py # ============================================import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data from config import cfg import numpy as npclass Common(object):def __init__(self):# 數據路徑self.data_file_path = cfg.COMMON.DATA_PATHpass# 讀取數據def read_data(self):# 數據下載地址: http://yann.lecun.com/exdb/mnist/mnist_data = input_data.read_data_sets(self.data_file_path, one_hot=True)train_image = mnist_data.train.imagestrain_label = mnist_data.train.labels_, n_feature = train_image.shape_, n_label = train_label.shapereturn mnist_data, n_feature, n_label# bn 操作def deal_bn(self, input_data, train_flag=True):bn_info = tf.layers.batch_normalization(input_data, beta_initializer=tf.zeros_initializer(),gamma_initializer=tf.ones_initializer(),moving_mean_initializer=tf.zeros_initializer(),moving_variance_initializer=tf.ones_initializer(),training=train_flag)return bn_infopass# 池化處理def deal_pool(self, input_data, ksize=(1, 2, 2, 1), strides=(1, 2, 2, 1),padding="VALID", name="avg_pool"):pool_info = tf.nn.avg_pool(value=input_data, ksize=ksize,strides=strides, padding=padding,name=name)tf.summary.histogram('pooling', pool_info)return pool_infopass# dropout 處理def deal_dropout(self, hidden_layer, keep_prob):with tf.name_scope("dropout"):tf.summary.scalar('dropout_keep_probability', keep_prob)dropped = tf.nn.dropout(hidden_layer, keep_prob)tf.summary.histogram('dropped', dropped)return droppedpass# 參數記錄def variable_summaries(self, param):with tf.name_scope('summaries'):mean = tf.reduce_mean(param)tf.summary.scalar('mean', mean)with tf.name_scope('stddev'):stddev = tf.sqrt(tf.reduce_mean(tf.square(param - mean)))tf.summary.scalar('stddev', stddev)tf.summary.scalar('max', tf.reduce_max(param))tf.summary.scalar('min', tf.reduce_min(param))tf.summary.histogram('histogram', param)# 全連接操作def neural_layer(self, x, n_neuron, name="fc"):# 包含所有的計算節點對于這一層, name_scope 可寫可不寫with tf.name_scope(name=name):n_input = int(x.get_shape()[1])stddev = 2 / np.sqrt(n_input)# 這層里面的w可以看成是二維數組,每個神經元對于一組w參數# truncated normal distribution 比 regular normal distribution的值小# 不會出現任何大的權重值,確保慢慢的穩健的訓練# 使用這種標準方差會讓收斂快# w參數需要隨機,不能為0,否則輸出為0,最后調整都是一個幅度沒意義with tf.name_scope("weights"):init_w = tf.truncated_normal((n_input, n_neuron), stddev=stddev)w = tf.Variable(init_w, name="weight")self.variable_summaries(w)with tf.name_scope("biases"):b = tf.Variable(tf.zeros([n_neuron]), name="bias")self.variable_summaries(b)with tf.name_scope("wx_plus_b"):z = tf.matmul(x, w) + btf.summary.histogram('pre_activations', z)return z# 卷積操作def conv2d(self, input_data, filter_shape, strides_shape=(1, 1, 1, 1),padding="VALID", train_flag=True, name="conv2d"):with tf.variable_scope(name):weight = tf.get_variable(name="weight", dtype=tf.float32,trainable=train_flag,shape=filter_shape,initializer=tf.random_normal_initializer(stddev=0.01))conv = tf.nn.conv2d(input=input_data, filter=weight,strides=strides_shape, padding=padding)conv_2_bn = self.deal_bn(conv, train_flag=train_flag)return conv_2_bnpasspass?
(7). 模型代碼?model_net.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # ============================================ # @Time : 2020/02/08 22:26 # @Author : WanDaoYi # @FileName : model_net.py # ============================================import tensorflow as tf from core.common import Commonclass ModelNet(object):def __init__(self):self.common = Common()passdef lenet_5(self, input_data, n_label=10, keep_prob=1.0, train_flag=True):with tf.variable_scope("lenet-5"):conv_1 = self.common.conv2d(input_data, (5, 5, 1, 6), name="conv_1")tanh_1 = tf.nn.tanh(conv_1, name="tanh_1")avg_pool_1 = self.common.deal_pool(tanh_1, name="avg_pool_1")conv_2 = self.common.conv2d(avg_pool_1, (5, 5, 6, 16), name="conv_2")tanh_2 = tf.nn.tanh(conv_2, name="tanh_2")avg_pool_2 = self.common.deal_pool(tanh_2, name="avg_pool_2")conv_3 = self.common.conv2d(avg_pool_2, (5, 5, 16, 120), name="conv_3")tanh_3 = tf.nn.tanh(conv_3, name="tanh_3")reshape_data = tf.reshape(tanh_3, [-1, 120])dropout_1 = self.common.deal_dropout(reshape_data, keep_prob)fc_1 = self.common.neural_layer(dropout_1, 84, name="fc_1")tanh_4 = tf.nn.tanh(fc_1, name="tanh_4")dropout_2 = self.common.deal_dropout(tanh_4, keep_prob)fc_2 = self.common.neural_layer(dropout_2, n_label, name="fc_2")scale_2 = self.common.deal_bn(fc_2, train_flag=train_flag)result_info = tf.nn.softmax(scale_2, name="result_info")return result_infopass這里的模型,我使用了 lenet-5,當然,以后想換其他模型,也是可以的。在 lenet-5 里面,模型的輸入 是要求 shape 要求是 32 x 32 大小的圖片,不然,尺度不夠的話,模型會報錯的。所以,要將圖像 resize 為 32 x 32 大小。
?
(8). 訓練代碼?cnn_mnist_train.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # ============================================ # @Time : 2020/02/08 19:24 # @Author : WanDaoYi # @FileName : cnn_mnist_train.py # ============================================from datetime import datetime import tensorflow as tf from config import cfg from core.common import Common from core.model_net import ModelNetclass CnnMnistTrain(object):def __init__(self):# 模型保存路徑self.model_save_path = cfg.TRAIN.MODEL_SAVE_PATHself.log_path = cfg.LOG.LOG_SAVE_PATHself.learning_rate = cfg.TRAIN.LEARNING_RATEself.batch_size = cfg.TRAIN.BATCH_SIZEself.n_epoch = cfg.TRAIN.N_EPOCHself.data_shape = cfg.COMMON.DATA_RESHAPEself.data_resize = cfg.COMMON.DATA_RESIZEself.common = Common()self.model_net = ModelNet()# 讀取數據和 維度self.mnist_data, self.n_feature, self.n_label = self.common.read_data()# 創建設計圖with tf.name_scope(name="input_data"):self.x = tf.placeholder(dtype=tf.float32, shape=(None, self.n_feature), name="input_data")self.y = tf.placeholder(dtype=tf.float32, shape=(None, self.n_label), name="input_labels")with tf.name_scope(name="input_shape"):# 784維度變形為圖片保持到節點# -1 代表進來的圖片的數量、28,28是圖片的高和寬,1是圖片的顏色通道image_shaped_input = tf.reshape(self.x, self.data_shape)# 將 輸入 圖像 resize 成 網絡所需要的大小image_resize = tf.image.resize_images(image_shaped_input, self.data_resize)tf.summary.image('input', image_resize, self.n_label)self.keep_prob_dropout = cfg.TRAIN.KEEP_PROB_DROPOUTself.keep_prob = tf.placeholder(tf.float32)# 獲取最后一層 lenet_5 的返回結果self.result_info = self.model_net.lenet_5(image_resize, n_label=self.n_label,keep_prob=self.keep_prob_dropout)# 計算損失with tf.name_scope(name="train_loss"):# 定義損失函數self.cross_entropy = tf.reduce_mean(-tf.reduce_sum(self.y * tf.log(self.result_info),reduction_indices=[1]))tf.summary.scalar("train_loss", self.cross_entropy)passwith tf.name_scope(name="optimizer"):self.optimizer = tf.train.AdamOptimizer(learning_rate=self.learning_rate)self.train_op = self.optimizer.minimize(self.cross_entropy)passwith tf.name_scope(name="accuracy"):self.correct_pred = tf.equal(tf.argmax(self.result_info, 1), tf.argmax(self.y, 1))self.acc = tf.reduce_mean(tf.cast(self.correct_pred, tf.float32))tf.summary.scalar("accuracy", self.acc)pass# 因為我們之前定義了太多的tf.summary匯總操作,逐一執行這些操作太麻煩,# 使用tf.summary.merge_all()直接獲取所有匯總操作,以便后面執行self.merged = tf.summary.merge_all()self.sess = tf.InteractiveSession()# 保存訓練模型self.saver = tf.train.Saver()# 定義兩個tf.summary.FileWriter文件記錄器再不同的子目錄,分別用來存儲訓練和測試的日志數據# 同時,將Session計算圖sess.graph加入訓練過程,這樣再TensorBoard的GRAPHS窗口中就能展示self.train_writer = tf.summary.FileWriter(self.log_path + 'train', self.sess.graph)self.test_writer = tf.summary.FileWriter(self.log_path + 'test')pass# 灌入數據def feed_dict(self, train_flag=True):# 訓練樣本if train_flag:# 獲取下一批次樣本x_data, y_data = self.mnist_data.train.next_batch(self.batch_size)keep_prob = self.keep_prob_dropoutpass# 驗證樣本else:x_data, y_data = self.mnist_data.test.images, self.mnist_data.test.labelskeep_prob = 1.0passreturn {self.x: x_data, self.y: y_data, self.keep_prob: keep_prob}passdef do_train(self):# 定義初始化init = tf.global_variables_initializer()self.sess.run(init)test_acc = Nonefor epoch in range(self.n_epoch):# 獲取總樣本數量batch_number = self.mnist_data.train.num_examples# 獲取總樣本一共幾個批次size_number = int(batch_number / self.batch_size)for number in range(size_number):summary, _ = self.sess.run([self.merged, self.train_op], feed_dict=self.feed_dict())# 第幾次循環i = epoch * size_number + number + 1self.train_writer.add_summary(summary, i)if number == size_number - 1:# 獲取下一批次樣本x_batch, y_batch = self.mnist_data.train.next_batch(self.batch_size)acc_train = self.acc.eval(feed_dict={self.x: x_batch, self.y: y_batch})print("acc_train: {}".format(acc_train))# 驗證 方法二 兩個方法,隨便挑一個都可以的。test_summary, acc_test = self.sess.run([self.merged, self.acc], feed_dict=self.feed_dict(False))print("epoch: {}, acc_test: {}".format(epoch + 1, acc_test))self.test_writer.add_summary(test_summary, epoch + 1)test_acc = acc_testpasssave_path = self.model_save_path + "acc={:.6f}".format(test_acc) + ".ckpt"# 保存模型self.saver.save(self.sess, save_path, global_step=self.n_epoch)self.train_writer.close()self.test_writer.close()passif __name__ == "__main__":# 代碼開始時間start_time = datetime.now()print("開始時間: {}".format(start_time))demo = CnnMnistTrain()demo.do_train()# 代碼結束時間end_time = datetime.now()print("結束時間: {}, 訓練模型耗時: {}".format(end_time, end_time - start_time))訓練代碼,只是粗來訓練一下,沒做到 網格搜索,也沒做到 fine-tunning。更沒有 early-stopping,有興趣的,可以自己添加一下。
?
(9). 測試代碼?cnn_mnist_test.py
#!/usr/bin/env python # _*_ coding:utf-8 _*_ # ============================================ # @Time : 2020/02/08 19:24 # @Author : WanDaoYi # @FileName : cnn_mnist_test.py # ============================================from datetime import datetime import tensorflow as tf import numpy as np from config import cfg from core.common import Common from core.model_net import ModelNetclass CnnMnistTest(object):def __init__(self):self.common = Common()self.model_net = ModelNet()# 讀取數據和 維度self.mnist_data, self.n_feature, self.n_label = self.common.read_data()# ckpt 模型self.test_ckpt_model = cfg.TEST.CKPT_MODEL_SAVE_PATHprint("test_ckpt_model: {}".format(self.test_ckpt_model))# tf.reset_default_graph()# 創建設計圖with tf.name_scope(name="input"):self.x = tf.placeholder(dtype=tf.float32, shape=(None, self.n_feature), name="input_data")self.y = tf.placeholder(dtype=tf.float32, shape=(None, self.n_label), name="input_labels")self.data_shape = cfg.COMMON.DATA_RESHAPEself.data_resize = cfg.COMMON.DATA_RESIZEwith tf.name_scope(name="input_shape"):# 784維度變形為圖片保持到節點# -1 代表進來的圖片的數量、28 x 28 是圖片的高和寬,1是圖片的顏色通道self.image_shaped_input = tf.reshape(self.x, self.data_shape)# 將 輸入 圖像 resize 成 網絡所需要的大小 32 x 32self.image_resize = tf.image.resize_images(self.image_shaped_input, self.data_resize)# 獲取最后一層 lenet_5 的返回結果self.result_info = self.model_net.lenet_5(self.image_resize, n_label=self.n_label)pass# 預測def do_ckpt_test(self):saver = tf.train.Saver()with tf.Session() as sess:saver.restore(sess, self.test_ckpt_model)# 預測output = self.result_info.eval(feed_dict={self.x: self.mnist_data.test.images})# 將 one-hot 預測值轉為 數字y_perd = np.argmax(output, axis=1)print("預測值: {}".format(y_perd[: 5]))# 真實值y_true = np.argmax(self.mnist_data.test.labels, axis=1)print("真實值: {}".format(y_true[: 5]))passpassif __name__ == "__main__":# 代碼開始時間start_time = datetime.now()print("開始時間: {}".format(start_time))demo = CnnMnistTest()# 使用 ckpt 模型測試demo.do_ckpt_test()# 代碼結束時間end_time = datetime.now()print("結束時間: {}, 訓練模型耗時: {}".format(end_time, end_time - start_time))?
(10). 日志效果查看
acc 的圖像:
graphs 圖像:
在 tensorboard 日志里面,可以雙擊 lenet-5,模型結構如下:
打開日志 graphs 之后,可以放大來看清晰的圖像
?
從這 DNN 和 CNN 的訓練中,不難看成,對于圖像信息預測,CNN 比 DNN 要好上一些(DNN 的 10 個 epoch 才 96% 的精度,而 CNN 的 10 個 epoch 精度則達到了 98%,當然,這個只是前期的訓練效果,不好直接說明什么。但是,兩者訓練的次數足夠的話,還是 CNN 的效果會稍微好點的。這,就是為什么,在圖像處理中,大多使用 CNN,而不是純粹的 DNN)
?
?
?
? ? ? ? ? ? ? ??
?
返回主目錄
返回 CNN 卷積神經網絡目錄
上一章:深度篇—— CNN 卷積神經網絡(三)?關于 ROI pooling 和 ROI Align?與 插值
總結
以上是生活随笔為你收集整理的深度篇—— CNN 卷积神经网络(四) 使用 tf cnn 进行 mnist 手写数字 代码演示项目的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【收藏】window、mac、linux
- 下一篇: 基于蜜蜂优化算法优化的卷积神经网络(CN