Практические занятия по распознаванию изображений
Обратите внимание, что для работы с этим руководством требуется последняя главная версия или предстоящий выпуск CNTK 1.7. Промежуточное двоичное скачивание можно найти в инструкциях для руководства по CNTK Hands-On KDD, для работы с которым изначально предназначено это руководство.
Hands-On лаборатории: распознавание изображений с помощью сверточных сетей, нормализации пакетной службы и остаточных сетей
В этой практической лаборатории показано, как реализовать распознавание изображений на основе сверток с помощью CNTK. Мы начнем с общей сверточной архитектуры распознавания изображений, добавим пакетную нормализацию, а затем расширим ее в остаточной сети (ResNet-20).
К методам, которые вы будете применять, относятся:
- изменение определения сети CNTK для добавления предопределенной операции (раскрывающегося списка)
- создание определяемых пользователем функций для извлечения повторяющихся частей в сети в многократно используемый модуль
- реализация пользовательских сетевых структур (пропустить подключение ResNet)
- создание нескольких слоев одновременно с помощью рекурсивных циклов
- параллельное обучение
- сверточная сеть
- пакетная нормализация
Предварительные требования
Предполагается, что вы уже установили CNTK и можете выполнить команду CNTK. Это руководство было проведено в KDD 2016 и требует недавней сборки, см. здесь, чтобы узнать инструкции по настройке. Вы можете просто следовать инструкциям по скачиванию пакета двоичной установки с этой страницы. Для задач, связанных с изображениями, это следует сделать на компьютере с gpu, совместимым с CUDA.
Затем скачайте ZIP-архив (около 12 МБ): щелкните эту ссылку и нажмите кнопку "Скачать".
Архив содержит файлы для этого руководства. Заархивируйте и задайте для рабочего каталога значение ImageHandsOn
.
Вы будете работать со следующими файлами:
ImageHandsOn.cntk
: файл конфигурации CNTK, с которым мы познакомимся ниже и будем работать.cifar10.pretrained.cmf
: результирующая модель конфигурации, с которого мы начнем.cifar10.ResNet.cmf
: полученная модель версии ResNet, которую мы создадим ниже.
Наконец, необходимо скачать и преобразовать набор данных CIFAR-10. Шаг преобразования займет около 10 минут. Выполните следующие два скрипта Python, которые вы также найдете в рабочем каталоге:
wget -rc http://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
tar xvf www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
python CifarConverter.py cifar-10-batches-py
Это преобразует изображения в PNG-файлы, 50000 для обучения и 10000 для тестирования, которые будут помещены в следующие два каталога соответственно: cifar-10-batches-py/data/train
и cifar-10-batches-py/data/test
.
Структура модели
Мы начнем с этого руководства с простой сверточная модель. Он состоит из 3 слоев из 5x5 свертки + нелинейности + 2x уменьшения измерения на 3x3 max-pooling, которые затем следуют плотный скрытый слой и плотное преобразование для формирования входных данных в 10-кратный классификатор softmax.
Или, как CNTK описание сети. Ознакомьтесь с кратким представлением и составьте его с описанием выше:
featNorm = features - Constant (128)
l1 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
l2 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride = (2:2)} (l2)
l3 = ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p2)
p3 = MaxPoolingLayer {(3:3), stride = (2:2)} (l3)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1)
Дополнительные сведения об этих операторах см. здесь: ConvolutionalLayer{}
, , DenseLayer{}
MaxPoolingLayer{}
. LinearLayer{}
Конфигурация CNTK
Файл конфигурации
Чтобы обучить и проверить модель в CNTK, необходимо предоставить файл конфигурации, который сообщает CNTK, какие операции требуется выполнить (command
переменная) и раздел параметров для каждой команды.
Для команды обучения необходимо указать CNTK:
- чтение данных (
reader
раздел) - функция модели и ее входные и выходные данные в графе вычислений (
BrainScriptNetworkBuilder
раздел) - hyper-parameters для учащегося (
SGD
раздел)
Для команды оценки CNTK необходимо знать:
- чтение тестовых данных (
reader
раздел) - какие метрики для оценки (
evalNodeNames
параметр)
Ниже приведен файл конфигурации, с который мы начнем. Как видите, файл конфигурации CNTK — это текстовый файл, состоящий из определений параметров, которые упорядочены в иерархии записей. Вы также можете увидеть, как CNTK поддерживает простую замену параметров с помощью синтаксиса$parameterName$
. Фактический файл содержит всего несколько параметров, чем упоминалось выше, но проверьте его и найдите только что упомянутые элементы конфигурации:
# CNTK Configuration File for training a simple CIFAR-10 convnet.
# During the hands-on tutorial, this will be fleshed out into a ResNet-20 model.
command = TrainConvNet:Eval
makeMode = false ; traceLevel = 0 ; deviceId = "auto"
rootDir = "." ; dataDir = "$rootDir$" ; modelDir = "$rootDir$/Models"
modelPath = "$modelDir$/cifar10.cmf"
# Training action for a convolutional network
TrainConvNet = {
action = "train"
BrainScriptNetworkBuilder = {
imageShape = 32:32:3
labelDim = 10
model (features) = {
featNorm = features - Constant (128)
l1 = ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride=(2:2)} (l1)
l2 = ConvolutionalLayer {32, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride=(2:2)} (l2)
l3 = ConvolutionalLayer {64, (5:5), pad=true, activation=ReLU,
init="gaussian", initValueScale=1.414} (p2)
p3 = MaxPoolingLayer {(3:3), stride=(2:2)} (l3)
d1 = DenseLayer {64, activation=ReLU, init="gaussian", initValueScale=12} (p3)
z = LinearLayer {10, init="gaussian", initValueScale=1.5} (d1)
}.z
# inputs
features = Input {imageShape}
labels = Input {labelDim}
# apply model to features
z = model (features)
# connect to system
ce = CrossEntropyWithSoftmax (labels, z)
errs = ErrorPrediction (labels, z)
featureNodes = (features)
labelNodes = (labels)
criterionNodes = (ce)
evaluationNodes = (errs)
outputNodes = (z)
}
SGD = {
epochSize = 50000
maxEpochs = 30 ; minibatchSize = 64
learningRatesPerSample = 0.00015625*10:0.000046875*10:0.000015625
momentumAsTimeConstant = 600*20:6400
L2RegWeight = 0.03
firstMBsToShowResult = 10 ; numMBsToShowResult = 100
}
reader = {
verbosity = 0 ; randomize = true
deserializers = ({
type = "ImageDeserializer" ; module = "ImageReader"
file = "$dataDir$/cifar-10-batches-py/train_map.txt"
input = {
features = { transforms = (
{ type = "Crop" ; cropType = "RandomSide" ; sideRatio = 0.8 ; jitterType = "UniRatio" } :
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
}
})
}
}
# Eval action
Eval = {
action = "eval"
minibatchSize = 16
evalNodeNames = errs
reader = {
verbosity = 0 ; randomize = true
deserializers = ({
type = "ImageDeserializer" ; module = "ImageReader"
file = "$dataDir$/cifar-10-batches-py/test_map.txt"
input = {
features = { transforms = (
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
}
})
}
}
Чтение данных и данных
После загрузки данных CIFAR-10 и запуска скрипта CifarConverter.py
, как описано в начале этого руководства, вы найдете каталог с именем cifar-10-batches-py/data
, который содержит два подкаталога и train
test
, полный PNG-файлов.
В CNTK ImageDeserializer
используются стандартные форматы изображений.
Вы также найдете два файла train_map.txt
и test_map.txt
. Глядя на последнее,
% more cifar-10-batches-py/test_map.txt
cifar-10-batches-py/data/test/00000.png 3
cifar-10-batches-py/data/test/00001.png 8
cifar-10-batches-py/data/test/00002.png 8
...
Оба файла состоят из двух столбцов, где первый содержит путь к файлу изображения, а второй — метку класса в виде числового индекса. Эти столбцы соответствуют входным features
данным средства чтения и labels
которые были определены следующим образом:
features = { transforms = (
{ type = "Crop" ; cropType = "RandomSide" ; sideRatio = 0.8 ; jitterType = "UniRatio" } :
{ type = "Scale" ; width = 32 ; height = 32 ; channels = 3 ; interpolations = "linear" } :
{ type = "Transpose" }
)}
labels = { labelDim = 10 }
В дополнительном transforms
разделе описывается ImageDeserializer
применение последовательности (общих) преобразований к изображениям по мере их чтения.
Дополнительные сведения см. здесь.
Запуск
Приведенный выше файл конфигурации можно найти под именем ImageHandsOn.cntk
в рабочей папке.
Чтобы запустить его, выполните указанную выше конфигурацию с помощью следующей команды:
cntk configFile=ImageHandsOn.cntk
Ваш экран оживится с шквалом сообщений журнала (CNTK может быть разговорчивым в разы), но если все пошло правильно, вы скоро увидите следующее:
Training 116906 parameters in 10 out of 10 parameter tensors and 28 nodes with gradient
затем следуют выходные данные, как показано ниже:
Finished Epoch[ 1 of 10]: [Training] ce = 1.66950797 * 50000; errs = 61.228% * 50000
Finished Epoch[ 2 of 10]: [Training] ce = 1.32699016 * 50000; errs = 47.394% * 50000
Finished Epoch[ 3 of 10]: [Training] ce = 1.17140398 * 50000; errs = 41.168% * 50000
Это говорит вам, что это обучение. Каждая эпоха представляет один проход через 50000 обучающих изображений.
Он также сообщает вам, что после второй эпохи критерий обучения, который называется ce
конфигурацией, достиг 1,33, как измеряется на 50000 выборок этой эпохи, и что частота ошибок составляет 47 % для тех же 50000 образцов обучения.
Обратите внимание, что компьютеры с ЦП работают примерно в 20 раз медленнее. Это займет много минут, пока вы даже не увидите первые выходные данные журнала. Чтобы убедиться, что система выполняется, можно включить трассировку, чтобы увидеть частичные результаты, которые должны отображаться достаточно быстро:
cntk configFile=ImageHandsOn.cntk traceLevel=1
Epoch[ 1 of 10]-Minibatch[-498- 1, 0.13%]: ce = 2.30260658 * 64; errs = 90.625% * 64
...
Epoch[ 1 of 10]-Minibatch[ 1- 100, 12.80%]: ce = 2.10434176 * 5760; errs = 78.472% * 5760
Epoch[ 1 of 10]-Minibatch[ 101- 200, 25.60%]: ce = 1.82372971 * 6400; errs = 68.172% * 6400
Epoch[ 1 of 10]-Minibatch[ 201- 300, 38.40%]: ce = 1.69708496 * 6400; errs = 62.469% * 6400
После завершения обучения (что занимает около 3 минут на Surface Book и на настольном компьютере с GPU Titan-X), окончательное сообщение будет выглядеть следующим образом:
Finished Epoch[10 of 10]: [Training] ce = 0.74679766 * 50000; errs = 25.486% * 50000
показывает, что сеть успешно сократила ce
потерю и достигла ошибки классификации на 25,5 % в обучаемом наборе. Так как наша command
переменная указывает вторую командуEval
, CNTK будет продолжать это действие. Она измеряет частоту ошибок классификации на 10000 изображений тестового набора.
Final Results: Minibatch[1-625]: errs = 24.180% * 10000
Частота ошибок тестирования близка к обучению. Так как CIFAR-10 является довольно небольшим набором данных, это показатель того, что наша модель еще не сходилась полностью (и на самом деле запуск ее в течение 30 эпох позволит вам примерно 20%).
Если вы не хотите ждать завершения этого действия, можно запустить промежуточную модель, например.
cntk configFile=ImageHandsOn.cntk command=Eval modelPath=Models/cifar10.cmf.5
Final Results: Minibatch[1-625]: errs = 31.710% * 10000
или запустите предварительно обученную модель, а также:
cntk configFile=ImageHandsOn.cntk command=Eval modelPath=cifar10.pretrained.cmf
Final Results: Minibatch[1-625]: errs = 24.180% * 10000
Изменение модели
В следующем примере вам будут предоставлены задачи для практики изменения конфигураций CNTK. Решения приведены в конце этого документа... но пожалуйста, попробуйте без!
Задача 1. Добавление раскрывающегося списка
Распространенной методикой повышения универсальности моделей является удаление. Чтобы добавить раскрывающийся список в модель CNTK, вам потребуется
- добавьте вызов функции CNTK
Dropout()
, в которую требуется вставить операцию удаления. - добавьте параметр
dropoutRate
в раздел,SGD
вызываемый для определения вероятности выпада
В этой конкретной задаче не укажите раскрывающийся список для первой 1 эпохи, за которым следует коэффициент выпадения в 50 %.
Ознакомьтесь с Dropout()
документацией, чтобы узнать, как это сделать.
Если все прошло хорошо, вы не будете наблюдать никаких изменений в течение первой 1 эпохи, но гораздо меньшее улучшение ce
один раз выпадения ударов с второй эпохой. Это ожидаемое поведение. (Для этой конкретной конфигурации точность распознавания на самом деле не улучшается.) Конечный результат при обучении только 10 эпох составляет около 32 %.
Для раскрывающегося списка недостаточно 10 эпох.
Ознакомьтесь с решением здесь.
Задача 2. Упрощение определения модели путем извлечения повторяющихся частей в функцию
В этом примере последовательность (объединение >> повторного пула) >> повторяется три раза. Ваша задача — написать функцию BrainScript, которая объединяет эти три операции в многократно используемый модуль. Обратите внимание, что все три варианта этой последовательности используют разные параметры (измерения вывода, вес инициализации). Таким образом, функция, написаемая, должна принимать эти два в качестве параметров в дополнение к входным данным. Например, его можно определить как
MyLayer (x, depth, initValueScale)
При выполнении этого действия вы ожидаете, что результирующий ce
результат и errs
значения будут одинаковыми.
Однако при запуске на GPU недетерминированность в реализации обратного распространения cuDNN приведет к незначительным изменениям.
См . здесь решение.
Задача 3. Добавление BatchNormalization
(Для этой задачи требуется GPU, так как реализация пакетной нормализации CNTK основана на cuDNN.)
Пакетная нормализация — это популярный способ ускорения и улучшения конвергенции.
В CNTK пакетная нормализация реализуется как BatchNormalizationLayer{}
.
Пространственная форма (где все позиции пикселей нормализованы с общими параметрами) вызывается необязательным параметром: BatchNormalizationLayer{spatialRank=2}
Добавьте пакетную нормализацию ко всем трем слоям свертки и между двумя плотными слоями.
Обратите внимание, что пакетная нормализация должна быть вставлена непосредственно перед нелинейной.
Поэтому необходимо удалить activation
параметр и вместо этого вставить явные вызовы функции ReLU()
CNTK.
Кроме того, пакетная нормализация изменяет скорость конвергенции. Таким образом, давайте увеличим темпы обучения для первых 7 эпох 3 раза, и отключите импульс и нормализацию L2, задав их параметры в значение 0.
При выполнении вы увидите дополнительные обучаемые параметры, перечисленные в журнале обучения. Окончательный результат составит около 28 %, что составляет 4 пункта лучше, чем без пакетной нормализации после того же количества итераций. Конвергенция ускоряется действительно.
См . здесь решение.
Задача 4. Преобразование в остаточную сеть
Приведенная выше конфигурация является примером "toy", чтобы получить руки грязными с запуском и изменением CNTK конфигураций, и мы намеренно не запускали до полной конвергенции, чтобы сохранить время обхода для вас низким. Итак, давайте теперь перейдем к более реальной конфигурации — остаточной сети. Остаточная сетка (https://arxiv.org/pdf/1512.03385v1.pdf) — это измененная глубокая сетевая структура, в которой слои, вместо обучения сопоставлению входных данных с выходными данными, выясним термин коррекции.
(Эта задача также требует GPU для операции пакетной нормализации, хотя при наличии большого количества времени вы можете попробовать запустить его на ЦП, изменив пакетную нормализацию вызовов при некоторой потере точности.)
Чтобы приступить к работе, измените предыдущую конфигурацию. Сначала замените функцию model(features)
модели следующим:
MySubSampleBN (x, depth, stride) =
{
s = Splice ((MaxPoolingLayer {(1:1), stride = (stride:stride)} (x) : ConstantTensor (0, (1:1:depth/stride))), axis = 3) # sub-sample and pad: [W x H x depth/2] --> [W/2 x H/2 x depth]
b = BatchNormalizationLayer {spatialRank = 2, normalizationTimeConstant = 4096} (s)
}.b
MyConvBN (x, depth, initValueScale, stride) =
{
c = ConvolutionalLayer {depth, (3:3), pad = true, stride = (stride:stride), bias = false,
init = "gaussian", initValueScale = initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2, normalizationTimeConstant = 4096} (c)
}.b
ResNetNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 1)
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (c2)
}.r
ResNetIncNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 2) # note the 2
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (c2)
}.r
model (features) =
{
conv1 = ReLU (MyConvBN (features, 16, 0.26, 1))
rn1 = ResNetNode (ResNetNode (ResNetNode (conv1, 16), 16), 16)
rn2_1 = ResNetIncNode (rn1, 32)
rn2 = ResNetNode (ResNetNode (rn2_1, 32), 32)
rn3_1 = ResNetIncNode (rn2, 64)
rn3 = ResNetNode (ResNetNode (rn3_1, 64), 64)
pool = AveragePoolingLayer {(8:8)} (rn3)
z = LinearLayer {labelDim, init = "gaussian", initValueScale = 0.4} (pool)
}.z
и измените конфигурацию УПРАВЛЕНИЯ НА:
SGD = {
epochSize = 50000
maxEpochs = 160 ; minibatchSize = 128
learningRatesPerSample = 0.0078125*80:0.00078125*40:0.000078125
momentumAsTimeConstant = 1200
L2RegWeight = 0.0001
firstMBsToShowResult = 10 ; numMBsToShowResult = 500
}
Задача состоит в том, чтобы изменить ResNetNode()
и ResNetNodeInc()
реализовать структуру, изложенную в следующем наградном искусстве ASCII:
ResNetNode ResNetNodeInc
| |
+------+------+ +---------+----------+
| | | |
V | V V
+----------+ | +--------------+ +----------------+
| Conv, BN | | | Conv x 2, BN | | SubSample, BN |
+----------+ | +--------------+ +----------------+
| | | |
V | V |
+-------+ | +-------+ |
| ReLU | | | ReLU | |
+-------+ | +-------+ |
| | | |
V | V |
+----------+ | +----------+ |
| Conv, BN | | | Conv, BN | |
+----------+ | +----------+ |
| | | |
| +---+ | | +---+ |
+--->| + |<---+ +------>+ + +<-------+
+---+ +---+
| |
V V
+-------+ +-------+
| ReLU | | ReLU |
+-------+ +-------+
| |
V V
Подтвердите выходные данные проверки в журнале, что вы сделали правильно.
Выполнение этого процесса займет много времени. Ожидаемые выходные данные будут выглядеть примерно так:
Finished Epoch[ 1 of 160]: [Training] ce = 1.57037109 * 50000; errs = 58.940% * 50000
Finished Epoch[ 2 of 160]: [Training] ce = 1.06968234 * 50000; errs = 38.166% * 50000
Finished Epoch[ 3 of 160]: [Training] ce = 0.85858969 * 50000; errs = 30.316% * 50000
в то время как неправильная модель без пропускных подключений выглядит следующим образом:
Finished Epoch[ 1 of 160]: [Training] ce = 1.72901219 * 50000; errs = 66.232% * 50000
Finished Epoch[ 2 of 160]: [Training] ce = 1.30180430 * 50000; errs = 47.424% * 50000
Finished Epoch[ 3 of 160]: [Training] ce = 1.04641961 * 50000; errs = 37.568% * 50000
См . здесь решение.
Задача 5. Автоматическое создание множества слоев
Наконец, для оптимальной производительности ResNet имеется 152 слоя. Так как это было бы очень мученно и подвержено ошибкам для записи 152 отдельных выражений, теперь мы изменим определение, чтобы автоматически создать стек ResNetNode()
.
Ваша задача — написать функцию с этой подписью:
ResNetNodeStack (x, depth, L)
где L
обозначает, ResNetNodes
сколько должно быть сложено, чтобы можно было заменить выражение выше rn1
параметризованным вызовом:
rn1 = ResNetNodeStack (conv1, 16, 3) # 3 means 3 such nodes
и аналогично для rn2
и rn3
.
Необходимые средства — это условное выражение:
z = if cond then x else y
и рекурсия.
Это обучение будет работать около половины нашего на Титан-X. Если вы сделали это правильно, рано в журнале будет содержаться следующее сообщение:
Training 200410 parameters in 51 out of 51 parameter tensors and 112 nodes with gradient:
Для справки мы рассмотрим предварительно обученную версию этой модели. Вы можете измерить частоту ошибок с помощью следующей команды:
cntk configFile=ImageHandsOn.ResNet.cntk command=Eval
и должен увидеть результат, как показано ниже:
Final Results: Minibatch[1-625]: errs = 8.400% * 10000; top5Errs = 0.290% * 10000
Эта частота ошибок очень близка к приведенной в исходном документе ResNet (https://arxiv.org/pdf/1512.03385v1.pdfтаблица 6).
См . здесь решение.
Задача 6. Параллельное обучение
Наконец, если у вас несколько GPU, CNTK позволяет параллелизировать обучение с помощью MPI (интерфейс передачи сообщений). Эта модель слишком мала, чтобы ожидать значительной скорости без дальнейшей настройки, например размер мини-батча (текущий параметр мини-batch-размера слишком мал, чтобы получить полное использование доступных ядер GPU). Тем не менее, позвольте нам пройти через движения, чтобы вы знали, как это сделать, как только вы перейдете к реальным рабочим нагрузкам.
Добавьте в блок следующую SGD
строку:
SGD = {
...
parallelTrain = {
parallelizationMethod = "DataParallelSGD"
parallelizationStartEpoch = 2
distributedMBReading = true
dataParallelSGD = { gradientBits = 1 }
}
}
а затем выполните следующую команду:
mpiexec -np 4 cntk configFile=ImageHandsOn.cntk stderr=Models/log parallelTrain=true
Что дальше?
В этом руководстве описано, как принять существующую конфигурацию и изменить ее определенными способами:
- добавление предопределенной операции (dropout)
- извлечение повторяющихся частей в многократно используемые модули (функции)
- рефакторинг (для вставки пакетной нормализации)
- пользовательские сетевые структуры (подключение resNet skip)
- параметризация повторяющихся структур с помощью рекурсии
и мы видели, как ускорить обучение путем параллелизации.
Так откуда мы идем отсюда? Возможно, вы уже обнаружили, что шаблон, используемый в этих примерах, который мы называем стилем построения графа , может быть довольно подвержен ошибкам. Замечает ошибку?
model (features) =
{
l1 = ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} (featNorm)
p1 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
l2 = ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} (p1)
p2 = MaxPoolingLayer {(3:3), stride = (2:2)} (l1)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p2)
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1)
}.z
Чтобы избежать этой ошибки, следует использовать композицию функций. Ниже приведен альтернативный, более краткий способ написания того же вида:
model = Sequential (
ConvolutionalLayer {32, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 0.0043} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
ConvolutionalLayer {64, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = 1.414} :
MaxPoolingLayer {(3:3), stride = (2:2)} :
DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} :
LinearLayer {10, init = "gaussian", initValueScale = 1.5}
)
Этот стиль будет представлен и использован в следующем практическом руководстве по распознаванию текста с повторяющимися сетями.
Решения
Решение 1. Добавление раскрывающегося списка
Измените определение модели следующим образом:
p3 = MaxPoolingLayer {(3:3), stride = (2:2)} (l3)
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
d1_d = Dropout (d1) ##### added
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1_d) ##### d1 -> d1_d
и раздел "ХУНД":
SGD = {
...
dropoutRate = 0*5:0.5 ##### added
...
}
Решение 2. Упрощение определения модели путем извлечения повторяющихся частей в функцию
Добавьте определение функции следующим образом:
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad = true, activation = ReLU,
init = "gaussian", initValueScale = initValueScale} (x)
p = MaxPoolingLayer {(3:3), stride = (2:2)} (c)
}.p
и обновите определение модели, чтобы использовать его.
featNorm = features - Constant (128)
p1 = MyLayer (featNorm, 32, 0.0043) ##### replaced
p2 = MyLayer (p1, 32, 1.414) ##### replaced
p3 = MyLayer (p2, 64, 1.414) ##### replaced
d1 = DenseLayer {64, activation = ReLU, init = "gaussian", initValueScale = 12} (p3)
Решение 3. Добавление BatchNormalization
Измените MyLayer()
.
MyLayer (x, depth, initValueScale) =
{
c = ConvolutionalLayer {depth, (5:5), pad = true, ##### no activation=ReLU
init = "gaussian", initValueScale = initValueScale} (x)
b = BatchNormalizationLayer {spatialRank = 2} (c)
r = ReLU (b) ##### now called explicitly
p = MaxPoolingLayer {(3:3), stride = (2:2)} (r)
}.p
и используйте его. Также вставьте пакетную нормализацию перед z
:
d1 = DenseLayer {64, init = "gaussian", initValueScale = 12} (p3)
d1_bnr = ReLU (BatchNormalizationLayer {} (d1)) ##### added BN and explicit ReLU
d1_d = Dropout (d1_bnr) ##### d1 -> d1_bnr
z = LinearLayer {10, init = "gaussian", initValueScale = 1.5} (d1_d)
И обновите эти параметры в разделе ПАРАМЕТРОВ, приведенных ниже.
SGD = {
....
learningRatesPerSample = 0.00046875*7:0.00015625*10:0.000046875*10:0.000015625
momentumAsTimeConstant = 0
L2RegWeight = 0
...
}
Решение 4. Преобразование в остаточную сеть
Правильные реализации для ResNetNode()
и ResNetNodeInc()
являются следующими:
ResNetNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 1)
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
r = ReLU (x + c2) ##### added skip connection
}.r
ResNetIncNode (x, depth) =
{
c1 = MyConvBN (x, depth, 7.07, 2) # note the 2
r1 = ReLU (c1)
c2 = MyConvBN (r1, depth, 7.07, 1)
xs = MySubSampleBN (x, depth, 2)
r = ReLU (xs + c2) ##### added skip connection
}.r
Решение 5. Автоматическое создание множества слоев
Это реализация:
ResNetNodeStack (x, depth, L) =
{
r = if L == 0
then x
else ResNetNode (ResNetNodeStack (x, depth, L-1), depth)
}.r
или короче:
ResNetNodeStack (x, depth, L) =
if L == 0
then x
else ResNetNode (ResNetNodeStack (x, depth, L-1), depth)
Кроме того, необходимо изменить функцию модели:
conv1 = ReLU (MyConvBN (features, 16, 0.26, 1))
rn1 = ResNetNodeStack (conv1, 16, 3) ##### replaced
rn2_1 = ResNetIncNode (rn1, 32)
rn2 = ResNetNodeStack (rn2_1, 32, 2) ##### replaced
rn3_1 = ResNetIncNode (rn2, 64)
rn3 = ResNetNodeStack (rn3_1, 64, 2) ##### replaced