Когда вы начинаете в любой новой области, всегда самое сложное - просто вскочить и начать играть. В глубоком обучении первым делом (и, как правило, ключевым моментом) является просмотр данных, поэтому нам нужен организованный способ загрузки данных изображения и работы с ним в нашем коде Python.

Цель здесь - помочь вам перейти от набора данных к реализации базового (но расширяемого) конвейера обработки изображений, который мы можем передать прямо в Keras. В нашем рабочем примере будет набор данных DAVIS 2019 Challenge, но он будет применяться к другим наборам данных на основе изображений (Berkeley DeepDrive 100K, nuScenes 3D Detection, Google Image Captioning и т. Д.), И большая часть его также будет применяться к любому контролируемому набору данных.

Детали

  1. Отслеживание данных
  2. Работа с наборами данных с помощью генераторов
  3. Собираем все вместе (в классе)

Отслеживание данных

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

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

Теперь files - это список (имен файлов) всех изображений, к которым у нас есть доступ в этой папке. os.path.exists(path) проверяет, существует ли путь в файловой системе, os.walk(path) возвращает генератор, который «проходит» по каталогу папки, начиная с пути, а os.path.join(path1, path2, ...) берет несколько частей пути (в данном случае путь к папке, а затем имя файла) и сделайте одну строку пути (позаботьтесь о «/», чтобы вам не приходилось это делать).

Если у нас есть видео, это лишь немного усложняет наш код (в зависимости от того, как хранится «видео»). Вместо того чтобы хранить список всех изображений, мы сохраним словарь, где ключи - это названия видео, а значения - списки изображений в этом видео. В DAVIS изображения помещаются в папки на основе видео, поэтому мы можем довольно легко получить список видео (и списки изображений).

Но почему мы хотим, чтобы изображения были отсортированы по видео? Иногда мы хотим иметь возможность просто видеть изображения из одного источника видео. Кроме того, наш раздел проверки должен разделяться по видео. Если есть изображения из видео как в наборе обучения, так и в наборе проверки, оценки проверки не так значимы, как должны быть (найдите «утечка данных»).

Мы можем использовать один и тот же код для загрузки файлов либо для входных изображений, либо для выходных (целевых) масок.

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

Загрузка изображений

Теперь, когда у нас есть пути к файлам для изображений, которые мы хотим загрузить, в Python есть множество библиотек обработки изображений, которые могут загружать изображения: matplotlib, scikit-image, opencv, Pillow и imageio, и это лишь некоторые из них. Код действительно прост и практически идентичен во всех случаях:

Я бы порекомендовал использовать один из первых 3 (matplotlib, scikit-image или opencv), так как они вернут numpy ndarray. (Вам нужно будет преобразовать другие в ndarray, прежде чем они станут полезными.)

При написании этого сценария, чтобы убедиться, что изображение загружается правильно, просто постройте изображение с помощью matplotlib: plt.imshow(img).

Загрузка масок экземпляров

Хотя мы можем загружать выходные маски в виде изображений, используя приведенный выше код, нам также необходимо выполнить некоторую предварительную обработку этих изображений, прежде чем их можно будет использовать для обучения. Большая проблема в том, что нам нужно быстро кодировать изображения. Обычно они поступают в виде одного канала (иногда 3), но их необходимо мгновенно закодировать в трехмерный массив numpy. Для этого есть много кода (вы можете легко найти его в StackOverflow, GitHub или в стартовом ядре Kaggle), но я думаю, что стоит попробовать сделать это один раз самостоятельно.

Работа с большими наборами данных с помощью генераторов

При глубоком обучении мы обычно работаем с очень большими наборами данных (от нескольких сотен гигабайт до терабайт). В большинстве случаев весь набор данных не помещается в память (даже если это так, мы действительно не хотим забивать память данными, которые мы не будем использовать какое-то время), поэтому нам нужно использовать шаблон генератора для загрузки нескольких пакетов за раз.

Наша цель - уметь это:

Хотя это выглядит как обычный цикл for в Python, генератор делает что-то особенное в цикле. Цикл for для списка данных сначала создаст список (сначала загружая все данные в память), а затем обработает каждый элемент. Цикл for, применяемый к генератору, вместо этого будет ленивым и загружать следующий элемент в память только тогда, когда он запрашивается. В любой момент времени только несколько элементов из набора данных находятся в памяти.

Хорошо, вот и цель, как на самом деле сделать генератор?

Пример: числа Фибоначчи

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

Чтобы изменить его стиль как генератор, мы пишем функцию, которая делает все это, но вместо того, чтобы возвращать prev или curr, она будет yield следующее число. Это работает, потому что вызов yield не просто возвращает управление выполнением обратно вызывающей стороне, но передает его и ожидает вернуть его в какой-то момент. Все локальные переменные функции сохраняются, и функция продолжает выполнение с того места, где она была остановлена.

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

Генераторы данных

Шаблон для генераторов данных, которые работают с Keras, очень похож.

Давайте заполним детали, используя набор данных DAVIS:

Использование генераторов

Теперь, когда у нас есть генератор для наших данных, мы можем использовать его сами в цикле for, как указано выше (например, чтобы распечатать входное изображение и выходные маски для сравнения), но нам не нужно делать это для обучения моделей Keras. . Классы Keras Model и Sequential имеют методы разного «вкуса». У вас есть обычные методы fit(), predict() и evaluate(), которые принимают весь набор данных в качестве параметра, но у вас также есть версии, которые принимают генераторы в качестве параметров: fit_generator(), predict_generator() и evaluate_generator().

Собираем все вместе в классе

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

И после всей проделанной работы им очень просто пользоваться:

Надеюсь, это короткое руководство даст вам некоторый контроль над огромными объемами данных, с которыми вы работаете (или даже если это меньшие объемы данных). Однако есть много вещей, которые не были рассмотрены, но которые вам необходимы, чтобы сделать его полностью функциональным. Некоторые идеи:

  • Нужен способ хранения / доступа к выходным классам для каждого экземпляра (возможно, также идентификаторам экземпляров)
  • При необходимости можно добавить множество других шагов предварительной обработки (нормализация, однократное кодирование, масштабирование / заполнение, увеличение и т. Д.).
  • Сопоставление входных изображений с соответствующими масками
  • Разделение на наборы train / val (на видео)
  • Параметризация метода generate_data() (например, вы всегда хотите перемешивать?)

Дайте мне знать, если у вас возникнут вопросы или предложения!