【深度学习】5:CNN卷积神经网络原理
前言:先坦白的說,深度神經網絡的學習在一開始對我造成的困擾還是很大的,我也是通過不斷地看相關的視頻資料、文獻講解嘗試去理解記憶。畢竟這些內容大多都是不可查的,我們看到的都只是輸入輸出的東西,里面的內部運作以及工作原理,都需要沉心靜思。
這篇CNN卷積神經網絡的原理介紹,也是自己通過收集來的資料閱讀、理解、操練后,有了一定的見解后才拙筆,里面的內容我會盡量詳盡,不清楚明白的地方,望大家慧眼指出。
–-----------------------------------------------------------------------------—------------------------------------------------
–-----------------------------------------------------------------------------—------------------------------------------------
一、機器如何識圖
先給大家出個腦筋急轉彎:在白紙上畫出一個大熊貓,一共需要幾種顏色的畫筆?——大家應該都知道,只需要一種黑色的畫筆,只需要將大熊貓黑色的地方涂上黑色,一個大熊貓的圖像就可以展現出來。
我們畫大熊貓的方式,其實和媽媽們的十字繡很接近——在給定的格子里,繡上不同的顏色,最后就可以展現出一幅特定的“圖片”。而機器識圖的方式正好和繡十字繡的方式相反,現在有了一幅圖片,機器通過識別圖片中每個格子(像素點)上的顏色,將每個格子里的顏色都用數字類型存儲,得到一張很大的數字矩陣,圖片信息也就存儲在這張數字矩陣中。
上圖中每一個格子代表一個像素點,像素點里的數字代表顏色碼,顏色碼范圍是[0,255],(各式各樣的顏色都是由紅、綠、藍三色組成,每個顏色都是0~255之間數字)
–-----------------------------------------------------------------------------—------------------------------------------------
–-----------------------------------------------------------------------------—------------------------------------------------
我們在得到的一張大數字矩陣的基礎上開展卷積神經網絡識別工作:
機器識圖的過程:機器識別圖像并不是一下子將一個復雜的圖片完整識別出來,而是將一個完整的圖片分割成許多個小部分,把每個小部分里具有的特征提取出來(也就是識別每個小部分),再將這些小部分具有的特征匯總到一起,就可以完成機器識別圖像的過程了
–-----------------------------------------------------------------------------—------------------------------------------------
–-----------------------------------------------------------------------------—------------------------------------------------
二、卷積神經網絡原理介紹
用CNN卷積神經網絡識別圖片,一般需要的步驟有:
1、卷積層工作原理
卷積層的作用:就是提取圖片每個小部分里具有的特征
假定我們有一個尺寸為6?66*66?6 的圖像,每一個像素點里都存儲著圖像的信息。我們再定義一個卷積核(相當于權重),用來從圖像中提取一定的特征。卷積核與數字矩陣對應位相乘再相加,得到卷積層輸出結果。
(429 = 18x1+54x0+51x1+55x0+121x1+75x0+35x1+24x0+204x1)
卷積核的取值在沒有以往學習的經驗下,可由函數隨機生成,再逐步訓練調整
當所有的像素點都至少被覆蓋一次后,就可以產生一個卷積層的輸出(下圖的步長為1)
機器一開始并不知道要識別的部分具有哪些特征,是通過與不同的卷積核相作用得到的輸出值,相互比較來判斷哪一個卷積核最能表現該圖片的特征——比如我們要識別圖像中的某種特征(比如曲線),也就是說,這個卷積核要對這種曲線有很高的輸出值,對其他形狀(比如三角形)則輸出較低。卷積層輸出值越高,就說明匹配程度越高,越能表現該圖片的特征。
卷積層具體工作過程:
比如我們設計的一個卷積核如下左,想要識別出來的曲線如下右:
現在我們用上面的卷積核,來識別這個簡化版的圖片——一只漫畫老鼠
當機器識別到老鼠的屁股的時候,真實區域數字矩陣與卷積核相乘作用后,輸出較大:6600
而用同一個卷積核,來識別老鼠的耳朵的時候,輸出則很小:0
我們就可以認為:現有的這個卷積核保存著曲線的特征,匹配識別出來了老鼠的屁股是曲線的。我們則還需要其他特征的卷積核,來匹配識別出來老鼠的其他部分。卷積層的作用其實就是通過不斷的改變卷積核,來確定能初步表征圖片特征的有用的卷積核是哪些,再得到與相應的卷積核相乘后的輸出矩陣
2、池化層工作原理
池化層的輸入就是卷積層輸出的原數據與相應的卷積核相乘后的輸出矩陣
池化層的目的:
- 為了減少訓練參數的數量,降低卷積層輸出的特征向量的維度
- 減小過擬合現象,只保留最有用的圖片信息,減少噪聲的傳遞
最常見的兩種池化層的形式:
- 最大池化:max-pooling——選取指定區域內最大的一個數來代表整片區域
- 均值池化:mean-pooling——選取指定區域內數值的平均值來代表整片區域
舉例說明兩種池化方式:(池化步長為2,選取過的區域,下一次就不再選取)
在4?44*44?4的數字矩陣里,以步長2?22*22?2選取區域,比如上左將區域[1,2,3,4]中最大的值4池化輸出;上右將區域[1,2,3,4]中平均值5/2池化輸出
3、全連接層工作原理
卷積層和池化層的工作就是提取特征,并減少原始圖像帶來的參數。然而,為了生成最終的輸出,我們需要應用全連接層來生成一個等于我們需要的類的數量的分類器。
全連接層的工作原理和之前的神經網絡學習很類似,我們需要把池化層輸出的張量重新切割成一些向量,乘上權重矩陣,加上偏置值,然后對其使用ReLU激活函數,用梯度下降法優化參數既可。
三、卷積神經網絡代碼解析
1、數據集的讀取,以及數據預定義
from tensorflow.examples.tutorials.mnist import input_data #讀取MNIST數據集 mnist = input_data.read_data_sets('MNIST_data', one_hot=True) #預定義輸入值X、輸出真實值Y placeholder為占位符 x = tf.placeholder(tf.float32, shape=[None, 784]) y_ = tf.placeholder(tf.float32, shape=[None, 10]) keep_prob = tf.placeholder(tf.float32) x_image = tf.reshape(x, [-1,28,28,1])- MNIST是Google的很經典的一個做圖像識別的數據集,圖片大小是28?2828*2828?28的,需要先下載才能使用。MNIST數據集下載鏈接, 密碼: jcam
- x、y_現在都是用占位符表示,當程序運行到一定指令,向x、y_傳入具體的值后,就可以代入進行計算了
- shape=[None, 784]是數據維度大小——因為MNIST數據集中每一張圖片大小都是28?2828*2828?28的,計算時候是將28?2828*2828?28的二維數據轉換成一個一維的、長度為784的新向量。None表示其值大小不定,意即選中的x、y_的數量暫時不定
- keep_prob 是改變參與計算的神經元個數的值。(下有詳細說明)
2、權重、偏置值函數
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)truncated_normal()函數:選取位于正態分布均值=0.1附近的隨機值
3、卷積函數、池化函數定義
def conv2d(x, W):#stride = [1,水平移動步長,豎直移動步長,1]return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):# stride = [1,水平移動步長,豎直移動步長,1]return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')- 輸入x是圖片信息矩陣,W是卷積核的值
- 卷積層conv2d()函數里strides參數要求第一個、最后一個參數必須是1;
- 第二個參數表示:卷積核每次向右移動的步長
- 第三個參數表示:卷積核每次向下移動的步長
在上面卷積層的工作原理中,有展示strides=[1, 1, 1, 1]的動態圖,
下面展示strides=[1, 2, 2, 1]時的情況:可以看到高亮的區域每次向右移動兩格,向下移動兩格
可以得到:當我們的卷積層步長值越大,得到的輸出圖像的規格就會越小。為了使得到的圖像的規格和原圖像保持一樣的大,在輸入圖像四周填充足夠多的 0 邊界就可以解決這個問題,這時padding的參數就為“SAME”(利用邊界保留了更多信息,并且也保留了圖像的原大小)下圖:
padding的另一個可選參數為“VALID”,和“SAME”不同的是:不用0來填充邊界,這時得到的圖像的規格就會小于原圖像。新圖像尺寸大小 = 原數據尺寸大小-卷積核尺寸大小+1(一般我們選用的padding都為“SAME”)
池化函數用簡單傳統的2x2大小的模板做max pooling,池化步長為2,選過的區域下次不再選取
4、第一次卷積+池化
x_image = tf.reshape(x, [-1,28,28,1])# 卷積層1網絡結構定義 # 卷積核1:patch=5×5;in size 1;out size 32;激活函數reLU非線性處理 W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) # output size 28*28*32 h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 14*14*32 h_pool1 = max_pool_2x2(h_conv1)- 圖片集是黑白單色,x_image 中的圖片尺寸參數最后一個 = 1,彩色 = 3
- 這里的卷積核大小是5?55*55?5的,輸入的通道數是1,輸出的通道數是32
- 卷積核的值這里就相當于權重值,用隨機數列生成的方式得到
- 由于MNIST數據集圖片大小都是28?2828*2828?28,且是黑白單色,所以準確的圖片尺寸大小是28?28?128*28*128?28?1(1表示圖片只有一個色層,彩色圖片都RGB3個色層),所以經過第一次卷積后,輸出的通道數由1變成32,圖片尺寸變為:28?28?3228*28*3228?28?32(相當于拉伸了高)
- 再經過第一次池化,池化步長是2?22*22?2,相當于每四個小格子池化成一個數值,所以經過池化后圖片尺寸為14?14?3214*14*3214?14?32
5、第二次卷積+池化
#卷積層2網絡結構定義 #卷積核2:patch=5×5;in size 32;out size 64;激活函數reLU非線性處理 W_conv2 = weight_variable([5, 5, 32, 64]) b_conv2 = bias_variable([64]) # output size 14*14*64 h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 7 *7 *64 h_pool2 = max_pool_2x2(h_conv2)- 這里的卷積核大小也是5?55*55?5的,第二次輸入的通道數是32,輸出的通道數是64
- 第一次卷積+池化輸出的圖片大小是14?14?3214*14*3214?14?32,經過第二次卷積后圖片尺寸變為:14?14?6414*14*6414?14?64
- 再經過第二次池化(池化步長也是2?22*22?2),最后輸出的圖片尺寸為7?7?647*7*647?7?64
6、全連接層1、全連接層2
# 全連接層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]) h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # 全連接層2 W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) prediction = tf.matmul(h_fc1_drop, W_fc2) + b_fc2- 全連接層的輸入就是第二次池化后的輸出,尺寸是7?7?647*7*647?7?64,全連接層1設置有1024個神經元
- tf.reshape(a,newshape)函數,當newshape = -1時,函數會根據已有的維度計算出數組的另外shape屬性值
- keep_prob 是為了減小過擬合現象。每次只讓部分神經元參與工作使權重得到調整。只有當keep_prob = 1時,才是所有的神經元都參與工作
- 全連接層2設置有10個神經元,相當于生成的分類器
- 經過全連接層1、2,得到的預測值存入prediction 中
7、梯度下降法優化、求準確率
#二次代價函數:預測值與真實值的誤差 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=prediction)) #梯度下降法:數據太龐大,選用AdamOptimizer優化器 train_step = tf.train.AdamOptimizer(1e-4).minimize(loss) #結果存放在一個布爾型列表中 correct_prediction = tf.equal(tf.argmax(prediction,1), tf.argmax(y_,1)) #求準確率 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))- 由于數據集太龐大,這里采用的優化器是AdamOptimizer,學習率是1e-4
- tf.argmax(prediction,1)返回的是對于任一輸入x預測到的標簽值,tf.argmax(y_,1)代表正確的標簽值
- correct_prediction 這里是返回一個布爾數組。為了計算我們分類的準確率,我們將布爾值轉換為浮點數來代表對與錯,然后取平均值。例如:[True, False, True, True]變為[1,0,1,1],計算出準確率就為0.75
8、其他說明、保存參數
for i in range(1000):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",i, "training accuracy",train_accuracy)train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})''' #保存模型參數 saver.save(sess, './model.ckpt') print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})) '''- batch 是來源于MNIST數據集,一個批次包含50條數據
- feed_dict=({x: batch[0], y_: batch[1], keep_prob: 0.5}語句:是將batch[0],batch[1]代表的值傳入x,y_;
- keep_prob = 0.5 只有一半的神經元參與工作
當完成訓練時,程序會保存學習到的參數,不用下次再訓練
特別提醒:運行非常占內存,而且運行到最后保存參數時,有可能卡死電腦
四、源碼及效果展示
# -*- coding:utf-8 -*- # -*- author:zzZ_CMing # -*- 2018/01/24;14:14 # -*- python3.5from tensorflow.examples.tutorials.mnist import input_data import tensorflow as tf import os os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'def weight_variable(shape):# 產生隨機變量# truncated_normal:選取位于正態分布均值=0.1附近的隨機值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)def conv2d(x, W):#stride = [1,水平移動步長,豎直移動步長,1]return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')def max_pool_2x2(x):# stride = [1,水平移動步長,豎直移動步長,1]return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],strides=[1, 2, 2, 1], padding='SAME')#讀取MNIST數據集 mnist = input_data.read_data_sets('MNIST_data', one_hot=True) sess = tf.InteractiveSession()#預定義輸入值X、輸出真實值Y placeholder為占位符 x = tf.placeholder(tf.float32, shape=[None, 784]) y_ = tf.placeholder(tf.float32, shape=[None, 10]) keep_prob = tf.placeholder(tf.float32) x_image = tf.reshape(x, [-1,28,28,1]) #print(x_image.shape) #[n_samples,28,28,1]#卷積層1網絡結構定義 #卷積核1:patch=5×5;in size 1;out size 32;激活函數reLU非線性處理 W_conv1 = weight_variable([5, 5, 1, 32]) b_conv1 = bias_variable([32]) h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) #output size 28*28*32 h_pool1 = max_pool_2x2(h_conv1) #output size 14*14*32#卷積層2網絡結構定義 #卷積核2:patch=5×5;in size 32;out size 64;激活函數reLU非線性處理 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) #output size 14*14*64 h_pool2 = max_pool_2x2(h_conv2) #output size 7 *7 *64# 全連接層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]) #[n_samples,7,7,64]->>[n_samples,7*7*64] h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) # 減少計算量dropout# 全連接層2 W_fc2 = weight_variable([1024, 10]) b_fc2 = bias_variable([10]) prediction = tf.matmul(h_fc1_drop, W_fc2) + b_fc2 #prediction = tf.nn.softmax(stf.matmul(h_fc1_drop, W_fc2) + b_fc2)#二次代價函數:預測值與真實值的誤差 loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=prediction)) #梯度下降法:數據太龐大,選用AdamOptimizer優化器 train_step = tf.train.AdamOptimizer(1e-4).minimize(loss) #結果存放在一個布爾型列表中 correct_prediction = tf.equal(tf.argmax(prediction,1), tf.argmax(y_,1)) #求準確率 accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))saver = tf.train.Saver() # defaults to saving all variables sess.run(tf.global_variables_initializer())for i in range(1000):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",i, "training accuracy",train_accuracy)train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})''' #保存模型參數 saver.save(sess, './model.ckpt') print("test accuracy %g"%accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})) '''效果展示如下:
訓練700次時候,成功率已經到達98%,越往后學習,準確率越高
特別提醒:由于我的電腦配置比較低,運行耗時較長,而且在保存參數時候還會出現卡死情況,大家請注意。
–-----------------------------------------------------------------------------—------------------------------------------------
–-----------------------------------------------------------------------------—------------------------------------------------
系列推薦:
【監督學習】1:KNN算法實現手寫數字識別的三種方法
–-----------------------------------------------------------------------------—------------------------------------------------
【無監督學習】1:K-means算法原理介紹,以及代碼實現
【無監督學習】2:DBSCAN算法原理介紹,以及代碼實現
【無監督學習】3:Density Peaks聚類算法(局部密度聚類)
–-----------------------------------------------------------------------------—------------------------------------------------
【深度學習】1:感知器原理,以及多層感知器解決異或問題
【深度學習】2:BP神經網絡的原理,以及異或問題的解決
【深度學習】3:BP神經網絡識別MNIST數據集
【深度學習】4:BP神經網絡+sklearn實現數字識別
【深度學習】5:CNN卷積神經網絡原理、MNIST數據集識別
【深度學習】8:CNN卷積神經網絡識別sklearn數據集(附源碼)
【深度學習】6:RNN遞歸神經網絡原理、MNIST數據集識別
【深度學習】7:Hopfield神經網絡(DHNN)原理介紹
–-----------------------------------------------------------------------------—------------------------------------------------
TensorFlow框架簡單介紹
–-----------------------------------------------------------------------------—------------------------------------------------
總結
以上是生活随笔為你收集整理的【深度学习】5:CNN卷积神经网络原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: sql server如何修改服务器名称,
- 下一篇: 计量学类毕业论文文献包含哪些?