Мы стремимся к тому, чтобы после заказа такси к пользователю приезжал чистый, исправный автомобиль той марки, того цвета и с тем номером, которые отображаются в приложении. И для этого мы используем дистанционный контроль качества (ДКК). Сегодня я расскажу читателям Хабра о том, как с помощью машинного обучения снизить затраты на контроль качества в быстро растущем сервисе с сотнями тысяч машин и не выпустить на линию машину, которая не соответствует правилам сервиса. 
Схема процесса ДКК В процессе ДКК мы проверяем фотографии автомобиля и принимаем решение о том, можно ли выполнять заказы на таком автомобиле или, например, перед этим его стоит помыть. Всё начинается с того, что через водительское приложение Таксометр мы вызываем водителя на ДКК. Обычно это происходит раз в 10 дней, но иногда реже или чаще – в зависимости от того, насколько успешно водитель проходил предыдущие проверки. Сразу после вызова на ДКК, водителю приходит сообщение с приглашением пройти фотоконтроль. Как только водитель принял приглашение, в том же приложении он фотографирует экстерьер и интерьер машины с разных ракурсов и отправляет фотографии Яндекс.Такси. Водитель может принимать заказы пока идёт ДКК. 
Стартовый экран ДКК в приложении Таксометр 
Экран фотографирования автомобиля в приложении Таксометр Полученные фотографии попадают в Яндекс.Толоку — сервис, в котором с помощью краудсорсинга можно быстро выполнять простые, но большие по объёму задания. О том как устроена и зачем нужна Яндекс.Толока мы писали в нашем блоге. В Яндекс.Толоке в процессе одной проверки как минимум три исполнителя отвечают на вопросы о состоянии автомобиля, и если исполнители пришли к единому мнению, на основе их ответов принимается решение о том, можно ли водителю принимать заказы. У проверки в Яндекс.Толоке два исхода: Если исполнители не пришли к единому мнению, фотографии отправляют к сотрудникам Яндекс.Такси — асессорам, которые более тщательно проверяют автомобиль, а затем принимают финальное решение. Асессоры проходят специальную программу обучения и у них больше опыта. 
Так видит ДКК исполнитель Яндекс.Толоки C ростом Яндекс.Такси растёт и количество проверок ДКК, а это значит, что растут затраты на толокеров и асессоров. Кроме того, падает скорость проверки автомобиля. Пока идёт ДКК, можно как разрешать водителям принимать заказы, так и не разрешать. У обоих вариантов есть свои минусы: в первом случае недобросовестный водитель успеет принять несколько заказов на автомобиле, который не соответствует стандартам, во втором — все вызванные на фотоконтроль водители не смогут работать пока проверка не завершится. Поэтому важно проверять автомобили быстро, чтобы и пользователи, и водители не сталкивались с неудобствами. Наблюдая за тем как графики затрат и среднего времени проверки растут, мы поняли, что хотим снизить затраты на Толоку, разгрузить асессоров и сократить среднее время проверки, иными словами, автоматизировать часть проверок. Естественно, мы не хотели жертвовать качеством сервиса и пропускать больше несоотвествующих стандартам качества автомобилей на линию, а также не хотели ограничивать принятие заказов добросовестными водителями. Нам нужно было автоматизировать ДКК и при этом не увеличить долю ошибок в общем потоке проверок. 
Схема процесса ДКК с ML внутри Для начала мы определились с постановкой задачи: автоматизировать как можно больше проверок, при этом не увеличив частоту ошибок в общем потоке. Давайте разберёмся с тем, какими бывают ошибки в нашей задаче. Они бывают двух видов: false positive и false negative. В нашей терминологии negative — результат проверки, с которым водитель может продолжать работать, а positive — результат, который влечёт за собой временное ограничение на приём заказов. Тогда false negative — случай, в котором мы были вынуждены разрешить водителю с плохим автомобилем принимать заказы, а false positive — наоборот, когда мы не разрешили работать водителю, у которого с автомобилем всё хорошо. Выходит, что False Negative Rate (FNR) — доля водителей с «плохими» автомобилями, которым мы разрешили принимать заказы, а False Positive Rate (FPR) — доля водителей, которым мы не разрешили работать, хотя у них с автомобилями всё хорошо. Таким образом, от внедрения машинного обучения в систему мы хотели следующего: автоматизировать как можно больше проверок, при этом не увеличив FPR и FNR в сравнении с системой без машинного обучения. Далее, необходимо было понять на какие метрики ориентироваться при выборе моделей и порогов для принятия решений на основе их предсказаний. Из условий задачи ясно, что нас интересуют три величины: Мы максимизируем первую величину при соблюдении ограничений на вторую и третью. Тут может возникнуть вопрос: почему бы не максимизировать экономию денег или минимизировать среднее время проверки напрямую, а не через долю автоматизированных проверок? Оптимизировать деньги — очень привлекательная идея, но, как правило, трудноосуществимая. В нашем случае экономия складывается из двух факторов: первый — экономия с каждой автоматизированной проверки, ведь каждая проверка у асессоров или в Яндекс.Толоке стоит денег; второй — экономия с уменьшения числа ошибок, ведь каждая ошибка стоит Яндекс.Такси денег. Объективно посчитать, во сколько нам обходятся ошибки, — очень сложная задача, поэтому мы ограничены расчётом экономии только по первому фактору. Такая величина монотонно возрастает по доле автоматизированных проверок, так что можно максимизировать эту долю вместо экономии. Такие же рассуждения применимы к среднему времени ДКК, оно так же монотонно убывает по доле автоматизированных проверок. Можно сказать, что проверка ДКК сводится к выбору вариантов ответов для ряда вопросов о состоянии автомобиля по его фотографиям, а это звучит как задача классификации изображений. Такие задачи решает компьютерное зрение, а в наше время – конкретный инструмент, свёрточные нейронные сети. Их мы и решили использовать для автоматизации ДКК. Теперь, когда мы поняли, что оптимизировать и для чего, настало время собрать данные и обучить на них модели. Собрать данные было легко, потому что все проверки ДКК логируются и лежат в хранилище в удобном виде. В первом варианте решения в качестве признаков выступали фото экстерьера и интерьера машины с четырёх ракурсов, марка, модель и цвет машины, а также результаты 10 предыдущих проверок ДКК. Как целевые переменные мы взяли ответы на все вопросы проверки, например: «Повреждён ли автомобиль?» или «Соответствует ли цвет автомобиля указанному в карточке водителя?». Главной целевой переменной был ответ на главный вопрос: «Нужно ли ограничивать возможность водителя принимать заказы?». Мы учили одну большую модель, очень похожую на VGG с SENet attention, отвечать на все вопросы одновременно и в итоге столкнулись с несколькими проблемами. 
Подход «всё и сразу» Проблемы подхода «всё и сразу»: Вместе эти четыре причины не позволили нам применить первый вариант решения на практике, но мы не стали унывать и придумали второй вариант. Мы решили сосредоточиться на проверках экстерьера машины, ведь они составляют около 70% всего потока. Кроме того, мы решили разбить общую задачу на подзадачи и научиться отвечать на все вопросы ДКК по отдельности. 
Подход «всё, но постепенно» Когда-то давно наша служба уже занималась автоматизацией ДКК и успела внедрить модель, которая позволяет фильтровать тёмные и нерелевантные фотографии. Эту модель мы продолжили использовать и дальше, чтобы отвечать на вопрос: «Присутствуют ли следующие настоящие фотографии машины: перед, левый бок, правый бок, зад?». Наша работа над вторым вариантом решения началась с того, что мы использовали модель службы компьютерного зрения Яндекс.Поиска (от тех самых людей, которые сделали DeepHD) для распознавания номерных знаков на автомобилях. Так мы смогли ответить на вопрос: «Соответствуют ли полностью номер и код региона автомобиля указанным в карточке водителя?» Если говорить об этом более подробно, мы сравнивали результат распознавания с указанным в карточке водителя номером и в зависимости от расстояния Левенштейна между ними выбирали один из вариантов ответа: «номер совпадает», «номер не совпадает» или «нельзя точно ответить на вопрос». Далее мы обучили классификаторы автомобилей распознавать марки и модели, а также цвета. С этого момента мы могли отвечать на вопрос: «Соответствуют ли марка, модель и цвет автомобиля указанным в карточке водителя?» В завершение мы обучили классификаторы находить повреждённые и грязные автомобили, это позволило закрыть вопросы: «Есть ли повреждения или дефекты на кузове автомобиля?» и «Насколько грязный кузов автомобиля?» Подход «всё, но постепенно» позволил нам решить проблему проверки номера автомобиля. Также мы смогли избавиться от неполноты и зашумлённости целевой переменной, ведь теперь у нас была выборка, где объектами класса negative были полностью успешные проверки, а объектами класса positive — проверки, где асессор или все три исполнителя Яндекс.Толоки нашли определённый недочёт, например повреждения корпуса. После решения первых двух проблем наши модели стали интерпретируемыми, и мы могли объяснить водителю причину ограничения, чтобы к следующей проверке он исправил недочёты. Общее качество ответов на вопросы также сильно выросло, а FPR и FNR для некоторых комбинаций порогов уверенности моделей упали до уровня Яндекс.Толоки, что позволило внедрять модели в продакшн. Перед нами стоял выбор: запустить регулярный процесс, который будет применять модели к скопившимся в очереди проверкам, или сделать отдельный сервис, куда можно будет ходить по API и получать ответы моделей в реальном времени. Так как для нас важно быстро находить «плохие» автомобили, мы выбрали второй вариант. Как только основная часть сервиса была написана и он смог поддерживать необходимую функциональность, мы начали добавлять в него модели. Чтобы полностью одобрить проверку, нужно уметь отвечать на все вопросы инструкции, но чтобы ограничить недобросовестному водителю доступ к сервису, в некоторых случаях достаточно уметь отвечать хотя бы на один вопрос. Поэтому мы решили не ждать, пока будут готовы все модели, а добавлять их по мере готовности. Обобщённо пайплайн добавления модели выглядит так: Такой подход позволил нам не только моментально находить всё больше «плохих» автомобилей по мере внедрения новых моделей, но и без дополнительных временных затрат измерять качество онлайн, пока модели работали в фоновом режиме. В конце концов наступил момент, когда мы добавили в сервис и протестировали последнюю модель. Теперь мы могли отвечать на все вопросы проверок, а значит автоматически их одобрять. Так как «хороших» автомобилей в Яндекс.Такси гораздо больше, чем «плохих», автоматическое одобрение проверок привело к резкому росту нашей основной метрики — части потока автоматизированных проверок. Нам оставалось только подобрать правильные пороги, которые бы максимизировали долю автоматизированных проверок, при этом сохраняя общие FPR и FNR всей системы на прежнем уровне. Для подбора порогов мы использовали выборку, которую независимо друг от друга размечали исполнители Яндекс.Толоки, асессоры и сотрудник Яндекс.Такси, обучавший асессоров проверять автомобили. Его разметку мы и использовали в качестве истинных значений целевой переменной. Как только мы включили модели в продакшн, нужно было измерить онлайн-качество решений, принятых на основе их ответов. И вот какие цифры мы увидели: Таким образом, внедрение машинного обучения не только помогло сэкономить деньги, но и позволило сделать сервис более безопасным и комфортным для пользователей. Тем не менее, это ещё далеко не конец истории. Наша быстро растущая команда и дальше будет активно работать над тем, чтобы автоматизировать ещё больше проверок и сделать Яндекс.Такси ещё более удобным, комфортным и безопасным. В работе над автоматизацией ДКК в Яндекс.Такси мы столкнулись с множеством проблем, отыскали несколько успешных решений и сделали шесть важных выводов: Динамическое ценообразование, или Как Яндекс.Такси прогнозирует высокий спрос. Как Яндекс.Такси прогнозирует время подачи автомобиля с помощью машинного обучения. Data Scientist