# 三、卷積神經網絡
在上一章中,我們探討了深度神經網絡,該神經網絡需要更多的參數才能擬合。 本章將指導您完成深度學習中最強大的開發之一,并讓我們使用有關問題空間的一些知識來改進模型。 首先,我們將解釋一個神經網絡中的卷積層,然后是一個 TensorFlow 示例。 然后,我們將對池化層執行相同的操作。 最后,我們將字體分類模型改編為卷積神經網絡(CNN),然后看看它是如何工作的。
在本章中,我們將介紹卷積神經網絡的背景。 我們還將在 TensorFlow 中實現卷積層。 我們將學習最大池化層并將其付諸實踐,并以單個池化層為例。
在本章的最后,您將對以下概念有很好的控制:
* 卷積層動機
* 卷積層應用
* 池化層動機
* 池化層應用
* 深度 CNN
* 更深的 CNN
* 深層 CNN 總結
現在讓我們進入卷積層。
# 卷積層動機
在本節中,我們將逐步使用示例圖像上的卷積層。 我們將以圖形方式看到卷積只是一個滑動窗口。 此外,我們將學習如何從窗口中提取多個特征以及如何接受到窗口的多層輸入。
在給定神經元的神經網絡的經典密集層中,每個輸入特征都具有自己的權重。

如果輸入特征完全獨立并測量不同的事物,那么這很好,但是如果特征之間存在結構,那該怎么辦。 想象發生這種情況的最簡單示例是,如果您的輸入特征是圖像中的像素。 一些像素彼此相鄰,而其他像素則相距較遠。
對于諸如圖像分類(尤其是字體分類)之類的任務,圖像中出現小比例尺特征通常并不重要。 我們可以通過在整個圖像中滑動較小的窗口來在較大的圖像中查找小比例尺特征,這對于使用相同的權重矩陣至關重要,無論該窗口在圖像中的位置如何。 這樣,我們可以隨時隨地尋找相同的特征。
假設我們有一個`10x10`的圖像,并且想在其中滑動`3x3`的窗口。 通常,機器學習工程師每次只能將此窗口滑動一個像素。 這稱為跨步,因此從一個窗口到下一個窗口會有一些重疊。 然后逐個元素地將我們小的`3x3`權重矩陣`W1`乘到我們的窗口`H1[00]`中,對結果求和,并通過稱為`F`的激活函數進行處理。

第一個窗口`W1`進入新矩陣的第一個位置,如右圖`H2`所示。 窗口以相同的權重滑過一個,但結果占據第二個位置。 請注意,我們實際上是使用左上像素作為存儲結果的參考點。 在整個輸入圖像上滑動窗口以生成卷積輸出。 下圖中的點只是提醒您,您將在整個空間中滑動此窗口,而不僅是圖中所示的兩個位置:

您可能想知道當窗口到達圖像邊緣時會發生什么。 選擇實際上是在忽略超出邊緣的窗口和使用占位符值填充窗口之間。 對于卷積層,通常的選擇是經常用零或平均值填充它們。 由于卷積的輸出形狀保持不變,因此在 Tensorflow 中被稱為相同的填充。

請注意,在最后一個窗口中,這實際上只是看一個值。 但是該像素還參與了許多其他位置,因此不要覺得它被排除在外了。

## 提取多個特征
上一節介紹了滑動窗口的一組權重。 這實際上使您可以計算滑動特征。 但是您可能想要在同一窗口中查找多個對象,例如垂直或水平邊緣。
要提取多個特征,您只需要將其他權重矩陣初始化為不同的值即可。 這些多組權重類似于其他神經元和緊密連接的層。 中心的每個權重矩陣`W1`(藍色)和`W2`(綠色)將為您提供另一個輸出矩陣,如下圖所示,它們分別為`H2[1]`(粉紅色)和`H2[0]`(橙色)。 正確的。

正如您可以從卷積中提取多個特征一樣,也可以將多個特征放入這樣的網絡中。 最明顯的例子是具有多種顏色的圖像。

現在,觀察上圖中所示的矩陣。 您確實有一個紅色值的矩陣,一個綠色值的矩陣和一個藍色值的矩陣。 現在,您的權重矩陣實際上是大小為`3x3x3`的權重張量,并且在所有顏色上的窗口大小均相同。 當然,您可以組合所有這些方法,并且通常在計算完窗口上的 32 個特征后,尤其是在第一個卷積層之后進行; 現在,您有許多用于下一層的輸入通道。
# 卷積層應用
現在讓我們在 TensorFlow 中實現一個簡單的卷積層。 首先,我們將遍歷此示例中使用的顯式形狀,因為這通常很棘手。 然后,我們將完成實現和卷積的 TensorFlow 調用。 最后,我們將通過傳遞一個簡單的示例圖像直觀地檢查卷積的結果。
## 探索卷積層
讓我們通過一個新的 IPython 會話直接進入代碼。

這只是一個小例子,可以幫助我們熟悉將 TensorFlow 用于卷積層。
導入必要的工具后,讓我們制作一個假的`10x10`圖像,但對角線的值較大:
```py
# Make some fake data, 1 data points
image = np.random.randint(10,size=[1,10,10]) + np.eye(10)*10
```
請注意前面代碼中指定的異常大小。 `10, 10`只是圖像尺寸,但是`1`是指輸入通道的數量。 在這種情況下,我們使用一個輸入通道,就像一個灰度圖像。 如果您有彩色圖像,則這可能是代表紅色,綠色和藍色的三個通道。
盡管此處的示例和研究問題只有一個通道(灰度級),但我們將在深度 CNN 部分中看到如何從卷積層產生多個輸入,從而在下一個卷積層中產生多通道輸入。 因此,您仍然會感覺如何處理。
向下移動到 TensorFlow 占位符,我們還做了一些看似不尋常的事情。
```py
x = tf.placeholder("float", [None, 10, 10])
x_im = tf.reshape(x, [-1,10,10,1])
```
在用`10, 10`和`None`自然地寫入了占位符變量以用于可能的許多圖像批量之后,我們將其稱為`tf.reshape`。 這是為了重新排列圖像的尺寸,并使它們具有 TensorFlow 期望的形狀。 `-1`只是意味著根據需要填寫尺寸以保持整體尺寸。 `10,10`當然是我們的圖像尺寸,最后的`1`現在是通道數。 同樣,如果您有一個帶有三個通道的彩色圖像,則為三個。
對于我們的卷積層示例,我們希望查看圖像的三個像素高和三個像素寬的窗口。 因此,我們指定了以下代碼所示的內容:
```py
# Window size to use, 3x3 here
winx = 3
winy = 3
```
另外,讓我們從每個窗口中提取兩個特征,這就是我們的過濾器數量:
```py
# How many features to compute on the window
num_filters = 2
```
您可能還會看到稱為內核數量的信息。
指定權重是使事情真正有趣的地方,但是一旦您知道語法,這并不難。
```py
W1 = tf.Variable(tf.truncated_normal(
[winx, winy,1, num_filters],
stddev=1./math.sqrt(winx*winy)))
```
我們正在像以前一樣使用`tf.truncated_normal`來生成隨機權重。 但是大小非常特殊。 屬性`winx`和`winy`當然是我們窗口的尺寸,`1`這里是輸入通道的數量,因此只是灰度,而最終尺寸(`num_filters`)是輸出尺寸,過濾器的數量。
同樣,這類似于密集連接層的神經元數量。 對于隨機性的標準差,我們仍然可以縮放到參數數量,但是請注意,每個權重都有一個參數,因此`win x*win y`。
當然,每個輸出神經元的偏差都需要一個變量,因此每個濾波器需要一個變量:
```py
b1 = tf.Variable(tf.constant(
0.1,shape=[num_filters]))
```
`tf.nn.conv2d`函數實際上是此處操作的核心。 我們首先傳遞調整后的輸入`x_im`,然后傳遞應用于每個窗口的權重,然后傳遞`strides`參數。
### 注意
`strides`參數告訴 TensorFlow 每一步移動窗口多少。
卷積層的典型用法是向右移動一個像素,完成一行后,向下移動一個像素。 因此有很多重疊之處。 如果要向右移動兩個像素,向下移動兩個像素; 但是,您可以輸入`strides=[1,2,2,1]`。 最后一個數字用于在通道上移動,第一個數字用于在一批中移動單獨的圖像。 將這些設置為`1`是最常見的方法。
```py
xw = tf.nn.conv2d(x_im, W1,
strides=[1, 1, 1, 1],
padding='SAME')
```
`padding='SAME'`與上一節完全相同。 這意味著即使部分滑動窗口超出了輸入圖像的范圍,滑動窗口也會運行。 結合跨度為 1 的步長,這意味著卷積輸出尺寸將與輸入相同,當然不計算通道或濾波器的數量。
最后,我們要通過激活函數傳遞此卷積輸出:
```py
h1 = tf.nn.relu(xw + b1)
```
在這里,我們使用`relu`函數,它代表整流線性。 基本上,這僅意味著將任何負輸入設置為零,而將正輸入保持不變。 您會看到這種激活常與卷積神經網絡一起使用。 因此,熟悉它是一件好事。 由于我們已經乘以`W1`權重,因此我們只需要在此處添加偏置項即可產生卷積層輸出。
在 TensorFlow 中初始化變量:
```py
# Remember to initialize!
sess.run(tf.global_variables_initializer())
```
現在,您有了一個有效的卷積。 太好了! 讓我們快速看一下我們勞動成果。
首先,我們需要求值`h1`節點,并將示例圖像作為數據傳遞:
```py
# Peek inside
H = h1.eval(feed_dict = {x: image})
```
因此,我們知道從哪里開始,讓我們使用以下代碼查看示例圖像:
```py
# Let's take a look
import matplotlib.pyplot as plt
plt.ion()
# Original
plt.matshow(image[0])
plt.colorbar()
```
前面代碼中的`0`只是因為奇怪的整形,實際上并沒有多個數據點。 您可以看到對角線上的值大于其他值,這與純隨機的區別在于:

讓我們看一下第一個輸出特征,回想一下輸出`H`的形狀為`1,10,10,2`,因為有`1`數據點,`10`像素的寬度和高度以及`2`特征。 因此,要抓住第一個,我們需要所有像素和零個帶過濾器。 好吧,那很有趣。
```py
# Conv channel 1
plt.matshow(H[0,:,:,0])
plt.colorbar()
```
請注意清零了多少個頭寸:

這是`relu`激活的糾正部分。 整齊。 第二個特征應該看起來相似,直到隨機初始化為止。 這些權重尚未經過任何訓練,因此我們不應該期望它們產生有意義的輸出。 在這里,我們看到碰巧有很多零,否則,有很多小值。

您的圖像看起來或多或少會有所不同,需要注意的重要一點是,我們的輸出尺寸相同,但是就像我們對同一圖像有兩個不同的視圖一樣。 在本部分中,我們在 TensorFlow 中創建了我們的第一個卷積層,以掌握所需的奇數形狀。
# 池化層動機
現在,讓我們了解池化層的共同合作伙伴。 在本節中,我們將學習與卷積層相似的最大池化層,盡管它們在通用用法上有所不同。 最后,我們將展示如何組合這些層以獲得最大效果。
## 最大池化層
假設您使用了卷積層從圖像中提取特征,并假設,您有一個小的權重矩陣,可以檢測圖像窗口中的狗形。

當您圍繞輸出進行卷積時,可能會報告許多附近呈狗形的區域。 但這實際上只是由于重疊。 盡管可能只有小狗的形象,但彼此之間可能并沒有多少只狗。 您真的只希望一次看到該特征,最好是在特征最強大的位置。 最大池化層嘗試執行此操作。 像卷積層一樣,池化層在圖像的小滑動窗口上工作。

通常,研究人員在一個或多個卷積層之后添加一個池化層。 您最常看到的窗口大小是`2x2`。 您要做的只是提取四個相鄰的值,此處指的是`H[00]`,通常不會對其施加任何權重。 現在,我們希望以某種方式組合這四個值,以提取此窗口最有趣的特征。 通常,我們要提取最引人注目的特征,因此我們選擇最大值(`max(H[00])`)的像素,然后丟棄其余像素。 但是,您也可以平均結果或做一些更奇特的事情。 同樣,盡管我們的卷積窗口有很多重疊,但對于合并窗口,我們通常不希望有任何重疊,因此此步長將等于窗口大小 2。
在前面的`10x10`示例輸出中,由于步幅的變化,我們的池化輸出僅為`5x5`。

與卷積層的另一個主要區別是,池化層通常使用不同的填充方案,而卷積層樂于使用相同的填充并以零填充,我們最常使用具有有效填充的池化層。 這意味著,如果窗口超出圖像的范圍,則將其丟棄。

這確實會丟失一些邊緣信息,但要確保輸出不會因填充值而產生偏差。
### 注意
請注意,此示例對池化層使用`9x9`輸入,但由于有效的填充和跨度為 2,因此輸出僅為`4x4`。 `8x8`輸入也將具有`4x4`輸出。
當您將卷積層和池化層組合在一起時,它們的真正優勢就體現出來了。 通常,您會在模型的頂部輸入端看到一個卷積層,也許帶有`3x3`窗口。

這會在圖像中的任何位置尋找相同的特征集。
然后立即出現一個`2x2`的最大池化層,僅池出最具特征的區域并縮小尺寸。 您也可以重復此過程。
合并后,您現在實際上具有較小的圖像`P1`,但是具有像素強度,而不是像素顏色強度。 因此,您可以創建另一個卷積層以讀取第一個池的輸出,即底部出現的`P1`,然后可以對此應用另一個最大池化層。 請注意,由于池化,圖像大小是如何逐漸縮小的。 直觀地,您可以將其視為建立跨越圖像較大區域的較大比例的特征。
早期的卷積權重經常訓練以檢測簡單的邊緣,而連續的卷積層將這些邊緣組合成逐漸更復雜的形狀,例如人臉,汽車甚至狗。
# 池化層應用
在本節中,我們將研究用于最大池化的 TensorFlow 函數,然后我們將討論從池化層過渡到完全連接層的過程。 最后,我們將目視觀察池輸出以驗證其減小的大小。
讓我們從上一節中停下來的示例開始。 在開始本練習之前,請確保您已執行所有操作直到英鎊池化層。
回想一下,我們通過`3x3`卷積和校正的線性激活來放置`10x10`圖像。 現在,讓我們在卷積層之后添加一個最大`2x2`的池化層。
```py
p1 = tf.nn.max_pool(h1, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='VALID')
```
關鍵是`tf.nn.max_pool`。 第一個參數只是我們先前的卷積層`h1`的輸出。 接下來,我們有一個奇怪的`ksize`。 這實際上只是定義了池的窗口大小。 在這種情況下,為`2x2`。 第一個`1`指的是一次或批量多少個數據點。 通常,我們將其保留為`1`。 最后的`1`指的是一次包含在合并中的通道數。 請注意,這里有兩個通道,因為卷積產生了兩個輸出濾波器。 但是我們只有`1`在這個位置; 這是一次最多只有一個特征的唯一故障。 步幅的工作方式與卷積層相同。 此處的區別在于我們使用`2x2`(即合并窗口的大小),因為我們不希望有任何重疊。 `1`之前和之后的值與卷積層中的值完全相同。
因此,我們的輸出將是每個尺寸的一半,這里是`5x5`。 最后,我們將`padding`設置為`VALID`。 這意味著,如果一個窗口超出了圖像的邊緣(實際上是卷積輸出),我們將把它扔掉而不使用它。 如果我們的池化層進入另一個卷積層,則可以在以下代碼行中添加它:
```py
# We automatically determine the size
p1_size = np.product([s.value for s in p1.get_shape()[1:]])
```
但是,如果您已經完成了卷積層的工作,并且想要像上一節中的模型那樣饋入經典的完全連接層,該怎么辦? 這很容易做到; 我們只需要將具有許多輸出通道的 2D 矩陣的輸出展平到長的一維向量即可。
該行是自動計算展平池輸出長度的一種方法。 它所做的就是乘以所有尺寸的大小。 因此,具有兩個通道的`5x5`矩陣將產生`5x5x2`,即`50`輸出。 下一行`tf.reshape`使用此值實際展平數組:
```py
p1f = tf.reshape(p1, [-1, p1_size ])
```
前面的代碼行中的`-1`用于一次處理許多輸入圖像的潛在批量。 它告訴 TensorFlow 選擇第一個維度,以便參數的總數保持不變。 讓我們看一下池化層的輸出,以便可以看到一個具體示例:
```py
P = p1.eval(feed_dict = {x: image})
```
首先,我們必須根據給定輸入圖像來實際求值池輸出。
由于池化層取決于卷積層,因此 TensorFlow 會自動將圖像首先放置在其中。 我們可以以與卷積輸出完全相同的方式查看結果。

僅查看前面的第一個過濾器輸出,您會注意到它是`5x5`。
還要注意,存在的值全部在卷積輸出的某些單元中。 由于我們在池化層上的唯一激活是最大值,因此在每個`2x2`窗口中會丟棄三個值,并且一個值會前進到下一層。
# 深度 CNN
現在,在本節中,讓我們著重考慮。 在本節中,我們將向我們的字體分類模型添加卷積和池化層組合。 我們將確保將其填充到一個密集層中,然后我們將看到此模型的工作方式。 在進入新的卷積模型之前,請確保開始一個新的 IPython 會話。 執行所有操作,直到`num_filters = 4`,您就可以準備就緒。
## 添加卷積和池化層組合
對于卷積層,我們將使用`5x5`窗口,其中提取了四個特征。 這比示例要大一些。
我們真的希望模型現在學習一些東西。 首先,我們應該使用`tf.reshape`將`36x36`的圖像放入大小為`36x36x1`的張量中。
```py
x_im = tf.reshape(x, [-1,36,36,1])
```
這僅對于保持通道數筆直很重要。 現在,我們將如上所述為過濾器和窗口的數量設置常量:
```py
num_filters = 4
winx = 5
winy = 5
```
我們可以像示例問題中那樣設置權重張量:
```py
W1 = tf.Variable(tf.truncated_normal(
[winx, winy, 1 , num_filters],
stddev=1./math.sqrt(winx*winy)))
```
`winx`和`winy`常數只是窗口尺寸。 `1`值是輸入通道數,僅是灰色,`num_filters`是我們要提取的特征數。 同樣,這就像密集層中神經元的數量。 偏差的工作方式相同,但只擔心過濾器的數量:
```py
b1 = tf.Variable(tf.constant(0.1,
shape=[num_filters]))
```
對`conv2d`本身的調用也與我們的示例相同。
```py
xw = tf.nn.conv2d(x_im, W1,
strides=[1, 1, 1, 1],
padding='SAME')
```
好東西,我們在那里推廣了它,現在使生活變得輕松。 以下是上述代碼行的描述:
* `x_im`是要轉換的輸入
* `W1`屬性是我們剛剛指定的權重矩陣
* `strides`告訴 TensorFlow 每一步將窗口移動一次
* `padding='SAME'`表示接受圖像邊緣上的窗口
現在,我們可以通過`relu`激活函數進行卷積,以完成卷積層。 做得好!
```py
h1 = tf.nn.relu(xw + b1)
```
池化層也與上一節完全相同:
```py
# 2x2 Max pooling, no padding on edges
p1 = tf.nn.max_pool(h1, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='VALID')
```
只是為了回顧一下,我們在每次跨步時將`2x2`窗口`ksize`在卷積輸出上滑動兩個。 當我們超出數據范圍時,`padding='VALID'`告訴我們停止。 現在我們有了卷積池和池化層的組合,讓我們附加一個典型的密集連接層:
```py
p1_size = np.product(
[s.value for s in p1.get_shape()[1:]])
p1f = tf.reshape(p1, [-1, p1_size ])
```
首先,我們需要將合??并輸出調整為一維向量。 這正是我們在上一節中所做的。 我們自動計算池輸出的尺寸,以獲取用于展平的參數數量。
## CNN 字體分類
現在讓我們創建一個包含 32 個神經元的密集連接層:
```py
# Dense layer
num_hidden = 32
W2 = tf.Variable(tf.truncated_normal(
[p1_size, num_hidden],
stddev=2./math.sqrt(p1_size)))
b2 = tf.Variable(tf.constant(0.2,
shape=[num_hidden]))
h2 = tf.nn.relu(tf.matmul(p1f,W2) + b2)
```
當然,我們需要使用`p1_size`該層的輸入數量來初始化權重矩陣。 那只是卷積和池輸出中的扁平數組。 我們需要`num_hidden` 32 個輸出。 有偏項對一些小的非零初始值以相同的方式工作。 在這里,我們碰巧也在使用`relu`激活。
最后,我們像往常一樣定義輸出邏輯回歸:
```py
# Output Layer
W3 = tf.Variable(tf.truncated_normal(
[num_hidden, 5],
stddev=1./math.sqrt(num_hidden)))
b3 = tf.Variable(tf.constant(0.1,shape=[5]))
keep_prob = tf.placeholder("float")
h2_drop = tf.nn.dropout(h2, keep_prob)
```
使用舊模型工作,只需確保最終權重使用`num_hidden, 5`作為尺寸即可。 我們在這里有一個名為`dropout`的新元素。 現在不用擔心。 我們將在下一部分中確切描述它的作用。 只知道它有助于過擬合。
現在,您可以初始化所有變量并實現對`softmax`的最終調用:
```py
# Just initialize
sess.run(tf.global_variables_initializer())
# Define model
y = tf.nn.softmax(tf.matmul(h2_drop,W3) + b3)
```
請注意您的變量名正確匹配。 好的,現在完成設置,讓我們對其進行訓練:
```py
# Climb on cross-entropy
cross_entropy = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
logits = y + 1e-50, labels = y_))
# How we train
train_step = tf.train.GradientDescentOptimizer(
0.01).minimize(cross_entropy)
# Define accuracy
correct_prediction = tf.equal(tf.argmax(y,1),
tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(
correct_prediction, "float"))
```
實際上,我們訓練模型的方式與之前的模型完全相同。 `cross_entropy`節點測量我們的預測有多少誤差,`GradientDescentOptimizer`調整矩陣的權重。 我們還應謹慎定義節點以提高準確率,以便以后進行測量。 現在讓我們訓練模型約 5,000 次:
```py
# Actually train
epochs = 5000
train_acc = np.zeros(epochs//10)
test_acc = np.zeros(epochs//10)
for i in tqdm(range(epochs), ascii=True):
# Record summary data, and the accuracy
if i % 10 == 0:
# Check accuracy on train set
A = accuracy.eval(feed_dict={x: train,
y_: onehot_train, keep_prob: 1.0})
train_acc[i//10] = A
# And now the validation set
A = accuracy.eval(feed_dict={x: test,
y_: onehot_test, keep_prob: 1.0})
test_acc[i//10] = A
train_step.run(feed_dict={x: train,
y_: onehot_train, keep_prob: 0.5})
```
這可能需要一個小時或更長時間。 但是試想一下,如果您必須為卷積中的每個窗口訓練不同的權重。 通過訓練模型,讓我們看一下精度曲線。

我們可以看到,該模型優于舊的緊密連接模型,現在達到了 76% 的訓練準確率和約 68% 的驗證。
這可能是因為字體即使創建許多不同的字母也以相同的方式使用了許多小范圍的特征。 讓我們也看看混淆矩陣。

在這里,我們看到該模型仍不完美,但正在取得進展。 第一類仍然沒有得到很好的代表,但是它至少在某種程度上是正確的,這與某些以前的模型從來都不是正確的不同。 其他類大多都不錯。 第三類實際上是完美的。 這不是一個容易的問題,因此任何改進都是好的。 我們還設置了一些代碼來專門檢查權重,但是我們將在以后的部分中保存它。 不過,請隨時與他們一起玩耍。 您可以將模型權重和信息保存在檢查點文件中。
```py
# Save the weights
saver = tf.train.Saver()
saver.save(sess, "conv1.ckpt")
# Restore
saver.restore(sess, "conv1.ckpt")
```
這很簡單。 您只需創建一個`saver`對象,然后將會話保存到文件名即可。 恢復同樣容易。 您告訴 TensorFlow 哪個會話將已保存的文件放入和退出。 如果您更喜歡使用 NumPy 手動保存權重,則代碼文件還提供以下函數:
```py
# Or use Numpy manually
def save_all(name = 'conv1'):
np.savez_compressed(name, W1.eval(),
b1.eval(), W2.eval(), b2.eval(),
W3.eval(), b3.eval())
save_all()
def load_all(name = 'conv1.npz'):
data = np.load(name)
sess.run(W1.assign(data['arr_0']))
sess.run(b1.assign(data['arr_1']))
sess.run(W2.assign(data['arr_2']))
sess.run(b2.assign(data['arr_3']))
sess.run(W3.assign(data['arr_4']))
sess.run(b3.assign(data['arr_5']))
load_all()
```
因為 NumPy 格式非常可移植且相當輕巧,所以這會更方便。 如果要將值導出到另一個 Python 腳本中,從而不需要 TensorFlow,則您可能更喜歡 NumPy。 在本節中,我們建立了卷積神經網絡對字體進行分類。 一個類似的模型可以解決當前的研究問題。 您處于 TensorFlow 深度學習的最前沿。
# 更深的 CNN
在本節中,我們將向模型添加另一個卷積層。 不用擔心,我們將逐步遍歷參數以使尺寸調整一致,并且我們將學習什么是丟棄訓練。
## 將 CNN 的一層添加到另一層
與往常一樣,在啟動新模型時,進行一個新的 IPython 會話并執行直到`num_filters1`的代碼。 太好了,現在您都可以開始學習了。 讓我們跳入卷積模型。
我們為何不抱有雄心,將第一個卷積層設置為具有`16`過濾器,遠遠超過舊模型中的`4`。 但是,這次我們將使用較小的窗口大小。 只有`3x3`。 另請注意,我們將某些變量名稱(例如`num_filters`更改為`num_filters1`)。 這是因為我們將在短時間內擁有另一個卷積層,并且我們可能希望在每個卷積層上使用不同數量的過濾器。 該層的其余部分與以前完全一樣,我們可以進行卷積并進行`2x2`最大池化,并使用整流的線性激活單元。
現在,我們添加另一個卷積層。 一些模型先進行幾次卷積,然后再進行池化,另一些模型先進行一次卷積,再進行一次池化,再進行一次卷積,依此類推。 我們在這里做后者。 假設您需要四個濾鏡和一個`3x3`的窗口。 這很容易產生權重; 與上一層的唯一大不同是我們現在有許多輸入通道,請參見`num_filters1`:
```py
# Conv layer 2
num_filters2 = 4
winx2 = 3
winy2 = 3
W2 = tf.Variable(tf.truncated_normal(
[winx2, winy2, num_filters1, num_filters2],
stddev=1./math.sqrt(winx2*winy2)))
b2 = tf.Variable(tf.constant(0.1,
shape=[num_filters2]))
```
這是因為我們有`16`輸入通道來自上一層。 如果我們使用`num_filters1 = 8`,則只有`8`輸入通道。 將此視為我們將要建立的低級特征。 請記住,通道的數量和輸入就像顏色的數量一樣,因此,如果您要這樣考慮,可能會有所幫助。
當我們進行實際的第二個卷積層時,請確保傳入第一個池化層`p1`的輸出。 現在,這可以進入新的`relu`激活函數,然后是另一個池化層。 像往常一樣,我們使用有效填充進行最大`2x2`的池化:
```py
# 3x3 convolution, pad with zeros on edges
p1w2 = tf.nn.conv2d(p1, W2,
strides=[1, 1, 1, 1], padding='SAME')
h1 = tf.nn.relu(p1w2 + b2)
# 2x2 Max pooling, no padding on edges
p2 = tf.nn.max_pool(h1, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='VALID')
```
展平卷積的池化輸出也遵循與最后一個模型相同的過程。 但是,這次,我們當然致力于合并輸出 2。 將其所有參數從窗口中的所有特征轉換為一個大向量:
```py
# Need to flatten convolutional output
p2_size = np.product(
[s.value for s in p2.get_shape()[1:]])
p2f = tf.reshape(p2, [-1, p2_size ])
```
現在,就像在前面的部分中所做的那樣,將密集連接的層插入到我們的神經網絡中。 只要確保更新變量名即可。
```py
# Dense layer
num_hidden = 32
W3 = tf.Variable(tf.truncated_normal(
[p2_size, num_hidden],
stddev=2./math.sqrt(p2_size)))
b3 = tf.Variable(tf.constant(0.2,
shape=[num_hidden]))
h3 = tf.nn.relu(tf.matmul(p2f,W3) + b3)
```
現在,我們看到了與我們使用的相同的`tf.nn.dropout`,但在上一個模型中沒有解釋:
```py
# Drop out training
keep_prob = tf.placeholder("float")
h3_drop = tf.nn.dropout(h3, keep_prob)
```
丟棄是一種從模型中暫時切斷神經元的方法。 我們在訓練過程中這樣做是為了避免過擬合。 每批 TensorFlow 將在此連接層選擇不同的神經元輸出以進行刪除。 面對訓練期間的細微變化,這有助于模型變得健壯。 `keep_prob`是保持特定神經元輸出的概率。 在訓練過程中通常將其設置為`0.5`。
再一次,最終的邏輯回歸層和訓練節點代碼與之前的相同:
```py
# Output Layer
W4 = tf.Variable(tf.truncated_normal(
[num_hidden, 5],
stddev=1./math.sqrt(num_hidden)))
b4 = tf.Variable(tf.constant(0.1,shape=[5]))
# Just initialize
sess.run(tf.initialize_all_variables())
# Define model
y = tf.nn.softmax(tf.matmul(h3_drop,W4) + b4)
### End model specification, begin training code
# Climb on cross-entropy
cross_entropy = tf.reduce_mean(
tf.nn.softmax_cross_entropy_with_logits(
y + 1e-50, y_))
# How we train
train_step = tf.train.GradientDescentOptimizer(
0.01).minimize(cross_entropy)
# Define accuracy
correct_prediction = tf.equal(tf.argmax(y,1),
tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(
correct_prediction, "float"))
```
您現在可以執行該操作。 現在,我們可以訓練我們的完整卷積神經網絡,這是到目前為止建模的頂點:
```py
# Actually train
epochs = 6000
train_acc = np.zeros(epochs//10)
test_acc = np.zeros(epochs//10)
for i in tqdm(range(epochs), ascii=True):
# Record summary data, and the accuracy
if i % 10 == 0:
# Check accuracy on train set
A = accuracy.eval(feed_dict={x: train,
y_: onehot_train, keep_prob: 1.0})
train_acc[i//10] = A
# And now the validation set
A = accuracy.eval(feed_dict={x: test,
y_: onehot_test, keep_prob: 1.0})
test_acc[i//10] = A
train_step.run(feed_dict={x: train,\
y_: onehot_train, keep_prob: 0.5})
```
訓練該模型可能需要幾個小時,因此您可能希望在下一節之前立即開始。
# 深度 CNN 總結
我們將通過評估模型的準確率來總結深層的 CNN。 上一次,我們建立了最終的字體識別模型。 現在,讓我們看看它是如何工作的。 在本節中,我們將學習如何在訓練期間處理丟棄問題。 然后,我們將看到模型達到了什么精度。 最后,我們將權重可視化以了解模型學到了什么。
確保在上一個模型中進行訓練后,在 IPython 會話中接手。 回想一下,當我們訓練模型時,我們使用`dropout`刪除了一些輸出。
盡管這有助于過擬合,但在測試過程中,我們要確保使用每個神經元。 這既提高了準確率,又確保我們不會忘記評估模型的一部分。 這就是為什么在以下代碼行中,`keep_prob`為`1.0`以便始終保留所有神經元的原因。
```py
# Check accuracy on train set
A = accuracy.eval(feed_dict={x: train,
y_: onehot_train, keep_prob: 1.0})
train_acc[i//10] = A
# And now the validation set
A = accuracy.eval(feed_dict={x: test,
y_: onehot_test, keep_prob: 1.0})
test_acc[i//10] = A
```
讓我們看看最終模型是如何做的; 像往常一樣看一下訓練和測試的準確率:

這里的訓練準確率高達 85%,并且測試準確率也相差不遠。還不錯。模型的效果取決于輸入數據的噪聲。 如果我們僅包含少量信息,無論是示例數量還是參數或像素數量,那么我們都無法期望模型表現完美。
在這種情況下,您可以應用的一種度量標準是人類將單個字母的圖像分類到這些字體中的每種字體的程度。 一些字體非常有特色,而另一些則相似,尤其是某些字母。 由于這是一個新穎的數據集,因此沒有直接的基準可以與之進行比較,但是您可以挑戰自己以擊敗本課程中介紹的模型。 如果這樣做,您可能希望減少訓練時間。 當然,具有較少參數和更簡單計算的較小網絡將更快。 另外,如果您開始使用 GPU 或至少使用多核 CPU,則可以顯著提高速度。 通常 10 倍更好,具體取決于硬件。
其中一部分是并行性,一部分是針對神經網絡進行了微調的高效低層庫。 但是,最簡單的方法是從簡單開始,逐步發展到更復雜的模型,就像您一直在處理此問題一樣。 回到這個模型,讓我們看一下混淆矩陣:
```py
# Look at the final testing confusion matrix
pred = np.argmax(y.eval(
feed_dict={x: test, keep_prob: 1.0,
y_: onehot_test}), axis = 1)
conf = np.zeros([5,5])
for p,t in zip(pred,np.argmax(onehot_test,
axis=1)):
conf[t,p] += 1
plt.matshow(conf)
plt.colorbar()
```
以下是輸出:

在這里,我們可以看到該模型通常在各個類上都做得很好。 類`1`仍然不是完美的,但是比以前的模型要好得多。 通過將較小比例的特征分解為較大的片段,我們終于找到了一些適合這些類的指標。 您的圖像可能看起來不完全相同。 根據權重的隨機初始化,結果可能會有些不幸。
讓我們看一下第一卷積層的 16 個特征的權重:
```py
# Let's look at a subplot of some weights
f, plts = plt.subplots(4,4)
for i in range(16):
plts[i//4,i%4].matshow(W1.eval()[:,:,0,i],
cmap = plt.cm.gray_r)
```
因為窗口大小是`3x3`,所以每個都是`3x3`矩陣。 嗯! 我們可以看到,權重肯定是縮小了小范圍的特征。

您可以看到某些事物,例如檢測到邊緣或圓角,諸如此類。 如果我們使用更大的窗口重做模型,這可能會更加明顯。 但是令人印象深刻的是,您可以在這些小補丁中發現多少特征。
我們還要看一下最終的層權重,以了解不同的字體類如何解釋最終的緊密連接的神經元。

每行代表一類,每列代表最終的隱藏層神經元之一。 有些類別受到某些神經元的強烈影響,而另一些類別的影響則微乎其微。 您會看到,對于某些類別,給定的神經元在積極或消極方面非常重要,而對于其他類別則非常重要。
請注意,因為我們已經使卷積變平,所以我們不希望在輸出中看到明顯的結構。 這些列可以按任何順序排列,但仍會產生相同的結果。 在本章的最后部分,我們檢查了一個真實的,實時的,坦率的,非常好的深度卷積神經網絡模型。 我們使用卷積層和池化層的做法來構筑該思想,以便提取結構化數據(例如圖像)中的小規模和大規模型征。
對于許多問題,這是神經網絡最強大的類型之一。
# 總結
在本章中,我們遍歷了示例圖像上的卷積層。 我們解決了理解卷積的實際問題。 它們可以令人費解,但希望不再造成混淆。 我們最終將此概念應用于 TensorFlow 中的一個簡單示例。 我們探索了卷積,池化層的共同伙伴。 我們解釋了常見的卷積伙伴最大池化層的工作原理。 然后,隨著我們的進步,我們通過在示例中添加一個池化層將其付諸實踐。 我們還練習了在 TensorFlow 中創建最大池化層。 我們開始將卷積神經網絡添加到字體分類問題中。
在下一章中,我們將研究具有時間成分的模型,即循環神經網絡(RNN)。
- TensorFlow 1.x 深度學習秘籍
- 零、前言
- 一、TensorFlow 簡介
- 二、回歸
- 三、神經網絡:感知器
- 四、卷積神經網絡
- 五、高級卷積神經網絡
- 六、循環神經網絡
- 七、無監督學習
- 八、自編碼器
- 九、強化學習
- 十、移動計算
- 十一、生成模型和 CapsNet
- 十二、分布式 TensorFlow 和云深度學習
- 十三、AutoML 和學習如何學習(元學習)
- 十四、TensorFlow 處理單元
- 使用 TensorFlow 構建機器學習項目中文版
- 一、探索和轉換數據
- 二、聚類
- 三、線性回歸
- 四、邏輯回歸
- 五、簡單的前饋神經網絡
- 六、卷積神經網絡
- 七、循環神經網絡和 LSTM
- 八、深度神經網絡
- 九、大規模運行模型 -- GPU 和服務
- 十、庫安裝和其他提示
- TensorFlow 深度學習中文第二版
- 一、人工神經網絡
- 二、TensorFlow v1.6 的新功能是什么?
- 三、實現前饋神經網絡
- 四、CNN 實戰
- 五、使用 TensorFlow 實現自編碼器
- 六、RNN 和梯度消失或爆炸問題
- 七、TensorFlow GPU 配置
- 八、TFLearn
- 九、使用協同過濾的電影推薦
- 十、OpenAI Gym
- TensorFlow 深度學習實戰指南中文版
- 一、入門
- 二、深度神經網絡
- 三、卷積神經網絡
- 四、循環神經網絡介紹
- 五、總結
- 精通 TensorFlow 1.x
- 一、TensorFlow 101
- 二、TensorFlow 的高級庫
- 三、Keras 101
- 四、TensorFlow 中的經典機器學習
- 五、TensorFlow 和 Keras 中的神經網絡和 MLP
- 六、TensorFlow 和 Keras 中的 RNN
- 七、TensorFlow 和 Keras 中的用于時間序列數據的 RNN
- 八、TensorFlow 和 Keras 中的用于文本數據的 RNN
- 九、TensorFlow 和 Keras 中的 CNN
- 十、TensorFlow 和 Keras 中的自編碼器
- 十一、TF 服務:生產中的 TensorFlow 模型
- 十二、遷移學習和預訓練模型
- 十三、深度強化學習
- 十四、生成對抗網絡
- 十五、TensorFlow 集群的分布式模型
- 十六、移動和嵌入式平臺上的 TensorFlow 模型
- 十七、R 中的 TensorFlow 和 Keras
- 十八、調試 TensorFlow 模型
- 十九、張量處理單元
- TensorFlow 機器學習秘籍中文第二版
- 一、TensorFlow 入門
- 二、TensorFlow 的方式
- 三、線性回歸
- 四、支持向量機
- 五、最近鄰方法
- 六、神經網絡
- 七、自然語言處理
- 八、卷積神經網絡
- 九、循環神經網絡
- 十、將 TensorFlow 投入生產
- 十一、更多 TensorFlow
- 與 TensorFlow 的初次接觸
- 前言
- 1.?TensorFlow 基礎知識
- 2. TensorFlow 中的線性回歸
- 3. TensorFlow 中的聚類
- 4. TensorFlow 中的單層神經網絡
- 5. TensorFlow 中的多層神經網絡
- 6. 并行
- 后記
- TensorFlow 學習指南
- 一、基礎
- 二、線性模型
- 三、學習
- 四、分布式
- TensorFlow Rager 教程
- 一、如何使用 TensorFlow Eager 構建簡單的神經網絡
- 二、在 Eager 模式中使用指標
- 三、如何保存和恢復訓練模型
- 四、文本序列到 TFRecords
- 五、如何將原始圖片數據轉換為 TFRecords
- 六、如何使用 TensorFlow Eager 從 TFRecords 批量讀取數據
- 七、使用 TensorFlow Eager 構建用于情感識別的卷積神經網絡(CNN)
- 八、用于 TensorFlow Eager 序列分類的動態循壞神經網絡
- 九、用于 TensorFlow Eager 時間序列回歸的遞歸神經網絡
- TensorFlow 高效編程
- 圖嵌入綜述:問題,技術與應用
- 一、引言
- 三、圖嵌入的問題設定
- 四、圖嵌入技術
- 基于邊重構的優化問題
- 應用
- 基于深度學習的推薦系統:綜述和新視角
- 引言
- 基于深度學習的推薦:最先進的技術
- 基于卷積神經網絡的推薦
- 關于卷積神經網絡我們理解了什么
- 第1章概論
- 第2章多層網絡
- 2.1.4生成對抗網絡
- 2.2.1最近ConvNets演變中的關鍵架構
- 2.2.2走向ConvNet不變性
- 2.3時空卷積網絡
- 第3章了解ConvNets構建塊
- 3.2整改
- 3.3規范化
- 3.4匯集
- 第四章現狀
- 4.2打開問題
- 參考
- 機器學習超級復習筆記
- Python 遷移學習實用指南
- 零、前言
- 一、機器學習基礎
- 二、深度學習基礎
- 三、了解深度學習架構
- 四、遷移學習基礎
- 五、釋放遷移學習的力量
- 六、圖像識別與分類
- 七、文本文件分類
- 八、音頻事件識別與分類
- 九、DeepDream
- 十、自動圖像字幕生成器
- 十一、圖像著色
- 面向計算機視覺的深度學習
- 零、前言
- 一、入門
- 二、圖像分類
- 三、圖像檢索
- 四、對象檢測
- 五、語義分割
- 六、相似性學習
- 七、圖像字幕
- 八、生成模型
- 九、視頻分類
- 十、部署
- 深度學習快速參考
- 零、前言
- 一、深度學習的基礎
- 二、使用深度學習解決回歸問題
- 三、使用 TensorBoard 監控網絡訓練
- 四、使用深度學習解決二分類問題
- 五、使用 Keras 解決多分類問題
- 六、超參數優化
- 七、從頭開始訓練 CNN
- 八、將預訓練的 CNN 用于遷移學習
- 九、從頭開始訓練 RNN
- 十、使用詞嵌入從頭開始訓練 LSTM
- 十一、訓練 Seq2Seq 模型
- 十二、深度強化學習
- 十三、生成對抗網絡
- TensorFlow 2.0 快速入門指南
- 零、前言
- 第 1 部分:TensorFlow 2.00 Alpha 簡介
- 一、TensorFlow 2 簡介
- 二、Keras:TensorFlow 2 的高級 API
- 三、TensorFlow 2 和 ANN 技術
- 第 2 部分:TensorFlow 2.00 Alpha 中的監督和無監督學習
- 四、TensorFlow 2 和監督機器學習
- 五、TensorFlow 2 和無監督學習
- 第 3 部分:TensorFlow 2.00 Alpha 的神經網絡應用
- 六、使用 TensorFlow 2 識別圖像
- 七、TensorFlow 2 和神經風格遷移
- 八、TensorFlow 2 和循環神經網絡
- 九、TensorFlow 估計器和 TensorFlow HUB
- 十、從 tf1.12 轉換為 tf2
- TensorFlow 入門
- 零、前言
- 一、TensorFlow 基本概念
- 二、TensorFlow 數學運算
- 三、機器學習入門
- 四、神經網絡簡介
- 五、深度學習
- 六、TensorFlow GPU 編程和服務
- TensorFlow 卷積神經網絡實用指南
- 零、前言
- 一、TensorFlow 的設置和介紹
- 二、深度學習和卷積神經網絡
- 三、TensorFlow 中的圖像分類
- 四、目標檢測與分割
- 五、VGG,Inception,ResNet 和 MobileNets
- 六、自編碼器,變分自編碼器和生成對抗網絡
- 七、遷移學習
- 八、機器學習最佳實踐和故障排除
- 九、大規模訓練
- 十、參考文獻