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)

С помощью приведенного выше кода мы можем улучшить функцию, включив в нее набор преобразований изображения, чтобы при обучении мы могли произвольно обрезать изображение, чтобы получить больше вариантов изображения.