- Microsoft Cognitive Toolkit (CNTK) 教程
- 主頁
- 介紹
- 入門
- CPU 和 GPU
- CNTK - 序列分類
- CNTK - 邏輯迴歸模型
- CNTK - 神經網路 (NN) 概念
- CNTK - 建立第一個神經網路
- CNTK - 訓練神經網路
- CNTK - 記憶體資料集和大型資料集
- CNTK - 效能測量
- 神經網路分類
- 神經網路二元分類
- CNTK - 神經網路迴歸
- CNTK - 分類模型
- CNTK - 迴歸模型
- CNTK - 記憶體不足的資料集
- CNTK - 監控模型
- CNTK - 卷積神經網路
- CNTK - 迴圈神經網路
- Microsoft Cognitive Toolkit 資源
- Microsoft Cognitive Toolkit - 快速指南
- Microsoft Cognitive Toolkit - 資源
- Microsoft Cognitive Toolkit - 討論
CNTK - 卷積神經網路
在本章中,我們將學習如何在 CNTK 中構建卷積神經網路 (CNN)。
介紹
卷積神經網路 (CNN) 也由具有可學習權重和偏差的神經元組成。因此,它們與普通神經網路 (NN) 類似。
如果我們回想一下普通神經網路的工作原理,每個神經元都會接收一個或多個輸入,取加權和,並透過啟用函式產生最終輸出。這裡的問題是,如果 CNN 和普通 NN 有如此多的相似之處,那麼是什麼讓這兩個網路彼此不同呢?
使它們不同的原因在於輸入資料的處理和層型別?普通 NN 會忽略輸入資料的結構,並在將其饋送到網路之前將其轉換為一維陣列。
但是,卷積神經網路架構可以考慮影像的二維結構,處理它們並允許它提取特定於影像的屬性。此外,CNN 具有一個或多個卷積層和池化層的優勢,它們是 CNN 的主要構建塊。
這些層之後是一個或多個全連線層,就像標準的多層神經網路一樣。因此,我們可以將 CNN 視為全連線網路的一個特例。
卷積神經網路 (CNN) 架構
CNN 的架構基本上是一個層列表,它將影像體積的三維(即寬度、高度和深度)轉換為三維輸出體積。這裡需要注意的一點是,當前層中的每個神經元都連線到前一層輸出的一小塊區域,這就像將 N*N 濾波器疊加到輸入影像上。
它使用 M 個濾波器,這些濾波器基本上是特徵提取器,可以提取邊緣、角點等特徵。以下是用於構建卷積神經網路 (CNN) 的層 **[INPUT-CONV-RELU-POOL-FC]**:
**INPUT** -顧名思義,此層儲存原始畫素值。原始畫素值表示影像資料的本來面目。例如,INPUT [64×64×3] 是一個寬度為 64、高度為 64、深度為 3 的 3 通道 RGB 影像。
**CONV** - 此層是 CNN 的構建塊之一,因為大部分計算都在此層中完成。例如 - 如果我們在上述 INPUT [64×64×3] 上使用 6 個濾波器,這可能會產生體積 [64×64×6]。
**RELU** - 也稱為整流線性單元層,它將啟用函式應用於前一層的輸出。換句話說,RELU 會向網路新增非線性。
**POOL** - 此層,即池化層是 CNN 的另一個構建塊。此層的 主要任務是下采樣,這意味著它獨立地對輸入的每個切片進行操作並對其進行空間調整大小。
**FC** - 它稱為全連線層,更具體地說,它是輸出層。它用於計算輸出類別分數,生成的輸出是大小為 1*1*L 的體積,其中 L 是對應於類別分數的數字。
下圖顯示了 CNN 的典型架構:
建立 CNN 結構
我們已經看到了 CNN 的架構和基礎知識,現在我們將使用 CNTK 來構建卷積網路。在這裡,我們將首先看到如何組合 CNN 的結構,然後我們將研究如何訓練它的引數。
最後,我們將看到如何透過使用各種不同的層設定來更改其結構來改進神經網路。我們將使用 MNIST 影像資料集。
因此,首先讓我們建立一個 CNN 結構。通常,當我們構建 CNN 來識別影像中的模式時,我們會執行以下操作:
我們使用卷積層和池化層的組合。
網路末尾的一個或多個隱藏層。
最後,我們使用 softmax 層完成網路以進行分類。
藉助以下步驟,我們可以構建網路結構:
**步驟 1** - 首先,我們需要匯入 CNN 所需的層。
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
**步驟 2** - 接下來,我們需要匯入 CNN 的啟用函式。
from cntk.ops import log_softmax, relu
**步驟 3** - 之後,為了稍後初始化卷積層,我們需要匯入 **glorot_uniform_initializer**,如下所示:
from cntk.initializer import glorot_uniform
**步驟 4** - 接下來,要建立輸入變數,請匯入 **input_variable** 函式。並匯入 **default_option** 函式,以使 NN 的配置更容易一些。
from cntk import input_variable, default_options
**步驟 5** - 現在,要儲存輸入影像,請建立一個新的 **input_variable**。它將包含三個通道,即紅色、綠色和藍色。它的尺寸將為 28 x 28 畫素。
features = input_variable((3,28,28))
**步驟 6** - 接下來,我們需要建立另一個 **input_variable** 來儲存要預測的標籤。
labels = input_variable(10)
**步驟 7** - 現在,我們需要為 NN 建立 **default_option**。並且,我們需要使用 **glorot_uniform** 作為初始化函式。
with default_options(initialization=glorot_uniform, activation=relu):
**步驟 8** - 接下來,為了設定 NN 的結構,我們需要建立一個新的 **Sequential** 層集。
**步驟 9** - 現在我們需要新增一個 **Convolutional2D** 層,其 **filter_shape** 為 5,**strides** 設定為 **1**,在 **Sequential** 層集中。此外,啟用填充,以便填充影像以保留原始尺寸。
model = Sequential([ Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
**步驟 10** - 現在是時候新增一個 **MaxPooling** 層,其 **filter_shape** 為 2,**strides** 設定為 2,以將影像壓縮一半。
MaxPooling(filter_shape=(2,2), strides=(2,2)),
**步驟 11** - 現在,就像我們在步驟 9 中所做的那樣,我們需要新增另一個 **Convolutional2D** 層,其 **filter_shape** 為 5,**strides** 設定為 1,使用 16 個濾波器。此外,啟用填充,以便保留前一層池化層生成的影像的大小。
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
**步驟 12** - 現在,就像我們在步驟 10 中所做的那樣,新增另一個 **MaxPooling** 層,其 **filter_shape** 為 3,**strides** 設定為 3,以將影像縮小到三分之一。
MaxPooling(filter_shape=(3,3), strides=(3,3)),
**步驟 13** - 最後,為 10 個可能的類別新增一個具有十個神經元的密集層,網路可以預測。為了將網路變成分類模型,請使用 **log_siftmax** 啟用函式。
Dense(10, activation=log_softmax) ])
建立 CNN 結構的完整示例
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling from cntk.ops import log_softmax, relu from cntk.initializer import glorot_uniform from cntk import input_variable, default_options features = input_variable((3,28,28)) labels = input_variable(10) with default_options(initialization=glorot_uniform, activation=relu): model = Sequential([ Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True), MaxPooling(filter_shape=(2,2), strides=(2,2)), Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True), MaxPooling(filter_shape=(3,3), strides=(3,3)), Dense(10, activation=log_softmax) ]) z = model(features)
使用影像訓練 CNN
由於我們已經建立了網路的結構,因此是時候訓練網路了。但在開始訓練我們的網路之前,我們需要設定小批次源,因為訓練使用影像的神經網路所需的記憶體比大多數計算機擁有的記憶體要多。
我們已經在前面的章節中建立了小批次源。以下是設定兩個小批次源的 Python 程式碼:
由於我們已經有了 **create_datasource** 函式,我們現在可以建立兩個單獨的資料來源(一個用於訓練,一個用於測試)來訓練模型。
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
現在,我們已經準備好了影像,我們可以開始訓練我們的神經網路了。就像我們在前面的章節中所做的那樣,我們可以對損失函式使用 train 方法來啟動訓練。以下是對此的程式碼:
from cntk import Function from cntk.losses import cross_entropy_with_softmax from cntk.metrics import classification_error from cntk.learners import sgd @Function def criterion_factory(output, targets): loss = cross_entropy_with_softmax(output, targets) metric = classification_error(output, targets) return loss, metric loss = criterion_factory(z, labels) learner = sgd(z.parameters, lr=0.2)
藉助之前的程式碼,我們已經為神經網路設定了損失和學習器。以下程式碼將訓練和驗證神經網路:
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
features: train_datasource.streams.features,
labels: train_datasource.streams.labels
}
loss.train(train_datasource,
max_epochs=10,
minibatch_size=64,
epoch_size=60000,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer, test_config])
完整的實現示例
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
from cntk.ops import log_softmax, relu
from cntk.initializer import glorot_uniform
from cntk import input_variable, default_options
features = input_variable((3,28,28))
labels = input_variable(10)
with default_options(initialization=glorot_uniform, activation=relu):
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Dense(10, activation=log_softmax)
])
z = model(features)
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
mapping_file = os.path.join(folder, 'mapping.bin')
image_transforms = []
if train:
image_transforms += [
xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
stream_definitions = StreamDefs(
features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
from cntk import Function
from cntk.losses import cross_entropy_with_softmax
from cntk.metrics import classification_error
from cntk.learners import sgd
@Function
def criterion_factory(output, targets):
loss = cross_entropy_with_softmax(output, targets)
metric = classification_error(output, targets)
return loss, metric
loss = criterion_factory(z, labels)
learner = sgd(z.parameters, lr=0.2)
from cntk.logging import ProgressPrinter
from cntk.train import TestConfig
progress_writer = ProgressPrinter(0)
test_config = TestConfig(test_datasource)
input_map = {
features: train_datasource.streams.features,
labels: train_datasource.streams.labels
}
loss.train(train_datasource,
max_epochs=10,
minibatch_size=64,
epoch_size=60000,
parameter_learners=[learner],
model_inputs_to_streams=input_map,
callbacks=[progress_writer, test_config])
輸出
------------------------------------------------------------------- average since average since examples loss last metric last ------------------------------------------------------ Learning rate per minibatch: 0.2 142 142 0.922 0.922 64 1.35e+06 1.51e+07 0.896 0.883 192 [………]
影像變換
正如我們所看到的,訓練用於影像識別的神經網路很困難,而且它們也需要大量資料進行訓練。另一個問題是,它們傾向於過度擬合訓練期間使用的影像。讓我們來看一個例子,當我們擁有直立位置的人臉照片時,我們的模型將很難識別以其他方向旋轉的人臉。
為了克服這個問題,我們可以使用影像增強,並且 CNTK 在為影像建立小批次源時支援特定的轉換。我們可以使用以下幾種轉換:
我們可以使用幾行程式碼隨機裁剪用於訓練的影像。
我們也可以使用縮放和顏色。
讓我們藉助以下 Python 程式碼,看看如何透過在前面用於建立小批次源的函式中包含裁剪轉換來更改轉換列表。
import os
from cntk.io import MinibatchSource, StreamDef, StreamDefs, ImageDeserializer, INFINITELY_REPEAT
import cntk.io.transforms as xforms
def create_datasource(folder, train=True, max_sweeps=INFINITELY_REPEAT):
mapping_file = os.path.join(folder, 'mapping.bin')
image_transforms = []
if train:
image_transforms += [
xforms.crop(crop_type='randomside', side_ratio=0.8),
xforms.scale(width=28, height=28, channels=3, interpolations='linear')
]
stream_definitions = StreamDefs(
features=StreamDef(field='image', transforms=image_transforms),
labels=StreamDef(field='label', shape=10)
)
deserializer = ImageDeserializer(mapping_file, stream_definitions)
return MinibatchSource(deserializer, max_sweeps=max_sweeps)
藉助上述程式碼,我們可以增強該函式以包含一組影像轉換,以便在進行訓練時,我們可以隨機裁剪影像,從而獲得更多影像變化。