Поиск по сайту Поиск

Многозначная классификация с помощью Keras

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

В этом руководстве мы расскажем, как создать многозначную нейронную сеть с помощью Keras.

https://www.pyimagesearch.com/wp-content/uploads/2018/05/keras_multi_label_animation.gif

Многозначная или нечёткая классификация (multi-label classification) позволяет определить, с какой вероятностью объект принадлежит к каждому из классов.

Урок разбит на четыре части.

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

Далее мы кратко обсудим SmallerVGGNet — архитектуру нашей глубокой свёрточной нейронной сети.

Затем мы обучим сеть с помощью подготовленного набора данных, и, наконец, оценим её на тестовых изображениях.

Cодержание

1. Набор данных
2. Структура проекта
3. Архитектура Keras-модели
4. Создание модели
5. Обучение модели
6. Применение модели к новым изображениям
7. Результаты
8. Загрузки

Набор данных

https://www.pyimagesearch.com/wp-content/uploads/2018/04/keras_multi_label_dataset.jpg

Набор данных состоит из 2167 изображений в шести категориях:

— Черные джинсы (344 изображений)

— Синее платье (386 изображений)

— Синие джинсы (356 изображений)

— Синяя рубашка (369 изображений)

— Красное платье (380 изображений)

— Красная рубашка (332 изображения)

Цель нейросети — классифицировать как цвет, так и тип одежды. Вы можете использовать этот датасет, а можете создать свой с помощью урока «Как (быстро) собрать набор данных для глубокого обучения».

Процесс загрузки и ручного удаления лишних изображений занимает примерно 30 минут.

Структура проекта

В разделе «Загрузки» вы можете скачать архив с кодом и набором данных. Структура проекта выглядит следующим образом:

В корне находятся 6 файлов и 3 папки.

Файлы, с которыми мы будем работать в этом уроке:

  1. search_bing_api.py: этот скрипт позволяет быстро создать набор данных для глубокого обучения. Вам не нужно запускать его, так как датасет уже находится в архиве. Можете использовать его в образовательных целях.
  2. train.py: скрипт для обучения классификатора.
  3. fashion.model: файл модели. Скрипт train.py сериализует модель Keras на диск. Мы будем использовать её позже в файле classify.py.
  4. mlb.pickle: бинаризатор меток MultiLabelBinarizer из scikit-learn, созданный с помощью train.py — этот файл содержит имена классов в виде сериализованных структур данных.
  5. plot.png: график, который создаёт обучающий скрипт. Если вы используете собственный набор данных, не забудьте проверить этот файл на точность, потери и переобучение.
  6. classify.py: скрипт для проверки классификатора. Всегда выполняйте проверку в исходном проекте, прежде чем использовать модель для других задач.

И три каталога:

  1. dataset: папка содержит набор изображений. У каждого класса свой соответствующий подкаталог. Когда датасет упорядочен таким образом, из него проще извлечь метки классов по заданному пути. 
  2. pyimagesearch: это модуль с нашей нейросетью.  В него входят файлы __init__.py и smallervggnet.py.
  3. examples: здесь находятся семь примеров изображений. На каждом из них мы протестируем результаты классификации с помощью скрипта classify.py.

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

Архитектура Keras-модели

https://www.pyimagesearch.com/wp-content/uploads/2018/04/cnn_keras_smallervggnet.png

Архитектура CNN (свёрточной нейронной сети) SmallerVGGNet, которую мы используем для этого урока — упрощённая версия своего прародителя VGGNet. Модель VGGNet впервые представили Карен Симонян и Эндрю Циссерман в своей статье “Very Deep Convolutional Networks for Large Scale Image Recognition в 2015 году.

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

Загрузив и распаковав zip-архив, откройте файл smallervggnet.py из каталога pyimagesearch:

В строках 2-10 мы импортируем необходимые модули. Создадим класс SmallerVGGNet:

Наш класс определён в строке 12. Затем мы создаём функцию сборки (build) в строке 14.

Метод требует четырёх параметров: ширина (width), высота (height), глубина (depth) и классы (classes). Глубина определяет количество цветовых каналов в исходном изображении, а классы — число категорий или меток (не их названия). Мы будем использовать эти параметры в обучающем скрипте для модели с формой входных данных 96 x 96 x 3.

Необязательный аргумент finalAct (значение по умолчанию — “softmax”) будет использоваться в конце построения архитектуры. Изменение этого значения на “sigmoid” позволит нам выполнить многозначную классификацию.

Внутри функции мы инициализируем модель (строка 17) и по умолчанию используем архитектуру “channel_last” в строках 18 и 19. Строки 23-25 нужны для бэкэндов с поддержкой “channel_first”.

Создадим первый слой CONV => RELU => POOL:

Слой CONV имеет 32 фильтра с ядром 3x3 и активацией RELU (Rectified Linear Unit). Мы применяем пакетную нормализацию, max-pooling и 25% исключений.

Исключение (dropout) — это процесс случайного отключения узлов, соединяющих текущий и последующий слои. Он помогает снизить переобучение, поскольку ни один узел не будет ответственным за прогноз определённого класса.

Далее перейдём к двум наборам слоёв (CONV => RELU) * 2 => POOL:

Обратите внимание на изменения размеров фильтров, ядер и pool_size. Так мы постепенно уменьшаем размер сети, но увеличиваем глубину.

За этим блоком следует набор слоёв FC => RELU:

Полностью связанные слои размещаются в конце сети (параметр Dense в строках 57 и 64).

В строке 65 мы определяем, использовать ли активацию “softmax” для классификации по одной метке, или активацию “sigmoid” для нескольких меток. Желаемый параметр надо указать в строке 14 этого сценария и в строке 95 файла train.py.

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

Теперь, когда мы инициализировали сеть SmallerVGGNet, давайте создадим скрипт для её обучения классификации по нескольким меткам. 

Откройте файл train.py:

В строках 2-19 импортируются необходимые пакеты и модули. Строка 3 подключает бэкэнд matplotlib для сохранения графиков на диск в фоновом режиме.

Мы исходим из того, что на этом этапе у вас уже установлены Keras, scikit-learn, matplotlib, imutils и OpenCV. Если же это ваш первый проект глубокого обучения, то вы можете получить все необходимые библиотеки двумя способами:

  1. используйте предварительно настроенную среду (требует 5 минут вашей работы, а обучение нейросети будет стоить дешевле чашки кофе);
  2. создайте собственное окружение (требует времени, терпения и настойчивости).

Предварительно настроенная среда позволяет быстро загрузить и собрать файлы, завершив работу в считанные минуты. Например, на наших облачных серверах с мощными GPU для глубокого обучения NVIDIA Tesla V100 можно использовать готовый образ с Ubuntu 18.04, CUDA Toolkit и Deepo: в нём уже настроен Jupyter Notebok и установлены популярные библиотеки для машинного обучения.

Если вы хотите создать свою среду (и у вас есть время на отладку и устранение неполадок), то советуем ознакомиться со статьями:

Настройка Ubuntu для глубокого обучения с Python  (только для CPU)

Настройка Ubuntu 16.04 + CUDA + GPU для глубокого обучения с Python  (GPU и CPU)

Настройка macOS для глубокого обучения с Python, TensorFlow и Keras

Итак, ваша среда настроена и все пакеты успешно импортированы. Давайте разберём аргументы командной строки:

Аргументы командной строки устанавливают параметры для скрипта подобно аргументам функций.

Мы работаем с четырьмя аргументами (строки 23-30):

--dataset: путь к набору изображений на диске.

--model: путь к сериализованной модели Keras.

--labelbin: путь к выходному бинаризатору с несколькими метками.

--plot: путь к выходному файлу графика обучения.

Перейдём к инициализации важных для процесса обучения переменных:

Переменные в строках 35-38 определяют, что:

— сеть будет обучаться в течение 75 эпох, обрабатывая тренировочные шаблоны и улучшая их распознавание методом обратного распространения ошибки;

— начальная скорость обучения равна 10-3 (значение по умолчанию для оптимизатора Adam);

— размер пакета составляет 32. Вы можете отрегулировать это значение в зависимости от возможностей графического процессора;

— как упоминалось выше, изображения имеют размер 96x96 и содержат 3 канала.

Загрузим и предварительно обработаем данные для обучения:

Здесь мы берём пути к изображениям imagePaths и случайным образом перемешиваем их. Затем инициализируем данные и метки (строки 47 и 48).

Далее пройдёмся по всем изображением, обработаем их и извлечём метки классов:

Сначала мы загружаем каждое изображение в память (строка 53). Затем выполняем его предварительную обработку (строки 54 и 55) и добавляем к списку данных (строка 56).

Строки 60 и 61 разделяют путь к изображению на несколько меток. Строка 60 создаёт список из двух элементов, который добавляется в массив меток в строке 61. Вот пример того, как это выглядит в терминале:

Как видите, список меток — это «массив массивов», в котором каждая составляющая является двухэлементным списком.

Но мы ещё не закончили с предварительной обработкой:

Всего двумя строками кода мы преобразовываем списки данных и меток в массивы NumPy и масштабируем интенсивности пикселей в диапазон [0, 1].

Давайте бинаризуем метки с помощью следующего блока кода:

Для этого мы используем класс MultiLabelBinarizer из scikit-learn. Вы не можете использовать стандартный LabelBinarizer для многозначной классификации. Строки 72 и 73 преобразуют метки в вектор, в котором закодирован представленный на изображении класс.

Вот пример, как кортеж ("red","dress") преобразуется в вектор с шестью категориями:

Операция One-hot encoding преобразует категориальные метки в вектор целых чисел. Тот же принцип применяется в последних двух строках, только у нас будет two-hot encoding.

В последней строке две метки являются “hot” (представлены цифрой «1»), что указывает на их наличие. Если какая-либо из меток отсутствует, то в массиве она будет равна нулю.

Создадим обучающую и тестовую выборки, а также инициализируем генератор данных:

В строках 81 и 82 мы выделяем 80% данных для обучения и 20% для тестирования.

Генератор для добавления изображений инициализируется в строках 85-87. Дополнение данных следует выполнять, если в каждом классе содержится не менее 1000 снимков.

Соберём модель и инициализируем оптимизатор Adam:

В строках 92-95 мы собираем модель с параметром finalAct="sigmoid" для многозначной классификации.

Скомпилируем модель и начнём обучение (это может занять некоторое время в зависимости от характеристик вашего процессора):

В строках 105 и 106 мы компилируем модель, используя бинарную кросс-энтропию ("binary_crossentropy"), а не категориальную ("categorical_crossentropy"). Это может показаться нелогичным для многозначной классификации, но цель — обработать каждую выходную метку как независимое распределение Бернулли. Так мы сможем штрафовать каждый выходной узел по отдельности.

В строках 110-114 запускается процесс обучения с ранее созданным генератором.

После этого сохраним нашу модель и бинаризатор меток на диске:

Построим график потерь и точности:

Процесс построения происходит в строках 127-137, а строка 138 выполняет сохранение файла на диск в виде изображения.

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

Откройте терминал, перейдите в каталог проекта и выполните команду:

Как видите, мы обучили сеть и достигли:

98.57% точности классификации на обучающей выборке

98.42% точности классификации на тестовой выборке.

График обучения показан на рисунке:

https://www.pyimagesearch.com/wp-content/uploads/2018/04/plot.png

Применение модели к новым изображениям

Проверим нашу обученную модель на снимках, не входящих в обучающий или тестовый наборы.

Откройте файл classify.py в каталоге проекта:

В строках 2-9 мы импортируем пакеты Keras и OpenCV.

Затем обрабатываем аргументы командной строки (12-19).

Загрузим и предварительно обработаем входное изображение:

Обработка происходит таким же образом, как и с обучающими данными.

Загрузим модель с бинаризатором меток и классифицируем изображение:

В строках 34 и 35 происходит загрузка модели и бинаризованных меток. Затем мы классифицируем обработанное изображение (строка 40) и извлекаем индексы двух меток классов (строка 41) следующим образом:

— сортируем индексы массива по их вероятности в порядке убывания;

— получаем два индекса класса.

При желании вы можете изменить этот код для работы с большим числом меток классов. Мы советуем также использовать пороговое значение вероятности и возвращать метки с достоверностью > N%.

Выведем метки класса и из значения на выходное изображение:

Цикл в строках 44-48 рисует две метки и соответствующие им значения вероятности на выходном изображении. Также мы отображаем прогнозы в терминале в строках 51 и 52. И, наконец, показываем изображение на экране (строки 55 и 56).

Результаты

Попробуем применить скрипт classify.py для классификации новых изображений. Если вы захотите использовать свои фотографии, то вам не нужно менять код выше — достаточно просто указать путь к ним с помощью аргументов командной строки в терминале.

Запустите команду:

https://www.pyimagesearch.com/wp-content/uploads/2018/04/keras_multi_label_output_01.png

Успех! Оба класса обнаружены с высокой вероятностью.

Теперь попробуем с синим платьем:

https://www.pyimagesearch.com/wp-content/uploads/2018/04/keras_multi_label_output_02.png

Тоже всё хорошо. Вы можете самостоятельно протестировать остальные примеры из папки examples и поделиться результатами.

Разберём пример с чёрным платьем (example_07.jpg). Раз наша нейросеть может определять чёрные и синие джинсы вместе с синими и красными платьями, сможет ли она классифицировать чёрное платье?

https://www.pyimagesearch.com/wp-content/uploads/2018/04/keras_multi_label_output_07.png

О нет, ошибка! Классификатор сообщает, что модель носит чёрные джинсы.

Что же произошло? Почему прогноз неверен?

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

Загрузки

Zip-архив с исходным кодом и набором данных можно скачать по ссылке (размер архива 562 МБ).

⌘⌘⌘

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

С оригинальной статьёй можно ознакомиться на портале pyimagesearch.com.

Domains weekly: конец стабильности .COM, детали продажи .ORG и сделки недели

Domains weekly: конец стабильности .COM, детали продажи .ORG и сделки недели

Из нового доменного дайджеста вы узнаете, может ли проигрыш спора за домен стать победой, чего ждать от повышения цен на...
Read More
PHP 7 или Как ускорить сайт за минуту без регистрации и СМС

PHP 7 или Как ускорить сайт за минуту без регистрации и СМС

Вы наверняка в курсе, что PHP удерживает лидерство среди языков, применяющихся для создания сайтов. В конце 2019 года версия PHP...
Read More
Приключенческая игра с самым опасным ИИ в мире

Приключенческая игра с самым опасным ИИ в мире

Долгие праздничные каникулы уже позади, но многим наверняка хочется ещё немного отдохнуть от серьёзных тем. Поэтому сегодня мы расскажем вам...
Read More
Новогодний чек-лист: не пропразднуй свой домен

Новогодний чек-лист: не пропразднуй свой домен

Не все задачи и дела подходят под фразу «А давайте уже после праздников». И те, что «горят прям горят», мы...
Read More
Domains weekly: рост Интернета, зона .AMAZON и домен на миллион

Domains weekly: рост Интернета, зона .AMAZON и домен на миллион

Приближаются праздники, и в доменной индустрии всё становится спокойнее… Или нет? Из последней подборки в этом году вы узнаете, насколько...
Read More
Итоги 2019 года в блоге REG.RU: создание сайтов, бэкапы, нейросети и UX‑дизайн

Итоги 2019 года в блоге REG.RU: создание сайтов, бэкапы, нейросети и UX‑дизайн

Год приближается к финишной прямой, и редакция блога, следуя тренду, подводит его итоги. Сегодня мы поделимся материалами, которые вы больше...
Read More
Стэнфордский курс: лекция 10. Рекуррентные нейронные сети

Стэнфордский курс: лекция 10. Рекуррентные нейронные сети

В прошлый раз мы рассказали о нескольких популярных свёрточных архитектурах и узнали об их влиянии на развитие машинного обучения. В...
Read More
Domains weekly: карта Интернет-мира, перспективы Китая и доменная зависть

Domains weekly: карта Интернет-мира, перспективы Китая и доменная зависть

Новая подборка новостей будет очень полезной для домейнеров. Насколько активно страны мира регистрируют сайты в своих национальных доменных зонах? Есть...
Read More
Shared или VPS — какой хостинг выбрать для сайта?

Shared или VPS — какой хостинг выбрать для сайта?

Один из наших читателей попросил рассказать, какой же вариант хостинга стоит использовать для своего онлайн-проекта. Этим вопросом задаётся почти каждый...
Read More
С чего начать SEO-продвижение сайта

С чего начать SEO-продвижение сайта

Вы создали красивый и яркий сайт, грамотно расписали контент, добавили акценты для целевой аудитории, но трафик не растёт? Скорее всего,...
Read More