CNTK - сверточная нейронная сеть
В этой главе давайте изучим, как построить сверточную нейронную сеть (CNN) в CNTK.
Введение
Сверточные нейронные сети (CNN) также состоят из нейронов, у которых есть обучаемые веса и смещения. Вот почему в этом смысле они похожи на обычные нейронные сети (NN).
Если мы вспомним работу обычных NN, каждый нейрон получает один или несколько входов, принимает взвешенную сумму и проходит через функцию активации для получения окончательного результата. Здесь возникает вопрос: если CNN и обычные NN имеют столько общего, то что отличает эти две сети друг от друга?
Что отличает их от других - это обработка входных данных и типы слоев? Структура входных данных игнорируется в обычном NN, и все данные преобразуются в одномерный массив перед подачей в сеть.
Но архитектура сверточной нейронной сети может учитывать двухмерную структуру изображений, обрабатывать их и позволять извлекать свойства, характерные для изображений. Более того, у CNN есть то преимущество, что у них есть один или несколько сверточных слоев и слой объединения, которые являются основными строительными блоками CNN.
За этими слоями следует один или несколько полностью связанных слоев, как в стандартных многослойных сетевых сетях. Итак, мы можем рассматривать CNN как частный случай полностью связанных сетей.
Архитектура сверточной нейронной сети (CNN)
Архитектура CNN - это в основном список слоев, который преобразует трехмерный, то есть ширину, высоту и глубину объема изображения в трехмерный выходной объем. Здесь следует отметить один важный момент: каждый нейрон в текущем слое подключен к небольшому участку вывода из предыдущего слоя, что похоже на наложение фильтра N * N на входное изображение.
Он использует фильтры M, которые в основном являются экстракторами функций, которые извлекают такие элементы, как края, угол и т. Д. Ниже приведены слои [INPUT-CONV-RELU-POOL-FC] которые используются для построения сверточных нейронных сетей (CNN) -
INPUT- Как следует из названия, этот слой содержит необработанные значения пикселей. Необработанные значения пикселей означают данные изображения как таковые. Например, INPUT [64 × 64 × 3] представляет собой трехканальное изображение RGB шириной 64, высотой 64 и глубиной 3.
CONV- Этот уровень является одним из строительных блоков CNN, поскольку большая часть вычислений выполняется на этом уровне. Пример - если мы используем 6 фильтров на вышеупомянутом INPUT [64 × 64 × 3], это может привести к объему [64 × 64 × 6].
RELU- Также называется выпрямленным линейным единичным слоем, который применяет функцию активации к выходным данным предыдущего слоя. В противном случае RELU добавит к сети нелинейность.
POOL- Этот уровень, то есть уровень объединения, является еще одним строительным блоком CNN. Основная задача этого уровня - понижающая дискретизация, что означает, что он работает независимо с каждым фрагментом ввода и изменяет его размер в пространстве.
FC- Это называется полностью подключенным слоем или, точнее, выходным слоем. Он используется для вычисления оценки выходного класса, и в результате получается объем размером 1 * 1 * L, где L - число, соответствующее оценке класса.
На диаграмме ниже представлена типичная архитектура CNN:
Создание структуры CNN
Мы увидели архитектуру и основы CNN, теперь мы собираемся построить сверточную сеть с использованием CNTK. Здесь мы сначала увидим, как собрать структуру CNN, а затем посмотрим, как обучить ее параметры.
Наконец, мы увидим, как мы можем улучшить нейронную сеть, изменив ее структуру с помощью различных настроек слоев. Мы собираемся использовать набор данных изображений MNIST.
Итак, сначала давайте создадим структуру CNN. Обычно, когда мы создаем CNN для распознавания шаблонов в изображениях, мы делаем следующее:
Мы используем комбинацию слоев свертки и объединения.
Один или несколько скрытых слоев в конце сети.
Наконец, мы завершаем сеть слоем softmax для целей классификации.
С помощью следующих шагов мы можем построить структуру сети:
Step 1- Во-первых, нам нужно импортировать необходимые слои для CNN.
from cntk.layers import Convolution2D, Sequential, Dense, MaxPooling
Step 2- Далее нам нужно импортировать функции активации для CNN.
from cntk.ops import log_softmax, relu
Step 3- После этого, чтобы позже инициализировать сверточные слои, нам нужно импортировать glorot_uniform_initializer следующим образом -
from cntk.initializer import glorot_uniform
Step 4- Затем, чтобы создать входные переменные, импортируйте input_variableфункция. И импортdefault_option функция, чтобы немного упростить настройку NN.
from cntk import input_variable, default_options
Step 5- Теперь, чтобы сохранить входные изображения, создайте новый input_variable. Он будет содержать три канала: красный, зеленый и синий. У него будет размер 28 на 28 пикселей.
features = input_variable((3,28,28))
Step 6−Далее нам нужно создать еще один input_variable хранить метки для прогнозов.
labels = input_variable(10)
Step 7- Теперь нам нужно создать default_optionдля NN. И нам нужно использоватьglorot_uniform в качестве функции инициализации.
with default_options(initialization=glorot_uniform, activation=relu):
Step 8- Далее, чтобы задать структуру NN, нам нужно создать новый Sequential набор слоев.
Step 9- Теперь нам нужно добавить Convolutional2D слой с filter_shape из 5 и strides установка 1в рамках Sequentialнабор слоев. Кроме того, включите заполнение, чтобы изображение было заполнено, чтобы сохранить исходные размеры.
model = Sequential([
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=8, pad=True),
Step 10- Теперь пора добавить MaxPooling слой с filter_shape из 2, а strides установка 2 для сжатия изображения вдвое.
MaxPooling(filter_shape=(2,2), strides=(2,2)),
Step 11- Теперь, как мы делали на шаге 9, нам нужно добавить еще один Convolutional2D слой с filter_shape из 5 и stridesустановка 1, используйте 16 фильтров. Кроме того, включите заполнение, чтобы сохранить размер изображения, созданного предыдущим слоем объединения.
Convolution2D(filter_shape=(5,5), strides=(1,1), num_filters=16, pad=True),
Step 12- Теперь, как в шаге 10, добавьте еще MaxPooling слой с filter_shape из 3 и strides установка 3 для уменьшения изображения до трети.
MaxPooling(filter_shape=(3,3), strides=(3,3)),
Step 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)
Теперь, когда мы подготовили изображения, мы можем приступить к обучению нашей 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, используемую для распознавания изображений, и им также требуется много данных для обучения. Еще одна проблема заключается в том, что они имеют тенденцию чрезмерно соответствовать изображениям, используемым во время обучения. Давайте посмотрим на примере, когда у нас есть фотографии лиц в вертикальном положении, нашей модели будет трудно распознать лица, повернутые в другом направлении.
Чтобы преодолеть такую проблему, мы можем использовать увеличение изображения, а 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)
С помощью приведенного выше кода мы можем улучшить функцию, включив в нее набор преобразований изображения, чтобы при обучении мы могли произвольно обрезать изображение, чтобы получить больше вариантов изображения.