在使用卷積神經網絡時,我們也總結了一些訓練技巧,下面就來介紹如何對卷積核進行優化,以及多通道卷積技術的使用。
一 優化卷積核
在實際的卷積訓練中,為了加快速度,常常把卷積核裁開。比如一個3x3的卷積核,可以裁成一個3x1和1x3的卷積核(通過矩陣乘法得知),分別對原有輸入做卷積運算,這樣可以大大提升運算的速度。
原理:在浮點運算中乘法消耗的資源比較多,我們目的就是盡量減少乘法運算。
- 比如對一個5x2的原始圖片進行一次3x3的SAME卷積,相當於生成的5x2的像素中,每一個像素都需要經歷3x3次乘法,那么一共是90次。
- 同樣是這樣圖片,如果先進行一次3X1的SAME卷積,相當於生成的5x2的像素中,每一個像素都需要經歷3x1次乘法,那么一共是30次。再進行一次1x3的SAME卷積也是計算30次,在一起總共60次。
這僅僅是一個很小的數據張量,而且隨着張量維度的增大,層數的增多,減少的運算更多。運算量減少了,運算速度會更快。
接下來我會演示一個例子,仍然是改寫第十三節對cifar10數據集分類的例子。
# -*- coding: utf-8 -*- """ Created on Thu May 3 12:29:16 2018 @author: zy """ ''' 優化卷積核 提高運算速度 ''' ''' 建立一個帶有全局平均池化層的卷積神經網絡 並對CIFAR-10數據集進行分類 1.使用3個卷積層的同卷積操作,濾波器大小為5x5,每個卷積層后面都會跟一個步長為2x2的池化層,濾波器大小為2x2 2.對輸出的10個feature map進行全局平均池化,得到10個特征 3.對得到的10個特征進行softmax計算,得到分類 ''' import cifar10_input import tensorflow as tf import numpy as np def weight_variable(shape): ''' 初始化權重 args: shape:權重shape ''' initial = tf.truncated_normal(shape=shape,mean=0.0,stddev=0.1) return tf.Variable(initial) def bias_variable(shape): ''' 初始化偏置 args: shape:偏置shape ''' initial =tf.constant(0.1,shape=shape) return tf.Variable(initial) def conv2d(x,W): ''' 卷積運算 ,使用SAME填充方式 卷積層后 out_height = in_hight / strides_height(向上取整) out_width = in_width / strides_width(向上取整) args: x:輸入圖像 形狀為[batch,in_height,in_width,in_channels] W:權重 形狀為[filter_height,filter_width,in_channels,out_channels] ''' return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME') def max_pool_2x2(x): ''' 最大池化層,濾波器大小為2x2,'SAME'填充方式 池化層后 out_height = in_hight / strides_height(向上取整) out_width = in_width / strides_width(向上取整) args: x:輸入圖像 形狀為[batch,in_height,in_width,in_channels] ''' return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME') def avg_pool_6x6(x): ''' 全局平均池化層,使用一個與原有輸入同樣尺寸的filter進行池化,'SAME'填充方式 池化層后 out_height = in_hight / strides_height(向上取整) out_width = in_width / strides_width(向上取整) args; x:輸入圖像 形狀為[batch,in_height,in_width,in_channels] ''' return tf.nn.avg_pool(x,ksize=[1,6,6,1],strides=[1,6,6,1],padding='SAME') def print_op_shape(t): ''' 輸出一個操作op節點的形狀 args: t:必須是一個tensor類型 t.get_shape()返回一個元組 .as_list()轉換為list ''' print(t.op.name,'',t.get_shape().as_list()) ''' 一 引入數據集 ''' batch_size = 128 learning_rate = 1e-4 training_step = 15000 display_step = 200 #數據集目錄 data_dir = './cifar10_data/cifar-10-batches-bin' print('begin') #獲取訓練集數據 images_train,labels_train = cifar10_input.inputs(eval_data=False,data_dir = data_dir,batch_size=batch_size) print('begin data') ''' 二 定義網絡結構 ''' #定義占位符 input_x = tf.placeholder(dtype=tf.float32,shape=[None,24,24,3]) #圖像大小24x24x input_y = tf.placeholder(dtype=tf.float32,shape=[None,10]) #0-9類別 x_image = tf.reshape(input_x,[batch_size,24,24,3]) #1.卷積層 ->池化層 W_conv1 = weight_variable([5,5,3,64]) b_conv1 = bias_variable([64]) h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1) #輸出為[-1,24,24,64] print_op_shape(h_conv1) h_pool1 = max_pool_2x2(h_conv1) #輸出為[-1,12,12,64] print_op_shape(h_pool1) #2.卷積層 ->池化層 卷積核做優化 W_conv21 = weight_variable([5,1,64,64]) b_conv21 = bias_variable([64]) h_conv21 = tf.nn.relu(conv2d(h_pool1,W_conv21) + b_conv21) #輸出為[-1,12,12,64] print_op_shape(h_conv21) W_conv2 = weight_variable([1,5,64,64]) b_conv2 = bias_variable([64]) h_conv2 = tf.nn.relu(conv2d(h_conv21,W_conv2) + b_conv2) #輸出為[-1,12,12,64] print_op_shape(h_conv2) h_pool2 = max_pool_2x2(h_conv2) #輸出為[-1,6,6,64] print_op_shape(h_pool2) #3.卷積層 ->全局平均池化層 W_conv3 = weight_variable([5,5,64,10]) b_conv3 = bias_variable([10]) h_conv3 = tf.nn.relu(conv2d(h_pool2,W_conv3) + b_conv3) #輸出為[-1,6,6,10] print_op_shape(h_conv3) nt_hpool3 = avg_pool_6x6(h_conv3) #輸出為[-1,1,1,10] print_op_shape(nt_hpool3) nt_hpool3_flat = tf.reshape(nt_hpool3,[-1,10]) y_conv = tf.nn.softmax(nt_hpool3_flat) ''' 三 定義求解器 ''' #softmax交叉熵代價函數 cost = tf.reduce_mean(-tf.reduce_sum(input_y * tf.log(y_conv),axis=1)) #求解器 train = tf.train.AdamOptimizer(learning_rate).minimize(cost) #返回一個准確度的數據 correct_prediction = tf.equal(tf.arg_max(y_conv,1),tf.arg_max(input_y,1)) #准確率 accuracy = tf.reduce_mean(tf.cast(correct_prediction,dtype=tf.float32)) ''' 四 開始訓練 ''' sess = tf.Session(); sess.run(tf.global_variables_initializer()) # 啟動計算圖中所有的隊列線程 調用tf.train.start_queue_runners來將文件名填充到隊列,否則read操作會被阻塞到文件名隊列中有值為止。 tf.train.start_queue_runners(sess=sess) for step in range(training_step): #獲取batch_size大小數據集 image_batch,label_batch = sess.run([images_train,labels_train]) #one hot編碼 label_b = np.eye(10,dtype=np.float32)[label_batch] #開始訓練 train.run(feed_dict={input_x:image_batch,input_y:label_b},session=sess) if step % display_step == 0: train_accuracy = accuracy.eval(feed_dict={input_x:image_batch,input_y:label_b},session=sess) print('Step {0} tranining accuracy {1}'.format(step,train_accuracy))
運行結果如下:
二 多通道卷積技術
多通道卷積技術詳細內容可以點擊:第十四節,卷積神經網絡之經典網絡Inception(四)
多通道卷積可以理解為一種新的CNN網絡模型,在原有的卷積模型基礎上擴展。
- 在原有的卷積層中是使用單個尺寸的卷積核對輸入數據進行卷積操作,生成若干個feature map。
- 而多通道卷積的變化就是,在單個卷積層中加入若干個不同尺寸的過濾器,這樣會使生成的feature map特征更加多樣性。
接下來我會演示一個例子,仍然是改寫第十三節對cifar10數據集分類的例子,我們為網絡的卷積層增加不同尺寸的卷積核。這里將原有的5x5卷積,擴展到7x7卷積,1x1卷積,3x3卷積,並將它們的輸出通過tf.concat()函數並在一起。
代碼如下:
# -*- coding: utf-8 -*- """ Created on Sat May 5 17:59:50 2018 @author: zy """ ''' 多通道卷積技術 ''' ''' 建立一個帶有全局平均池化層的卷積神經網絡 並對CIFAR-10數據集進行分類 1.使用3個卷積層的同卷積操作,濾波器大小為5x5,每個卷積層后面都會跟一個步長為2x2的池化層,濾波器大小為2x2 2.對輸出的10個feature map進行全局平均池化,得到10個特征 3.對得到的10個特征進行softmax計算,得到分類 ''' import cifar10_input import tensorflow as tf import numpy as np def weight_variable(shape): ''' 初始化權重 args: shape:權重shape ''' initial = tf.truncated_normal(shape=shape,mean=0.0,stddev=0.1) return tf.Variable(initial) def bias_variable(shape): ''' 初始化偏置 args: shape:偏置shape ''' initial =tf.constant(0.1,shape=shape) return tf.Variable(initial) def conv2d(x,W): ''' 卷積運算 ,使用SAME填充方式 卷積層后 out_height = in_hight / strides_height(向上取整) out_width = in_width / strides_width(向上取整) args: x:輸入圖像 形狀為[batch,in_height,in_width,in_channels] W:權重 形狀為[filter_height,filter_width,in_channels,out_channels] ''' return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME') def max_pool_2x2(x): ''' 最大池化層,濾波器大小為2x2,'SAME'填充方式 池化層后 out_height = in_hight / strides_height(向上取整) out_width = in_width / strides_width(向上取整) args: x:輸入圖像 形狀為[batch,in_height,in_width,in_channels] ''' return tf.nn.max_pool(x,ksize=[1,2,2,1],strides=[1,2,2,1],padding='SAME') def avg_pool_6x6(x): ''' 全局平均池化層,使用一個與原有輸入同樣尺寸的filter進行池化,'SAME'填充方式 池化層后 out_height = in_hight / strides_height(向上取整) out_width = in_width / strides_width(向上取整) args; x:輸入圖像 形狀為[batch,in_height,in_width,in_channels] ''' return tf.nn.avg_pool(x,ksize=[1,6,6,1],strides=[1,6,6,1],padding='SAME') def print_op_shape(t): ''' 輸出一個操作op節點的形狀 args: t:必須是一個tensor類型 t.get_shape()返回一個元組 .as_list()轉換為list ''' print(t.op.name,'',t.get_shape().as_list()) ''' 一 引入數據集 ''' batch_size = 128 learning_rate = 1e-4 training_step = 15000 display_step = 200 #數據集目錄 data_dir = './cifar10_data/cifar-10-batches-bin' print('begin') #獲取訓練集數據 images_train,labels_train = cifar10_input.inputs(eval_data=False,data_dir = data_dir,batch_size=batch_size) print('begin data') ''' 二 定義網絡結構 ''' #定義占位符 input_x = tf.placeholder(dtype=tf.float32,shape=[None,24,24,3]) #圖像大小24x24x input_y = tf.placeholder(dtype=tf.float32,shape=[None,10]) #0-9類別 x_image = tf.reshape(input_x,[batch_size,24,24,3]) #1.卷積層 ->池化層 W_conv1 = weight_variable([5,5,3,64]) b_conv1 = bias_variable([64]) h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1) + b_conv1) #輸出為[-1,24,24,64] print_op_shape(h_conv1) h_pool1 = max_pool_2x2(h_conv1) #輸出為[-1,12,12,64] print_op_shape(h_pool1) #2.卷積層 ->池化層 這里使用多通道卷積 W_conv2_1x1 = weight_variable([1,1,64,64]) b_conv2_1x1 = bias_variable([64]) W_conv2_3x3 = weight_variable([3,3,64,64]) b_conv2_3x3 = bias_variable([64]) W_conv2_5x5 = weight_variable([5,5,64,64]) b_conv2_5x5 = bias_variable([64]) W_conv2_7x7 = weight_variable([7,7,64,64]) b_conv2_7x7 = bias_variable([64]) h_conv2_1x1 = tf.nn.relu(conv2d(h_pool1,W_conv2_1x1) + b_conv2_1x1) #輸出為[-1,12,12,64] h_conv2_3x3 = tf.nn.relu(conv2d(h_pool1,W_conv2_3x3) + b_conv2_3x3) #輸出為[-1,12,12,64] h_conv2_5x5 = tf.nn.relu(conv2d(h_pool1,W_conv2_5x5) + b_conv2_5x5) #輸出為[-1,12,12,64] h_conv2_7x7 = tf.nn.relu(conv2d(h_pool1,W_conv2_7x7) + b_conv2_7x7) #輸出為[-1,12,12,64] #合並 3表示沿着通道合並 h_conv2 = tf.concat((h_conv2_1x1,h_conv2_3x3,h_conv2_5x5,h_conv2_7x7),axis=3) #輸出為[-1,12,12,256] h_pool2 = max_pool_2x2(h_conv2) #輸出為[-1,6,6,256] print_op_shape(h_pool2) #3.卷積層 ->全局平均池化層 W_conv3 = weight_variable([5,5,256,10]) b_conv3 = bias_variable([10]) h_conv3 = tf.nn.relu(conv2d(h_pool2,W_conv3) + b_conv3) #輸出為[-1,6,6,10] print_op_shape(h_conv3) nt_hpool3 = avg_pool_6x6(h_conv3) #輸出為[-1,1,1,10] print_op_shape(nt_hpool3) nt_hpool3_flat = tf.reshape(nt_hpool3,[-1,10]) y_conv = tf.nn.softmax(nt_hpool3_flat) ''' 三 定義求解器 ''' #softmax交叉熵代價函數 cost = tf.reduce_mean(-tf.reduce_sum(input_y * tf.log(y_conv),axis=1)) #求解器 train = tf.train.AdamOptimizer(learning_rate).minimize(cost) #返回一個准確度的數據 correct_prediction = tf.equal(tf.arg_max(y_conv,1),tf.arg_max(input_y,1)) #准確率 accuracy = tf.reduce_mean(tf.cast(correct_prediction,dtype=tf.float32)) ''' 四 開始訓練 ''' sess = tf.Session(); sess.run(tf.global_variables_initializer()) # 啟動計算圖中所有的隊列線程 調用tf.train.start_queue_runners來將文件名填充到隊列,否則read操作會被阻塞到文件名隊列中有值為止。 tf.train.start_queue_runners(sess=sess) for step in range(training_step): #獲取batch_size大小數據集 image_batch,label_batch = sess.run([images_train,labels_train]) #one hot編碼 label_b = np.eye(10,dtype=np.float32)[label_batch] #開始訓練 train.run(feed_dict={input_x:image_batch,input_y:label_b},session=sess) if step % display_step == 0: train_accuracy = accuracy.eval(feed_dict={input_x:image_batch,input_y:label_b},session=sess) print('Step {0} tranining accuracy {1}'.format(step,train_accuracy))
運行結果如下(我只截取了一部分):