本篇介紹卷積神經網絡之前向傳播的基本實作。
本篇中卷積神經網絡的結構為:
卷積層->池化層->卷積層->池化層->全連接配接層->全連接配接層
其中的全連接配接層還引入了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()即可得到的分類結果
複制
由于此卷積神經網絡的待訓練參數比較多,是以訓練(卷積神經網絡的訓練下篇會介紹)起來比較慢。若是電腦性能不太好,可以适當減少參數數量,比如可以增大卷積層和池化層的過濾器的尺寸和移動步長,以及減少全連接配接層的節點數。