Использование Keras и обучение по MNIST

Понимание автоэнкодеров:

Автоэнкодеры - это класс неконтролируемых сетей, который состоит из двух основных сетей: кодировщиков и декодеров.

Неконтролируемая сеть - это сеть, которая изучает закономерности на основе данных без каких-либо обучающих меток. Сеть находит свои шаблоны в данных, не сообщая, какими должны быть шаблоны.

Напротив, есть контролируемые сети, в которых сеть обучается возвращать определенные выходные данные при заданных определенных входах.

Кодер обычно использует серию плотных и / или сверточных слоев для кодирования изображения в вектор фиксированной длины, который представляет изображение в компактной форме, в то время как декодер использует плотные и / или сверточные слои для преобразования вектора скрытого представления обратно в тот же самый изображение или другое измененное изображение.

На изображении выше показан пример простого автокодировщика. В этом автоэнкодере вы можете видеть, что входные данные размера X сжимаются в скрытый вектор размера Z, а затем распаковываются в то же изображение размера X.

Для генерации изображения в сеть декодера передается случайный входной вектор. Сеть декодера преобразует входной вектор в полное изображение.

Создание автоэнкодера:

Я рекомендую использовать Google Colab для запуска и обучения модели Autoencoder.

Установка Tensorflow 2.0

#If you have a GPU that supports CUDA
$ pip3 install tensorflow-gpu==2.0.0b1
#Otherwise
$ pip3 install tensorflow==2.0.0b1

В Tensorflow 2.0 в качестве высокоуровневого API встроен Keras. Керас доступен через этот импорт:

import tensorflow.keras as keras

Импорт необходимых модулей / пакетов

from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Dense, Input, Flatten,\
                                    Reshape, LeakyReLU as LR,\
                                    Activation, Dropout
from tensorflow.keras.models import Model, Sequential
from matplotlib import pyplot as plt
from IPython import display # If using IPython, Colab or Jupyter
import numpy as np

Загрузка данных MNIST

(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train/255.0
x_test = x_test/255.0

Набор данных MNIST состоит из 70000 изображений рукописных цифр размером 28 на 28 пикселей и 70000 векторов, содержащих информацию о том, какая цифра принадлежит каждой из них.

Данные обучения изображения масштабируются от [0, 255] до [0,1], чтобы можно было использовать функцию активации сигмовидной кишки.

Чтобы проверить наши данные, мы построим первое изображение в наборе обучающих данных.

# Plot image data from x_train
plt.imshow(x_train[0], cmap = "gray")
plt.show()

Определение скрытого размера

Скрытый размер - это размер скрытого пространства: вектор, содержащий информацию после сжатия. Это значение является важным гиперпараметром. Если это значение слишком мало, данных для реконструкции будет недостаточно, а если значение слишком велико, может произойти переобучение.

Я обнаружил, что хороший, успешный скрытый размер состоит из 32 значений.

LATENT_SIZE = 32

Создание кодировщика

encoder = Sequential([
    Flatten(input_shape = (28, 28)),
    Dense(512),
    LR(),
    Dropout(0.5),
    Dense(256),
    LR(),
    Dropout(0.5),
    Dense(128),
    LR(),
    Dropout(0.5),
    Dense(64),
    LR(),
    Dropout(0.5),
    Dense(LATENT_SIZE),
    LR()
])

Кодировщик состоит из серии плотных слоев с промежуточными слоями Dropout и LeakyReLU. Плотные слои допускают сжатие входного тензора 28x28 до скрытого вектора размером 32. Слои Dropout помогают предотвратить переоснащение, а LeakyReLU, являясь слоем активации, вносит нелинейность в микс. Dense(LATENT_SIZE) создает окончательный вектор размером 32.

Создание декодера

decoder = Sequential([
    Dense(64, input_shape = (LATENT_SIZE,)),
    LR(),
    Dropout(0.5),
    Dense(128),
    LR(),
    Dropout(0.5),
    Dense(256),
    LR(),
    Dropout(0.5),
    Dense(512),
    LR(),
    Dropout(0.5),
    Dense(784),
    Activation("sigmoid"),
    Reshape((28, 28))
])

Декодер по сути такой же, как и кодировщик, но в обратном порядке. Однако последний слой активации - сигмовидный. Выходные значения сигмовидной функции активации находятся в диапазоне [0, 1], который идеально соответствует нашим масштабированным данным изображения.

Создание полной модели

Для создания полной модели необходимо использовать Keras Functional API. Функциональный API позволяет нам объединить несколько моделей.

img = Input(shape = (28, 28))

Это создаст тензор-заполнитель, который мы можем передать в каждую сеть, чтобы получить результат всей модели.

latent_vector = encoder(img)
output = decoder(latent_vector)

Лучшая часть функционального API Keras - это то, насколько он удобочитаем. Функциональный API Keras позволяет вам вызывать модели непосредственно в тензоры и получать выходные данные от этого тензора. Вызывая модель encoder на тензор img, я получаю latent_vector. То же самое можно сделать с моделью decoder на latent_vector, который дает нам результат.

model = Model(inputs = img, outputs = output)
model.compile("nadam", loss = "binary_crossentropy")

Для создания самой модели вы используете класс Model и определяете входные и выходные данные модели.

Чтобы обучить модель, вы должны ее скомпилировать. Чтобы скомпилировать модель, вы должны выбрать оптимизатор и функцию потерь. В качестве оптимизатора я выбрал Nadam, то есть ускоренный градиент Нестерова, примененный к адаптивной оценке момента. Это модифицированный оптимизатор Адама. В качестве проигрыша я выбрал двоичную кросс-энтропию. Двоичная кросс-энтропия очень часто используется с автоэнкодерами. Однако обычно двоичная кросс-энтропия используется с двоичными классификаторами. Кроме того, двоичная кросс-энтропия может использоваться только между выходными значениями в диапазоне [0, 1].

Обучение модели

EPOCHS = 60

Значение EPOCHS - это гиперпараметр, установленный на 60. Как правило, чем больше эпох, тем лучше, по крайней мере, до тех пор, пока модель не выйдет на плато.

#Only do plotting if you have IPython, Jupyter, or using Colab

Повторное построение действительно рекомендуется только в том случае, если вы используете IPython, Jupyter или Colab, чтобы графики matplotlib были встроенными и не создавали повторно отдельные графики.

for epoch in range(EPOCHS):
    fig, axs = plt.subplots(4, 4)
    rand = x_test[np.random.randint(0, 10000, 16)].reshape((4, 4, 1, 28, 28))
    
    display.clear_output() # If you imported display from IPython
    
    for i in range(4):
        for j in range(4):
            axs[i, j].imshow(model.predict(rand[i, j])[0], cmap = "gray")
            axs[i, j].axis("off")
    
    plt.subplots_adjust(wspace = 0, hspace = 0)
    plt.show()
    print("-----------", "EPOCH", epoch, "-----------")
    model.fit(x_train, x_train)

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

Затем мы очищаем экран (работает только на IPython, Jupyter и Colab) и строим прогнозы модели на изображениях случайного тестирования.

Наконец, обучаем модель. Чтобы обучить модель, мы просто вызываем model.fit для данных обучающего изображения. Помните, как цель автокодировщика состоит в том, чтобы взять входные данные, сжать их, распаковать, а затем вывести копию входных данных? Что ж, это означает, что входные и целевые выходные данные являются данными тренировочного образа.

Как видите, эти сгенерированные изображения довольно хороши. Однако самая большая проблема с изображениями - размытость. Многие из этих проблем можно решить с помощью других типов генерирующих сетей или даже других типов автоэнкодеров.

Полный код

from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Dense, Input, Flatten,\
                                    Reshape, LeakyReLU as LR,\
                                    Activation, Dropout
from tensorflow.keras.models import Model, Sequential
from matplotlib import pyplot as plt
from IPython import display # If using IPython, Colab or Jupyter
import numpy as np
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train/255.0
x_test = x_test/255.0
# Plot image data from x_train
plt.imshow(x_train[0], cmap = "gray")
plt.show()
LATENT_SIZE = 32
encoder = Sequential([
    Flatten(input_shape = (28, 28)),
    Dense(512),
    LR(),
    Dropout(0.5),
    Dense(256),
    LR(),
    Dropout(0.5),
    Dense(128),
    LR(),
    Dropout(0.5),
    Dense(64),
    LR(),
    Dropout(0.5),
    Dense(LATENT_SIZE),
    LR()
])
decoder = Sequential([
    Dense(64, input_shape = (LATENT_SIZE,)),
    LR(),
    Dropout(0.5),
    Dense(128),
    LR(),
    Dropout(0.5),
    Dense(256),
    LR(),
    Dropout(0.5),
    Dense(512),
    LR(),
    Dropout(0.5),
    Dense(784),
    Activation("sigmoid"),
    Reshape((28, 28))
])
img = Input(shape = (28, 28))
latent_vector = encoder(img)
output = decoder(latent_vector)
model = Model(inputs = img, outputs = output)
model.compile("nadam", loss = "binary_crossentropy")
EPOCHS = 60
#Only do plotting if you have IPython, Jupyter, or using Colab
for epoch in range(EPOCHS):
    fig, axs = plt.subplots(4, 4)
    rand = x_test[np.random.randint(0, 10000, 16)].reshape((4, 4, 1, 28, 28))
    
    display.clear_output() # If you imported display from IPython
    
    for i in range(4):
        for j in range(4):
            axs[i, j].imshow(model.predict(rand[i, j])[0], cmap = "gray")
            axs[i, j].axis("off")
    
    plt.subplots_adjust(wspace = 0, hspace = 0)
    plt.show()
    print("-----------", "EPOCH", epoch, "-----------")
    model.fit(x_train, x_train)

Google Colab для этого кода можно найти здесь.

После 60-летней тренировки у меня получилось вот такое изображение:

Как видите, результаты неплохие. Автоэнкодер успешно кодирует и декодирует векторы скрытого пространства с довольно хорошим качеством. Этот автоэнкодер представляет собой «ванильную» разновидность, но другие типы, такие как вариационные автоэнкодеры, имеют изображения еще лучшего качества. Кроме того, увеличивая количество эпох, результаты можно улучшить.

Использование автоэнкодеров

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

Практические применения сети автоэнкодера включают в себя:

  • Шумоподавление
  • Реконструкция изображения
  • Генерация изображения
  • Сжатие и декомпрессия данных