基於cifar10數據集的cnn圖片分類模型


數據集下載地址(python版):
https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
該數據集分成了幾部分/批次(batches)。CIFAR-10 數據集包含 5 個部分,名稱分別為 `data_batch_1`、`data_batch_2`,以此類推。每個部分都包含以下某個類別的標簽和圖片:

* 飛機
* 汽車
* 鳥類
* 貓
* 鹿
* 狗
* 青蛙
* 馬
* 船只
* 卡車
import的helper是一個自己寫的工具包
在我這篇隨筆里:helper工具包——基於cifar10數據集的cnn分類模型的模塊,把內容復制下來用python編輯器寫成py文件,名字為helper,放到下載的數據集一個路徑下,即可
代碼大部分我都仔仔細細的注釋過了,希望大家認真看,一定可以看懂的。
import os import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import matplotlib as mpl import helper import pickle import tensorflow as tf import random # 設置字符集,防止中文亂碼
mpl.rcParams['font.sans-serif'] = [u'simHei'] mpl.rcParams['axes.unicode_minus'] = False # 0、定義模型超參數。
learning_rate = 0.01  #學習率,梯度下降時,走的步長
batch_size = 256  #批量大小,將數據集分為好幾批,一批批的輸入神經網絡中,每一批256條數據,一批批的執行梯度下降
keep_probability = 0.7  #dropout流程的保留比例,比如某一隱藏層神經節點參與運算時,使其百分之70為0
image_shape = [32, 32, 3]  # 輸入圖片的尺寸 [32, 32, 3]
n_classes = 10    #數據集的類別數量
epochs = 2000   #訓練過程中,所有數據將被前向傳播反向傳播更新輪多少次,輪的次數越多,模型越准確,但容易過擬合,比如: # 訓練集有1000個樣本,batchsize(批量大小)=10,那么: 訓練完整個樣本集需要: 100次iteration(迭代次數),1次epoch。
every_save_model = 2 # 每多少個epoch保存1次模型

''' 神經網絡中常用的超參數 1. 學習率 η,2. 正則化參數 λ,3. 神經網絡的層數 L,4. 每一個隱層中神經元的個數 j,5. 學習的回合數Epochs,6. 小批量數據 minibatch 的大小,7. 輸出神經元的編碼方式,8. 代價函數的選擇,9. 權重初始化的方法,10. 神經元激活函數的種類,11.參加訓練模型數據的規模 '''

""" 網絡結構圖。 input_x [-1, 32, 32, 3] 輸入層,意為輸入圖片數據是32*32*3的尺寸,長、寬與通道數,-1意為bantch_size(批量)大小,此處設置為自動 w1 [5, 5, 3, 32] 權重,意為5*5*3的卷積核,32個,意為提取32張特征圖,即32個特征 conv1 [-1, 32, 32, 32] 卷積層一,經過input_x與濾波器w1進行卷積運算后,得到:批量大小自動,長、寬、通道數為32*32*32的尺寸 池化1(步幅為2) [-1, 32/2, 32/2, 32] 池化層一:壓縮數據,縮小圖像,減小參數的數量和計算,意為,批量大小自動,執行步幅為2的平均池化或最 大池化,此步完成后得到的是16*16*32尺寸的數據 w2 [5, 5, 32, 128] 權重,意為5*5*32的卷積核,128個,卷積核數量通常都是上一次卷積核數量的倍數增加,意為提取128張特征圖,即 128個特征 conv2 [-1, 16, 16, 128] 卷積層二,經過池化一后的數據與濾波器w2進行卷積運算后,得到:批量大小自動,長、寬、通道數為16*16*128的 尺寸,此次卷積相當於在第一次卷積提取出的特征的基礎上,將第一次提取出來的特征的一些特征組合也提取出 來,相較於第一次卷積結果,因為隨着網絡的加深,feature map的長寬尺寸縮小(池化),本卷積層的每個map提 取的特征越具有代表性(精華部分),所以后一層卷積層需要增加feature map的數量,才能更充分的提取出前一層 的特征,一般是成倍增加 池化2(步幅為2) [-1, 16/2, 16/2, 128] 拉平層 [N, 8, 8, 128] --> [N, 8*8*128] FC1(權重) [8*8*128, 1024] logits(權重) [1024, 10] 預測概率值 -----> 使用softmax激活 """
#vgg16,resnet50這種層數的計算:層數只包含有參數的層,像池化層啊,relu激活層啊,loss層啊這些,都不計數 # 構建模型圖 1、創建變量
graph = tf.Graph() #complete
with graph.as_default(): weights = { 'conv1': tf.get_variable('w1', shape=[5, 5, 3, 32], initializer=tf.truncated_normal_initializer(stddev=0.1)), # tf.truncated_normal_initializer從截斷的正態分布中輸出隨機值。生成的值服從具有指定平均值和標准偏差的正態分布,如果生成的值大於平均值2 個標准偏差的值則丟棄重新選擇。
        'conv2': tf.get_variable('w2', shape=[5, 5, 32, 128], initializer=tf.truncated_normal_initializer(stddev=0.1)), 'fc1': tf.get_variable('w3', shape=[8*8*128, 1024], initializer=tf.truncated_normal_initializer(stddev=0.1)), 'fc2': tf.get_variable('w4', shape=[1024, n_classes], initializer=tf.truncated_normal_initializer(stddev=0.1)) } biases = { 'conv1': tf.get_variable('b1', shape=[32], initializer=tf.zeros_initializer()), #tf.zeros_initializer 全0初始化
        'conv2': tf.get_variable('b2', shape=[128], initializer=tf.zeros_initializer()), 'fc1': tf.get_variable('b3', shape=[1024], initializer=tf.zeros_initializer()), 'fc2': tf.get_variable('b4', shape=[n_classes], initializer=tf.zeros_initializer()) } cifar10_dataset_folder_path = '../datas/cifar-10-batches-py'
if os.path.exists(cifar10_dataset_folder_path): #os.path.exists 判斷括號里的文件是否存在的意思,括號內的可以是文件路徑。存在輸出Ture,不存在輸出False
    print('yes') def explore_data(): #探索一下數據,第五批次中第1001個樣本的信息
    batch_id = 5  #批次編號
    sample_id = 1001  #樣本編號
    nums = helper.display_stats(cifar10_dataset_folder_path, batch_id, sample_id) # epochs = nums // batch_id

def normalize(images,training=True): #complete
    """ 歸一化圖片數據。將其縮放到(0,1)之間 :param images: 圖片數據,圖片的shape =[32, 32, 3] :return: 歸一化以后的numpy的數據 """
    return tf.layers.batch_normalization(images, training=True) #tf.layers下封裝了一些函數,其中包括此函數,批歸一化的函數,參數很多不一一列舉,,return的這兩個參數分別是輸入的圖片,是否參與模型的訓練,此處為參與,注意,當模型訓練好后,用在驗證數據上時,不再歸一化,所以測試數據時,應為False.

def one_hot_encode(x):  #complete
    """ 對輸入的列表(真實類別標簽),轉換為one-hot形式 :param x: 標簽的list。 :return: one-hot編碼后的結果,是一個numpy數組。 """
    return np.eye(10)[x.reshape(-1)].T #np.eye()意為生成多少行多少列的單位矩陣,因為one_hot編碼,即是哪個類別,則第幾位為1,其他幾位都為0
    #x.reshape(-1)將標簽構成的數組x橫向平鋪成0123456789,

def preprocess_data_and_save(): # 預處理訓練,驗證、測試數據集。
 helper.preprocess_and_save_data(cifar10_dataset_folder_path, normalize, one_hot_encode) # todo 檢查點。若預處理數據已經完成,並保存到本地磁盤,那么每次可以從這里開始運行(之前的代碼不用再執行了)
valid_features, valid_labels = pickle.load( open('../datas/cifar10/preprocess_validation.p', mode='rb')) # print(len(valid_features)) #pickle我翻譯為腌咸菜模塊,作用是把python運行中得到的一些列表字典之類永久保存下來,其有兩個方法,dump與load,dump(obj(對象), file(文件夾), [protocol可以為012,0是文本形式,1是老二進制,2是新二進制]),protocol意為協議 # load(文件夾),保存為python文件到文件夾中 #open函數 打開一個文件,如果不存在則創建。rb是以二進制讀模式打開 # load(文件夾),保存為python文件到文件夾中


def cnn_net_input(image_shape, n_classes): """ 定義 input_x, input_y ,keep_prob等占位符。 :param image_shape: 最原始的輸入圖片的尺寸 :param n_classes: 類別數量。 :return: """ input_x = tf.placeholder(tf.float32, [None, image_shape[0], image_shape[1], image_shape[2]], name='input_x') #創建占位符,參數依然是 批量大小,長、寬、通道數
    input_y = tf.placeholder(tf.float32, [None, n_classes], name='input_y') change_learning_rate = tf.placeholder(tf.float32, shape=None, name='change_learning_rate') #學習率 無形狀,標量
    keep_probab = tf.placeholder(tf.float32, shape=None, name='keep_probab') #保留比例
    return input_x, input_y, change_learning_rate, keep_probab def conv2d(input_tensor, filter_w, filter_b, strides=[1, 1, 1, 1]):  #complete
    """ 實現 1、卷積 + 2、偏置項相加 + 3、激活 :param x: :param filter_w: :param filter_b: :param strides: :return: """
    # 1、卷積
    conv = tf.nn.conv2d( input=input_tensor, filter=filter_w, strides=strides, padding='SAME' ) #tf.nn是tensorflow一個內置的十分豐富的函數大集錦,其中就包括了conv2d這個函數,計算給定4-D輸入和濾波器張量的2-D卷積
    #以上四個參數分別是;輸入數據;濾波器;strides=[1, 1, 1, 1]是設置的濾波器的步長,steides四個1分別是N H W C,樣本數,高度,寬度,通道數,第 一個和最后一個1是官方規定的必須是1,第二個和第三個分別是水平步長和垂直方向步長;當輸入數據的 矩陣不夠卷積核掃描時是否在四周填充0,使輸 入圖片和卷積后的圖片長寬尺寸一樣。若是設置valid(合理的)則不會填充,從而有可能形狀變小
    # 2、偏置項相加
    conv = tf.nn.bias_add(conv, filter_b) #將偏置項bias的向量加到value的矩陣上,是向量與矩陣的每一行進行相加,得到的結果和value矩陣大小相同
    # 3、激活
    conv = tf.nn.relu(conv) return conv def maxpool2d(input_tensor, k=2):  #complete
    kernel_size = [1, k, k, 1] #池化也是用濾波器來達到計算目的的,也叫池化核,此為池化核大小,四個參數同上面的strides,N,H,W C 樣本數,高,寬,通道數
    strides = [1, k, k, 1] #池化步幅為2,原矩陣會縮小一半,步幅為1 時原矩陣尺寸不變
    maxpool_out = tf.nn.max_pool( value=input_tensor, ksize=kernel_size, strides=strides, padding='SAME' ) #最大池化操作,padding=same為矩陣周邊填充0
    return maxpool_out def flatten(input_tensor): #complete
    """ flatten層,實現特征圖 維度從 4-D 重塑到 2-D形狀 [Batch_size, 列維度] :param input: :return: """ shape = input_tensor.get_shape()  # [N, 8, 8, 128]
    flatten_shape = shape[1] * shape[2] * shape[3] flatted = tf.reshape(input_tensor, shape=[-1, flatten_shape]) #tf.reshape 改變指定數據的形狀
    return flatted def fully_connect(input_tensor, weights, biases, activation=tf.nn.relu): #complete
    """ 實現全連接 或者 輸出層。 :param input_tensor: :param num_outputs: 輸出的隱藏層節點數量。 :return: """
    #卷積后要激活,池化不要激活,最后一步是全連接時得到output,要激活才能得到預測值
    fc = tf.matmul(input_tensor, weights) + biases if activation: fc = activation(fc) return fc else: # 這里是為了返回最終輸出的logits。
        return fc def model_net(input_x, weights, biases, keep_prob, istrain): #complete
    """ 構建模型 :param input_x: 原始圖片的占位符 :param keep_prob: 定義的keep_prob的占位符。 :return: logits """

    """ 網絡結構圖。 input_x [-1, 32, 32, 3] w1 [5, 5, 3, 32] conv1 [-1, 32, 32, 32] 池化1(步幅為2) [-1, 32/2, 32/2, 32] w2 [5, 5, 32, 128] conv2 [-1, 16, 16, 128] 池化2(步幅為2) [-1, 16/2, 16/2, 128] 拉平層 [N, 8, 8, 128] --> [N, 8*8*128] FC1(權重) [8*8*128, 1024] logits(權重) [1024, 10] 預測概率值 -----> 使用softmax激活 """
    # conv1--dropout(可選)--池化1--conv2--dropout(可選)--池化2--拉平層--全連接層*N--輸出層 得到logits
    with tf.variable_scope('Network'): # 卷積1 [N, 32, 32, 3] --> [N, 32, 32, 32]
        conv1 = conv2d( input_tensor=input_x, filter_w=weights['conv1'], filter_b=biases['conv1']) conv1 = tf.nn.dropout(conv1, keep_prob=keep_probability) if istrain: conv1 = normalize(conv1) # 池化1 [N, 32, 32, 32] -->[N, 16, 16, 32]
        pool1 = maxpool2d(conv1) # 卷積2 [N, 16, 16, 32] --> [N, 16, 16, 128]
        conv2 = conv2d( input_tensor=pool1, filter_w=weights['conv2'], filter_b=biases['conv2']) conv2 = tf.nn.dropout(conv2, keep_prob=keep_probability) if istrain: conv2 = normalize(conv2) # 池化2 [N, 16, 16, 128] -->[N, 8, 8, 128]
        pool2 = maxpool2d(conv2) # 拉平層 [N, 8, 8, 128] ---> [N, 8*8*128]
        shape = pool2.get_shape()  # [N, 8, 8, 128]
        flatten_shape = shape[1] * shape[2] * shape[3] flatted = tf.reshape(pool2, shape=[-1, flatten_shape]) # 全連接層1 [N, 8*8*128] ---> [N, 1024]
        fc1 = fully_connect( input_tensor=flatted, weights=weights['fc1'], biases=biases['fc1']) fc1 = tf.nn.dropout(fc1, keep_prob=keep_prob) # 全連接層2(輸出層) [N, 1024] ---> [N, 10]
        logits = fully_connect( input_tensor=fc1, weights=weights['fc2'], biases=biases['fc2'], activation=None ) return logits # todo 自己定義兩個執行會話環節需要使用的輔助函數。
def train_session(sess, train_opt, input_x, input_y, batch_x, batch_y, keep_prob, keep_probability, change_learning_rate, learning_rate): """ 執行的跑 模型優化器的函數 :param sess: 會話的實例對象 :param train_opt: 優化器對象 :param keep_probability: 實數,保留概率 :param batch_x: 當前的批量的images數據 :param batch_y: 當前批量的標簽數據。 :return: 僅僅是執行優化器,無需返回值。 """ feed = {input_x: batch_x, input_y: batch_y, keep_prob: keep_probability, change_learning_rate: learning_rate} sess.run(train_opt, feed_dict=feed)  # 執行模型訓練


def print_stats(sess, loss, accuracy, input_x, input_y, batch_x, batch_y, keep_probab, keep_probability, change_learning_rate, learning_rate): """ 使用sess跑loss和 Accuracy,並打印出來 :param sess: 會話的實例對象 :param batch_x: 當前的批量的images數據 :param batch_y: 當前批量的標簽數據。 :param loss: 圖中定義的loss tensor對象 :param accuracy: 圖中定義的accuracy tensor對象 :return: 僅僅是打印模型,無需返回值。 """ feed = {input_x: batch_x, input_y: batch_y, keep_probab: keep_probability, change_learning_rate: learning_rate} change_loss, change_acc = sess.run( [loss, accuracy], feed) loss_ = change_loss accuracy_ = change_acc print('Loss:{:.5f} - Valid Accuracy:{:.4f}'.format(loss_, accuracy_)) def create_file_path(path): #complete
    """ 創建文件夾路徑函數 """
    if not os.path.exists(path): os.makedirs(path) print('成功創建路徑:{}'.format(path)) # ____________________________________________________________________________________________________
def train_single_batch(): """ 先跑 preprocess-batch-1 這個訓練數據集,確認模型ok之后,跑所有的數據。 :return: """ tf.reset_default_graph() my_graph = tf.Graph() # 一、建圖
 with my_graph.as_default(): # 1、創建占位符(輸入圖片,輸入的標簽,dropout)
        input_x, input_y,change_learning_rate, keep_prob = cnn_net_input(image_shape, n_classes) # 2、構建cnn圖(傳入輸入圖片,獲得logits)
        logits = model_net(input_x, weights, biases, keep_probab, True) # 3、構建損失函數
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2( logits=logits, labels=input_y )) # 4、構建優化器。
        update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) with tf.control_dependencies(update_ops): #保證train_op在update_ops執行之后再執行。
            optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate) train_opt = optimizer.minimize(loss) # 5、計算准確率
        correct_pred = tf.equal(tf.argmax(logits, axis=1), tf.argmax(input_y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 二、構建會話
 with tf.Session() as sess: # 1、初始化全局變量
 sess.run(tf.global_variables_initializer()) # 2、構建迭代的循環
            for epoch in range(epochs): batch_i = 1
                # 3、構建批量數據的循環
                # 3、構建批量數據的循環
                for batch_x, batch_y in helper.load_preprocess_training_batch(batch_i, batch_size): # 4、跑train_opt
 train_session(sess, train_opt, input_x, input_y, batch_x, batch_y, keep_probab, keep_probability, change_learning_rate, learning_rate) print('Epoch {:>2}, CIFAR-10 Batch:{}'.format(epoch + 1, batch_i), end='') # 5、跑 模型損失和 准確率,並打印出來。
 print_stats(sess, loss, accuracy, input_x, input_y, batch_x, batch_y, keep_probab, keep_probability, change_learning_rate, learning_rate) # # 執行模型持久化的。
                # if epoch % every_save_model == 0:
                # save_file = '_{}_model.ckpt'.format(epoch)
                # save_file = os.path.join(save_path, save_file)
                # saver.save(sess, save_path=save_file)
                # print('Model saved to {}'.format(save_file)) # ________________________________________________________________________________________________________

def train_all_batch(): """ 跑所有的數據。 """
    # 一、建圖
 with graph.as_default(): # 1、創建占位符(輸入圖片,輸入的標簽,dropout)
        input_x, input_y, change_learning_rate, keep_probab = cnn_net_input(image_shape, n_classes) # 2、構建cnn圖(傳入輸入圖片,獲得logits)
        logits = model_net(input_x, weights, biases, keep_probab, True) # 3、構建損失函數
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=input_y)) # 4、構建優化器。
        optimizer = tf.train.AdamOptimizer(learning_rate = change_learning_rate) train_opt = optimizer.minimize(loss) # 5、計算准確率
        correct_pred = tf.equal(tf.argmax(logits, axis=1), tf.argmax(input_y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 6、構建持久化模型的對象 並創建 持久化文件保存的路徑
        saver = tf.train.Saver(max_to_keep=2) save_path = './models/checkpoints' create_file_path(save_path) # 二、構建會話
 with tf.Session() as sess: # 1、初始化全局變量
            ckpt = tf.train.get_checkpoint_state(save_path) if ckpt is not None: saver.restore(sess, ckpt.model_checkpoint_path) saver.recover_last_checkpoints(ckpt.all_model_checkpoint_paths) print('從持久化文件中恢復模型') else: sess.run(tf.global_variables_initializer()) print('沒有持久化文件,從頭開始訓練!') # 2、構建迭代的循環
            print("epochs: {}".format(epochs)) for epoch in range(epochs): # 多加一個循環,遍歷所有的訓練數據的batch
                n_batches = 5
                for batch_i in range(1, n_batches+1): # 3、構建批量數據的循環
                    for batch_x, batch_y in helper.load_preprocess_training_batch(batch_i, batch_size): # 4、跑train_opt
 train_session(sess, train_opt, input_x, input_y, batch_x, batch_y, keep_probab, keep_probability, change_learning_rate, learning_rate) print('Epoch {:>2}, CIFAR-10 Batch:{}'.format(epoch+1, batch_i), end='') # 5、跑 模型損失和 准確率,並打印出來。
 print_stats(sess, loss, accuracy, input_x, input_y, batch_x, batch_y, keep_probab, keep_probability, change_learning_rate, learning_rate) # 執行模型持久化的。
                if epoch % every_save_model == 0: save_file = '_{}_model.ckpt'.format(epoch) save_file = os.path.join(save_path, save_file) saver.save(sess, save_path=save_file) print('Model saved to {}'.format(save_file)) def gotest_model(): """ 調用持久化文件跑測試數據集的數據。(要求准確率在60%以上) """ tf.reset_default_graph() test_features, test_labels = pickle.load( open('../datas/cifar10/preprocess_test.p', mode='rb') ) # 一、建圖
 with graph.as_default(): # 1、創建占位符(輸入圖片,輸入的標簽,dropout)
        input_x, input_y, change_learning_rate, keep_probab = cnn_net_input(image_shape, n_classes) # 2、構建cnn圖(傳入輸入圖片,獲得logits)
        logits = model_net(input_x, weights, biases, keep_probab, True) # 3、構建損失函數
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(logits=logits, labels=input_y)) # 4、構建優化器。
        optimizer = tf.train.AdamOptimizer(learning_rate=change_learning_rate) train_opt = optimizer.minimize(loss) # 5、計算准確率
        correct_pred = tf.equal(tf.argmax(logits, axis=1), tf.argmax(input_y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_pred, tf.float32)) # 6、構建持久化模型的對象 並創建 持久化文件保存的路徑
        saver = tf.train.Saver(max_to_keep=2) save_path = './models/checkpoints'
        # 二、構建會話
 with tf.Session() as sess: # 2、獲取持久化的信息對象
            ckpt = tf.train.get_checkpoint_state(save_path) if ckpt is not None: saver.restore(sess, ckpt.model_checkpoint_path) saver.recover_last_checkpoints(ckpt.all_model_checkpoint_paths) print('從持久化文件中恢復模型') else: sess.run(tf.global_variables_initializer()) print('沒有持久化文件,從頭開始訓練!') # 2、保存每個批次數據的准確率,再求平均值。
            test_acc_total = [] # 3、構建迭代的循環
            for test_batch_x, test_batch_y in helper.batch_features_labels(test_features, test_labels, batch_size): test_dict = {input_x: test_batch_x, input_y: test_batch_y, keep_probab: 1.0} test_batch_acc = sess.run(accuracy, test_dict) test_acc_total.append(test_batch_acc) print('Test Accuracy:{:.5f}'.format(np.mean(test_acc_total))) if np.mean(test_acc_total) > 0.6: print('恭喜你,通過了Cifar10項目!你已經掌握了CNN網絡的基礎知識!') if __name__=='__main__': # explore_data()
    # train_all_batch()
 gotest_model() # train_single_batch()

 


免責聲明!

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



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