Шаг 204.
Глубокое обучение на Python. Введение в глубокое обучение в технологиях компьютерного зрения. Введение в сверточные нейронные сети

    На этом шаге мы рассмотрим пример простой сверточной сети.

    Здесь мы погрузимся в теорию сверточных нейронных сетей и выясним причины их успеха в задачах распознавания образов. Но сначала рассмотрим практический пример простой сверточной нейронной сети, классифицирующей изображения рукописных цифр из набора MNIST. Эту задачу мы решили на 26 шаге, использовав полносвязную сеть (ее точность на контрольных данных составила 97,8%). Несмотря на простоту сверточной нейронной сети, ее точность будет значительно выше полносвязной модели из этого шага.

    В следующем примере показано, как выглядит простая сверточная нейронная сеть. Это стек слоев Conv2D и MaxPooling2D. Как она действует, рассказывается чуть ниже. Мы построим модель с помощью функционального API, с которым вы познакомились на предыдущих шагах.


Пример 8.1. Создание небольшой сверточной нейронной сети
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.datasets import mnist

inputs = keras.Input(shape=(28, 28, 1))
x = layers.Conv2D(filters=32, kernel_size=3, activation="relu")(inputs)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=64, kernel_size=3, activation="relu")(x)
x = layers.MaxPooling2D(pool_size=2)(x)
x = layers.Conv2D(filters=128, kernel_size=3, activation="relu")(x)
x = layers.Flatten()(x)
outputs = layers.Dense(10, activation="softmax")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

    Важно отметить, что данная сеть принимает на входе тензоры с формой

  (высота_ изображения, ширина_изображения, каналы)   , 
не включая измерение, определяющее пакеты. В данном случае мы настроили сеть на обработку входов с размерами (28, 28, 1), соответствующими формату изображений в наборе MNIST.

    Рассмотрим поближе текущую архитектуру сети.


Пример 8.2. Сводная информация о сети
print(model.summary())

Model: "functional"
 Layer (type)                          Output Shape                         Param # 

 input_layer (InputLayer)              (None, 28, 28, 1)                          0 
 conv2d (Conv2D)                       (None, 26, 26, 32)                       320 
 max_pooling2d (MaxPooling2D)          (None, 13, 13, 32)                         0 
 conv2d_1 (Conv2D)                     (None, 11, 11, 64)                    18,496 
 max_pooling2d_1 (MaxPooling2D)        (None, 5, 5, 64)                           0 
 conv2d_2 (Conv2D)                     (None, 3, 3, 128)                     73,856 
 flatten (Flatten)                     (None, 1152)                               0 
 dense (Dense)                         (None, 10)                            11,530 

 Total params: 104,202 (407.04 KB)
 Trainable params: 104,202 (407.04 KB)
 Non-trainable params: 0 (0.00 B)

    Как видите, все слои Conv2D и MaxPooling2D выводят трехмерный тензор с формой

  (высота, ширина, каналы)   . 
Измерения ширины и высоты сжимаются с ростом глубины сети. Количество каналов управляется первым аргументом, передаваемым в слои Conv2D (32, 64 или 128).

    Последний слой Conv2D выдает результат с формой (3, 3, 128) - карту признаков 3 * 3 со 128 каналами. Следующий шаг - передача этого результата на вход полносвязной классифицирующей сети, подобной той, с которой мы уже знакомы: стека слоев Dense. Эти классификаторы обрабатывают векторы - одномерные массивы, - тогда как текущий выход является трехмерным тензором. Чтобы преодолеть это несоответствие, мы преобразуем трехмерный вывод в одномерный с помощью слоя Flatten, а затем добавляем полносвязные слои Dense.

    В заключение выполняется классификация по десяти категориям, поэтому последний слой имеет десять выходов и активацию softmax.

    Теперь обучим сверточную сеть распознаванию цифр MNIST. Мы будем повторно брать большое количество программного кода из шагов, начиная с 26-го. Поскольку модель выполняет классификацию по десяти категориям с активацией softmax, мы используем функцию потерь категориальной перекрестной энтропии, а так как метки являются целыми числами, нам понадобится разреженная версия sparse_categorical_crossentropy.


Пример 8.3. Обучение сверточной нейронной сети на данных из набора MNIST
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28, 28, 1))
train_images = train_images.astype("float32") / 255
test_images = test_images.reshape((10000, 28, 28, 1))
test_images = test_images.astype("float32") / 255
model.compile(optimizer="rmsprop",
              loss="sparse_categorical_crossentropy",
              metrics=["accuracy"])
model.fit(train_images, train_labels, epochs=5, batch_size=64)

    Оценим модель на контрольных данных.


Пример 8.4. Оценка сверточной сети
test_loss, test_acc = model.evaluate(test_images, test_labels)
print(f"Test accuracy: {test_acc:.3f}")

Test accuracy: 0.990

Блокнот с этим примером можно взять здесь.

    Полносвязная сеть из 26 шага показала точность 97,8% на контрольных данных, а простенькая сверточная нейронная сеть - 99,0%.

    Но почему такая простая сверточная нейронная сеть работает намного лучше полносвязной модели? Чтобы ответить на этот вопрос, погрузимся в особенности работы слоев Conv2D и MaxPooling2D.

    На следующем шаге мы рассмотрим операцию свертывания.




Предыдущий шаг Содержание Следующий шаг