Microsoft-cognitive-toolkit-convolution-neural-network
CNTK-畳み込みニューラルネットワーク
この章では、CNTKでたたみ込みニューラルネットワーク(CNN)を構築する方法について説明します。
前書き
畳み込みニューラルネットワーク(CNN)も、学習可能な重みとバイアスを持つニューロンで構成されています。 これが、このように、通常のニューラルネットワーク(NN)のような理由です。
通常のNNの働きを思い出すと、すべてのニューロンは1つ以上の入力を受け取り、加重和を取り、アクティベーション関数を通過して最終的な出力を生成します。 ここで、CNNと通常のNNに非常に多くの類似点がある場合、これら2つのネットワークが互いにどう違うのかという疑問が生じます。
入力データとレイヤーのタイプの扱いが異なる点は何ですか? 入力データの構造は通常のNNでは無視され、すべてのデータは1次元配列に変換されてからネットワークに送られます。
ただし、たたみ込みニューラルネットワークアーキテクチャでは、画像の2D構造を考慮して処理し、画像に固有のプロパティを抽出できます。 さらに、CNNには、CNNの主要な構成要素である1つ以上のたたみ込み層とプーリング層があるという利点があります。
これらの層の後には、標準の多層NNのように、1つ以上の完全に接続された層が続きます。 したがって、完全に接続されたネットワークの特殊なケースとして、CNNを考えることができます。
畳み込みニューラルネットワーク(CNN)アーキテクチャ
CNNのアーキテクチャは基本的に、3次元を変換するレイヤーのリストです。 画像ボリュームの幅、高さ、奥行きを3次元の出力ボリュームに変換します。 ここで注意すべき重要な点の1つは、現在のレイヤーのすべてのニューロンが、前のレイヤーからの出力の小さなパッチに接続されていることです。これは、入力画像にN * Nフィルターをオーバーレイするようなものです。
Mフィルターを使用します。これは、基本的にエッジ、コーナーなどの特徴を抽出する特徴抽出器です。 以下は、畳み込みニューラルネットワーク(CNN)を構築するために使用されるレイヤー [INPUT-CONV-RELU-POOL-FC] です。
- 入力 −名前が示すように、このレイヤーは生のピクセル値を保持します。 生のピクセル値は、画像のデータをそのまま意味します。 例、INPUT [64×64×3]は、幅64、高さ64、奥行き3の3チャネルRGB画像です。
- * CONV * −ほとんどの計算はこのレイヤーで行われるため、このレイヤーはCNNのビルディングブロックの1つです。 例-上記のINPUT [64×64×3]で6つのフィルターを使用すると、ボリュームが[64×64×6]になる場合があります。
- * RELU *-前の層の出力にアクティブ化関数を適用する、修正線形単位層とも呼ばれます。 他の方法では、非線形性がRELUによってネットワークに追加されます。
- * POOL * −このレイヤー、つまり プーリング層は、CNNのもう1つのビルディングブロックです。 このレイヤーの主なタスクはダウンサンプリングです。つまり、入力のスライスごとに独立して動作し、空間的にサイズを変更します。
- * FC −完全に接続されたレイヤー、より具体的には出力レイヤーと呼ばれます。 出力クラススコアを計算するために使用され、結果の出力はサイズ1 1 * _L_のボリュームです。 ここで、Lはクラススコアに対応する数です。
次の図は、CNNの一般的なアーキテクチャを表しています。
CNN構造の作成
CNNのアーキテクチャと基本を見てきましたが、今度はCNTKを使用してたたみ込みネットワークを構築します。 ここでは、最初にCNNの構造をまとめる方法を確認し、次にそのパラメーターをトレーニングする方法を確認します。
最後に、さまざまな異なるレイヤー設定で構造を変更することにより、ニューラルネットワークを改善する方法を説明します。 MNIST画像データセットを使用します。
それでは、最初にCNN構造を作成しましょう。 一般的に、画像のパターンを認識するためのCNNを構築する場合、次のことを行います
- 畳み込み層とプーリング層の組み合わせを使用します。
- ネットワークの終わりにある1つ以上の非表示レイヤー。
- 最後に、分類の目的でネットワークを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 を作成します。 赤、緑、青の3つのチャネルが含まれます。 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- Sequential レイヤーセット内に filter_shape が5で strides 設定が 1 の Convolutional2D レイヤーを追加する必要があります。 また、パディングを有効にして、元の寸法を維持するように画像にパディングを行います。
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
ステップ10-次に、 filter_shape が2の MaxPooling レイヤーを追加し、 strides 設定を2にして、画像を半分に圧縮します。
MaxPooling(filter_shape=(2,2), strides=(2,2)),
ステップ11-次に、ステップ9で行ったように、 filter_shape が5で strides 設定が1である別の Convolutional2D レイヤーを追加する必要があります。16フィルターを使用します。 また、パディングを有効にして、以前のプーリングレイヤーによって生成された画像のサイズを保持する必要があります。
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
ステップ12-次に、ステップ10で行ったように、 filter_shape が3で strides 設定が3の別の MaxPooling レイヤーを追加して、画像を3分の1に減らします。
MaxPooling(filter_shape=(3,3), strides=(3,3)),
ステップ13-最後に、ネットワークが予測できる10の可能なクラスに対して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のトレーニング
ネットワークの構造を作成したので、ネットワークをトレーニングします。 ただし、ネットワークのトレーニングを開始する前に、ミニバッチソースをセットアップする必要があります。これは、画像を処理するNNをトレーニングするには、ほとんどのコンピューターよりも多くのメモリが必要になるためです。
前のセクションですでにミニバッチソースを作成しました。 以下は、2つのミニバッチソースをセットアップするPythonコードです-
*create_datasource* 関数があるので、モデルをトレーニングするために2つの別個のデータソース(トレーニングとテスト1つ)を作成できます。
train_datasource = create_datasource('mnist_train')
test_datasource = create_datasource('mnist_test', max_sweeps=1, train=False)
これで画像を準備できたので、NNのトレーニングを開始できます。 前のセクションで行ったように、損失関数で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)
以前のコードの助けを借りて、NNの損失と学習者を設定しました。 次のコードはNN-
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
[………]
画像変換
これまで見てきたように、画像認識に使用されるNNをトレーニングすることは困難であり、トレーニングにも多くのデータが必要です。 もう1つの問題は、トレーニング中に使用される画像に適合しすぎる傾向があることです。 例を見てみましょう。顔の写真が直立している場合、モデルは別の方向に回転している顔を認識しにくくなります。
このような問題を克服するために、画像のミニバッチソースを作成するときに、画像の拡大を使用でき、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)
上記のコードの助けを借りて、関数を拡張して一連の画像変換を含めることができます。そのため、トレーニングするときに、画像をランダムにトリミングして、画像のバリエーションを増やすことができます。