天天看點

卷積神經網絡處理圖像識别(二)

本篇介紹卷積神經網絡之前向傳播的基本實作。

卷積神經網絡處理圖像識别(二)

本篇中卷積神經網絡的結構為:

卷積層->池化層->卷積層->池化層->全連接配接層->全連接配接層

其中的全連接配接層還引入了dropout的概念。dropout在訓練時會随機将部分節點的輸出改為0(使神經元以一定機率失活)。dropout可以避免過拟合(overfitting)問題。

代碼和注釋中有詳細的介紹:

import  tensorflow as tf
#基于MNIST 資料集,稍作更改便可應用于其他資料集。#MNIST 資料集資訊,IMAGE_HEIGHT = 28IMAGE_WIDTH = 28NUM_CHANNELS = 1#顔色通道數NUM_LABELS =10#神經網絡參數#INPUT_NODE = IMAGE_HEIGHT*IMAGE_WIDTHOUTPUT_NODE = NUM_LABELS#第1層卷積層的尺寸(5x5)和深度CONV1_SIZE = 5CONV1_DEEP = 32#第2層卷積層的尺寸(5x5)和深度CONV2_SIZE = 5CONV2_DEEP = 64#第一個全連接配接層的節點個數FC1_SIZE = 520
def inference(input_tensor, train, regularizer, avg_class, reuse = True):    '''卷積神經網絡前向傳播,參數train用于區分訓練過程和測試過程'''       #第一層,卷積層    with tf.variable_scope('layer1-conv1', reuse =reuse):        #權重矩陣        conv1_weights = tf.get_variable("weight", [CONV1_SIZE, CONV1_SIZE, NUM_CHANNELS, CONV1_DEEP],                                        initializer = tf.truncated_normal_initializer(stddev = 0.1))        #偏置矩陣        conv1_biases = tf.get_variable("bias", [CONV1_DEEP], initializer = tf.constant_initializer(0.0))               #過濾器x,y向步長均為1 (strides的第2,3個元素)。strides的第1,第4個元素隻能為1        #全0填充 padding='SAME';不填充則用padding='VALID'        conv1 = tf.nn.conv2d(input_tensor, conv1_weights, strides =[1,1,1,1], padding='SAME')        #添加偏置        conv1 = tf.nn.bias_add(conv1, conv1_biases)        #ReLU引入非線性        relu1 = tf.nn.relu(conv1)        #輸出矩陣shape 為 IMAGE_HEIGHTXIMAGE_WIDTHXCONV1_DEEP, 即28x28x32          #第二層,池化層(下采樣)    with tf.variable_scope('layer2-pool1', reuse =reuse):        #使用最大池化層,過濾器的尺寸為2X2, 過濾器x,y向步長均為2,全零填充        pool1 = tf.nn.max_pool(relu1, ksize=(1,2,2,1), strides =[1,2,2,1], padding='SAME')#輸出矩陣shape 為 14x14x32$$$$           #第三層,卷積層    with tf.variable_scope('layer3-conv2', reuse =reuse):        #權重矩陣        conv2_weights = tf.get_variable("weight", [CONV2_SIZE, CONV2_SIZE, CONV1_DEEP, CONV2_DEEP],                                        initializer = tf.truncated_normal_initializer(stddev = 0.1))        #偏置矩陣        conv2_biases = tf.get_variable("bias", [CONV2_DEEP], initializer = tf.constant_initializer(0.0))               #過濾器x,y向步長均為1 (strides的第2,3個元素)。strides的第1,第4個元素隻能為1        #全0填充 padding='SAME';不填充則用padding='VALID'        conv2 = tf.nn.conv2d(pool1, conv2_weights, strides =[1,1,1,1], padding='SAME')        #添加偏置        conv2 = tf.nn.bias_add(conv2, conv2_biases)        #ReLU引入非線性        relu2 = tf.nn.relu(conv2)        #輸出矩陣shape ,我們僅改變了深度,即14X14X64             #第四層,池化層(下采樣)    with tf.variable_scope('layer4-pool2', reuse =reuse):         #使用最大池化層,過濾器的尺寸為2X2, 過濾器x,y向步長均為2,全零填充        pool2 = tf.nn.max_pool(relu2, ksize=(1,2,2,1), strides =[1,2,2,1], padding='SAME')#        #輸出矩陣shape 為 7X7X64           

複制

#第五層,(第1個)全連接配接層    #全連接配接層的輸入格式為向量,張量需要展平。    #get_shape().as_list()可以計算(最後一個池化層)各次元大小,傳回到一個清單    # 因每層神經網絡的輸入輸出都為一個batch(一同訓練的一批樣本)的張量,pool_shape[0]為一個batch中樣本的個數    #而inference函數前面都不用考慮batch    pool_shape = pool2.get_shape().as_list()    nodes =  pool_shape[1] * pool_shape[2] * pool_shape[3] #得到第一個全連接配接層的輸入節點數    #注意    #pool_shape[0]這裡不如用-1 。    # -1(自動調整)可以适應 placeholder中batch size 為None,測試集,驗證集batch不比對的問題(測試集batch為batchsize,驗證集為整個集樣本數)    #reshaped = tf.reshape(pool2,[pool_shape[0], nodes]) # X的第一維size傳入None時會報錯    reshaped = tf.reshape(pool2,[-1, nodes]) #注意這裡的中括号,與numpy.reshape()不同    #這裡引入了dropout的概念。dropout在訓練時會随機将部分節點的輸出改為0。dropout可以避免過拟合(overfitting)問題    #dropout 一般隻在全連接配接層使用    with tf.variable_scope('layer5_fc1', reuse =reuse):        #權重        fc1_weights =  tf.get_variable("weight", [nodes, FC1_SIZE, ],                                       initializer = tf.truncated_normal_initializer(stddev = 0.1))               #隻有全連接配接層的權重需要加入正則化,也是為了避免過拟合        if regularizer != None:            tf.add_to_collection('losses', regularizer(fc1_weights))                   #偏置        fc1_biases = tf.get_variable('bias', [FC1_SIZE], initializer=tf.constant_initializer(0.0))        #平均移動模型        if avg_class != None:            fc1_weights = avg_class.average(fc1_weights)            fc1_biases = avg_class.average(fc1_biases)        #權重        fc1 = tf.matmul(reshaped, fc1_weights) + fc1_biases        #ReLU激活        fc1 = tf.nn.relu(fc1)        if train:            fc1 = tf.nn.dropout(fc1, 0.5) #每個神經元 以 50% 機率被抑制          #第六層,(第2個,最後一個)全連接配接層    #輸出長度為10 (因為隻有十個類别)的向量。之後無需引入激活函數。輸出通過Softmax()就得到了分類的結果    with tf.variable_scope('layer6_fc2', reuse =reuse):        #權重        fc2_weights =  tf.get_variable("weight", [FC1_SIZE, NUM_LABELS, ],                                       initializer = tf.truncated_normal_initializer(stddev = 0.1))               #隻有全連接配接層的權重需要加入正則化,也是為了避免過拟合        if regularizer is not None:            tf.add_to_collection('losses', regularizer(fc2_weights))                   #偏置        fc2_biases = tf.get_variable('bias', [NUM_LABELS], initializer=tf.constant_initializer(0.0))               #平均移動模型        if avg_class is not None:            fc2_weights = avg_class.average(fc2_weights)            fc2_biases = avg_class.average(fc2_biases)                   #權重        logit = tf.matmul(fc1, fc2_weights) + fc2_biases
    #傳回最後一層全連接配接層的輸出    return logit    # 通過tf.argmax()即可得到的分類結果           

複制

由于此卷積神經網絡的待訓練參數比較多,是以訓練(卷積神經網絡的訓練下篇會介紹)起來比較慢。若是電腦性能不太好,可以适當減少參數數量,比如可以增大卷積層和池化層的過濾器的尺寸和移動步長,以及減少全連接配接層的節點數。