深度學習之卷積神經網路原理詳解(一)
阿新 • • 發佈:2019-01-04
初探CNN卷積神經網路
1、概述
- 典型的深度學習模型就是很深層的神經網路,包含多個
隱含層
,多隱層的神經網路很難直接使用BP演算法
進行直接訓練,因為反向傳播誤差時往往會發散,很難收斂 CNN
節省訓練開銷的方式是權共享weight sharing,讓一組神經元使用相同的權值- 主要用於影象識別領域
2、卷積(Convolution)特徵提取
卷積核
(Convolution Kernel),也叫過濾器filter
,由對應的權值W
和偏置b
體現- 下圖是
3x3
的卷積核在5x5
的影象上做卷積的過程,就是矩陣做點乘之後的和
第i
個隱含單元的輸入就是:,其中就是與過濾器filter過濾到的圖片 - 另外上圖的步長
stride
為1
,就是每個filter
每次移動的距離 卷積特徵提取的原理
- 卷積特徵提取利用了自然影象的統計平穩性,這一部分學習的特徵也能用在另一部分上,所以對於這個影象上的所有位置,我們都能使用同樣的學習特徵。
- 當有多個
filter
時,我們就可以學到多個特徵,例如:輪廓、顏色等
多個過濾器
filter
(卷積核)- 例子如下
- 一張圖片有RGB
三個顏色通道,則對應的filter過濾器也是三維的,影象經過每個filter
做卷積運算後都會得到對應提取特徵的影象,途中兩個filter
:W0和W1,輸出的就是兩個影象
- 這裡的步長stride
為2
(一般就取2,3)
- 在原圖上新增zero-padding
超引數
,主要用於控制輸出的大小 - 同樣也是做卷積操作,以下圖的一步卷積操作為例:
與w0[:,:,0]卷積:
0x(-1)+0x0+0x1+0x1+0x0+1x(-1)+1x0+1x(-1)+2x0=-2
與w0[:,:,1]卷積:
2x1+1x(-1)+1x1=2
與w0[:,:,2]卷積:
1x(-1)+1x(-1)=-2
最終結果:
-2+2+(-2)+1=-1
(1為偏置) 3、池化(Pooling)
- 也叫做下采樣
Pooling
過程- 把提取之後的特徵看做一個矩陣,並在這個矩陣上劃分出幾個不重合的區域,
- 然後在每個區域上計算該區域內特徵的均值或最大值,然後用這些均值或最大值參與後續的訓練
-下圖是使用最大
Pooling
的方法之後的結果
Pooling
的好處
- 很明顯就是減少引數
Pooling
就有平移不變性((translation invariant)
如圖feature map
是12x12
大小的圖片,Pooling區域為6x6,所以池化後得到的feature map
為2x2
,假設白色畫素值為1,灰色畫素值為0,若採用max pooling
之後,左上角視窗值為**
將影象右移一個畫素,左上角視窗值仍然為1
將影象縮放之後,左上角視窗值仍然為1
Pooling
的方法中average
方法對背景保留更好,max
對紋理提取更好- 深度學習可以進行多次卷積、池化操作
4、啟用層
- 在每次卷積操作之後一般都會經過一個非線性層,也是啟用層
- 現在一般選擇是
ReLu
,層次越深,相對於其他的函式效果較好,還有Sigmod,tanh
函式等
- sigmod
和tanh
都存在飽和的問題,如上圖所示,當x軸上的值較大時,對應的梯度幾乎為0,若是利用BP反向傳播演算法, 可能造成梯度消失的情況,也就學不到東西了
5、全連線層 Fully connected layer
- 將多次卷積和池化後的影象展開進行全連線,如下圖所示。
- 接下來就可以通過BP反向傳播進行訓練了
- 所以總結起來,結構可以是這樣的
6、CNN是如何工作的
- 每個過濾器可以被看成是特徵識別符號
( feature identifiers)
- 如下圖一個曲線檢測器對應的值
- 我們有一張圖片,當過濾器移動到左上角時,進行卷積運算
- 當與我們的過濾器的形狀很相似時,得到的值會很大
- 若是滑動到其他的部分,可以看出很不一樣,對應的值就會很小,然後進行啟用層的對映。
- 過濾器filter
的值怎麼求到,就是我們通過BP
訓練得到的。
CNN的Tensorflow實現
import tensorflow as tf
import numpy as np
from tensorflow.examples.tutorials.mnist import input_data # 匯入mnist資料集
'''計算準確度函式'''
def compute_accuracy(xs,ys,X,y,keep_prob,sess,prediction):
y_pre = sess.run(prediction,feed_dict={xs:X,keep_prob:1.0}) # 預測,這裡的keep_prob是dropout時用的,防止過擬合
correct_prediction = tf.equal(tf.argmax(y_pre,1),tf.argmax(y,1)) #tf.argmax 給出某個tensor物件在某一維上的其資料最大值所在的索引值,即為對應的數字,tf.equal 來檢測我們的預測是否真實標籤匹配
accuracy = tf.reduce_mean(tf.cast(correct_prediction,tf.float32)) # 平均值即為準確度
result = sess.run(accuracy,feed_dict={xs:X,ys:y,keep_prob:1.0})
return result
'''權重初始化函式'''
def weight_variable(shape):
inital = tf.truncated_normal(shape, stddev=0.1) # 使用truncated_normal進行初始化
return tf.Variable(inital)
'''偏置初始化函式'''
def bias_variable(shape):
inital = tf.constant(0.1,shape=shape) # 偏置定義為常量
return tf.Variable(inital)
'''卷積函式'''
def conv2d(x,W):#x是圖片的所有引數,W是此卷積層的權重
return tf.nn.conv2d(x,W,strides=[1,1,1,1],padding='SAME')#strides[0]和strides[3]的兩個1是預設值,中間兩個1代表padding時在x方向運動1步,y方向運動1步
'''池化函式'''
def max_pool_2x2(x):
return tf.nn.max_pool(x,ksize=[1,2,2,1],
strides=[1,2,2,1],
padding='SAME')#池化的核函式大小為2x2,因此ksize=[1,2,2,1],步長為2,因此strides=[1,2,2,1]
'''執行主函式'''
def cnn():
mnist = input_data.read_data_sets('MNIST_data', one_hot=True) # 下載資料
xs = tf.placeholder(tf.float32,[None,784]) # 輸入圖片的大小,28x28=784
ys = tf.placeholder(tf.float32,[None,10]) # 輸出0-9共10個數字
keep_prob = tf.placeholder(tf.float32) # 用於接收dropout操作的值,dropout為了防止過擬合
x_image = tf.reshape(xs,[-1,28,28,1]) #-1代表先不考慮輸入的圖片例子多少這個維度,後面的1是channel的數量,因為我們輸入的圖片是黑白的,因此channel是1,例如如果是RGB影象,那麼channel就是3
'''第一層卷積,池化'''
W_conv1 = weight_variable([5,5,1,32]) # 卷積核定義為5x5,1是輸入的通道數目,32是輸出的通道數目
b_conv1 = bias_variable([32]) # 每個輸出通道對應一個偏置
h_conv1 = tf.nn.relu(conv2d(x_image,W_conv1)+b_conv1) # 卷積運算,並使用ReLu啟用函式啟用
h_pool1 = max_pool_2x2(h_conv1) # pooling操作
'''第二層卷積,池化'''
W_conv2 = weight_variable([5,5,32,64]) # 卷積核還是5x5,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)
'''全連線層'''
h_pool2_flat = tf.reshape(h_pool2, [-1,7*7*64]) # 將最後操作的資料展開
W_fc1 = weight_variable([7*7*64,1024]) # 下面就是定義一般神經網路的操作了,繼續擴大為1024
b_fc1 = bias_variable([1024]) # 對應的偏置
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat,W_fc1)+b_fc1) # 運算、啟用(這裡不是卷積運算了,就是對應相乘)
'''dropout'''
h_fc1_drop = tf.nn.dropout(h_fc1,keep_prob) # dropout操作
'''最後一層全連線'''
W_fc2 = weight_variable([1024,10]) # 最後一層權重初始化
b_fc2 = bias_variable([10]) # 對應偏置
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop,W_fc2)+b_fc2) # 使用softmax分類器
cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys*tf.log(prediction),reduction_indices=[1])) # 交叉熵損失函式來定義cost function
train_step = tf.train.AdamOptimizer(1e-3).minimize(cross_entropy) # 呼叫梯度下降
'''下面就是tf的一般操作,定義Session,初始化所有變數,placeholder傳入值訓練'''
sess = tf.Session()
sess.run(tf.initialize_all_variables())
for i in range(1000):
batch_xs, batch_ys = mnist.train.next_batch(100) # 使用SGD,每次選取100個數據訓練
sess.run(train_step, feed_dict={xs: batch_xs, ys: batch_ys, keep_prob: 0.5}) # dropout值定義為0.5
if i % 50 == 0:
print(compute_accuracy(xs,ys,mnist.test.images, mnist.test.labels,keep_prob,sess,prediction)) # 每50次輸出一下準確度
if __name__ == '__main__':
cnn()