卷積網絡
卷積神經網絡(Convolutional Neural Network,CNN)專門處理具有類似網格結構的資料的神經網絡。如:
- 時間序列資料(在時間軸上有規律地采樣形成的一維網格);
- 圖像資料(二維的像素網格);
卷積網絡是指至少在網絡的一層中使用卷積運算來替代一般的矩陣乘法運算的神經網絡。
卷積
前面講過卷積,
相關算法這裡直接使用。
卷積公式為:\(s(t)=\int_{-\infty}^{t}x(\tau)w(t-\tau)d\tau\),記作\(s(t)=(x*w)(t)\)。
這裡卷積第一個參數(函數\(x\))叫做輸入,第二個參數叫做核函數,輸出有時叫做特征映射。
卷積濾波器(原文)
CNN中的濾波器與權重矩陣一樣,它與輸入圖像的一部分相乘以産生一個回旋輸出。我們假設有一個大小為
28 * 28的圖像,我們随機配置設定一個大小為3 * 3的濾波器,然後與圖像不同的3 * 3部分相乘,形成所謂
的卷積輸出。濾波器尺寸通常小于原始圖像尺寸。在成本最小化的反向傳播期間,濾波器值被更新為重量值。
卷積神經網絡(原文)
卷積神經網絡基本上應用于圖像資料。假設我們有一個輸入的大小(28 * 28 * 3),如果我們使用正常的
神經網絡,将有2352(28 * 28 * 3)參數。并且随着圖像的大小增加參數的數量變得非常大。我們“卷積”
圖像以減少參數數量(如上面濾波器定義所示)。當我們将濾波器滑動到輸入體積的寬度和高度(寬度為28,
高度為28)時,将産生一個二維激活圖,給出該濾波器在每個位置的輸出。我們将沿深度(深度為3)尺寸堆疊
這些激活圖,并産生輸出量。
你可以看到下面的圖,以獲得更清晰的印象。
池化(Pooling)(原文)
通常在卷積層之間定期引入池層。這基本上是為了減少一些參數,并防止過度拟合。最常見的池化類型是
使用MAX操作的濾波器尺寸(2,2)的池層。它會做的是,它将占用原始圖像的每個4 * 4矩陣的最大值。

你還可以使用其他操作(如平均池)進行池化,但是最大池數量在實踐中表現更好。
填充(Padding)
填充是指在圖像之間添加額外的零層,以使輸出圖像的大小與輸入相同。這被稱為相同的填充。
在應用濾波器之後,在相同填充(shape=same)的情況下,卷積層具有等于實際圖像的大小。
有效填充(shape=valid)是指将圖像保持為具有實際或“有效”的圖像的所有像素。在這種情
況下,在應用濾波器之後,輸出的長度和寬度的大小在每個卷積層處不斷減小。
資料集增強
建立假資料以實作更好的泛化:對資料進行變換或加噪聲(如對圖形進行旋轉平移,加高斯噪聲等)後作為訓練資料集,保證分類效果不變|
卷積神經網絡案例1:MNIST進階(原文)
----中間幾章辣麼多的理論的東西,現在我們繼續看實作部分。----
首先應該回顧TensorFlow學習筆記3-從MNIST開始。
其中,在MNIST上隻有91%正确率,實在太糟糕。在此,我們用一個稍微複雜的模型:卷積神經網絡來改善效果。這會達到大概99.2%的準
确率。雖然不是最高,但是還是比較讓人滿意。
權重初始化
\(w\)初始化為高斯噪聲,防止輸出恒為0,進而一開始就處在激活狀态,而偏置\(b\)初始化為較小的正數。
為了代碼更簡潔,我們把這部分抽象成一個函數。
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
卷積和池化
卷積使用1步長(stride size),0邊距(padding size)的模闆,保證輸出和輸入是同一個大小。我們的池化用
簡單傳統的2x2大小的模闆做max pooling。為了代碼更簡潔,我們把這部分抽象成一個函數。
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
其中TensorFlow中的
tf.nn.conv2d
函數:
tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=true, name=None)
-
參數:給傳回的tensor命名。給輸出feature map起名字。name
- 第一個參數
:指需要做卷積的輸入圖像,它要求是一個Tensor,具有input
這樣的shape,具體含義是[batch, in_height, in_width, in_channels]
,注意這是一個4維的Tensor,要求類型為float32或float64;[訓練時一個batch的圖檔數量, 圖檔高度, 圖檔寬度, 圖像通道數]
- 第二個參數
:相當于CNN中的卷積核,它要求是一個Tensor,具有filter
[filter_height, filter_width, in_channels, out_channels]
,要求類型為float32或float64;[卷積核的高度,卷積核的寬度,圖像通道數,卷積核個數]
- 第三個參數
:卷積時在圖像每一維的步長,一個一維的向量,長度4;如果你的資料是圖檔,則為[1,stepx,stepy,1]。strides
- 第四個參數
:padding
類型的量,隻能是"SAME"或"VALID",這個值決定了卷積方式;string
- 第五個參數
:bool類型,是否使用cudnn加速,預設為use_cudnn_on_gpu
;true
輸出,就是我們常說的feature map,shape仍然是
[batch, height, width, channels]
這種形式。
例如:如輸入三通道rgb圖,濾波器的out_channels設為1的話,就是三通道對應值相加,最後輸出一個卷積和,即一個通道。
第一層卷積
第一層由一個卷積接一個max pooling完成。卷積在5x5的卷積核中共算出32個特征。卷積的權重張量形狀
是[5, 5, 1, 32],前兩個次元是卷積核的大小,接着是輸入的通道數目,最後是輸出的通道數目。 而對于
每一個輸出通道都有一個對應的偏置量。
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
為了用這一層,我們把
x
變成一個4d向量,其第2、第3維對應圖檔的寬、高,最後一維代表圖檔的顔色通道數(因為是灰階圖是以這裡的通道數為1,如果是rgb彩色圖,則為3)。
x_image = tf.reshape(x, [-1,28,28,1])
然後用權重對
x_image
進行卷積, 添加偏置, 應用ReLU函數, 和max-pool函數. 我們把x_image和權值向量進行卷積,加上偏置項,然後應用ReLU激活函數,最後進行max pooling。
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) #單通道的圖像資料(28*28)經卷積後變為32通道的特征MAP(28*28)
h_pool1 = max_pool_2x2(h_conv1) # 32通道的特征MAP(28*28)經池化後變為32通道的特征MAP(14*14)
第二層卷積
為了建構一個更深的網絡,我們會把幾個類似的層堆疊起來。第二層中,5x5的卷積核共得到64通道的特征MAP。
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) #32通道的特征MAP(14*14)經卷積後變為64通道的特征MAP(14*14)
h_pool2 = max_pool_2x2(h_conv2) # 64通道的特征MAP(14*14)經池化後變為64通道的特征MAP(7*7)
密集連接配接層(為了将現在的特征值的數目進一步減小)
現在,圖檔尺寸減小到7x7,加入一個有1024個神經元(1024個神經元,意味着W的列數為1024?)的全連接配接層,用于處理整個圖檔。我們把池化層輸出的張量reshape成一些向量,乘上權重矩陣,加上偏置,然後對其使用ReLU。
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) #将h_pool2這個64通道的特征MAP(7*7)展開到1維進行扁平化,現在h_pool2_flat是一個-1*(7*7*64)的設計矩陣,每行代表1張圖檔
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) #h_fc1是(-1)*1024的設計矩陣。
Dropout
為了減少過拟合,我們在輸出層之前加入dropout。我們用一個placeholder來代表一個神經元的輸出在dropout中保持不變的機率。這樣我們可以在訓練過程中啟用dropout,在測試過程中關閉dropout。 TensorFlow的tf.nn.dropout操作除了可以屏蔽神經元的輸出外,還會自動處理神經元輸出值的scale。是以用dropout的時候可以不用考慮scale。
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
輸出層(其實這個等效于TensorFlow學習筆記3-從MNIST開始中的神經元結構)
最後,我們添加一個softmax層,就像前面的單層softmax regression一樣。
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
訓練和評估模型
這個模型的效果如何呢?
為了進行訓練和評估,我們使用與之前簡單的單層SoftMax神經網絡模型幾乎相同的一套代碼,隻是我們會用更加複雜的ADAM優化器來做梯度最速下降,在feed_dict中加入額外的參數keep_prob來控制dropout比例。然後每100次疊代輸出一次日志。
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x:batch[0], y_: batch[1], keep_prob: 1.0})
print "step %d, training accuracy %g"%(i, train_accuracy)
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
print "test accuracy %g"%accuracy.eval(feed_dict={
x: mnist.test.assets, y_: mnist.test.labels, keep_prob: 1.0})
以下是完整代碼:
# 加載資料
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
# 産生随機變量,符合 normal 分布
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
# 産生常量矩陣
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
# 定義2維的convolutional圖層
# strides:每跨多少步抽取資訊,strides[1, x_movement,y_movement, 1], 對圖檔而言,strides[0]和strides[3]必須為1
# padding:邊距處理,“SAME”表示輸出圖層和輸入圖層大小保持不變,設定為“VALID”時表示舍棄多餘邊距(丢失資訊)
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
# 定義pooling圖層
# pooling:解決跨步大時可能丢失一些資訊的問題,max-pooling就是在前圖層上依次不重合采樣2*2的視窗最大值
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
x = tf.placeholder(tf.float32, [None, 784])
x_image = tf.reshape(x, [-1, 28, 28, 1]) # 将原圖reshape為4維,-1表示資料是黑白的,28*28=784,1表示顔色通道數目
y_ = tf.placeholder(tf.float32, [None, 10])
### 1. 第一層卷積網絡
# 把x_image的厚度由1增加到32,長寬由28*28縮小為14*14
W_conv1 = weight_variable([5, 5, 1, 32]) # 按照[5,5,輸入通道=1,輸出通道=32]生成一組随機變量
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # 輸出size 28*28*32(因為conv2d()中x和y步長都為1,邊距保持不變)
h_pool1 = max_pool_2x2(h_conv1) # 輸出size 14*14*32
### 2. 第二層卷積網絡
# 把h_pool1的厚度由32增加到64,長寬由14*14縮小為7*7
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
### 3. 第一層全連接配接
# 把h_pool2由7*7*64,變成1024*1
W_fc1 = weight_variable([7*7*64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) # 把pooling後的結構reshape為一維向量
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder('float')
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # 按照keep_prob的機率扔掉一些,為了減少過拟合
### 4. 輸出層
#使用softmax計算機率進行分類, 最後一層網絡,1024 -> 10,
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))
sess = tf.Session()
sess.run(tf.initialize_all_variables())
for i in range(20000): #訓練20000次
batch = mnist.train.next_batch(50)
if i % 100 == 0:
train_accuracy = accuracy.eval(session = sess, feed_dict={x: batch[0], y_: batch[1], keep_prob: 1.0})
print('step %d, training accuracy %g' % (i, train_accuracy))
sess.run(train_step, feed_dict = {x: batch[0], y_: batch[1], keep_prob: 0.5})
print('test accuracy %g' % accuracy.eval(session = sess, feed_dict={x: mnist.test.assets, y_: mnist.test.labels, keep_prob: 1.0}))
正确率達到了99.27%。
參考文獻
主要參考TensorFlow中文社群網站,深度學習(古德費羅),以及網際網路上其他資源。