Нейрокурятник: часть 4 — итоговая модель и код на прод
Чтобы довести, наконец, проект нейрокурятника до своего логического завершения, нужно произвести на свет работающую модель и задеплоить ее на продакшен, да еще и так, чтобы соблюдался ряд условий:
- Точность предсказаний не менее 70-90%;
- Raspberry pi в самом курятнике в идеале мог бы определять принадлежности фотографий к классам;
- Нужно как минимум научиться отличать всех кур друг от друга. Программа максимум — также научиться считать яйца;
Статьи про нейрокурятник
- Вступление про обучение себя нейросетям
- Железо, софт и конфиг для наблюдения за курами , который постит события из жизни кур — без нейросети датасетов
- Работающая модельдля распознавания кур в курятнике
- Итог — работающий бот, распознающий кур в курятнике
0. TL;DR
Для самых нетерпеливых:
- Точность получилась в итоге порядка 80%;
- Датасет можно скачать тут;
- ipynb jupyter notebook'а со всеми выкладками и boiler-plate код с пояснениями можно скачать тут, html версию тут;
- Скрипты для демонизации и деплоя на прод — тут (ipynb);
1. Прелюдии
Из современных инструментов, которые адекватно подходят этой цели в голову приходят только сверточные нейросети. Поковырявшись (по ссылке рекурсивная статья про нейросети, если вы хотите научиться) весьма основательное время в нейросетях, поучаствовав в нескольких соревнованиях (к сожалению уже закрытых) на Kaggle (из открытых заинтересовали морские котики, но там сложновато) и тем самым по большей части пройдя курс от fast.ai, я обрисовал для себя примерно такой план:
- Разметить небольшой датасет (а сегодня реально не нужно много данных, чтобы построить классификатор);
- Использовать keras для тестирования максимально большого числа архитектур в течение максимум 1 дня;
- Если получится использовать неразмеченные фотографии, чтобы увеличить точность (semi-supervised подход);
- Попытаться сделать визуализацию активаций внутренних слоев нейросети;
2. Посмотрим, что получилось!
Итоговая архитектура модели in a nutshell, которая дала порядка 80% точности
Для начала давайте разберемся, что же творится в курятнике. Чтобы успешно сделать прикладной проект, всегда нужно пытаться контролировать максимальную часть технологической цепочки. Вряд ли получится сделать проект, если данные поступают в виде «вот вам 10 терабайт видео в ужасном качестве, сделайте нам модель распознавания видео в режиме реального времени за 10,000 рублей » или «вот вам 50 фото этикеток от пива» (юмор, но это примеры, основанные на реальных… проектах, слава богу не моих).
- Куры постоянно залазят в гнездо, чтобы просто посидеть или из любопытства, особенно молодые;
- Молодые куры (белые на фото выше) — до определенного момента не откладывали яйца в гнездо, а просто ковыряли чужие яйца;
- У кур бывают стычки, но в 95% случаев соблюдаются правила: i) в гнезде 1 курица ii) за время пребывания курицы в гнезде делается 10-20 уникальных фоток iii) курица как правило польностью закрывает яйца собой iv) текущий вид на куриц является продуктом нескольких итераций поиска оптимального расположения камеры;
- В день делается порядка 300-400 фото, откладывается 5-8 яиц (молодые куры по глупости и боязни неслись в прямом смысле слова под пол, потом после того как было найдено скопище из 22 яиц под полом, они стали нестись в гнездо);
- На ночь курам выключают свет — они ложатся спать;
- Было размечено порядка 900 фото;
Изначально увидев такой расклад, я подумал, что получится даже отличать фотографии с количеством яиц, но нет. Чтобы максимизировать точность на такой небольшой выборке, надо постараться сделать несколько вещей:
- Правильно выбрать алгоритмы предобработки данных (логично, что куры крутятся в гнезде, поэтому можно вращать фотографии хоть на 180 градусов!);
- Выбрать адекватную по размеру и сложности модель;
Вы можете посмотреть почти полный лог экспериментов тут:
- ipynb jupyter notebook'а со всеми выкладками и boiler-plate код с пояснениями можно скачать тут, html версию тут;
Вот ее графическое представление:
Поясню состав слоев и почему в принципе почти любая современная CNN выглядит очень похоже:
- BatchNormalization — усредняет входящие данные (вычитает среднее и делит на стандартное отклонение), что ускоряет тренировку нейросети в десятки раз. Если интересно почему — то уходите в рекурсию тут или тут. Обязателен к применению;
- Convolution2D — сверточные слои. По сути представляют собой фильтры, чтобы нейросеть смогла научиться распознавать абстрактные образы. По идее визуализация должна была быть включена в эту статью, но у я не осилил в итоге (если вы не знаете как выглядят изнутри нейросети, то вот ссылки для вас 1 2 3;
- MaxPooling2D — усреднение значений фильтров. Обязателен после сверточных слоев;
- Dropout — по сути нужен для регуляризации. В эту спецификацию модели не включил его, потому что брал код из другого своего проекта и просто забыл из-за высокой точности модели;
- Flatten — чтобы потом вставить dense слой, иначе не получится;
Обратите внимание, что используется categorical_crossentropy вместе с softmax. В качестве оптимизатора используется adam. Не буду останавливаться почему я выбрал именно их (такое сочетание по сути стандарт), но вы можете почитать тут и посмотреть тут 1, 2 и 3.
Сам процесс поиска оптимальной модели выглядит так:
- Пробуем разные способы предобработки данных с простейшей моделью (внезапно веса простейшей dense модели из 3 слоев весят несколько гигабайт для больших фото, что не очень хорошо) и выбираем оптимальный;
- Пробуем разные архитектуры сверточных (или каких-либо других) нейросетей, выбираем оптимальную;
- При достижении какого-либо значимого бенчмарка (50%+ точности, 75% точности — выбирайте от задачи, чем больше классов, тем более мягким должен бенчмарк) — нужно проанализовать, с предсказанием каких фото и каких классов у модели возникают проблемы;
- Ансамбли и fine-tuning внешних слоев у больших архитектур нейросетей — опционально — если хотите выиграть конкурс;
- Можно попробовать подмешать в свою выборку 20-30% фото из тестовой, валидационной или просто неразмеченной части датасета (semi-supervised, код по идее работает, но у меня именно в этой задаче постоянно умирали kernel-ы, я забил);
- Повторять до бесконечности;
А так выглядит процесс гипнотизации прогресс индикатора.
Кстати этот сниппет кода поможет вам, если вы захотите сделать semi-supervised модель в keras
Но если вернуться к нашим курам, то после шага 3, проанализировав итоги модели, я увидел забавную закономерность.
Вот соотношение правильных предсказаний к числу фото
А вот основной источник неверных классификаций
Как ни странно, на такой небольшой выборке оказалось, что нейросеть хорошо отделяет кур от яиц, но яйца считает с трудом. Я смог построить отдельную модель, которая считала яйца с 50% точностью, но не стал продолжать ее тренировать, возможно яйца проще считать так.
Поместив все яйца в один класс в итоге я и получил модель примерно с 80% точности на небольшом датасете.
3. Что пойдет на прод?
Последним штрихом остался деплой модели на прод. Тут понятное дело нужно сохранить веса и написать пару функций, которые будут крутиться в демоне в бекграунде. Но тут на помощь пришла паста из-под видео в начале статьи.
Весь код такого боевого деплоя выглядит примерно так (тут используется tensor flow в качестве бекенда).
Вот в принципе и все.
Модель предсказывает верную курицу ) Ждите финального бота, который постит верных кур.