Kaggle – наша экскурсия в царство оверфита

Kaggle — это платформа для проведения конкурсов по машинному обучению. На Хабре частенько пишут про неё: 1, 2, 3, 4, и.т.д.
Конкурсы на Kaggle интересные и практичные. Первые места обычно сопровождаются неплохими призовыми (топовые конкурсы — более 100к долларов). В последнее время на Kaggle предлагали распознавать:
  • Диабет по сетчатке глаза
  • Фотографии со спутников и их порядок
  • Одинаковые рекламные объявления

И многое-многое другое.
Мне давно хотелось попробовать, но что-то всё время мешало. Я разрабатывал много систем, связанных с обработкой изображений: тематика близка. Навыки более лежат в практической части и классических Computer Vision (CV) алгоритмах, чем в современных Machine Learning техниках, так что было интересно оценить свои знания на мировом уровне плюс подтянуть понимание свёрточных сетей.
И вот внезапно всё сложилось. Выпало пару недель не очень напряжённого графика. На kaggle проходил интересный конкурс по близкой тематике.Я обновил себе комп. А самое главное — подбил vasyutka и Nikkolo на то, чтобы составить компанию.
Сразу скажу, что феерических результатов мы не достигли. Но 18 место из 1.5 тысяч участников я считаю неплохим. А учитывая, что это наш первый опыт участия в kaggle, что из 3х месяц конкурса мы участвовали лишь 2.5 недели, что все результаты получены на одной единственной видеокарте — мне кажется, что мы хорошо выступили.
О чём будет эта статья? Во-первых, про саму задачу и наш метод её решения. Во-вторых, про процесс решения CV задач. Я писал достаточно много статей на хабре о машинном зрении (1,2,3), но писанину и теорию всегда лучше подкреплять примером. А писать статьи по какой-то коммерческой задаче по очевидным причинам нельзя. Теперь наконец расскажу про процесс. Тем более что тут он самый обычный, хорошо иллюстрирующий как задачи решаются. В-третьих, статья про то, что идёт после решения идеализированной задаче в вакууме: что будет когда задача столкнётся с реальностью.
2200603a2cb5438798dec072ee0c500c.jpg

Анализ задачи


Задача, которую мы начали делать звучала следующим образом: «Определить принадлежность водителя на фотографии к одной из десяти групп: аккуратное вождение, телефон в правой руке, телефон у правого уха, телефон в левой руке, телефон у левого уха, настройка музыки, пьёт жидкость, тянется назад, поправление причёски (красит губы, чешет затылок), разговор с соседом». Но, как мне кажется, лучше один раз посмотреть на примеры:
031f9a4d6badb4318989a0b62252fc137.jpg 126fc7b6f7e7f4f9c9742d1cbac440766.jpg 2eda9a090a94d45ddb9a50a759a35f099.jpg
30c688546b0824224aa24df0c09994cf7.jpg 49cbf6908289b4c578deae97acf90839e.jpg 58762da094444454da5320fa35a3ff107.jpg
68910901bb68c42038e57dcc86880fbd5.jpg 73c929674ffab4d8aaf89917b149eff7d.jpg 85fbde76ebb7b44d6b7361a1ae62110d5.jpg
9bc1935f8fe5747b291acb2095bfa74ce.jpg
Англ.
  • c0: safe driving
  • c1: texting — right
  • c2: talking on the phone — right
  • c3: texting — left
  • c4: talking on telephone — left
  • c5: operating the radio
  • c6: drinking
  • c7: reaching behind
  • c8: hair and makeup
  • c9: talking to passenger


*******************************************
Всё кажется понятным и очевидным. Но это не совсем так. Какому классу принадлежат вот эти два примера?
665cbc1ea74c40db9fcab658a31ef95a.jpgfc8e0f5307984033b13a18a585a5fd5f.jpg
Первый пример — 9 класс, разговор. Второй пример — первый класс, безопасное вождение.
По нашей оценке точность человека при распознавании класса по базе составляет где-то 94%. При этом наибольшую сумятицу вносит именно первый и десятый классы. На момент начала нашего участия первые места имели точность примерно на уровне 97% правильных распознаваний. Да-да! Роботы уже лучше машин!
Несколько подробностей:
  • Размер базы для обучения 22 тысячи изображений.
  • Примерно по 2 тысячи на класс.
  • Всего в обучающей базе 27 водителей.
  • Тестовая база — 79 тысяч изображений.

Сегодня основным средством для решения задач такого плана являются свёрточные нейронные сети. Они производят анализ изображения на многих уровнях, самостоятельно выделяя ключевые особенности и их отношения. Прочитать про свёрточные сети можно тут, тут и тут. У свёрточных сетей есть ряд минусов:
  • Нужно много разнообразных данных для обучения. Две тысячи на класс это более менее достаточно, хотя и мало. Но то, что водителей всего 27 — очень ограничивает.
  • Свёрточные сети склонны к «оверфиту». При обучении могут зацепиться за какой-то малозначимый признак, свойственный нескольким изображениям, но который не несёт существенного веса. У нас таким признаком стало открытие солнцезащитного козырька. Все фотографии с ним автоматом шли в 8 класс. Да и вообще: при оверфите сетка просто напросто может запомнить все 22 тысячи входных изображений. Вот хороший пример оверфита.
  • Свёрточные сети неоднозначны из-за того что сложны. Приблизительно одно и то же решение на разных фреймворках может давать принципиально разные результаты. Несколько чуть-чуть изменённых параметров сети принципиально изменяют результат. Многие называют настройку сетей чем-то сродни искусству.

Альтернативам свёрточных сетей может являться ручной менеджмент низкоуровневых признаков. Выделить руки. Положение лица. Выражение лица. Открытый/закрытый козырёк у машины.
Свёрточных сетей очень много разных. Классический подход — использовать наиболее распространённые сети из «Зоопарка» (caffe theano keras). Это в первую очередь VGG16, VGG19, GoogleNet, ResNet. Для этих сетей есть много вариаций плюс можно использовать техники ускоряющие обучение. Конечно, данный подход используют все участники. Но базовый неплохой результат можно получить только на нём.

Наш сетап


Все вычисления в нашей работе проходили на одной единственной GTX1080. Это последняя игровая карта у NVIDIA. Не самый лучший вариант из того, что есть на рынке, но достаточно неплохой.
Мы хотели использовать кластер с тремя Tesla на одной из работ, но из-за ряда технических сложностей это не получилось. Так же обдумывали использование какой-то старой видеокарты из ноута на 4Gb, но в результате решили не идти по этому пути, там было сильно меньше скорости.
Используемый фреймворк — Caffe. Можно было бы использовать и Keras с Theano, что, безусловно повысило бы наш результат из-за несколько другой имплементации обучения. Но у нас не было на это времени, так что использовали по максимуму именно Caffe.
Оперативка — 16Gb, максимум при обучении использовалось 10Gb. Процессор — i5 последний.
Если вдруг кому интересно, но ничего особенного нет
2215e4fb5e9345eb9c67b79bfb2d0778.jpg

Пару слов о правилах


Я думаю, что большая часть читателей никогда не участвовала в kaggle, так что проведу небольшой экскурс в то, какие правила у соревнования:
  • Участникам представляется два набора данных. Тренировочный — на котором подписаны целевые результаты. Тестовый — по которому нужно произвести распознавание.
  • Участник должен распознать все изображения из тестового сета и отправить их на сайт в csv-формате (текстовый файл вида «номер изображения» — вероятность первого класса, вероятность второго класса, …).
  • Всего в день можно сделать 5 попыток отправки.
  • После каждой отправки пользователю говорят текущий процент по тестовой базе. Но, нужно учесть один интересный момент. Пользователю говорят результат не по всей базе, а только по небольшой её части. В нашей задаче это было 39%.
  • Итоговый результат соревнования считается по оставшемуся куску базы (61%) после закрытия конкурса. Это может привести к серьёзным перестановкам участников.
  • Первые три места — призовые. Если участник попадает в них, он должен опубликовать своё решение и организатор проверит его.
  • Решение не должно:
    • Содержать проприетарные данные
    • Использовать размеченную пользователем тестовую выборку при обучении. При этом доразмечать обучающую выборку разрешается.

Пару слов о метрике


Предположим, что мы придумали некоторый механизм распознавания и распознали все изображения. Как они будут дальше проверяться? В данной задаче использовался подход вычисления мультиклассовых логарифмических потерь. Вкратце его можно записать как:
c4d08d5e8fd8400a8ac0941ff2a717e9.png
y — матрица решений. Единица если объект принадлежит классу.
p — матрица ответов которые прислал пользователь. Лучше всего записывать вероятность принадлежности классу.
M-число классов
N- число объектов
В окрестности нуля значение заменяется константой.
Мы прикинули, что вероятности »95%» соответствует значение logloss примерно »0.2», значение »0.1» соответствует вероятности »97.5%». Но это грубые оценки.
Мы ещё вернёмся к этой функции, но несколько ниже.

Первые шаги


Теория хороша. Но с чего начинать? Начнём с самого простого: возьмём сетку CaffeNet, которая приложена к Caffe и для которой есть пример.
После того, как я сделал аналогичное действие — сразу получил результат »0.7786», который был где-то на 500 месте. Забавно, но у многих результат был значительно хуже. При этом, стоит отметить, что 0.77 приблизительно соответствует 80–85% правильных распознаваний.
Не будем останавливаться на этой сетке, уже достаточно устаревшей. Возьмём что-нибудь стандартное современное. К стандартным можно причислить:
  • Семейство VGG:
    • VGG-16
    • VGG-19

  • Семейство ResNet:
    • ResNet-50
    • ResNet-101
    • ResNet-152

  • Семейство Inception

Про нестандартные методы можно прочитать чуть ниже в части «Идеи, которые не прокатили».
Так как мы начинали где-то через два с половиной месяца после начала соревнования имело смысл исследовать форум. На форуме советовали VGG-16. Автор поста уверял, что получил решение с потерями »0.23» на базе данной сети.
Рассмотрим решение автора:
  1. Он использовал предобученную сеть VGG. Это значительно повышает скорость обучения.
  2. Он обучил не одну, а 8 сеток. При обучении каждой сетки на вход подавалось лишь 1/8 входной базы данных (22/8 тысячи изображений).
  3. Полученное решение давало уровень в 0.3–0.27
  4. Итоговый результат он получил сложением результата этих 8 сеток.

Решение повторить у нас не удалось. Впрочем, много у кого не получилось сделать это. Хотя автор выкладывал обучающий скрипт на Keras«е. Судя по всему на Keras«е его можно было достичь, но не на caffe. Победитель, занявший третье место тоже считал VGG на Keras, а все остальные сетки на Caffe и Theano.
Нам же чистый VGG давал 0.4, что, конечно, улучшало наш результат на тот момент, но лишь места до 300ого.
В результате от VGG мы решили отказаться и опробовали обучение предобученного ResNet-50 (вот тут можно интересно почитать что это такое). Что немедленно дало нам 0.3–0.29.
Маленькая ремарка: мы так и не использовали технику «разбить базу на 8 частей». Хотя, скорее всего, она бы принесла нам небольшую дополнительную точность. Но такое обучение бы заняло несколько дней, что было нам неприемлемо.
Зачем нужно разбиение базы на 8 частей и обучение независимых сеток? Предположим, первая из сеток всегда ошибается в пользу ситуации А, при выборе из А и В. Вторая сетка наоборот — выносит решение В. При этом обе сетки часто ошибаются относительно реального решения. Но сумма сеток будет более корректно оценивать риск А/В. По сути она в большинстве спорных ситуаций поставит 50% — А, 50% — В. Такой подход минимизирует наши потери. Мы же достигали его по-другому.

Чтобы повысить точность с 0.3 мы сделали следующий ряд действий:
  • Обучили несколько сеток ResNet-50 с различными гиперпараметрами. Все они давали где-то 0.3–0.28, но сумма их была больше.
  • Обучили сетку ResNet-101, которая сама по себе дала где-то 0.25
  • Обучили сетку ResNet-50 с изменениями изображения, что дало где-то 0.26

Изменения изображения следующие: при обучении вместо исходной обучающей картинки подаётся повёрнутая картинка, картинка подрезанная, картинка, зашумлённая помехами. Это повышает стабильность работы и повышает точность.
Итоговый результат сложения всех перечисленных сеток был на уровне »0.23–0.22».

К новым вершинам


Результат 0.22 был где-то в районе 100 места. Это уже неплохой результат. По сути тот максимум, который даёт корректно настроенная сеть. Но чтобы идти дальше нужно остановиться, подумать и осмыслить сделанное.
Самый простой способ сделать это — посмотреть «Confusion Matrix». По сути за этим понятием скрывается бюджет ошибок. Как и когда мы ошибаемся. Вот матрица, которая у нас вышла:
3c081ad4f0e14484a7ef044848cd10d8.png
В этой матрице по оси x — это объекты исходного класса. По оси y — куда они были отнесены. Например, из 100% объектов нулевого класса 72% было успешно отнесено в него самого, 0.8%- в первый класс, 16.8% — в девятый.
Из матрицы можно сделать следующие выводы:
  • Наибольшее число ошибок — нулевой и девятый класс. Это логично. Я зачастую сам не могу понять, говорит человек, или нет. Выше по тексту был пример.
  • Второй по числу ошибок — восьмой класс. Любой класс где рука бывает рядом с головой может быть отнесён в восьмой класс, если информации недостаточно. Это разговоры по телефону. Это разговоры с соседом, с активной жестикуляцией. В некоторых ситуациях, когда человек пьёт жидкость — он тоже может быть похож.

Следовательно, нужно разработать алгоритм, который более корректно может различить эти три класса.
Для того, чтобы это сделать мы воспользовались следующими идеями:
  • На взгляд человека 90% информации, позволяющей различить 0 и 9 класс содержится на лице. При этом в большинстве случаев, когда ошибалось отнесение в 8 класс — тоже итоговое решение принимается по области вокруг лица.
  • Все сетки, приведённые выше были заточены на разрешение 224×224. При этом качество лица сильно просаживается.

Значит, нам нужно сохранить разрешение около лица и использовать его для уточнения 0–8–9 классов. Всего у нас было три идеи как это сделать. Про две из них будет написано ниже в разделе «Идеи, которые не прокатили». Прокатила следующая идея:
Обучаем простой классификатор Хаара на выделение лица. В принципе, лицо можно даже неплохо по цвету выделить. Учитывая что мы неплохо знаем, где лицо должно оказаться.
Правила соревнований не запрещали ручную разметку тренировочной базы. Поэтому мы отметили где-то на 400 изображениях лицо. И получили весьма неплохую подборку кадров распознанную в автоматическом режиме (лица найдены корректно на 98–99% кадров):
d8d522b791e94995a2d0ac1bc92d3c37.jpg2090f8b64e6a462892010ee5e6759fcc.jpg9e62dde536b045b2b984223a3e1f96d3.jpg
Обучив ResNet-100 по изображениям мы получили точность где-то на уровне 80%. Но добавление результатов обучения в сумму используемых сетей дало дополнительные 0.02 по тестовой выборке переместив нас в район тридцатых мест.

Идеи, которые не прокатили


Разорвём стройную канву повествования и сделаем небольшой шаг в сторону. Без этого шага всё хорошо, а с этим шагом становиться понятно, что происходило в голове на тот момент.
Идей, которые не дают результата в любой исследовательской задаче сильно больше, чем идей, которые дают результат. А иногда случается, что идеи по тем или иным причинам нельзя использовать. Тут небольшой список идей, которые мы уже успели опробовать на момент выхода на тридцатые места.
Первая идея была простой как бревно. Мы уже писали на Хабре (1, 2) про сети, которые могут раскрашивать изображения по классовой принадлежности объектов. Нам показалось, что это очень хороший подход. Можно научить сеть детектировать именно то, что нужно: телефоны, руки, открытый козырёк у машины. Мы даже потратили два дня на разметку, настройку и обучения SegNet. И вдруг мы поняли, что SegNet имеет закрытую не OpenSource лицензию. А следовательно мы не можем честно его использовать. Пришлось отказаться. А результаты автоматической разметки были перспективными (здесь показано сразу несколько подходов).
Первый:
24410c0c740a4cceba29cc83a9f1d117.png9960a8d4224b42c29940976a00c4c2a7.png
Второй:
92a671b80f4d4fbd85b918dbece6097e.png171f59d754f74800a4358aa699aad4b9.png
А вот так выглядит процесс разметки:
e4c40208a6ce40488f9302618dfe6bae.jpg6d0f40185bcd4f5fa4987279cab6c9b5.jpg

Вторая идея касалась того, что разрешения 224×224 нам не хватает для принятия решения 0 класс или 9. Наибольшие проблемы доставляет потеря разрешения на лицах. Но нам было известно, что лицо практически всегда находиться в левой верхней части изображения. Поэтому мы перетянули картинки, получив таких милых головастиков с максимальным разрешением в интересных нам местах:
0ecc33271a1a47e1ba20bc9ad110f610.jpgaeab8d0ba5584a089224b6e7b77f4010.jpg

Не прокатило. Результат был примерно как от обычного обучения + сильно коррелированный с тем, что у нас было.
Следующая идея была достаточно большой и объемлющей. Посты на форуме конкурса натолкнули нас на размышления:, а что же сеть видит в реальности? Что её интересует?
Есть целый подбор статей на эту тему: 1, 2, 3
На форуме Kaggle приводились такие классные картинки:
image

Мы, естественно, решили изобрести свой велосипед, для данной задачи. Тем более пишется он очень просто.
Берём исходное изображение и начинаем двигать по нему чёрный квадрат. При этом смотрим, как изменяется отклик системы.
9be3bf6229d24e62a2c068f85e09a986.jpg
Результат же отрисуем в качестве тепловой карты.
510040b30db24c58a811bb834e29fbf4.jpg
На изображении тут показан пример неправильной классификации изображения класса 9 (разговор с соседом). Изображение классифицировано как «тянется к контролю музыкой». И действительно, похоже. Ведь сеть сама по себе не видит 3d. Она видит руку, которая лежит в направлении к переключателю приборной панели. Ну и что что рука лежит на ноге.
Посмотрев ещё с несколько десятков ошибок мы поняли, что опять всё упирается туда же: сетка не смотрит на то, что происходит на лице.
Поэтому мы придумали другой способ обучения. На вход сети подавался набор, где половина картинок шло напрямую из обучающей базы, а половина была с затёртым всем кроме лица:
a4ab9c32ac044ca8824fa93140f3e697.jpg
И о чудо, сетка стала на порядки лучше работать! Например по ошибочно классифицированному мужику:
3e5c8474375b4c658c4e139a94c24e9d.jpg
Или вот, по другому (как было — как стало):
3687e41e89934155a3a1834db32d051a.jpg14cbe671635c4dc592010a89c52c3c1f.jpg
При этом сеть неплохо работала и по остальным классам (отмечала корректные зоны инереса).
Мы уже хотели праздновать победу. Загрузили сеть на проверку — хуже. Объединили с нашей лучшей — итоговый результат не улучшился. Даже не помню, стали ли мы её добавлять в наш лучший ответ. Долго думали, что у нас какая-то ошибка, но ничего не нашли.
Вроде как с одной стороны — сеть стала правильнее смотреть и много какие ошибки исправлять. С другой стороны где-то начала новые делать. Но при этом значимой статистической разницы не давала.
Идей которые не прокатили было сильно больше. Был Dropout, который нам почти ничего не дал. Были дополнительные различнае шумы: тоже не помогло. Но про это ничего красивого и не напишешь.

Вернёмся к нашим баранам


Мы остановились где-то в районе 30х мест. Осталось немного. Множество идей уже провалилось, на компе накопилось штук 25 тестовых проектов. Улучшений не было. Наши знания о нейронных сетях плавно начали исчерпываться. Поэтому пошли гуглить форум текущего конкурса и старые форумы kaggle. И решение нашлось. Оно называлось «pseudo labeling» и «Semi-supervised learning». И ведёт оно на тёмную сторону. Вернее серую. Но было объявлено админами конкурса как легальное.
Вкратце: мы используем тестовую выборку для обучения разметив её алгоритмом обученным по тренировочной выборке. Звучит странно и сумбурно. Но если подумать, то оно забавно. Загоняя в сеть объекты, которые ей же размечены мы ничего не улучшаем в локальной перспективе. Но. Во-первых, мы учимся выделять те признаки, которые дают такой же результат, но проще. По-сути мы обучаем свёрточные уровни таким образом, чтобы они научились выделять новые признаки. Может на каком-то следующем изображении они выделяться и лучше помогут. Во-вторых, мы защищаем сеть от переобучения и оверфита, внося псевдо-произвольные данные, которые заведомо не ухудшат сеть.
Почему этот путь ведёт на серую сторону? Потому что формально использовать тестовую выборку для обучения запрещено. Но ведь мы тут её не используем для обучения. Только для стабилизации. Тем более админы разрешили.
Результат: + 10 позиций. Попадаем в двадцатку.
Итоговый график блуждания результатов выглядел примерно следующим образом (в начале мы не расходовали все попытки в день):
9c75a922e6314f24ae231b53e2175024.png

И ещё раз про LogLoss


Где-то в начале статьи я упомянул про то, что к LogLoss я ещё вернусь. С ним всё не так просто. Можно заметить, что log (0) это минус бесконечность => если вдруг поставить 0 в классе где ответ единица, то мы получим минус бесконечность.
c4d08d5e8fd8400a8ac0941ff2a717e9.png
Неприятно. Но организаторы защитили нас от этого. Они говорят, что они заменяют величину стоящую под log на max (a,10^(-15)). А значит мы получаем добавку -15/N к результату. Что равно -0.000625 к результату за каждое ошибочное изображение для публичной проверки и -0.0003125 для закрытой. Десять изображений влияют на ошибку в третьем знаке. А это уже позиции.
Но ошибку можно уменьшить. Предположим, вместо 10^(-15) мы поставим 10^(-4). Тогда если мы ошибёмся мы получим -4/N. Но если мы угадаем правильно, то у нас тоже будут потери. Вместо log (1)=0 мы возьмём log (0.9999), что равно -4×10^(-5). Если мы ошибаемся раз в 10 попыток, то нам безусловно это выгоднее, чем потери 10^(-15).
А дальше начинается магия. Нам нужно объединить 6–7 результатов, чтобы оптимизировать метрику LogLoss.
Всего мы делали 80 отправок результата. Из них где-то 20–30 было посвящено именно оптимизации потерь.
Я думаю, что 5–6 мест у нас отыграно за этот счёт. Хотя, как мне кажется, все этим занимаются.
Всю эту магию делал Vasyutka. Я даже не знаю, как последний вариант выглядел. Лишь его описание, которое мы себе сохранили, достигало двух абзацев.

Что мы не сделали


На момент окончания конкурса у нас остался небольшой стэк идей. Наверное, если бы мы всё успели, то это стоило бы нам ещё пяток позиций. Но мы понимали, что это явно не выход в топ-3, поэтому не бросали на борьбу все оставшиеся силы.
  • Мы так и не исследовали разбиение базы на небольшие кусочки и независимое их обучение. В теории это добавляет стабильности. Мы использовали похожий подход при Semi-supervised learning, но тоже не целиком исследовав всё поле возможностей.
  • Был ещё один серый механизм, который мы всё же не решили использовать, но долго думал. Хотя от администрации не было явного на него запрета. Механизм: найти на тестовой выборке фотографии которые соответствуют разным машинам, построить их модель и вычесть из фотографий фон. Работы тут где-то на 2–3 дня. Но не хотелось: не верили ни в то, что выведет в десятку, ни в то, что идею не запретят.
  • Мы не исследовали формирование батча (небольшого массива по которому происходит шаг обучения). Только общий принцип, чтобы статистически он был правильный. Можно было создавать батчи из разных водителей, из одинаковых, и.т.д. Народ на форуме этим развлекался. В принципе идея здравая. Но решили не делать по соображениям времени.
  • За пару дней до конца я вспомнил о существовании такого метода. Этот метод позволяет заставить сеть обучаться на общих признаках классов, а не на одинаковых. По сути, исключить фоновые помехи, личность водителя и тип машины. Исходников на этот метод нет, пришлось бы делать сеть с нуля. Так же не ясен вопрос с открытостью/закрытостью. Так как времени оставалось 2 дня — отказались.

Анализ решений других участников


После конца конкурса многие участники публикуют свои решения. Вот тут ведётся статистика по лучшим 20 из них. Сейчас где-то половина из топ-20 опубликовала решения.
Начнём с лучшего из опубликованных. Третье место.
imageimage
Скажу сразу. Решение мне не нравиться и кажется нарушением правил конкурса.
Авторы решения отметили, что все примеры снимались последовательно. Для этого они проанализировали тестовую выборку в автоматическом режиме и нашли соседние кадры. Соседние кадры — это изображения с минимальным изменением => они принадлежат одному классу => по всем близким решениям можно вынести единый ответ.
И да, это ужасно помогает. Если вы разговариваете по телефону держа его в левой руке — есть кадры где не видно телефона и непонятно, говорите вы или чешите голову. Но посмотрев на соседний кадр можно всё прояснить.
Я бы не возражал, если бы таким образом накапливалась статистика и вычиталась фоновая машина. Но реверсинженерить видео — это по мне за гранью добра и зла. Почему-объясню ниже. Но, конечно, решение за организаторами.

Мне очень нравиться пятое решение. Оно такое классное и настолько тривиальное. Мысль приходила мне в голову раз десять по ходу конкурса. Но каждый раз я отметал её. «Зачем?!». «Почему это вообще будет работать?!». «Лень тратить время на эту бесперспективщину».
Я даже не обсудил её с товарищами. Как оказалось — зря. Идея вот:
image
Взять две картинки одного класса. Разбить пополам и склеить. Подать в обучение. Всё.
Я не очень понимаю почему это работает (кроме того, что это стабилизирует выборку: 5 миллионов сэмплов это круто, это не 22 тыщи). А ещё у ребят было 10 карточек TitanX. Может это сыграло важную роль.

Шестое решение плохо описано, я его не понял. Девятое очень похоже на наше. Да и точность отличается не сильно. Судя по всему ребята смогли натренировать сети лучше и качественнее. Подробно они не описали за счёт чего небольшой прирост.

Десятое решение реализовало часть наших идей, но чуть чуть по-другому:
image
Вырезать область с человеком, чтобы увеличить разрешение → подать на обучение. Решаются те же проблемы которые мы решали вырезанием лица. Но, судя по всему, лучше.

15 решение — всё как у нас. Даже лиц так же вырезали (плюс область руля от чего мы отказались).
Но… Они натренировали 150 моделей и складывали их. 150!!!

19, 20 решение — всё как у нас, но только без лиц. И 25 натренированных моделей.

От игрушек к делу


Предположим вы страховая фирма, которая хочет внеедрить систему определения того что делает водитель. Вы собрали базу, несколько команд предложили алгоритмы. Что имеем:
  1. Алгоритм, который использует данные о соседних кадрах. Это странно. Если бы вы хотели, чтобы ваш алгоритм так работал — вы бы предложили анализировать не фотографии, а видео. Более того. Такой алгоритм почти невозможно завязать с видео как таковым. Если у вас есть алгоритмы, которые распознают отдельный кадр — его очень легко можно надстроить до алгоритма работающего с видео. Алгоритм, который использует »5 ближайших кадров» — очень сложно.
  2. Алгоритмы, которые используют по 150 нейронных сетей. Это огромные вычислительные мощности. Такой продукт не получиться делать массовым. 2–3 сети это по хорошему максимум. Ок, пусть 10 — предел. Одно дело, если бы ваша задача была детектировать рак. Там такие затраты допустимы. Там нужно бороться за каждый процент. Но ваша цель — массовый продукт.

Но, ещё получили несколько неплохих и интересных моделей.
Давайте пойдём дальше. И поймём насколько работают эти модели. К сожалению, их у меня нет, так что тестировать буду на нашей, которая дала 18й результат, что в принципе не так уж и плохо.
Из всех статей, которые я написал на Хабр моя любимая про то как надо собирать базу. Вот с этой стороны и подойдём к анализу. Что мы знаем о собранной базе?
  • При наборе базы водители машину не водили. Машина ехала на грузовике, водителям давались задания что делать.
  • Вся база набиралась в дневные часы. Судя по всему в рабочие. Нет низкого солнца. Нет вечерних кадров.
  • Все ситуации перфекционизированны. Люди не держат телефон левой рукой у правого уха. Телефоны у всех плюс-минус одинаковые.
  • База набиралась в Америке. Вы спросите как я понял? Нет ни одной машины с ручной коробкой…

На первый раз хватит четырёх ситуаций. Но их значительно больше. Все ситуации нельзя предвидеть никогда. Именно поэтому базу нужно набирать настоящую, а не моделировать.
Поехали. Кадры ниже делал сам.
Как повлияло то, что при наборе базы водители не водили. Вождение, это достаточно сложный процесс. Нужно крутить головой градусов на 120, смотреть на светофоры, круто поворачивать на перекрёстках. Всего этого в базе нет. А следовательно, такие ситуации определяются как «разговор с соседом». Вот пример:
863c50cb5fcf45e4b9e449914a7039e6.jpgd01ea221219d403a83652eb90965103d.jpga378ef944d9c4c089008ea538fd83d9e.jpg
Дневные часы. Это очень большая проблема. Конечно, можно сделать систему, которая будет ночью смотреть на водителя включая ИК подсветку. Но в ИК подсветке человек выглядит совсем по другому. Нужно будет целиком переделывать алгоритм. Скорее всего тренировать один на день и один на ночь. Но есть не только проблема ночи. Вечер — когда ещё светло, но уже темно для камеры и есть шумы. Сетка начинается путаться. Веса на нейронах гуляют. Первая из картинок распознана как разговор с соседом. Вторая картинка — прыгает между «тянется назад», «мэйкап» (что в рамках сети логично, ведь я тянусь к козырьку), «разговор с соседом», «безопасное вождение». Солнце на лице — очень неприятный фактор, знаете ли…
14e1eb6ba4fb40d788f645d0759f9159.jpgc1e7cd7276b7416288ae53a9103bb7b8.jpg
Про перфекционизм. Вот более-менее реальная ситуация:
3d837cd2a9894540968fa133e131639b.jpg
И веса на нейронах: 0.04497785 0.00250986 0.23483475 0.05593431 0.40234038 0.01281587 0.00142132 0.00118973 0.19188504 0.0520909
Максимум на том, что телефон у левого уха. Но сеть немножко сошла сума.
Про Россию вообще молчу. Почти все кадры с рукой на ручке передач распознаны как «тянется назад»:
01c6b58e12274835b75a8a118fddf0aa.jpg1b34ace178c04d10b2c8338066be401f.jpg

Видно, что проблем достаточно много.Я не стал тут добавлять снимки где я пробовал обмануть сеть (включением фонаря на телефоне, странными шапками, и.т.д.). Это реально и это обманывает сеть.

Паника. Почему всё так плохо? Вы же обещали 97%!


А проблема в том, что НИКАКАЯ система компьютерного зрения не делается с первой итерации. Всегда должен быть процесс запуска. Начиная с простого тестового образца. С набора статистики. С исправлением появляющихся проблем. А они будут всегда. При этом 90% нужно исправлять не программно, а административно. Попался на том, что пробовал обмануть систему — получи пирожок. Кто-то не так крепит камеру? Будь добр, крепи по человечески. А не то сам дурак.
Делая разработку нужно быть готовым, что прежде чем получиться идеальный результат нужно будет всё 2–3 раза переделать.
И мне кажется, что для данной задачи всё не плохо, а наоборот, хорошо. Показанные 95–97% работоспособности по тесту хороши и означают, что система перспективна. Это значит, что итоговую систему можно будет довести до плюс-минус такой же точности. Просто нужно вложить в разработку ещё в 2–3 раза больше сил:)
Кстати. Про крепление камеры. судя по всему, то как крепили камеру набирая статистику мешает пассажиру. То, как крепил камеру я — даёт немножко другую картинку, по которой падает статистика. Зато не мешает пассажиру. Думаю, что решение с камерой мешающей пассажиру будет непригодно, скорее всего будут пересобирать базу. Или сильно дособирать.
Так же не понятно где планируются обрабатываться изображения. В модуле, который будет снимать? Тогда нужно там иметь прилично вычислительной мощи. Отправлять на сервер? Тогда это явно единичные кадры. Сохранять на карте и раз в неделю передавать с домашнего компа? Неудобный форм-фактор.

Сколько же

© Habrahabr.ru