1. 程式人生 > >TensorFlow實戰:Chapter-3(CNN-1-卷積神經網路簡介)

TensorFlow實戰:Chapter-3(CNN-1-卷積神經網路簡介)

卷積神經網路簡介

卷積神經網路(convolutional neural network,CNN)最初是用來解決影象識別等問題設計的,隨著計算機的發展,現在CNN的應用已經非常廣泛了,在自然語言處理(NLP)、醫藥發現、文字處理等等中都有應用。這裡我們著重分析CNN在影象處理上的應用。

在早期影象處理識別研究中,最大的問題是如何組織特徵,這是因為影象資料不像其他型別的資料可以通過人工理解來提取特徵。在影象中,我們很難根據人為理解提取出有效的特徵。在深度學習廣泛應用之前,我們必須藉助SIFT、HoG等特徵提取演算法結合SVM等機器學習演算法進行影象處理識別。

CNN作為一個深度學習架構被提出的最初訴求,是降低對影象資料預處理的要求,以及避免複雜的特徵工程。CNN可以直接使用影象作為輸入,減少了許多特徵提取的手續。CNN的最大特點在於卷積的權值共享結構,大幅度的減少神經網路的引數,同時防治了過擬合

CNN的提出

CNN概念最早來自感受野(Receptive Field),科學家對貓的視覺皮層細胞研究發現,每一個視覺神經元只會處理一小塊區域的視野影象,即感受野

這裡寫圖片描述

後來,提出神經認識機(Neocognitron)概念,神經認識機包含兩類神經元,用來抽取特徵的S-cells,還有用來抗形變的C-cells。S-cells對應CNN中的卷積濾波操作,而C-cells則對應啟用函式、最大池化等操作

這裡寫圖片描述

所以說吧,最厲害的計算機還是大腦.

CNN的壯大

在最近的時間裡,CNN最先是在ImageNet上大放光彩,隨後被大眾熟知,一直到現在成為影象處理的利器。先說說ImageNet.

ImageNet是一個基於WordNet(一個大型語義庫)的大型影象資料庫。在ImageNet中,將近1500萬圖片被關聯到WordNet的大約20000個名詞同義詞集上,可以被認為是分類問題的一個類別。ImageNet每年都會舉辦影象識別相關的競賽(ILSCVRC)。

這裡寫圖片描述

這裡寫圖片描述

先總體上對CNN有一個瞭解,下面該細緻的剖析CNN的每個部分

卷積神經網路結構

學習CNN的結構之前,可以先到點這裡 這個視覺化應用上看看CNN的執行流程。
這裡寫圖片描述

看完後,我們來對比一下CNN與傳統神經網路的區別。下圖顯示了全連線神經網路的結構和CNN的結構對比

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

可以看到,CNN相對全連線神經網路所使用的權值引數大大減少了

卷積神經網路的常見網路結構

常見的架構圖如下:
這裡寫圖片描述

從一個實際的圖片型別識別角度來看:
這裡寫圖片描述

總結下來, 一般來說,一個卷積神經網路主要由以下結構組成:

  • 輸入層
    輸入層是整個神經網路的輸入,一般代表的是圖片的畫素矩陣(一般為三維矩陣,即畫素x畫素x通道)

  • 卷積層
    卷積層試圖從輸入層中抽象出更高的特徵。

  • 池化層
    保留最顯著的特徵,提升模型的畸變容忍能力。

  • 全連線層
    影象中被抽象成了資訊含量更高的特徵在經過神經網路完成後續分類等任務。

  • 輸出層
    一般是使用softmax輸出概率值或者分類結果。

在CNN中使用的輸入層、輸出層、全連線層在前面章節已經介紹過了,下面重點介紹卷積層、池化層。

卷積

再說卷積層之前,我們先談談什麼的卷積。

訊號處理中的卷積

說到卷積,我這個學工科的,最先想到的就是訊號處理裡面的”線性卷積”、”圓周卷積”,這些怪東西里面的卷積都出自同一個公式。公式如下:
這裡寫圖片描述

我們可能對這個公式的計算忘得差不多了,但是我們要記得訊號處理中的卷積作用:一個函式(f)在另一個函式上(f)的加權疊加。下面我們來對比影象處理中的卷積。

更多卷積的理解可以點這裡

影象處理中的卷積

在學習影象處理課程時,接觸到了影象卷積。影象處理中的許多操作我們都可以看做是卷積的應用,例如:高斯濾波、均值濾波、膨脹、腐蝕等.

影象處理中的卷積計算,會先定義一個卷積核,而這個卷積核就是定義一個濾波器,卷積核與輸入訊號做加權疊加得到輸出.
也可以理解為二維變數的離散卷積,無論哪種卷積都離不開加權疊加的要義。

影象卷積示例如圖:
這裡寫圖片描述

Kernel即定義的卷積核,Kernel與原影象(同等大小的區域)對應的加權疊加得到的值即為輸出。

到這裡,我們應該對卷積有了一個直觀的認識了。

CNN內的卷積層處理和影象處理中的卷積非常相似,下面主要講講影象處理中的卷積型別與特點。

卷積的型別的引數

一個常見的卷積過程:

這裡寫圖片描述

(藍色為輸入資料、陰影為卷積核、綠色為卷積輸出)
輸入尺寸大小為:4x4
濾波器尺寸大小為:3x3
輸出尺寸大小為:2x2

在卷積過程中:有一個步長(stride)引數:步長即每次filter移動的間隔距離。這裡stride=1;
如果難以理解stride的含義,看下一個例子。

下圖為stride=2(橫豎兩個方向上)的卷積過程:
這裡寫圖片描述

輸入尺寸大小為:4x4
濾波器尺寸大小為:3x3
輸出尺寸大小為:2x2

從上兩個例子可以看出,只要濾波器的尺寸不是1x1,那麼輸出尺寸必定會小於輸入尺寸大小,而在實際的圖片處理過程中,許多時候我們需要保持影象的大小不變,以便於影象的處理。為了保持輸入和輸出尺寸一致,同時也有卷積的效果。我們可以對輸入圖片的外圈做填充操作。

這裡我們引入了另外一個引數:padding. zero-padding即在輸入的外圈補零的圈數(這裡為什麼要補零,而不是其他數字:是因為數字零對卷積貢獻為零,不會產生額外的偏差).

如果難以理解zero-padding的含義,看下一個例子。

下圖為輸入影象外圈補一圈零,即padding=1的卷積過程:
這裡寫圖片描述

輸入尺寸大小為:5x5
濾波器尺寸大小為:3x3
輸出尺寸大小為:5x5

可以看出輸入和輸出的尺寸大小一致

到這裡,我們可以給出輸入尺寸、輸出尺寸、濾波器尺寸、stride和padding的關係:

輸入圖片的尺寸大小W1 x H1

卷積核(又稱濾波器,以下都稱濾波器)的大小F x F

輸出圖片的尺寸大小W2 x H2

stride:S padding:P

關係式如下:
W2 = (W1-F+2P)/S + 1
H2 = (H1-F+2P)/S + 1

除了上述的卷積型別,還有如下的卷積:
這裡寫圖片描述

這裡寫圖片描述

更多卷積型別可參考點這裡

講完了各式各樣的卷積型別,下面該回歸主題,講講CNN裡面的卷積層了

卷積層

在CNN中,每一個卷積層會直接接受影象畫素級輸入,每一個卷積操作只會處理一小塊影象,經過卷積變換後再傳到後面的網路,每一層卷積都會提取資料特徵,再經過組合和抽象形成更高階的特徵

卷積層原理

下面剖析一個卷積層的原理:

  • 1.
    一張32x32x3的圖片(大小為32x32的3通道圖片),濾波器大小為5x5x3(濾波器的深度必須與輸入層的深度相同).
    這裡寫圖片描述

  • 2.
    卷積運算同樣也是通過向量運算實現的,這裡我們把濾波器的值甩成一列向量W.(W大小為75x1).
    同時從輸入圖片中取同樣大小的輸入資料X(X大小也為75x1),

    :ωTx+b.(bbias.)
    每一次卷積的輸出是一個數值,所以輸出層的深度變為1.

這裡寫圖片描述

  • 3.
    一次完整的卷積過程後,得到一個28x28x1大小的輸出圖(這裡又稱為activation maps).

這裡寫圖片描述

如果我們有6個5x5的濾波器,會得到一個6個28x28x1的輸出圖,那麼疊加到一起就得到28x28x**6**的輸出層
這裡寫圖片描述

  • 4.
    多個卷積層之間連線一起(多層之間又經過了啟用函式),從低層的特徵抽象到高層的特徵.
    (這裡其實有一個問題:就是多個卷積層直接連線,影象的尺寸會迅速下降,這不是我們想要的,在這裡我們可以前面提到的zero-padding來維持輸入輸出尺寸一致)
    這裡寫圖片描述

實際圖片處理過程如下:
這裡寫圖片描述

到這裡,我們講完的卷積層的運算原理.

卷積層演算法

這裡我們重點講解一下輸入與輸出的尺寸運算,直接引用了cs231n的課件如下:
這裡寫圖片描述

卷積層特點

一般卷積神經網路由多個卷積層構成,每個卷積層中通常會進行如下幾個操作。

  1. 影象通過多個不同的卷積核的濾波,並新增偏置(bias),提取區域性特徵,每一個卷積核會映射出一個新的2D影象
  2. 將前面卷積核的濾波輸出結果,進行非線性的啟用函式處理
  3. 對啟用函式的結構再進行池化操作(即降取樣,後面會詳細講解),目前一般使用最大池化,保留最大特徵,提示模型的畸變容忍能力

這幾個步驟構成了常見的卷積層,當然也可以再加上LRN(Local Response Normalization,區域性響應歸一化層),還有Batch Normalization等.

權值共享

每一個卷積層中使用的過濾器引數是相同的,這就是卷積核的權值共享,這是CNN的一個非常重要的性質。

  • 從直觀上理解,共享卷積核可以使得影象上的內容不受位置的影響,這提高了模型對平移的容忍性,這大大的提高了模型提取特徵的能力
  • 從網路結構上來說,共享每一個卷積層的卷積核,可以大大減少網路的引數,這不僅可以降低計算的複雜度,而且還能減少因為連線過多導致的嚴重過擬合,從而提高了模型的泛化能力

權值共享與傳統的全連線的區別如下圖:
這裡寫圖片描述

多卷積核

在每一個卷積層中,會使用多個卷積核運算,這是因為每一個卷積核濾波得到的影象就是一類特徵的對映,我們提供越多的卷積核,能提供多個方向上的影象特徵,可以從影象中抽象出有效而豐富的高階特徵
這裡寫圖片描述

池化層

在通過卷積層獲得輸入的特徵時,我們需要做的利用這麼特徵繼續做分類運算。但是針對多個卷積核下的輸出特徵,依舊會面臨著超龐大的引數運算,為了解決這個問題,我們依舊需要減少引數量。這裡我們琢磨下,在使用卷積層時,是因為卷積運算可以有效的從輸入中提取特徵,我們可以對特徵做再統計。這一再統計既要能夠反映原輸入的特徵,又要能夠降低資料量,我們很自然的想到了取平均值、最大值。這也是池化操作。

池化層原理

從CNN的總體結構上來看,在卷積層之間往往會加入一個池化層(pooling layer).池化層可以非常有效地縮小圖片的尺寸。從而減少最後全連線層的引數,在加快計算速度的同時也防止了過擬合的產生。

池化層前向傳播過程類似於卷積層的操作一樣,也是通過移動一個類似濾波器的結構完成的,不同於卷積層的是,池化層的濾波器計算不是加權求和,而且求區域內的極大值或者平均值。

  1. 使用最多的是最大值操作的池化層,又稱最大池化層(max pooling),計算影象區域內最大值,提取紋理效果較好.
    這裡寫圖片描述

  2. 使用平均值操作的池化層,又稱平均池化層(mean pooling),計算影象區域的平均值,保留背景更好
    這裡寫圖片描述

  3. 隨機池化(Stochastic-pooling),介於兩者之間,通過對畫素點按照數值大小賦予概率,再按照概率進行亞取樣
    這裡寫圖片描述

池化層演算法

這裡我們重點講解一下輸入與輸出的尺寸運算,還是引用了cs231n的課件如下:
這裡寫圖片描述

池化層特點

池化單元的平移不變性

這類似於卷積層的平移不變性。

顯著減少引數數量

降取樣進一步降低了輸出引數量,並賦予模型對輕度形變的容忍性,提高了模型的泛化能力

到這裡,我們算是講完了CNN的結構了,在回頭看看CNN視覺化應用,應該會理解的更深吧

TensorFlow中的CNN

說了那麼多CNN的理論,下面該看看如何在TensorFlow中實現CNN了

卷積

不同的ops下使用的卷積操作總結如下:

  • conv2d:Arbitrary filters that can mix channels together(通道混合處理的任意濾波器)
  • depthwise_conv2d:Filters that operate on each channel independently.(單獨處理每個通道的濾波器)
  • separable_conv2d:A depthwise spatial filter followed by a pointwise filter.(一個深度方向的濾波器後跟著一個點濾波器)

strides引數

在TensorFlow中,無論是卷積操作 tf.nn.con2d或者是後面的池化操作tf.nn.max_pool都涉及到了引數strides的指定,這是因為這兩種操作都需要在輸入影象上滑動濾波器,我們需要指定在每一個維度滑動濾波器的步長.

TensorFlow的文件中給出strides的描述:
A list of ints.一個長度為4的1-D tensor.指定了在輸入的Tensor中每個維度滑動濾波的步長。一般要求,strides[0] = strides[3] = 1
為什麼要這麼要求?
這是因為對於輸入而言,無論資料型別是”NHWC”或者”NCHW”,輸入的Tensor都包含了[batch,width,height,channels],對應的是strides剛好是這個4個維度的步長

  • 其中strides[0] = 1 代表從batch(即樣本)一個一個遍歷(如果設定為2,代表間隔性的遍歷樣本,與其這樣,我倒不如縮減樣本)
  • strides[3] = 1代表在channels上是一個一個通道滑動的(一般我們也是這麼做的)

padding引數

根據選擇padding的選擇為“SAME”或“VALID”的填充方案,計算輸出大小和填充畫素。

  1. 如果為”SAME”,則輸出尺寸計算如下:

    out_height = ceil(float(in_height) / float(strides[1]))
    out_width  = ceil(float(in_width) / float(strides[2]))
    
    在上方和左方填充後計算如下:
    pad_along_height = max((out_height - 1) * strides[1] +
                filter_height - in_height, 0)
    pad_along_width = max((out_width - 1) * strides[2] +
                       filter_width - in_width, 0)
    pad_top = pad_along_height // 2
    pad_bottom = pad_along_height - pad_top
    pad_left = pad_along_width // 2
    pad_right = pad_along_width - pad_left
    

    注意,除以2意味著可能會出現兩側(頂部與底部,右側和左側)有多一個填充的情況。 在這種情況下,底部和右側總是得到一個額外的填充畫素。 例如,當pad_along_height為5時,我們在頂部填充2個畫素,在底部填充3個畫素。 請注意,這不同於現有的庫,如cuDNN和Caffe,它們明確指定了填充畫素的數量,並且始終在兩側都填充相同數量的畫素。

  2. 如果為”VALID”,則輸出尺寸計算如下:

    out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))
    out_width  = ceil(float(in_width - filter_width + 1) / float(strides[2]))
    
    填充值為0,計算輸出為:
    output[b, i, j, :] =
    sum_{di, dj} input[b, strides[1] * i + di - pad_top,
                   strides[2] * j + dj - pad_left, ...] *
             filter[di, dj, ...]
    

卷積下常用的方法有:

方法(加粗的有詳解) description
tf.nn.convolution Computes sums of N-D convolutions
tf.nn.conv2d Computes a 2-D convolution given 4-D input and filter tensors
tf.nn.depthwise_conv2d Depthwise 2-D convolution
tf.nn.depthwise_conv2d_native Computes a 2-D depthwise convolution given 4-D input and filter tensors.
tf.nn.separable_conv2d Computes a 2-D depthwise convolution given 4-D input and filter tensors
tf.nn.atrous_conv2d 2-D convolution with separable filters
tf.nn.atrous_conv2d_transpose The transpose of atrous_conv2d
tf.nn.conv2d_transpose The transpose of conv2d(卷積的逆向過程)
tf.nn.conv1d Computes a 1-D convolution given 3-D input and filter tensors
tf.nn.conv3d Computes a 3-D convolution given 5-D input and filter tensors
tf.nn.conv3d_transpose The transpose of conv3d
tf.nn.conv2d_backprop_filter Computes the gradients of convolution with respect to the filter.
tf.nn.conv2d_backprop_input Computes the gradients of convolution with respect to the input
tf.nn.conv3d_backprop_filter_v2 Computes the gradients of 3-D convolution with respect to the filter.
tf.nn.depthwise_conv2d_native_backprop_filter Computes the gradients of depthwise convolution with respect to the filter.
tf.nn.depthwise_conv2d_native_backprop_input Computes the gradients of depthwise convolution with respect to the input.

tf.nn.conv2d

conv2d(
    input,
    filter,
    strides,
    padding,
    use_cudnn_on_gpu=None,
    data_format=None,
    name=None
)

給定輸入Tensor的shape為[batch, in_height, in_width, in_channels],濾波器Tensor的shape為 [filter_height, filter_width, in_channels, out_channels], 計算過程如下:

  1. 將濾波器平坦化為2-D矩陣,形狀為[filter_height * filter_width * in_channels,output_channels]
  2. 從輸入張量中提取影象塊,形成一個形狀為[batch,out_height,out_width,filter_height * filter_width * in_channels]的虛擬張量。
  3. 對於每一個影象塊,做影象塊向量右乘濾波器矩陣。

    #預設格式為NHWC:
    
    output[b, i, j, k] =
    sum_{di, dj, q} input[b, strides[1] * i + di, strides[2] * j + dj, q] *
                    filter[di, dj, q, k]
    
    必須保持 strides[0] = strides[3] = 1.大多數情況下水平方向和深度方向的步長是一致的:即strides = [1, stride, stride, 1]
    
引數 description
input A Tensor.型別必須為如下其一:half, float32 .是一個4-D的Tensor,格式取決於data_format引數
filter A Tensor.和input型別一致,是一個4-D的Tensor且shape為
[filter_height, filter_width, in_channels, out_channels]
strides A list of ints.一個長度為4的1-D tensor.指定了在輸入的Tensor中每個維度滑動濾波的步長
padding 選擇不同的填充方案,可選”SAME”或”VALID”
use_cudnn_on_gpu An optional bool. Defaults to True
data_format 指定輸入和輸出的資料格式(可選).可選擇”NHWC”或者”NCHW”.預設為”NHWC”
”NHWC”:資料按[batch, height, width, channels]儲存
”NCHW”: 資料按[batch, channels, height, width]儲存
name 該ops的name(可選)
返回值 A Tensor. 型別和input相同,一個4-D的Tensor,格式取決於data_format

tf.nn.conv3d

conv3d(
    input,
    filter,
    strides,
    padding,
    data_format=None,
    name=None
)

在訊號處理中,互相關是兩個波形的相似度的度量,作為應用於其中之一的時滯的函式。也常被稱為 sliding dot product 或sliding inner-product.

我們的Conv3D類似於互相關的處理形式。

引數 description
input A Tensor.型別必須為如下其一:float32,float64,shape為
[batch, in_depth, in_height, in_width, in_channels]
filter A Tensor.和input型別一致.Shape為
[filter_depth, filter_height, filter_width, in_channels, out_channels]
in_channels要在input和filter之間匹配
strides A list of ints.一個長度大於等於5的1-D tensor.
其中要滿足strides[0] = strides[4] = 1
padding 選擇不同的填充方案,可選”SAME”或”VALID”
data_format 指定輸入和輸出的資料格式(可選).可選擇”NDHWC”或”NCDHW”.預設為”NDHWC”
”NDHWC”:資料按 [batch, in_depth, in_height, in_width, in_channels]儲存
”NCDHW”: 資料按 [batch, in_channels, in_depth, in_height, in_width]儲存
name 該ops的name(可選)
返回值 A Tensor. 型別和input相同

池化

Each pooling op uses rectangular windows of size ksize separated by offset strides. For example, if strides is all ones every window is used, if strides is all twos every other window is used in each dimension.

In detail, the output is

output[i] = reduce(value[strides * i:strides * i + ksize])

池化常用的方法有:

方法(加粗的有詳解) description
tf.nn.avg_pool Performs the average pooling on the input
tf.nn.max_pool Performs the max pooling on the input
tf.nn.max_pool_with_argmax Performs max pooling on the input and outputs both max values and indices
tf.nn.avg_pool3d Performs 3D average pooling on the input
tf.nn.max_pool3d Performs 3D max pooling on the input
tf.nn.fractional_avg_pool Performs fractional average pooling on the input
tf.nn.fractional_max_pool Performs fractional max pooling on the input
tf.nn.pool Performs an N-D pooling operation.

tf.nn.max_pool

max_pool(
    value,
    ksize,
    strides,
    padding,
    data_format='NHWC',
    name=None
)
引數 description
value A 4-D Tensor 型別為 tf.float32.
shape為 [batch, height, width, channels]
ksize A list of ints that has length >= 4. The size of the window for each dimension of the input tensor.
strides A list of ints that has length >= 4. The stride of the sliding window for each dimension of the input tensor.
padding ‘VALID’ 或 ‘SAME’
data_format ‘NHWC’ 或 ‘NCHW’
name 該ops的name(可選)
返回值 A Tensor with type tf.float32

圖形學濾波器

類似於影象處理中的圖形學處理(腐蝕、膨脹等)

圖形學常用的方法有:

方法(加粗的有詳解) description
tf.nn.dilation2d(膨脹) Computes the grayscale dilation of 4-D input and 3-D filter tensors
tf.nn.erosion2d(腐蝕) Computes the grayscale erosion of 4-D value and 3-D kernel tensors
tf.nn.with_space_to_batch Performs op on the space-to-batch representation of input

啟用函式

方法(加粗的有詳解) description
tf.nn.relu Computes rectified linear: max(features, 0)
tf.nn.relu6 Computes Rectified Linear 6: min(max(features, 0), 6)
tf.nn.crelu Computes Concatenated ReLU
tf.nn.elu Computes exponential linear: exp(features) - 1 if < 0, features otherwise.
tf.nn.softplus Computes softplus: log(exp(features) + 1).
tf.nn.softsign Computes softsign: features / (abs(features) + 1)
tf.nn.dropout Computes dropout
tf.nn.bias_add Adds bias to value
tf.sigmoid Computes sigmoid of x element-wise
tf.tanh Computes hyperbolic tangent of x element-wise

tf.nn.relu

relu(
features,
name=None
)

計算非線性對映:函式關係為 max(features, 0)

引數 description
features A Tensor. 型別是如下一個: float32, float64, int32, int64, uint8, int16, int8, uint16, half
name 該ops的name(可選)
返回值 A Tensor with sam type as features.

tf.nn.dropout

dropout(
    x,
    keep_prob,
    noise_shape=None,
    seed=None,
    name=None
)

使用概率keep_prob,將輸入元素按比例放大1 / keep_prob,否則輸出0.縮放是為了使預期的和不變

By default, each element is kept or dropped independently. If noise_shape is specified, it must be broadcastable to the shape of x, and only dimensions with noise_shape[i] == shape(x)[i] will make independent decisions. For example, if shape(x) = [k, l, m, n] and noise_shape = [k, 1, 1, n], each batch and channel component will be kept independently and each row and column will be kept or not kept together.
引數 description
x A tensor.
keep_prob A scalar Tensor with the same type as x. The probability that each element is kept
noise_shape A 1-D Tensor of type int32, representing the shape for randomly generated keep/drop flags
seed A Python integer. Used to create random seeds
name 該ops的name(可選)
返回值 A Tensor of the same shape of x.

tf.nn.bias_add

bias_add(
    value,
    bias,
    data_format=None,
    name=None
)
引數 description
value A Tensor with type float, double, int64, int32, uint8, int16, int8, complex64, or complex128.
bias A 1-D Tensor with size matching the last dimension of value. Must be the same type as value unless value is a quantized type, in which case a different quantized type may be used.
data_format ‘NHWC’ or ‘NCHW’
name 該ops的name(可選)
返回值 A Tensor with the same type as value

正則化

方法 description
tf.nn.l2_normalize Normalizes along dimension dim using an L2 norm.
tf.nn.local_response_normalization Local Response Normalization.
tf.nn.sufficient_statistics Calculate the sufficient statistics for the mean and variance of x.
tf.nn.normalize_moments Calculate the mean and variance of based on the sufficient statistics.
tf.nn.moments Calculate the mean and variance of x.
tf.nn.weighted_moments Returns the frequency-weighted mean and variance of x
tf.nn.fused_batch_norm Batch normalization.
tf.nn.batch_normalization Batch normalization.
tf.nn.batch_norm_with_global_normalization Batch normalization.

損失函式

方法(加粗的有詳解) description
tf.nn.l2_loss Computes half the L2 norm of a tensor without the sqrt:
output = sum(t ** 2) / 2
tf.nn.log_poisson_loss Computes log Poisson loss given log_input

分類器

方法(加粗的有詳解) description
tf.nn.sigmoid_cross_entropy_with_logits Computes sigmoid cross entropy given logits
tf.nn.softmax Computes softmax activations
tf.nn.log_softmax Computes log softmax activations:
logsoftmax = logits - log(reduce_sum(exp(logits), dim))
tf.nn.softmax_cross_entropy_with_logits Computes softmax cross entropy between logits and labels
tf.nn.sparse_softmax_cross_entropy_with_logits Computes sparse softmax cross entropy between logits and labels
tf.nn.weighted_cross_entropy_with_logits Computes a weighted cross entropy

api看完了,該動手擼程式碼了

TensorFlow實現簡易神經網路模型

MNIST資料集下的基礎CNN

本節使用的依舊是MNIST資料集,預期可到達到99.1%的準確率。
使用的是結構為卷積–>池化–>卷積–>池化–>全連線層–>softmax層的卷積神經網路。
下面直接貼程式碼:

    # coding:utf8

    from tensorflow.examples.tutorials.mnist import input_data
    import tensorflow as tf


    def weight_variable(shape):
        '''
        使用卷積神經網路會有很多權重和偏置需要建立,我們可以定義初始化函式便於重複使用
        這裡我們給權重製造一些隨機噪聲避免完全對稱,使用截斷的正態分佈噪聲,標準差為0.1
        :param shape: 需要建立的權重Shape
        :return: 權重Tensor
        '''
        initial = tf.truncated_normal(shape, stddev=0.1)
        return tf.Variable(initial)


    def bias_variable(shape):
        '''
        偏置生成函式,因為啟用函式使用的是ReLU,我們給偏置增加一些小的正值(0.1)避免死亡節點(dead neurons)
        :param shape:
        :return:
        '''
        initial = tf.constant(0.1, shape=shape)
        return tf.Variable(initial)


    def conv2d(x, W):
        '''
        卷積層接下來要重複使用,tf.nn.conv2d是Tensorflow中的二維卷積函式,
        :param x: 輸入 例如[5, 5, 1, 32]代表 卷積核尺寸為5x5,1個通道,32個不同卷積核
        :param W: 卷積的引數
            strides:代表卷積模板移動的步長,都是1代表不遺漏的劃過圖片的每一個點.
            padding:代表邊界處理方式,SAME代表輸入輸出同尺寸
        :return:
        '''
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding="SAME")


    def max_pool_2x2(x):
        '''
        tf.nn.max_pool是TensorFLow中最大池化函式.我們使用2x2最大池化
        因為希望整體上縮小圖片尺寸,因而池化層的strides設為橫豎兩個方向為2步長
        :param x:
        :return:
        '''
        return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding="SAME")


    def train(mnist):
        # 使用佔位符
        x = tf.placeholder(tf.float32, [None, 784])     # x為特徵
        y_ = tf.placeholder(tf.float32, [None, 10])     # y_為label

        # 卷積中將1x784轉換為28x28x1  [-1,,,]代表樣本數量不變 [,,,1]代表通道數
        x_image = tf.reshape(x, [-1, 28, 28, 1])

        # 第一個卷積層  [5, 5, 1, 32]代表 卷積核尺寸為5x5,1個通道,32個不同卷積核
        # 建立濾波器權值-->加偏置-->卷積-->池化
        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) #28x28x1 與32個5x5x1濾波器 --> 28x28x32
        h_pool1 = max_pool_2x2(h_conv1)  # 28x28x32 -->14x14x32

        # 第二層卷積層 卷積核依舊是5x5 通道為32   有64個不同的卷積核
        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) #14x14x32 與64個5x5x32濾波器 --> 14x14x64
        h_pool2 = max_pool_2x2(h_conv2)  #14x14x64 --> 7x7x64

        # h_pool2的大小為7x7x64 轉為1-D 然後做FC層
        W_fc1 = weight_variable([7*7*64, 1024])
        b_fc1 = bias_variable([1024])
        h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])    #7x7x64 --> 1x3136
        h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)  #FC層傳播 3136 --> 1024

        # 使用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_variable([1024, 10])  #MNIST只有10種輸出可能
        b_fc2 = bias_variable([10])
        y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

        # 定義損失函式,依舊使用交叉熵  同時定義優化器  learning rate = 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))

        #開始訓練
        with tf.Session() as sess:
            init_op = tf.global_variables_initializer() #初始化所有變數
            sess.run(init_op)

            STEPS = 20000
            for i in range(STEPS):
                batch = mnist.train.next_batch(50)
                if i % 100 == 0:
                    train_accuracy = sess.run(accuracy, 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})

            acc = sess.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})
            print("test accuracy %g" % acc)



    if __name__=="__main__":
        mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) # 載入資料集
        train(mnist)

我配置1080GPU版,訓練時間差不多3-4分鐘,從4000次開始在訓練集上的準確率已經接近為1了,最終在測試集上的準確率差不多為99.3%.
輸出:

    Extracting MNIST_data/train-images-idx3-ubyte.gz
    Extracting MNIST_data/train-labels-idx1-ubyte.gz
    Extracting MNIST_data/t10k-images-idx3-ubyte.gz
    Extracting MNIST_data/t10k-labels-idx1-ubyte.gz

    I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:910] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
    I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties: 
    name: GeForce GTX 1080
    major: 6 minor: 1 memoryClockRate (GHz) 1.873
    pciBusID 0000:01:00.0
    Total memory: 7.92GiB
    Free memory: 6.96GiB
    I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0 
    I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y 
    I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 1080, pci bus id: 0000:01:00.0)
    step 0,training accuracy 0.06
    step 100,training accuracy 0.84
    step 200,training accuracy 0.92
    step 300,training accuracy 0.88
    step 400,training accuracy 0.98
    step 500,training accuracy 0.96
    step 600,training accuracy 1
    step 700,training accuracy 0.98
    step 800,training accuracy 0.88
    step 900,training accuracy 1
    step 1000,training accuracy 0.94
    step 1100,training accuracy 0.9
    step 1200,training accuracy 1
    step 1300,training accuracy 0.98
    step 1400,training accuracy 0.98
    ....
    step 4200,training accuracy 1
    step 4300,training accuracy 1
    step 4400,training accuracy 1
    step 4500,training accuracy 1
    step 4600,training accuracy 1
    step 4700,training accuracy 0.98
    step 4800,training accuracy 0.96
    step 6700,training accuracy 1
    step 6800,training accuracy 1
    step 6900,training accuracy 1
    ....
    step 19100,training accuracy 1
    step 19200,training accuracy 1
    step 19300,training accuracy 1
    step 19400,training accuracy 1
    step 19500,training accuracy 1
    step 19600,training accuracy 1
    step 19700,training accuracy 1
    step 19800,training accuracy 1
    step 19900,training accuracy 1
    test accuracy 0.993

可以看到CNN模型的準確率遠高於深度神經網路的準確率,這主要歸功於CNN的網路設計,CNN對影象特徵的提取和抽象能力。依靠這卷積核權值共享,CNN的引數量沒有爆炸,訓練速度得到保證的同時也減輕了過擬合,整個模型的效能大大提升

下面是我們使用更為複雜的CNN模型,同時改用CIFAR-10資料集進行測試訓練.

CIFAR-10資料集下的進階CNN

CIFAR-10資料集介紹

這裡寫圖片描述
這裡寫圖片描述

這裡寫圖片描述

模型修正

  • 對weights進行了L2的正則化
  • 對輸入圖片進行翻轉、隨機剪下等資料增強,製造更多的樣本
  • 在每個卷積-池化層後使用LRN(區域性響應歸一化)層,增強模型的泛化能力

本次模型的網路結構如下:

Layer name description shape變化
conv1 卷積並ReLU啟用