Полное архитектурное описание сверточных нейросетей
Описав составляющие сверточных нейросетей, начнем сводить их вместе. На рис. 5.14 приведены несколько архитектур, которые могут быть полезны на практике.
Рис. 5.14. Различные архитектуры сверточных нейросетей разного уровня сложности. Архитектура VGGNet, глубокой сверточной сети, разработанной для ImageNet, показана на схеме справа
При построении более глубоких сетей применяется следующий шаблон: мы сокращаем число уровней подвыборки и строим несколько сверточных слоев в тандемах. Обычно это эффективно, потому что операции подвыборки по природе своей деструктивны. Стекирование нескольких сверточных слоев перед каждым слоем подвыборки позволяет достичь большей репрезентативности.
На практике глубокие сверточные сети могут занимать значительное место, и большинство практиков сталкиваются с проблемой бутылочного горлышка: их ограничивает память графических процессоров. Архитектура VGGNet, например, занимает примерно 90 MB на изображение при прямом проходе и более 180 MB при обратном для обновления параметров [65]. Многие глубокие сети реализуют компромисс, используя в первом сверточном слое такие сдвиги и пространственные протяженности, которые сокращают объем информации, необходимый для распространения по сети.
Работа с MNIST с помощью сверточных сетей
Теперь мы лучше знаем, как строить сети для эффективного анализа изображений, и возвращаемся к проблеме MNIST, с которой разбираемся уже несколько глав. Здесь мы используем сверточную сеть для распознавания рукописных цифр. Наша сеть с прямым распространением сигнала сумела добиться точности 98,2%. Сейчас наша цель — превзойти этот результат.
Построим сверточную сеть с самой что ни на есть стандартной архитектурой (по модели второй сети с рис. 5.14): два слоя подвыборки и два сверточных друг за другом, а за ними полносвязный слой (с прореживанием, p = 0,5) и конечный слой с мягким максимумом. Чтобы упростить создание сети, допишем пару вспомогательных методов к генератору слоев из сети с прямым распространением сигнала:
def conv2d(input, weight_shape, bias_shape):
in = weight_shape[0] * weight_shape[1] * weight_shape[2]
weight_init = tf.random_normal_initializer(stddev=(2.0/in)**0.5)
W = tf.get_variable("W", weight_shape, initializer=weight_init)
bias_init = tf.constant_initializer(value=0)
b = tf.get_variable("b", bias_shape, initializer=bias_init)
conv_out = tf.nn.conv2d(input, W, strides=[1, 1, 1, 1], padding='SAME')
return tf.nn.relu(tf.nn.bias_add(conv_out, b))
def max_pool(input, k=2):
return tf.nn.max_pool(input, ksize=[1, k, k, 1], strides=[1, k, k, 1], padding='SAME')
Первый вспомогательный метод генерирует сверточный слой конкретной формы. Мы задаем значение 1 для сдвига и дополнение, чтобы ширина и высота входного и выходного тензоров совпадали. Мы также инициализируем веса при помощи той же эвристики, что применялась в сети с прямым распространением сигнала. Но здесь количество входящих в нейрон весов включает высоту и ширину фильтра и глубину входного тензора.
Второй вспомогательный метод генерирует слой max pooling с неперекрывающимися окнами размера k. Рекомендуемое значение по умолчанию k=2, им мы и воспользуемся для нашей сверточной сети MNIST.
С этими вспомогательными методами можно создать новый конструктор логического вывода:
def inference(x, keep_prob):
x = tf.reshape(x, shape=[-1, 28, 28, 1])
with tf.variable_scope("conv_1"):
conv_1 = conv2d(x, [5, 5, 1, 32], [32])
pool_1 = max_pool(conv_1)
with tf.variable_scope("conv_2"):
conv_2 = conv2d(pool_1, [5, 5, 32, 64], [64])
pool_2 = max_pool(conv_2)
with tf.variable_scope("fc"):
pool_2_flat = tf.reshape(pool_2, [-1, 7 * 7 * 64])
fc_1 = layer(pool_2_flat, [7*7*64, 1024], [1024])
# apply dropout
fc_1_drop = tf.nn.dropout(fc_1, keep_prob)
with tf.variable_scope("output"):
output = layer(fc_1_drop, [1024, 10], [10])
return output
Понять код несложно. Сначала берем входные значения пикселов в виде плоского вектора и преобразуем их в тензор N ×28×28×1, где N — количество примеров в мини-выборке, 28 — ширина и высота каждого изображения, а 1 — глубина (изображения черно-белые; если бы они были в режиме RGB, глубина равнялась бы 3, по числу цветов). Затем строим сверточный слой с 32 фильтрами, имеющими пространственную протяженность 5. В результате получаем объем входных значений глубиной 1 и выдаем выходной тензор глубиной 32. Он передается через слой max pooling, где информация сжимается. Затем мы строим второй сверточный слой с 64 фильтрами, снова пространственной протяженностью 5: берем входной тензор глубиной 32 и выдаем выходной глубиной 64. Он снова проходит через слой max pooling, где информация сжимается.
Мы готовы передать выходные данные из слоя max pooling на полносвязный слой. Для этого мы преобразуем тензор в плоский вектор. Это можно сделать, вычислив полный размер каждого «субтензора» в мини-выборке. У нас 64 фильтра, что соответствует глубине 64. Теперь нужно определить высоту и ширину после прохода через два слоя max pooling. Используя формулы из предыдущего раздела, легко убедиться, что каждая карта признаков имеет высоту и ширину 7. Подтверждение этого мы оставляем читателю в качестве упражнения.
Читать дальше
Конец ознакомительного отрывка
Купить книгу