人工神經網絡VS卷積神經網絡
- 參數太多,在cifar-10的數據集中,只有32*32*3,就會有這么多權重,如果說更大的圖片,比如200*200*3就需要120000多個,這完全是浪費。
- 沒有利用像素之間位置信息,對於圖像識別任務來說,每個像素與周圍的像素都是聯系比較緊密的。
- 網絡層數限制 我們知道網絡層數越多其表達能力越強,但是通過梯度下降方法訓練深度人工神經網絡很困難,因為全連接神經網絡的梯度很難傳遞超過3層。因此,我們不可能得到一個很深的全連接神經網絡,也就限制了它的能力。
那么,卷積神經網絡又是怎樣解決這個問題的呢?主要有三個思路:
- 局部連接:這個是最容易想到的,每個神經元不再和上一層的所有神經元相連,而只和一小部分神經元相連。這樣就減少了很多參數。
- 權值共享:一組連接可以共享同一個權重,而不是每個連接有一個不同的權重,這樣又減少了很多參數。
- 下采樣:可以使用Pooling來減少每層的樣本數,進一步減少參數數量,同時還可以提升模型的魯棒性。對於圖像識別任務來說,卷積神經網絡通過盡可能保留重要的參數,去掉大量不重要的參數,來達到更好的學習效果
卷積神經網絡CNN
卷積神經網絡與上一篇文章中的普通神經網絡非常相似:它們由具有學習權重和偏差的神經元組成。每個神經元接收一些輸入,執行點積,並且可選地以非線性跟隨它。整個網絡仍然表現出單一的可微分評分功能:從一端的原始圖像像素到另一個類的分數。並且在最后(完全連接)層上它們仍然具有損失函數(例如SVM / Softmax),並且我們為學習正常神經網絡開發的所有技巧/技巧仍然適用。
CNN每一層都通過可微分的函數將一個激活的值轉換為另一個,一般來說CNN具有卷積層,池化層和完全連接層FC(正如在常規神經網絡中所見),在池化層之前一般會有個激活函數,我們將堆疊這些層,形成一個完整的架構。我們先看下大概的一個圖:
CNN它將一個輸入3D體積變換為輸出3D體積,正常的神經網絡不同,CNN具有三維排列的神經元:寬度,高度,深度。
卷積層
參數及結構
四個超參數控制輸出體積的大小:過濾器大小,深度,步幅和零填充。得到的每一個深度也叫一個Feature Map。
卷積層的處理:在卷積層有一個重要的就是過濾器大小(需要自己指定),若輸入值是一個[32x32x3]的大小(例如RGB CIFAR-10彩色圖像)。如果每個過濾器(Filter)的大小為5×5,則CNN層中的每個Filter將具有對輸入體積中的[5x5x3]區域的權重,總共5 *5* 3 = 75個權重(和+1偏置參數),輸入圖像的3個深度分別與Filter的3個深度進行運算。請注意,沿着深度軸的連接程度必須為3,因為這是輸入值的深度,並且也要記住這只是一個Filter。
- 假設輸入卷的大小為[16x16x20]。然后使用3x3的示例接收字段大小,CNN中的每個神經元現在將具有總共3 *3* 20 = 180個連接到輸入層的連接。
卷積層的輸出深度:那么一個卷積層的輸出深度是可以指定的,輸出深度是由你本次卷積中Filter的個數決定。加入上面我們使用了64個Filter,也就是[5,5,3,64],這樣就得到了64個Feature Map,這樣這64個Feature Map可以作為下一次操作的輸入值。
卷積層的輸出寬度:輸出寬度可以通過特定算數公式進行得出,后面會列出公式。
卷積輸出值的計算
我們用一個簡單的例子來講述如何計算卷積,然后,我們抽象出卷積層的一些重要概念和計算方法。
假設有一個5*5的圖像,使用一個3*3的filter進行卷積,得到了到一個3*3的Feature Map,至於得到3*3大小,可以自己去計算一下。如下所示:
我們看下它的計算過程,首先計算公式如下:
根據計算的例子,第一次:
第二次:
通過這樣我們可以依次計算出Feature Map中所有元素的值。下面的動畫顯示了整個Feature Map的計算過程:
步長
那么在卷積神經網絡中有一個概念叫步長,也就是Filter移動的間隔大小。上面的計算過程中,步幅(stride)為1。步幅可以設為大於1的數。例如,當步幅為2時,我們可以看到得出2*2大小的Feature Map,發現這也跟步長有關。Feature Map計算如下:
外圍補充與多Filter
我們前面還曾提到,每個卷積層可以有多個filter。每個filter和原始圖像進行卷積后,都可以得到一個Feature Map。因此,卷積后Feature Map的深度(個數)和卷積層的filter個數是相同的。
如果我們的步長移動與filter的大小不適合,導致不能正好移動到邊緣怎么辦?
以上就是卷積層的計算方法。這里面體現了局部連接和權值共享:每層神經元只和上一層部分神經元相連(卷積計算規則),且filter的權值對於上一層所有神經元都是一樣的。
總結輸出大小
卷積網絡API
tf.nn.conv2d(input, filter, strides=, padding=, name=None):
- 計算給定4-D input和filter張量的2維卷積
- input:給定的輸入張量,具有[batch,heigth,width,channel],類型為float32,64
- filter:指定過濾器的大小,[filter_height, filter_width, in_channels, out_channels]
- strides:strides = [1, stride, stride, 1],步長
- padding:“SAME”, “VALID”,使用的填充算法的類型,使用“SAME”。其中”VALID”表示滑動超出部分舍棄,“SAME”表示填充,使得變化后height,width一樣大
新的激活函數-Relu
一般在進行卷積之后就會提供給激活函數得到一個輸出值。我們不使用sigmoid,softmax,而使用Relu。該激活函數的定義是:
Relu函數如下:
特點
- 速度快,和sigmoid函數需要計算指數和倒數相比,relu函數其實就是一個max(0,x),計算代價小很多
- 稀疏性,通過對大腦的研究發現,大腦在工作的時候只有大約5%的神經元是激活的,而采用sigmoid激活函數的人工神經網絡,其激活率大約是50%。有論文聲稱人工神經網絡在15%-30%的激活率時是比較理想的。因為relu函數在輸入小於0時是完全不激活的,因此可以獲得一個更低的激活率。
rule激活函數API
tf.nn.relu(features, name=None)
- features:卷積后加上偏置的結果
- return:結果
Pooling計算
Pooling層主要的作用是特征提取,通過去掉Feature Map中不重要的樣本,進一步減少參數數量。Pooling的方法很多,最常用的是Max Pooling。
除了Max Pooing之外,常用的還有Mean Pooling——取各樣本的平均值。對於深度為D的Feature Map,各層獨立做Pooling,因此Pooling后的深度仍然為D。
Pooling API
tf.nn.max_pool(value, ksize=, strides=, padding=,name=None)
- 輸入上執行最大池數
- value:4-D Tensor形狀[batch, height, width, channels]
- ksize:池化窗口大小,[1, ksize, ksize, 1]
- strides:步長大小,[1,strides,strides,1]
- padding:“SAME”, “VALID”,使用的填充算法的類型
Mnist數據集卷積網絡實現
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
def weight_variables(shape):
"""權重初始化函數"""
w = tf.Variable(tf.random_normal(shape=shape, mean=0.0, stddev=1.0))
return w
def bias_variables(shape):
"""偏置初始化函數"""
b = tf.Variable(tf.constant(0.0, shape=shape))
return b
def model():
"""自定義的卷積模型"""
# 建立數據的占位符
with tf.variable_scope("data"):
x = tf.placeholder(tf.float32, [None, 28 * 28])
y_true = tf.placeholder(tf.float32, [None, 10])
# 第一層卷積 5*5*1,32個 strides=1
with tf.variable_scope("conv1"):
w_conv1 = weight_variables([5, 5, 1, 32])
b_conv1 = bias_variables([32])
x_reshape = tf.reshape(x, [-1, 28, 28, 1])
x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding="SAME") + b_conv1)
# 池化
x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
# 第二層卷積層,5*5*32,64個filter,strides=1
with tf.variable_scope("conv2"):
w_conv2 = weight_variables([5, 5, 32, 64])
b_conv2 = bias_variables([64])
x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, w_conv2, strides=[1, 1, 1, 1], padding="SAME") + b_conv2)
# 池化
x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")
# 全連接
with tf.variable_scope("fc"):
w_fc = weight_variables([7 * 7 * 64, 10])
b_fc = weight_variables([10])
x_fc_reshape = tf.reshape(x_pool2, [-1, 7 * 7 * 64])
y_predict = tf.matmul(x_fc_reshape, w_fc) + b_fc
return x, y_true, y_predict
def conv_fc():
# 准備數據
mnist = input_data.read_data_sets("./data/mnist/", one_hot=True)
x, y_true, y_predict = model()
# 所有樣本損失值的平均值
with tf.variable_scope("soft_loss"):
loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_true, logits=y_predict))
# 梯度下降
with tf.variable_scope("optimizer"):
train_op = tf.train.GradientDescentOptimizer(0.0001).minimize(loss)
# 計算准確率
with tf.variable_scope("acc"):
equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
accuracy = tf.reduce_mean(tf.cast(equal_list, tf.float32))
init_op = tf.global_variables_initializer()
with tf.Session() as sess:
sess.run(init_op)
for i in range(4000):
mnist_x, mnist_y = mnist.train.next_batch(50)
sess.run(train_op, feed_dict={x: mnist_x, y_true: mnist_y})
print("訓練第%d步, 准確率為%f" % (i, sess.run(accuracy, feed_dict={x: mnist_x, y_true: mnist_y})))
if __name__ == '__main__':
conv_fc()
經過3000次訓練后,准確率達到百分之八九十。。。