Опубликовано 17 мая 2018 года
Сразу после установки улья я подумал: «Интересно, как подсчитать количество прилетающих и улетающих пчёл?»
Небольшое исследование показало: похоже, до сих пор никто не придумал хорошей неинвазивной системы, решающей эту задачу. А ведь было бы наверное полезно иметь такую информацию для проверки здоровья улья.
Во-первых, нужно собрать образцы данных. Raspberry Pi, стандартная камера Pi и солнечная панель: этого простого оборудования достаточно, чтобы записывать один кадр каждые 10 секунд и сохранять 5000+ изображений в день (с 6 утра до 9 вечера).

Ниже пример изображения… Сколько пчёл вы можете сосчитать?

Во-вторых, нужно сформулировать проблему, что конкретно делать нейронной сети. Если задача «подсчёт пчёл в изображении», то можно попробовать получить конкретные числа, но это не кажется самым простым вариантом, да и отслеживание отдельных пчёл между кадрами не доставляет никакого удовольствия. Вместо этого я решил сосредоточиться на локализации каждой пчелы в изображении.
Быстрая проверка стандартного покадрового детектора не дала особых результатов. Это как бы не удивительно, особенно учитывая плотность пчёл вокруг входа в улей (подсказка: перенос обучения не всегда работает), но это нормально. Итак, у меня очень маленькое изображение, только один класс для распознавания объектов и нет особых проблем с ограничивающим прямоугольником как таковым. Просто решить, есть пчела или нет. Какое решение будет попроще?
Первым быстрым экспериментом стал детектор «пчела на картинке есть/нет». То бишь какова вероятность, что на данном фрагменте изображения есть по крайней мере одна пчела. Делать это в виде полностью сверточной сети на очень маленьких фрагментах картинки означает, что можно легко обработать данные в полном разрешении. Подход вроде работал, но терпел неудачу для района входа в улей с очень большой плотностью пчёл.
Я быстро понял, что задачу можно свести к проблеме трансформации изображения. На входе сигнал камеры RGB, а на выходе — изображение одиночного канала где «белый» пиксель обозначает центр пчёлы.

RGB-вход (фрагмент) и одноканальный выход (фрагмент)
Третий шаг — маркировка, то есть присвоение обозначений. Не слишком сложно развернуть небольшое приложение TkInter для выбора/отмены выбора пчёл на изображении и сохранения результатов в базе данных SQLite. Я потратил довольно много времени, чтобы правильно настроить этот инструмент: любой, кто выполнял вручную существенный объём маркировки, меня поймёт :/
Позже мы увидим, к счастью, что на большом количестве образцов можно получить довольно хороший результат полуавтоматическими методами.
Архитектура сети — вполне стандартная u-net.
После некоторых эмпирических экспериментов я решил вернуться к декодированию с половинным разрешением. Его было достаточно.
Я сделал декодирование через изменение размера по ближайшим соседям вместо деконволюции скорее по привычке.
Сеть обучалась методом Adam и оказалась слишком мала, чтобы применить пакетную нормализацию. Дизайн оказался на удивление простым, хватило небольшого количества фильтров.

Я примен��л стандартный метод аугментации данных, случайное вращение и искажение цвета. Обучение на фрагментах означает, что мы по сути получаем вариант случайной нарезки изображения. Я не поворачивал изображения, потому что камера всегда стоит на одной стороне улья.
Тут есть некоторый нюанс в постобработке предсказаний на выходе. С вероятностной выдачей мы получаем размытое облако там, где могут быть пчёлы. Чтобы преобразовать его в чёткую картинку по одному пикселю на пчелу, я добавил пороговое значение, учёт связанных компонентов и обнаружение центроидов с помощью модуля skimage measure. Всё это пришлось устанавливать вручную и настраивать чисто на глаз, хотя теоретически его можно добавить в конец стека как элемент обучения. Может, есть смысл сделать это в будущем… :)

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

Три образца, полученные в первый день
Всё стало сложнее, когда я начал учитывать более длительные периоды в несколько дней. Одно из ключевых отличий — разница в освещении (время суток и разная погода). Другая причина — то, что я каждый день устанавливал камеру вручную, просто приклеивая её липучкой. Третьим и самым неожиданным отличием стало то, что при росте травы бутоны одуванчиков выглядят как пчёлы (то есть в первом раунде обученная модель не видела бутонов, а потом они появились и обеспечили непрерывный поток ложноположительных срабатываний).
Бóльшую часть проблем удалось решить аугментацией данных, и ни одна проблема не стала критичной. В целом данные не слишком варьируются. Это замечательно, потому что позволяет ограничиться простой нейросетью и схемой обучения.

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

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

Слева направо: высокая плотность вокруг входа; пчёлы разного размера; пчёлы на высокой скорости!
Возможность получения большого количества изображений сразу наталкивает на мысль использовать полуконтролируемое обучение.
Очень простой подход:
В результате
Вот пример. Обратите внимание, что

Слева model_1, справа model_2
Подобные данные также являются отличным примером, как исправление плохой модели происходит быстрее, чем маркировка с нуля…
Это очень распространённый шаблон обучения, и иногда он заставляет немного пересмотреть свой инструмент маркировки.
Возможность обнаружения пчёл означает, что мы можем их посчитать! И нарисовать ради удовольствия прикольные графики, которые показывают количество пчёл в течение дня. Мне нравится, как они трудятся весь день и возвращаются домой около 4 вечера. :)

Запуск модели на Pi был важной частью этого проекта.
Изначально планировалось заморозить граф TensorFlow и просто напрямую запустить его на Pi. Это работает без проблем, но вот только Pi снимает лишь 1 изображение в секунду. :/
Меня очень заинтересовала возможность запустить модель на Pi с помощью Movidus Neural Compute Stick. Это удивительный гаджет.
К сожалению, ничего не получилось :/. API для преобразования графа TensorFlow в их внутренний формат модели не поддерживает мой способ декодирования. Поэтому пришлось увеличивать размер (upsizing), используя деконволюцию вм��сто изменений размера по ближайшим соседям. Здесь нет проблем кроме той, что ничего не получилось. Возникает куча маленьких сложностей, из-за которых множились баги. Когда их исправят, можно вернуться к этой теме…
Модель v3: изображение RGB → подсчёт пчёл
Это привело меня к третьей версии модели: можем ли мы перейти непосредственно со входа RGB на подсчёт пчёл? Так мы избежим любых проблем с неподдерживаемыми операциями на Movidus Neural Compute Stick, хотя маловероятно, что результат получится таким же хорошим, как в модели центроидов v2.
Сначала я опасался пробовать этот метод: я думал, что для него потребуется гораздо больше маркировки (это больше не система на основе фрагментов). Но! Имея модель, которая довольно хорошо справляется с поиском пчёл, и много немаркированных данных, можно сгенерировать неплохой набор синтетических данных, применив модель v2 и просто подсчитывая количество обнаружений.
Такая модель довольно проста в обучении и даёт осмысленные результаты… (хотя она всё-таки не так хороша, как простой подсчёт центроидов, обнаруженных моделью v2).
… к сожалению, модель по-прежнему не работает на Neural Compute Stick (то есть работает, но выдаёт только случайные результаты). Я составил ещё несколько баг-репортов и опять отложил гаджет, чтобы вернуться потом… когда-нибудь…
Как всегда, осталась куча мелочей…
Весь код опубликован на Github.
Сразу после установки улья я подумал: «Интересно, как подсчитать количество прилетающих и улетающих пчёл?»
Небольшое исследование показало: похоже, до сих пор никто не придумал хорошей неинвазивной системы, решающей эту задачу. А ведь было бы наверное полезно иметь такую информацию для проверки здоровья улья.
Во-первых, нужно собрать образцы данных. Raspberry Pi, стандартная камера Pi и солнечная панель: этого простого оборудования достаточно, чтобы записывать один кадр каждые 10 секунд и сохранять 5000+ изображений в день (с 6 утра до 9 вечера).

Ниже пример изображения… Сколько пчёл вы можете сосчитать?

В чём конкретно вопрос?
Во-вторых, нужно сформулировать проблему, что конкретно делать нейронной сети. Если задача «подсчёт пчёл в изображении», то можно попробовать получить конкретные числа, но это не кажется самым простым вариантом, да и отслеживание отдельных пчёл между кадрами не доставляет никакого удовольствия. Вместо этого я решил сосредоточиться на локализации каждой пчелы в изображении.
Быстрая проверка стандартного покадрового детектора не дала особых результатов. Это как бы не удивительно, особенно учитывая плотность пчёл вокруг входа в улей (подсказка: перенос обучения не всегда работает), но это нормально. Итак, у меня очень маленькое изображение, только один класс для распознавания объектов и нет особых проблем с ограничивающим прямоугольником как таковым. Просто решить, есть пчела или нет. Какое решение будет попроще?
v1: полностью свёрточная сеть «пчела есть/нет» на фрагменте
Первым быстрым экспериментом стал детектор «пчела на картинке есть/нет». То бишь какова вероятность, что на данном фрагменте изображения есть по крайней мере одна пчела. Делать это в виде полностью сверточной сети на очень маленьких фрагментах картинки означает, что можно легко обработать данные в полном разрешении. Подход вроде работал, но терпел неудачу для района входа в улей с очень большой плотностью пчёл.
v2: изображение RGB → чёрно-белое растровое изображение
Я быстро понял, что задачу можно свести к проблеме трансформации изображения. На входе сигнал камеры RGB, а на выходе — изображение одиночного канала где «белый» пиксель обозначает центр пчёлы.

RGB-вход (фрагмент) и одноканальный выход (фрагмент)
Маркировка
Третий шаг — маркировка, то есть присвоение обозначений. Не слишком сложно развернуть небольшое приложение TkInter для выбора/отмены выбора пчёл на изображении и сохранения результатов в базе данных SQLite. Я потратил довольно много времени, чтобы правильно настроить этот инструмент: любой, кто выполнял вручную существенный объём маркировки, меня поймёт :/
Позже мы увидим, к счастью, что на большом количестве образцов можно получить довольно хороший результат полуавтоматическими методами.
Модель
Архитектура сети — вполне стандартная u-net.
- полностью свёрточная сеть обучена на фрагментах с половинным разрешением, но работает на изображениях с полным разрешением;
- кодирование представляет собой последовательность из четырёх свёртываний 3×3 с шагом 2
- декодирование — последовательность изменений размера по ближайшим соседям + свёртывание 3×3 с шагом 1 + пропуск соединения от кодеров;
- окончательный слой свёртывания 1×1 с шагом 1 с активацией сигмоидной функции (то есть двоичный выбор «пчела есть/нет» для каждого пикселя).
После некоторых эмпирических экспериментов я решил вернуться к декодированию с половинным разрешением. Его было достаточно.
Я сделал декодирование через изменение размера по ближайшим соседям вместо деконволюции скорее по привычке.
Сеть обучалась методом Adam и оказалась слишком мала, чтобы применить пакетную нормализацию. Дизайн оказался на удивление простым, хватило небольшого количества фильтров.

Я примен��л стандартный метод аугментации данных, случайное вращение и искажение цвета. Обучение на фрагментах означает, что мы по сути получаем вариант случайной нарезки изображения. Я не поворачивал изображения, потому что камера всегда стоит на одной стороне улья.
Тут есть некоторый нюанс в постобработке предсказаний на выходе. С вероятностной выдачей мы получаем размытое облако там, где могут быть пчёлы. Чтобы преобразовать его в чёткую картинку по одному пикселю на пчелу, я добавил пороговое значение, учёт связанных компонентов и обнаружение центроидов с помощью модуля skimage measure. Всё это пришлось устанавливать вручную и настраивать чисто на глаз, хотя теоретически его можно добавить в конец стека как элемент обучения. Может, есть смысл сделать это в будущем… :)

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

Три образца, полученные в первый день
За много дней
Всё стало сложнее, когда я начал учитывать более длительные периоды в несколько дней. Одно из ключевых отличий — разница в освещении (время суток и разная погода). Другая причина — то, что я каждый день устанавливал камеру вручную, просто приклеивая её липучкой. Третьим и самым неожиданным отличием стало то, что при росте травы бутоны одуванчиков выглядят как пчёлы (то есть в первом раунде обученная модель не видела бутонов, а потом они появились и обеспечили непрерывный поток ложноположительных срабатываний).
Бóльшую часть проблем удалось решить аугментацией данных, и ни одна проблема не стала критичной. В целом данные не слишком варьируются. Это замечательно, потому что позволяет ограничиться простой нейросетью и схемой обучения.

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

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

Слева направо: высокая плотность вокруг входа; пчёлы разного размера; пчёлы на высокой скорости!
Хитрости маркировки
Полуконтролируемое обучение
Возможность получения большого количества изображений сразу наталкивает на мысль использовать полуконтролируемое обучение.
Очень простой подход:
- Съёмка 10 000 изображений.
- Маркировка 100 изображений и обучение
model_1. - Использование
model_1для маркировки остальных 9900 изображений. - Обучение
model_2на «размеченных» 10 000 изображений.
В результате
model_2 показывает лучший результат, чем model_1.Вот пример. Обратите внимание, что
model_1 демонстрирует некоторые ложноположительные (слева посередине и травинка) и ложноотрицательные срабатывания (пчёлы вокруг входа в улей).
Слева model_1, справа model_2
Маркировка путём исправления плохой модели
Подобные данные также являются отличным примером, как исправление плохой модели происходит быстрее, чем маркировка с нуля…
- Помечаем 10 изображений и обучаем модель.
- Используем модель для разметки следующих 100 изображений.
- Применяем инструмент маркировки для исправления меток на этих 100 изображениях.
- Переобучаем модель на 110 картинках.
- Повторяем...
Это очень распространённый шаблон обучения, и иногда он заставляет немного пересмотреть свой инструмент маркировки.
Подсчёт
Возможность обнаружения пчёл означает, что мы можем их посчитать! И нарисовать ради удовольствия прикольные графики, которые показывают количество пчёл в течение дня. Мне нравится, как они трудятся весь день и возвращаются домой около 4 вечера. :)

Вывод на Raspberry Pi
Запуск модели на Pi был важной частью этого проекта.
Непосредственно на железе Pi
Изначально планировалось заморозить граф TensorFlow и просто напрямую запустить его на Pi. Это работает без проблем, но вот только Pi снимает лишь 1 изображение в секунду. :/
Запуск на вычислительном модуле Movidius
Меня очень заинтересовала возможность запустить модель на Pi с помощью Movidus Neural Compute Stick. Это удивительный гаджет.
К сожалению, ничего не получилось :/. API для преобразования графа TensorFlow в их внутренний формат модели не поддерживает мой способ декодирования. Поэтому пришлось увеличивать размер (upsizing), используя деконволюцию вм��сто изменений размера по ближайшим соседям. Здесь нет проблем кроме той, что ничего не получилось. Возникает куча маленьких сложностей, из-за которых множились баги. Когда их исправят, можно вернуться к этой теме…
Модель v3: изображение RGB → подсчёт пчёл
Это привело меня к третьей версии модели: можем ли мы перейти непосредственно со входа RGB на подсчёт пчёл? Так мы избежим любых проблем с неподдерживаемыми операциями на Movidus Neural Compute Stick, хотя маловероятно, что результат получится таким же хорошим, как в модели центроидов v2.
Сначала я опасался пробовать этот метод: я думал, что для него потребуется гораздо больше маркировки (это больше не система на основе фрагментов). Но! Имея модель, которая довольно хорошо справляется с поиском пчёл, и много немаркированных данных, можно сгенерировать неплохой набор синтетических данных, применив модель v2 и просто подсчитывая количество обнаружений.
Такая модель довольно проста в обучении и даёт осмысленные результаты… (хотя она всё-таки не так хороша, как простой подсчёт центроидов, обнаруженных моделью v2).
| Реальное и прогнозное количество пчёл в некоторых тестовых образцах | ||||||||||||
| Реальное | 40 | 19 | 16 | 15 | 13 | 12 | 11 | 10 | 8 | 7 | 6 | 4 |
| v2 (центроиды) прогнозное | 39 | 19 | 16 | 13 | 13 | 14 | 11 | 8 | 8 | 7 | 6 | 4 |
| v3 (простой подсчёт) прогнозное | 33,1 | 15,3 | 12,3 | 12,5 | 13,3 | 10,4 | 9,3 | 8,7 | 6,3 | 7,1 | 5,9 | 4,2 |
… к сожалению, модель по-прежнему не работает на Neural Compute Stick (то есть работает, но выдаёт только случайные результаты). Я составил ещё несколько баг-репортов и опять отложил гаджет, чтобы вернуться потом… когда-нибудь…
Что дальше?
Как всегда, осталась куча мелочей…
- Запуск на Neural Compute Stick (NCS); сейчас ждём некоторой работы с их стороны...
- Портировать всё на встроенную камеру JeVois. Я немного повозился с ней, но в первую очередь хотел запустить модель на NCS. Я хочу отслеживать пчёл на 120 FPS!!!
- Отслеживать пчёл между несколькими кадрами/камерами для визуализации оптического поток��.
- Более детально изучить преимущества полуконтролируемого подхода и обучить более крупную модель маркировать данные для меньшей модели.
- Исследовать возможности NCS; что делать с настройкой гиперпарамеров?
- Перейти к разработке маленькой версии FarmBot для выполнения некоторых генетических экспериментов с рассадой под управлением ЧПУ (то есть нечто совершенно другое).
Код
Весь код опубликован на Github.
