В этой статье мы будем использовать машинное обучение с Python для создания подписей для различных изображений.

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

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

Например, сеть может генерировать любые из следующих заголовков, которые имеют отношение к изображению ниже i, e «Белая собака в траве», « Белая собака с коричневыми пятнами »или даже« Собака на траве и розовых цветах ».

Набор данных

Мы выбрали набор данных «Flickr 8k». Мы выбрали эти данные, потому что они были легкодоступными и имели идеальный размер, который можно было бы обучить на обычном ПК, а также достаточный для правильного обучения сети для создания соответствующих подписей. Данные разделены на три набора, в основном обучающий набор, содержащий 6 тыс. Изображений, набор для разработки, содержащий 1 тыс. Изображений, и тестовый набор, содержащий 1 тыс. Изображений. Каждое изображение содержит 5 подписей. Один из примеров такой:

  1. Ребенок в розовом платье поднимается по лестнице в подъезде.
  2. Девушка входит в деревянное здание.
  3. Маленькая девочка забирается в деревянный домик.
  4. Маленькая девочка поднимается по лестнице в свой игровой домик.
  5. Маленькая девочка в розовом платье входит в деревянную хижину.

Очистка данных:

Первым и самым важным шагом любой программы машинного обучения является очистка данных и избавление от любых нежелательных данных. Поскольку мы имеем дело с текстовыми данными в заголовках, мы будем выполнять основные шаги по очистке, такие как преобразование всех букв в нижний регистр, как для компьютера «Привет» и «Привет» - два совершенно разные слова, удаляя специальные символы и знаки препинания, такие как *, (, £, $,% и т. д., и удаляя любые слова, содержащие числа.

Сначала мы создаем словарь для всего уникального в нашем наборе данных, то есть 8000 (количество изображений) * 5 (подписи для каждого изображения) = 40000 подписей. Мы обнаружили, что это значение равно 8763. Но большинство этих слов встречается всего один или два раза, и мы не хотели бы, чтобы они использовались в нашей модели, поскольку это не сделает нашу модель устойчивой к выбросам. Следовательно, мы устанавливаем порог из 10 минимальных вхождений слова, которое должно быть включено в наш словарь, и это оказывается равным 1652 уникальным словам.

Еще мы добавляем два токена к каждому описанию, чтобы указать начало и конец заголовка. Два токена - это ‘startseq’ и ‘endseq’, представляющие начало и конец заголовка соответственно.

Начнем с импорта всех необходимых библиотек:

Давайте определим несколько вспомогательных функций:

Давайте объясним их по порядку:

  • load_doc: берет путь к файлу и возвращает содержимое внутри этого файла
  • load_descriptions: берет содержимое файла, содержащего описания, и создает словарь с идентификаторами изображений в качестве ключей и описаниями в виде списка значений.
  • clean_descriptions: очищает описание, делая все буквы строчными, игнорируя числовые символы и знаки препинания, а также слова, состоящие только из одного символа.
  • save_descriptions: сохраняет словарь описаний в памяти как текстовый файл
  • loads_set: загружает все уникальные идентификаторы изображений из текстового файла.
  • load_clean_descriptions: загружает все очищенные описания с использованием уникальных идентификаторов, извлеченных выше.

Предварительная обработка данных:

Затем мы проводим предварительную обработку данных как для изображений, так и для подписей. Изображения в основном являются нашими векторами функций, то есть нашим входом в сеть. Вот почему нам нужно преобразовать их в вектор фиксированного размера, прежде чем передавать в нейронную сеть. Для этого мы используем трансферное обучение из модели Inception V3 (сверточная нейронная сеть), созданной Google Research [3]. Эта модель была обучена на наборе данных ImageNet [4] для выполнения классификации изображений на 1000 изображений, но наша цель не состоит в том, чтобы выполнять классификацию, и поэтому мы удаляем последний слой softmax и извлекаем 2048 фиксированных вектор для каждого изображения, как показано на рисунке ниже:

Подписи - это результат нашей модели, то есть то, что мы должны предсказать. Но предсказание не происходит сразу, мы будем предсказывать наши подписи слово за словом. А для этого нам нужно закодировать каждое из наших слов в вектор фиксированного размера (что будет сделано в следующем разделе). Для этого нам сначала нужно создать два словаря, а именно 'word to index', которые сопоставят каждое слово с индексом, который в нашем случае будет от 1 до 1652, и 'index to word ', который сопоставляет каждый индекс с соответствующим словом. Последнее, что мы должны сделать, это вычислить длину описания, имеющего максимальную длину в нашем наборе данных, чтобы мы могли дополнить все остальные, чтобы поддерживать фиксированную длину. В нашем случае эта длина оказывается равной 34.

Вложения слов:

Как было сказано ранее, мы будем отображать каждое слово в вектор фиксированного размера (например, 200), мы будем использовать предварительно обученную модель GLOVE. Наконец, мы создаем матрицу вложения для всех 1652 слов в нашем словаре, содержащую вектор фиксированного размера для каждого слова в нашем словаре.

Давайте разберем этот фрагмент кода:

  • Строка 1–5: извлеките все описания всех обучающих изображений в единый список.
  • Строка 9–18: выберите в словаре только те слова, которые встречаются более 10 раз.
  • Строка 21–30: Создайте слово для индексации и словарь для индексации слов.
  • Строка 33–42: Загрузить вложения перчатки в словарь со словом в качестве ключа и вектором вложений в качестве значения.
  • Строка 44–52: Создайте матрицу встраивания для слов из нашего словаря, используя вложения, загруженные выше.

Подготовка данных:

Это один из важнейших аспектов этого проекта. Что касается изображений, нам нужно преобразовать их в вектор фиксированного размера, используя модель Inception V3, как описано ранее.

  • Строка 1–22: Загрузите пути поездов и тестовые изображения в отдельные списки.
  • Строка 25–53. Прокрутите каждое изображение в обучающем и тестовом наборах, загрузив их в фиксированный размер, предварительно обработав их, извлекая функции с помощью модели InceptionV3 и, наконец, изменяя их форму.
  • Строка 56–63: Сохраните извлеченные объекты на диск.

Теперь мы не сможем спрогнозировать нашу подпись сразу, то есть не просто дадим компьютеру изображение и не попросим его сгенерировать для него подпись. Мы бы дали ему вектор признаков изображения, а также первое слово подписи и позволили бы ему предсказать второе слово. Затем мы даем ему первые два слова и позволяем ему угадывать третье слово. Давайте рассмотрим изображение, приведенное в разделе набора данных, и подпись «Девушка входит в деревянное здание». В этом случае следующие значения будут нашими входами (Xi) и выходами (Yi) в каждом случае после добавления токенов ‘startseq’ и ‘endseq’.

После этого мы изменили бы каждое из слов в наших входах и выходах, чтобы сопоставить индекс, используя созданный нами словарь «word to index». Поскольку мы выполняем пакетную обработку, нам нужно, чтобы все последовательности были одинаковой длины, и поэтому мы должны добавлять к каждой последовательности нули, пока они не достигнут максимально возможной длины (34, как вычислено выше). Как можно видеть, это огромный объем данных, и загрузить его в память сразу невозможно, и для этого мы будем использовать генератор данных, который загружает их небольшими порциями, т.е. только то, что нужно и не нужно ». т потребляет всю память.

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

Модельная архитектура и обучение:

Как указывалось ранее, наша модель в каждой точке имеет два входа: один - вектор изображения признака, а другой - частичный заголовок. Сначала мы применяем Dropout 0,5 к вектору изображения, а затем соединяем его со слоем из 256 нейронов. Для частичных подписей мы сначала соединяем их со слоями встраивания с весами матрицы встраивания из Предварительно обученной перчатки, как указано выше. Затем мы применяем Dropout 0,5 и LSTM (Long Short Term Memory). Наконец, мы объединяем их и соединяем со слоем из 256 нейронов и, наконец, слоем softmax, который предсказывает вероятность каждого слова в нашем словаре. Архитектуру высокого уровня можно резюмировать с помощью следующего рисунка:

Ниже приведены гиперпараметры, выбранные во время обучения: потеря была выбрана как «энтропия категориальных потерь», оптимизатор - «Адам». Модель обучалась в общей сложности для 30 эпох, но для первых 20 эпох размер пакета и скорость обучения составляли 0,001 и 3, а для следующих 10 эпох - 0,0001 и 6 соответственно.

Давайте немного поясним код:

  • Строка 1-11: Определение архитектуры модели
  • Строка 13–14: установите веса слоя внедрения равными матрице внедрения, созданной выше, а также установите trainable=False, чтобы слой больше не обучался.
  • Строка 16–33: Обучите модель в двух отдельных интервалах с гиперпараметрами, как указано выше.

Вывод:

Потери при обучении для первых 20 эпох, а затем для следующих 10 эпох показаны ниже:

Для вывода мы пишем функцию, которая предсказывает следующее слово как слово, имеющее максимальную вероятность в соответствии с нашей моделью (т.е. жадное).

Что вы можете сделать:

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

  • Большой набор данных
  • Дополнительная настройка гиперпараметров
  • Изменение архитектуры модели.

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