tensorflow學習筆記:卷積神經網絡最終筆記

  這已是個人第四篇博客學習卷積神經網絡了。以前的文章分別是:html

  1,Keras深度學習之卷積神經網絡(CNN),這是開始學習Keras,瞭解到CNN,其實不懂的仍是有點多,固然第一次筆記主要是給本身心中留下一個印象,知道什麼是卷積神經網絡,固然主要是學習Keras,順便走一下CNN的過程。python

  2,深刻學習卷積神經網絡(CNN)的原理知識,此次是對CNN進行深刻的學習,對其原理知識認真學習,明白了神經網絡如何識別圖像,知道了卷積如何運行,池化如何計算,經常使用的卷積神經網絡都有哪些等等。git

  3,TensorFlow學習筆記——圖像識別與卷積神經網絡,這裏開始對TensorFlow進行完整的學習,以CNN爲基礎開始對TensorFlow進行完整的學習,而這裏練習的是 LeNet網絡。算法

  今天構建兩個完整卷積神經網絡,一個簡單的MNIST熱個身,一個稍微複雜的Cifar,今天學習完,再也不作卷積神經網絡的相關筆記,我相信本身已經掌握了。數組

  卷積神經網絡(Convolutional Neural Network  CNN)最初是爲解決圖像識別等問題設計的,固然其如今的應用不只限於圖像和視頻,也可用於時間序列信號,好比音頻信號,文本數據等。在早期的圖像識別研究中,最大的挑戰是如何組織特徵,由於圖像數據不像其餘類型的數據那樣能夠經過人工理解來提取特徵。在股票預測等模型中,咱們能夠從原始數據中提取過往的交易價格波動,市盈率,市淨率,盈利增加等金融因子,這既是特徵工程。可是在圖像中,咱們很難根據人爲理解提取出有效而豐富的特徵。在深度學習出現以前,咱們必須藉助 SIFT,HoG等算法提取具備良好區分性的特徵,再集合 SVM 等機器學習算法進行圖像識別。以下圖所示,SIFT對必定程度內的縮放,平移,旋轉,視覺改變,亮度調整等畸變,都具備不變性,是當前最重要的圖像特徵提取方法之一。能夠說在以前只能依靠SIFT等特徵提取算法才能勉強進行可靠地圖像識別。網絡

   然而SIFT這類算法提取的特徵仍是有侷限性的,在ImageNet ILSVRC 比賽的最好結果的錯誤率也有26%以上,並且常年難以產生突破。卷積神經網絡提取的特徵則能夠達到更好的效果,同時它不須要將特徵提取和分類訓練兩個過程分開,它在訓練時就自動提取了最有效的特徵。CNN做爲一個深度學習架構被提出的最初訴求,是下降對圖像數據預處理的要求,以及避免複雜的特徵工程。CNN能夠直接使用圖像的原始像素做爲輸入,而沒必要使用SIFT等算法提取特徵,減輕了使用傳統算法如SVM時必須作的大量重複,繁瑣的數據預處理工做。和SITF等算法相似,CNN的最大特色在於卷積的權值共享結構,能夠大幅減小神經網絡的參數量,防止過擬合的同時又下降了神經網絡模型的複雜度。CNN的權重共享其實也很像早期的延時神經網絡(TDNN),只不事後者是在時間這一個維度上進行權值共享,下降了學習時間序列信號的複雜度。session

1,CNN在圖像分類問題上有什麼優點?

  這裏使用水果分類來分析一下SVM以及神經網絡的劣勢。架構

  若是咱們有一組水果的圖片,裏面有草莓,香蕉和橘子。在圖片尺寸較大的狀況下,使用SVM分類的步驟是:機器學習

  • 人工提取特徵,好比說大小,形狀,重量,顏色等。
  • 根據上述特徵,把每一張圖片映射到空間中的一個點,空間的維度和特徵的數量相等。
  • 相同類別的物體具備相似的特徵,因此空間中標記爲草莓的點確定是聚在一塊兒的,香蕉和橘子也是同理。這時候使用SVM算法在空間中畫出各種點之間的分界線就完成了分類。

  在最後一步中,不使用SVM,使用別的分類器也是能夠的,好比KNN,貝葉斯,甚至神經網絡都是能夠的。雖然不一樣算法中性能會有差別,可是這裏我想說的就是在圖像分類問題上的瓶頸並不在算法的性能上,而是在特徵的提取上。ide

  區分草莓和橘子的特徵是容易提取的,那橘子和橙子呢?若是上述四個特徵不能很好的區分橘子和橙子,想要進一步提高算法的性能怎麼辦?一般的作法是須要提取新的特徵。那麼新的特徵如何選擇呢?對於我這種水果盲來講,這個問題是具備必定難度的。

  除了橘子橙子問題,咱們還有貓狗如何區分,狗品種如何識別等一系列問題。我想對於大部分人來講,狗狗品種的識別是很是有難度的。轉了一圈回來,忽然發現,圖像分類任務的瓶頸驚人出如今特徵選擇上。

  若是咱們用神經網絡直接對貓狗進行分類呢?這樣不就避開了特徵提取這一步了啊?假設輸入圖片大小爲30*30,那麼設置900個輸入神經元,隱含層設置1000個神經元,輸出神經元個數對應須要的輸出數量不就行了嗎?甚至用SVM也能夠這樣作,把一張30*30的圖看作900維空間中的一個點,表明貓的點和表明狗的點在這個900維的空間中必然是相聚於兩個簇,而後咱們就可使用SVM來劃出分界線了。

  可是這樣計算開銷就太大了,對於30*30的圖片咱們也許能夠這樣作,對於1000*1000的圖片咱們這樣作的話就須要至少一百萬個隱層神經元,這樣咱們就至少更新10^12個參數。而SVM的話,則至關於在一百萬維的空間中運行了。運行量將會大的難以估計。另外圖中並非全部的信息都和咱們須要的。背景對咱們的分類毫無價值,然而在這種一股腦所有拿來作輸入的狀況下,背景也被當成了特徵進入了模型當中,準確度天然會有所降低。

  總之,若是不人工提取特徵,那麼計算量會很是大,精確度也沒法保證。而人工提取特徵的方式又會在某些問題下難以進行,好比狗狗品種分類。

  而CNN經過它獨有的方式,成功的解決了這兩個問題。也就是說,CNN是一個能夠自動提取特徵而卻待訓練參數相對不那麼多的神經網絡,這就是CNN在圖像分類任務中決定性的優點。

2,爲何要使用卷積層?

  和神經網絡模型相似,CNN的設計靈感一樣來自於對神經細胞的研究。

    1981 年的諾貝爾醫學獎,頒發給了 David Hubel、TorstenWiesel,以及 
Roger Sperry。他們的主要貢獻,是發現了人的視覺系統的信息處理是分級的。

    從低級的V1區提取邊緣特徵,再到V2區的形狀或者目標的部分等,再到更高層,
整個目標、目標的行爲等。也就是說高層的特徵是低層特徵的組合,從低層到高層
的特徵表示愈來愈抽象,愈來愈能表現語義或者意圖。而抽象層面越高,存在的可
能猜想就越少,就越利於分類。

  值得一提的是,最低級的V1區須要提取邊緣特徵,而在上面提到的分類中,神經網絡其實是把30 * 30 的圖片按照 900 個像素點處理的,那麼有沒有一種方法可以神經網絡像人同樣,按照邊緣來理解呢?

  卷積計算並不複雜,矩陣對應元素相乘的和就是卷積的結果,到了神經網絡中會多出偏置b還有激活函數,具體方法以下圖:

  圖片中展現的是由九個權重組成的矩陣和圖片上九個像素點組成矩陣進行卷積過程。在偏置b爲0,激活函數使用ReLU的狀況下,過程就像圖片右下角的公式同樣,對應元素乘積的和,再加上值爲0的b,而後外套激活函數獲得輸出0。

  你可能會想到這部分的計算和普通的神經網絡沒神經網絡沒什麼差異,形式都是 f(wx + b)。那麼這麼處理和邊緣有什麼關係?多作幾回卷積就知道了。

  Filter 指的是權重組成矩陣,Input Layer中存的是圖片中的所有像素。Convolutional Layer存的是Filter與圖片中全部3*3矩陣依次卷積後獲得的結果。在輸出中咱們能夠看到兩個3,他們比其餘的元素0都要大。是什麼決定了卷積結果的大小?觀察後發現,圖中參與卷積的部分1的排序和活動窗口中1的排列徹底同樣時,輸出爲3。而像素的排列方式其實就是圖片中的形狀,這說明若是圖像中的形狀和Filter中的形狀類似的話,輸出值就大,不像就小。所以,卷積的操做創建了神經網絡與圖像邊緣的聯繫。

  實際上CNN通過訓練以後,Filter中就是圖片的邊緣,角落之類的特徵。也便是說,卷積層是在自動提取圖片中的特徵。

  除此以外,卷積還有一種局部鏈接的思想在裏面。他對圖片的處理方式是一塊一塊的,並非全部像素值一塊兒處理。所以能夠極大地下降參數值的總量。這裏我須要使用那張經典的圖片來講明卷積層是如何下降參數總量的:

  對於一張1000*1000的圖片來講,對他進行分類,至少須要10的12次方個參數。而若是對圖片使用卷積操做,每一個神經元之和圖像上的10*10的像素鏈接的話,參數總量就變成了10的8次方。可是這樣的操做會致使一個問題,每一個神經元只對圖片一部分的內容,那麼這個神經元學到的內容就不能應用到其餘神經元上。好比說有這樣一個訓練集,一樣自私的貓出如今黑色神經元負責的區域中,可是測試集中,貓可能出如今圖片的任何位置。按照局部鏈接的作法,其餘區域的貓是沒法被正確識別的。

   而爲了讓出如今任何位置的貓都可以被正確的識別,提出了權重共享。讓紅綠藍黑神經元中的參數全都同樣,這樣就可使得模型的準確率不受物體位置的影響,看起來就像同一個Filter劃過了整個圖片。從提取特徵的角度上來說,一個Filter顯然不能知足需求,所以須要生成多個不一樣的Filter來對圖片進行卷積。更棒的是,爲了得到平移不變性而使用的權重共享法,又之外的再一次下降了待訓練參數總數。

  就是使用100個權值共享的10*10 Filter來卷積,總參數也才10的4次方。也就是說,參數相較於普通的神經網絡而言,總共降低了整整8個數量級,這種提高是誇張的。

  因此,卷積層的工做方式已經所有出來了,具體工做流程入下:

  藍色的部分表明輸入圖像;綠色的部分表明輸出矩陣;周圍的虛線是padding操做,能夠看作圖像的一部分;下方不斷移動的陰影就是FIlter,其大小,數量,一次移動的距離都是能夠自定義的;陰影至上方綠色的連線表明相乘再加以後的結果輸出到了輸出矩陣的那個位置。卷積層的折中操做方式,成功的模擬了生物視覺系統中的邊緣特徵提取部分。

  而CNN中對於分級結構的模擬,是經過卷積層的層層疊加實現的。AlexNet的論文中不止一次的提到,網絡的深度對CNN性能的影響是顯著的。能夠認爲卷積層的不斷疊加會使得提取到的特徵愈來愈複雜,整個流程就像上述引用中提到的人類的視覺系統的工做方式同樣運行,最終完成對圖片的分類。

  那麼如今就能夠很輕鬆的回答上面的問題了,使用卷積層是由於卷積層本質上是在自動提取圖片的特徵,並且在提取特徵的同時,極大的下降了網絡整體待訓參數的總量。這兩個特徵使得CNN克服了特徵提取困難,待訓參數龐大的問題,成功制霸圖片分類問題。

3,爲何要使用池化層?

   你可能會問,卷積層就已經把參數將了下來,還解決了特徵提取的問題,那還加一個池化層幹什麼呢?(可能池化層只是工程上想讓網絡更深而作出的一個無奈之舉)

  就以最精彩出現的最大池化爲例,來看看所謂的池化操做有多隨便吧。

   一個2*2 的最大池化操做如上圖,它作的就是把2*2窗口中的最大值存下來。因此綠色部分留下來了6;棕色部分是8;藍色部分是4。因此就很簡單,很隨便,可是這個操做有什麼優勢呢?不知道,可是缺點卻顯而易見——損失了細節。爲何損失細節也要作這一步呢?可能就是要壓縮矩陣,這樣能夠在模型中多加幾層卷積層,用來提取更高維,更復雜的特徵。而從壓縮的角度來看,這一步可謂簡單有效,一個取最大值的操做,就讓矩陣大小變爲四分之一。

  AlexNet的出現時2012年,那時候用到的是GTX580,3G顯存,文章中提到只用一塊GPU是不行的,由於顯存會爆,所以用了兩塊GPU並行進行這個任務。可能做者也是苦於老是爆顯存,而不得不加上池化層。就算這樣,還要用到兩塊GPU才成功訓練了整個網絡。

  然而池化層的應用彷佛帶來更多的便利之處。因爲其只取最大值,忽視掉了其餘影響較小的值,因此在當內容發生很小的變化的時候包括一些平移旋轉,CNN任然可以穩定識別對應內容。也就是說池化層給模型帶來了必定程序上的不變性。

  而應不該該使用池化層仍是一個正在討論的問題,有的網絡用,有的網絡不用。按照個人理解,在顯存夠用的狀況下,不用池化層。這種丟失細節提高模型不變性的方法有點傷人傷己的意思。並且咱們但願獲得的模型並非在不知道圖片變化了的狀況下能夠獲得正確結果,咱們但願的是模型能夠認識到差別卻依然能作出正確的分類纔對。

4,全鏈接層的做用

  在通過幾回捲積和池化的操做以後,卷積神經網絡的最後一步是全鏈接層。這一步就和最最普通的神經網絡沒有什麼區別。我認爲這裏的神經網絡就是充當一個分類器的做用,輸入時不一樣特徵的特徵值,輸出的是分類。其實能夠在訓練好以後,把全鏈接層砍掉,把卷積部分的輸出當作是特徵,全鏈接層換成SVM或者別的分類器,從新訓練,也是能夠取得良好效果的。

5,總結

  • 1,CNN以前的圖片分類算法性能受制於特徵的提取以及龐大參數數量致使的計算困難。
  • 2,使用卷積來模擬人類視覺系統的工做方式,而這種方式極大的下降了神經網絡的帶訓練參數數量。
  • 3,爲了得到平移不變形,使用了權重共享技術,該技術進一步下降了帶訓練參數數量
  • 4,卷積層其實是在自動提取圖片特徵,解決了圖像特徵提取這一難題。
  • 5,使用池化層的根部緣由是下降計算量,而其帶來的不變形並非咱們須要的。不過在以模型準確率爲綱的大背景下,繼續使用無可厚非。
  • 6,全鏈接層實質上是一個分類器。

6,卷積層的步驟

  通常的卷積神經網絡由多個卷積層構成,每一個卷積層中一般會進行以下幾個操做。

  (1)圖像經過多個不一樣的卷積核的濾波,並加偏置(bias),提取出局部特徵,每個卷積核會映射出一個新的2D圖像。

  (2)將前面卷積核的濾波輸出結果,進行非線性的激活函數處理。目前最多見的是使用ReLU函數,而之前Sigmoid 函數用的比較多。

  (3)對激活函數的結果再進行池化操做(即降採樣,好比將 2*2 的圖片降爲 1*1 的圖片),目前通常是使用最大池化,保留最顯著的特徵,並提高模型的畸變容忍能力。

  這幾個步驟就構成了最多見的卷積層,固然也能夠加上一個 LRN(Local Response Normalization,局部響應歸一化層)層,目前很是流暢的Trick 還有 Batch Normalization等。

七,CNN在MNIST數據集上的圖像分類

  代碼以下:

# _*_coding:utf-8_*_
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets('MNIST_data/', one_hot=True)
sess = tf.InteractiveSession()


# 權重和偏置須要建立,咱們這裏定義好初始化函數以便重複使用
# 這裏咱們使用截斷的正態分佈噪聲,標準差設爲0.1
def weight_varibale(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)


# 卷積層,池化層也是重複使用的,所以分別定義函數,以便後面重複使用
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')


# 定義輸入的: x 特徵,y_ 真實的label
x = tf.placeholder(tf.float32, [None, 784])
y_ = tf.placeholder(tf.float32, [None, 10])
# 由於須要將1D 的輸入向量轉爲2D的圖片結構,並且顏色只有一個通道
# 因此最終尺寸以下,固然-1表明樣本數量不固定,不用擔憂
x_image = tf.reshape(x, [-1, 28, 28, 1])

# 第一個卷積層
W_conv1 = weight_varibale([5, 5, 1, 32])
b_conv1 = bias_variable([32])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

# 第二個卷積層(區別第一個卷積,卷積核變爲64,也就是說這一層會提取64個特徵)
W_conv2 = weight_varibale([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)

# 當經歷了兩次步長爲2*2的最大池化,因此邊長已經只有1/4
# 因此圖片尺寸由28*28邊長7*7
W_fc1 = weight_varibale([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)

# 爲了防止過擬合,下面使用一個Dropout層
# Dropout層是經過一個placeholder傳入 keep_prob比率來控制的
# 在訓練時候,咱們隨機丟掉一部分節點的數據來減輕過擬合
# 在預測時候,咱們則保留所有數據來追求最好的預測性能
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# 將Dropout層的輸出鏈接一個Softmax層,獲得最後的機率輸出
W_fc2 = weight_varibale([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

# 定義損失函數cross_entropy,可是優化器使用Adam 並給予學習率1e-4
cross_entropy = tf.reduce_mean(-tf.reduce_sum(y_ * tf.log(y_conv),
                                              reduction_indices=[1]))
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, tf.float32))

tf.global_variables_initializer().run()
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.images,
    y_: mnist.test.labels,
    keep_prob: 1.0
}))

  結果以下:

step 0, training accuracy 0.1
step 100, training accuracy 0.9
step 200, training accuracy 0.86
step 300, training accuracy 0.92
step 400, training accuracy 0.92
step 500, training accuracy 0.94
step 600, training accuracy 0.98
step 700, training accuracy 0.96
......
step 7500, training accuracy 1
step 7600, training accuracy 0.98
... ...
step 19700, training accuracy 1
step 19800, training accuracy 1
step 19900, training accuracy 1
test accuracy 0.9929

  最後,這個CNN模型能夠獲得的準確率約爲99.29%,基本能夠知足對手寫數字識別準確率的要求。相比以前MLP的2%的錯誤率,CNN的錯誤率降低了大約60%,這其中主要的性能提高都來自於更優秀的網絡設計,即卷積網絡對圖像特徵的提取和抽象能力。依靠卷積核的權值共享,CNN的參數量並無爆炸,下降計算量的同時也減輕了過擬合,所以整個模型的性能有較大的提高。

  本文以CIFAR-10 爲數據集,基於Tensorflow介紹CNN(卷積神經網絡)圖像分類模型的構建過程,着重分析了在建模過程當中卷積層,池化層,扁平化層,全鏈接層,輸出層的運算機理,以及通過運算後圖像尺寸,數據維度等參數的變化狀況。

八,CIFAR-10數據集介紹

  官網地址:http://www.cs.toronto.edu/~kriz/cifar.html

  Cifar數據集是一個影響力很大的圖像分類數據集,Cifar數據集分爲了Cifar-10和Cifar-100兩個問題,他們都是圖像詞典項目(Visual Dictionary)中800萬張圖片的一個子集。

  CIFAR-10數據集由60000張彩色圖片構成,其中包括50000張訓練集圖片、10000張測試集圖片,每張圖片的shape爲(32,32,3),即圖片尺寸爲32*32,通道數爲3;全部圖片能夠被分爲10類,包括:

飛機, 汽車, 鳥, 貓, 鹿, 狗, 青蛙, 馬, 船以及卡車。

  官網截圖以下所示:

  和MNIST數據集相似,Cifar-10 中的圖片大小都是固定的且每一張圖片中僅僅包含一個種類的實體。可是和MNIST相比,Cifar數據集最大的區別在於圖片由黑白變成的彩色,且分類的難度也相對更高。在Cifar-10數據集上,人工標註的正確率大概爲94%,這比MNIST數據集上的人工表現要低不少。目前在Cifar-10數據集上最好的圖像識別算法正確率爲95.59%,達到這個正確率的算法使用了卷積神經網絡。

  本次學習的目標是創建一個用於識別圖像的相對較小的卷積神經網絡,在這過程當中,咱們將會學到:

  1 着重創建一個規範的網絡組織結構,訓練兵進行評估

  2 爲創建更大規模更加複雜的模型提供一個範例

  選擇CIFAR-10是由於它的複雜程度足以用來檢驗TensorFlow中的大部分功能,並可將其擴展爲更大的模型。與此同時因爲模型較小因此訓練速度很快,比較適合用來測試新的想法,檢驗新的技術。

代碼組織

  官網教程的代碼位於tensorflow/models/image/cifar10/.

文件 做用
cifar10_input.py 讀取本地CIFAR-10的二進制文件格式的內容。
cifar10.py 創建CIFAR-10的模型。
cifar10_train.py 在CPU或GPU上訓練CIFAR-10的模型。
cifar10_multi_gpu_train.py 在多GPU上訓練CIFAR-10的模型。
cifar10_eval.py 評估CIFAR-10模型的預測性能。

  TensorFlow擅長訓練深度神經網絡,被認定爲是神經網絡中最好用的庫之一。經過使用TensorFlow咱們能夠快速入門神經網絡, 大大下降了深度學習(也就是深度神經網絡)的開發成本和開發難度。 
  Tensorflow使用數據流圖進行數值計算,圖中的節點表明數學運算,圖中的邊表明在這些節點之間傳遞的多維數組(張量)。在使用其構建模型時,先搭建好流圖結構——相似於鋪好管道,而後再加載數據——向管道中注水,讓數據在各節點計算、沿各管道流動;數據在流圖中計算、傳遞時採用多維數組(張量)的形式,所以在Tensorflow中參與計算的均爲數組數據類型。 
  本文使用Tensorflow構建簡單的CNN圖像多分類模型,其由3個卷積(含激活函數)與池化層、1個扁平層、3個全鏈接層、1個輸出層構成,示意圖以下所示: 

 

  訓練本身的圖片(CNN):https://blog.csdn.net/Missayaaa/article/details/79119839

  該數據集的頁面:http://www.cs.toronto.edu/~kriz/cifar.html

  CIFAR-10和CIFAR-100是帶有標籤的數據集,都出自於規模更大的一個數據集,他有八千萬張小圖片(http://groups.csail.mit.edu/vision/TinyImages/。這個是一個大項目,你能夠點擊那個big map提交本身的標籤,能夠幫助他們訓練讓計算機識別物體的模型)

數據的下載:

 

九,CNN在Cifar-10數據集上的圖像分類

  代碼:

#_*_coding:utf-8_*_
# import cifar10
import cifar10_input
import tensorflow as tf
import numpy as np
import time


max_steps = 3000
batch_size = 128
data_dir = 'cifar10\CIFAR-10\cifar-10-batches-bin'


def variable_with_weight_loss(shape, stddev, w1):
    '''
    權重初始化函數
    :param shape: 卷積核參數,格式相似於[5, 5, 3, 32],表明卷積核尺寸(前兩個數字通道數和卷積核個數)
    :param stddev:  標準差
    :param w1:  L2正則化的權重參數
    :return: 返回帶有L2正則的初始化的權重參數
    '''
    # 截斷產生正態分佈,即產生正態分佈的值與均值的差值大於兩倍的標準差,那就從新寫
    var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
    if w1 is not None:
        # 給權重W加上L2 正則,並用W1 參數控制L2 Loss大小
        weight_loss = tf.multiply(tf.nn.l2_loss(var), w1, name='weight_loss')
        # 將weight_loss 存在一個名爲 「losses」 的collection裏,後面會用到
        tf.add_to_collection('losses', weight_loss)
    return var

# 咱們對圖像進行數據加強的操做須要耗費大量CPU時間,所以distorted_inputs使用了16個
# 獨立的線程來加速任務,函數內部會產生線程池,在須要使用時會經過queue進行調度。
images_train, labels_train = cifar10_input.distorted_inputs(
    data_dir=data_dir, batch_size=batch_size
)

# 生成測試數據
images_test, labels_test = cifar10_input.inputs(eval_data=True,
                                                data_dir=data_dir,
                                                batch_size=batch_size)

# 數據中圖片的尺寸爲24*24,即裁剪後的大小,而顏色的通道數則爲3,表明RGB
image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
label_holder = tf.placeholder(tf.int32, [batch_size])

# 第一個卷積層
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2,
                                    w1=0.0)
kernel1 = tf.nn.conv2d(image_holder, weight1, [1, 1, 1, 1],
                       padding='SAME')
bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                       padding='SAME')
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)

# 第二個卷積層,上一層卷積核數量爲64,因此本層卷積核尺寸的第三個維度也需調整64
# 這裏bias值所有初始化爲0.1,而不是0,最後調整了最大池化層和LRN層的順序,先LRN層處理
weight2 = variable_with_weight_loss(shape=[5, 5, 64, 64], stddev=5e-2, w1=0.0)
kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                       padding='SAME')

# 全鏈接層
reshape = tf.reshape(pool2, [batch_size, -1])
dim = reshape.get_shape()[1].value
weight3 = variable_with_weight_loss(shape=[dim, 384], stddev=0.04, w1=0.004)
bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)

# 全鏈接層2,和以前很像,只不過其隱含節點數降低了一半,只有192個,其餘超參數不變
weight4 = variable_with_weight_loss(shape=[384, 192], stddev=0.04, w1=0.004)
bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)

# 最後一層,先創建weight,其正態分佈標準差爲上一個隱含層的節點數的倒數,而且不計入L2正則
weight5 = variable_with_weight_loss(shape=[192, 10], stddev=1/192.0, w1=0.0)
bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
logits = tf.add(tf.matmul(local4, weight5), bias5)

  上面代碼就完成了整個網絡inference的部分。梳理整個網絡結構能夠獲得以下表,從上到下,依次是整個卷積神經網絡從輸入到輸出的流程。能夠觀察到,其實設計CNN主要就是安排卷積層,池化層,全鏈接層的分佈和順序,以及其中超參數的設計,Trick的使用等。設計性能良好的CNN必定有規律可循的,可是要想針對某個問題設計最合適的網絡結構,是須要大量實踐摸索的。

   完成了模型 inference 部分的構建,接下來計算 CNN的 loss。這裏依然使用 cross_entropy,主要注意的是這裏咱們把softmax的計算和 cross_entropy_loss的計算合併在了一塊兒。

#_*_coding:utf-8_*_
# import cifar10
import cifar10_input
import tensorflow as tf
import numpy as np
import time


max_steps = 3000   #訓練輪數(每一輪一個batch參與訓練)
batch_size = 128   # batch 大小
data_dir = 'cifar10\CIFAR-10\cifar-10-batches-bin' # 數據目錄


def variable_with_weight_loss(shape, stddev, w1):
    '''
    權重初始化函數
    :param shape: 卷積核參數,格式相似於[5, 5, 3, 32],表明卷積核尺寸(前兩個數字通道數和卷積核個數)
    :param stddev:  標準差
    :param w1:  L2正則化的權重參數
    :return: 返回帶有L2正則的初始化的權重參數
    '''
    # 截斷產生正態分佈,即產生正態分佈的值與均值的差值大於兩倍的標準差,那就從新寫
    var = tf.Variable(tf.truncated_normal(shape, stddev=stddev))
    if w1 is not None:
        # 給權重W加上L2 正則,並用W1 參數控制L2 Loss大小
        weight_loss = tf.multiply(tf.nn.l2_loss(var), w1, name='weight_loss')
        # 將weight_loss 存在一個名爲 「losses」 的collection裏,後面會用到
        tf.add_to_collection('losses', weight_loss)
    return var

def loss(logits, labels):
    # 類型轉換爲 tf.int64
    labels = tf.cast(labels, tf.int64)
    cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(
        logits=logits, labels=labels,
        name='cross_entropy_per_example'
    )
    # 計算一個batch中交叉熵的均值
    cross_entropy_mean = tf.reduce_mean(cross_entropy,
                                        name='cross_entropy')
    # 將交叉熵存在 losses的collection
    tf.add_to_collection('losses', cross_entropy_mean)
    return tf.add_n(tf.get_collection('losses'), name='total_loss')

# 咱們對圖像進行數據加強的操做須要耗費大量CPU時間,所以distorted_inputs使用了16個
# 獨立的線程來加速任務,函數內部會產生線程池,在須要使用時會經過queue進行調度。
# images_train, labels_train = cifar10_input.distorted_inputs(
#     data_dir=data_dir, batch_size=batch_size
# )
images_train, labels_train = cifar10_input.distorted_inputs(
    batch_size=batch_size
)

# 生成測試數據,每次執行都會生成一個 batch_size 的數量的測試樣本
# images_test, labels_test = cifar10_input.inputs(eval_data=True,
#                                                 data_dir=data_dir,
#                                                 batch_size=batch_size)

images_test, labels_test = cifar10_input.inputs(eval_data=True,
                                                batch_size=batch_size)
# 數據中圖片的尺寸爲24*24,即裁剪後的大小,而顏色的通道數則爲3,表明RGB
image_holder = tf.placeholder(tf.float32, [batch_size, 24, 24, 3])
label_holder = tf.placeholder(tf.int32, [batch_size])

# 第一個卷積層
# 第一層權重初始化,產生64個三通道(RGB圖片),尺寸爲5*5的卷積核,不帶L2正則(w1=0)
weight1 = variable_with_weight_loss(shape=[5, 5, 3, 64], stddev=5e-2,
                                    w1=0.0)
# 對輸出原始圖像進行卷積操做,步長爲[1, 1, 1, 1]即將每個像素點都計算到
kernel1 = tf.nn.conv2d(image_holder, weight1, [1, 1, 1, 1],
                       padding='SAME')
# 定義第一層的偏置參數,因爲有64個卷積核,這裏有偏置尺寸爲64
bias1 = tf.Variable(tf.constant(0.0, shape=[64]))
# 卷積結果加偏置後採用relu激活
conv1 = tf.nn.relu(tf.nn.bias_add(kernel1, bias1))
# 第一層的池化操做,使用尺寸爲3*3,步長爲2*2 的池化層進行操做
# 這裏的ksize和strides 第一個和第四個數字通常都是1
pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                       padding='SAME')
# 用LRN對結果進行處理,使用比較大的值變得更大,比較小的值變得更小,模仿神經系統的側抑制機制
norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)

# 第二個卷積層,上一層卷積核數量爲64,因此本層卷積核尺寸的第三個維度也需調整64
# 這裏bias值所有初始化爲0.1,而不是0,最後調整了最大池化層和LRN層的順序,先LRN層處理
weight2 = variable_with_weight_loss(shape=[5, 5, 64, 64], stddev=5e-2, w1=0.0)
kernel2 = tf.nn.conv2d(norm1, weight2, [1, 1, 1, 1], padding='SAME')
bias2 = tf.Variable(tf.constant(0.1, shape=[64]))
conv2 = tf.nn.relu(tf.nn.bias_add(kernel2, bias2))
norm2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
pool2 = tf.nn.max_pool(norm2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1],
                       padding='SAME')

# 全鏈接層
# 將上面的輸出結果展平,-1表明不肯定多大
reshape = tf.reshape(pool2, [batch_size, -1])
# 獲得數據扁平化後的長度
dim = reshape.get_shape()[1].value
# 創建一個隱含節點數爲384的全鏈接層
weight3 = variable_with_weight_loss(shape=[dim, 384], stddev=0.04, w1=0.004)
bias3 = tf.Variable(tf.constant(0.1, shape=[384]))
local3 = tf.nn.relu(tf.matmul(reshape, weight3) + bias3)

# 全鏈接層2,和以前很像,只不過其隱含節點數降低了一半,只有192個,其餘超參數不變
weight4 = variable_with_weight_loss(shape=[384, 192], stddev=0.04, w1=0.004)
bias4 = tf.Variable(tf.constant(0.1, shape=[192]))
local4 = tf.nn.relu(tf.matmul(local3, weight4) + bias4)

# 最後一層,先創建weight,其正態分佈標準差爲上一個隱含層的節點數的倒數,而且不計入L2正則
weight5 = variable_with_weight_loss(shape=[192, 10], stddev=1/192.0, w1=0.0)
bias5 = tf.Variable(tf.constant(0.0, shape=[10]))
# 注意這裏,直接是網絡的原始輸出(即wx+b 的形式),沒有加softmax激活
logits = tf.add(tf.matmul(local4, weight5), bias5)


# 將logits節點和label_holder傳入loss函數得到最終的loss
loss = loss(logits, label_holder)
#優化器依然選擇 adam Optimizer 學習速率設置爲1e-3
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)
# 關於tf.nn.in_top_k函數的用法見http://blog.csdn.net/uestc_c2_403/article/details/73187915
# tf.nn.in_top_k會返回一個[batch_size, classes(類別數)]大小的布爾型張量,記錄是否判斷正確
top_k_op = tf.nn.in_top_k(logits, label_holder, 1)

# 建立默認的session,接着初始化所有模型參數
sess = tf.InteractiveSession()
tf.global_variables_initializer().run()

# 啓動圖片數據加強的線程隊列,這裏一共使用了16個線程來進行加速
# 注意:若是不啓動線程,後續的inference及訓練的操做都是沒法開始的
tf.train.start_queue_runners()

for step in range(max_steps):
    start_time = time.time()
    # 得到一個batch的訓練數據
    image_batch, label_batch = sess.run([images_train, labels_train])
    # 運行訓練過程並得到一個batch的total_loss
    _, loss_value = sess.run([train_op, loss],
                             feed_dict={image_holder: image_batch,
                                        label_holder: label_batch})
    # 記錄跑一個batch所耗費的時間
    duration = time.time() - start_time

    # 每10個batch輸出信息
    if step % 10 == 0:
        # 計算每秒能跑多少個樣本
        examples_per_sec = batch_size / duration
        # 計算每一個batch須要耗費的時間
        sec_per_batch = float(duration)

        format_str = ('step %d, loss=%.2f(%.1f examples/sec; %.3f sec/batch)')
        print(format_str%(step, loss_value, examples_per_sec, sec_per_batch))


num_examples = 10000
import math
num_iter = int(math.ceil(num_examples / batch_size))
true_count = 0
total_sample_count = num_iter * batch_size
step = 0
while step < num_iter:
    image_batch, label_batch = sess.run([images_test, labels_test])
    predictions = sess.run([top_k_op], feed_dict={
        image_holder: image_batch, label_holder: label_batch
    })
    true_count += np.sum(predictions)
    step += 1

precision = true_count / total_sample_count
print('precsion @ 1 = %.3f' %precision)

  部分結果以下:

step 0, loss=4.68(136.3 examples/sec; 0.939 sec/batch)
step 10, loss=3.66(187.9 examples/sec; 0.681 sec/batch)
step 20, loss=3.13(175.1 examples/sec; 0.731 sec/batch)
step 30, loss=2.83(181.5 examples/sec; 0.705 sec/batch)
step 40, loss=2.47(177.5 examples/sec; 0.721 sec/batch)
step 50, loss=2.25(185.2 examples/sec; 0.691 sec/batch)
step 60, loss=2.18(196.3 examples/sec; 0.652 sec/batch)
step 70, loss=2.12(191.3 examples/sec; 0.669 sec/batch)
step 80, loss=1.94(187.4 examples/sec; 0.683 sec/batch)
... ...
step 160, loss=1.86(206.4 examples/sec; 0.620 sec/batch)
step 170, loss=1.94(207.4 examples/sec; 0.617 sec/batch)
step 180, loss=1.85(209.8 examples/sec; 0.610 sec/batch)
... ...
step 310, loss=1.60(183.1 examples/sec; 0.699 sec/batch)
step 320, loss=1.57(193.3 examples/sec; 0.662 sec/batch)

  

 

 

參考文獻:https://blog.csdn.net/BaiHuaXiu123

相關文章
相關標籤/搜索