Часто дело не в модели и даже не в самих данных, а скорее в двигателе, который продолжает работать.

Nauto является ведущим поставщиком передовых систем помощи водителю, которые повышают безопасность коммерческих автопарков сегодня и автономных транспортных средств завтрашнего дня. С этой целью мы ежемесячно обрабатываем терабайты данных о вождении, собираемых устройствами, установленными на лобовом стекле автомобилей по всему миру. Эти данные используются для постоянного улучшения моделей, лежащих в основе нашего стека безопасности транспортных средств, от предупредительных предупреждений о столкновениях в реальном времени, развернутых на наших устройствах, до аналитики безопасности, которая работает в облаке. Данные управляют всем.

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

Если мы хотим улучшить обнаружение транспортных средств, включающее предупреждение о прямом столкновении (FCW), в первую очередь мы будем обращать внимание на ложные срабатывания, вызванные FCW. То же самое касается пешеходов и PCW. Со временем каждая функция создает свой собственный набор данных с релевантными примерами и интересными пограничными случаями, которые затем возвращаются обратно в базовую модель во время обучения. Этот замкнутый цикл формирует основу очень мощного механизма обработки данных, который обеспечивает постоянное улучшение всех наших функций.

Проблема

Проблема в том, что несколько функций часто поддерживаются одной моделью. Для нас один мультиклассовый детектор объектов поддерживает Tailgating, FCW и PCW. Хотя это здорово с точки зрения вычислений, это немного усложняет обучение.

Возьмем в качестве примера FCW. Скажем, он был развернут в течение некоторого времени и уже собрал значительный набор данных о рискованных событиях, связанных с головным транспортным средством. Мы заметили, что его ложные срабатывания в основном связаны с неспособностью локализовать транспортные средства странной формы, поэтому мы отбираем эти события, аннотируем их высококачественными ограничивающими прямоугольниками транспортных средств и создаем новый пакет обучающих данных, состоящий из 50 000 изображений.

Загвоздка в том, что мы не можем просто добавить эту новую партию транспортных средств в наш существующий набор данных с несколькими классами. Это приведет к несправедливому наказанию модели за правильное обнаружение, скажем, пешехода или красного света в примере, который был помечен только для транспортных средств. В конечном итоге модель оптимизируется с помощью функции потерь, которая сравнивает ее прогнозы с истинными. Функция потерь не делает различий между несуществующим объектом и объектом, который просто не был помечен в примере. Мы не можем просто объединить набор частично помеченных наборов данных. вместе — иначе это приведет к несправедливому и запутанному обучению.

К счастью, у нас есть несколько вариантов.

Аннотировать все классы для всех наборов данных.

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

Первая проблема заключается в масштабируемости. В Nauto наш основной детектор объектов обнаруживает более 20 классов в рамках 5 основных задач, и их число постоянно растет. При таком подходе нам нужно было бы запустить этот единственный пакет данных FCW не только через рабочий процесс аннотации транспортного средства, но и рабочие процессы пешеходов, светофоров, обнаружения полосы движения и т. д.

Общая рабочая нагрузка по аннотациям мультипликативно масштабируется в зависимости от:

            #_annotations = (#_examples) x (#_tasks)

Это быстро становится непрактичным, особенно если учесть, что происходит, когда мы решаем добавить новую задачу, например обнаружение дорожных конусов. Мало того, что новые данные о дорожных конусах должны быть аннотированы классами из всех 5 наших существующих задач, но и каждый отдельный пример из нашего существующего набора данных должен быть помечен дорожными конусами обратно.

Вторая проблема – нерелевантность. Наша новая партия из 50 000 изображений, полученных из FCW, скорее всего, будет содержать много интересных примеров транспортных средств странной формы, которые должны значительно улучшить обнаружение транспортных средств. Но есть большая вероятность, что там не будет много пешеходов. И даже если это так, он наверняка не будет содержать столько релевантных пограничных случаев, скажем, пешеходов странной формы, потому что эти изображения были получены из FCW, а не из PCW. Тем не менее, мы должны одинаково запускать эти 50 тыс. изображений через оба рабочих процесса аннотации.

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

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

Используйте автоматическую маркировку.

Более масштабируемым решением было бы использовать наши существующие модели для заполнения отсутствующих меток. В нашем новом пакете данных FCW мы только пометим соответствующие классы транспортных средств вручную и обратимся к нашей коллекции существующих детекторов для заполнения классов пешеходов и светофоров.

Что замечательно, так это то, что эти «автомаркировщики» не ограничены никакими ограничениями, с которыми сталкивается наша производственная модель на устройстве. Они могут работать на мощных облачных графических процессорах, могут работать в автономном режиме без ограничений по задержке и могут использовать данные, собранные до и после определенного момента времени. Они также могут быть специалистами; каждая задача может иметь свой собственный специализированный автоматический ярлык, если это означает более высокую производительность, чем модель с несколькими задачами.

Преимущество перед подходом грубой силы очевидно; люди могут сосредоточить свои ресурсы на создании высококачественных этикеток только для наиболее релевантных примеров для каждой задачи, в то время как машины могут заполнить оставшиеся, менее релевантные данные. С бюджетом на маркировку 100 000 изображений в этом месяце транспортные средства и пешеходы, мы могли бы пометить 50 000 примеров FCW интересными транспортными средствами и 50 000 примеров PCW интересными пешеходами — вместо того, чтобы тратить все на первое. Об остальном позаботятся наши модели.

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

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

Это потому, что мы на самом деле не уменьшаем мультипликативную сложность первого подхода, а просто переносим нагрузку с человека на машину. Замените «релевантный» на «человек» и «нерелевантный» на «машина», и взаимосвязь станет ясной.

Вот пример. Допустим, мы хотели бы объединить новую задачу обнаружения конусов с нашим существующим набором данных о транспортных средствах и пешеходах из 100 000 изображений, поэтому мы добавляем новый пакет из 10 000 изображений, которые вручную помечены дорожными конусами. Теперь мы должны пометить отсутствующие автомобили и пешеходы в новом пакете из 10 тыс. Но мы также должны сделать то же самое для немаркированных дорожных конусов на всех 100 000 изображений нашего исходного набора данных. В конце концов, мы увеличили количество человеческих меток в нашем тренировочном наборе, но не без увеличения объема машинных меток еще на порядок.

Используйте функцию частичной потери.

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

Такая функция потерь должна различать несуществующие объекты и объекты, которые просто не были помечены в каждом примере. Затем он просто распространял бы потери только для классов, которые были помечены. Любое правильное обнаружение объекта, который не был помечен для данного примера, не будет несправедливо оштрафован.

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

С нашей новой порцией данных FCW мы можем просто пометить транспортные средства и готово! Когда эти примеры отбираются во время обучения, функция потерь будет наказывать модель только за ее прогнозы транспортных средств — и ничего больше. Когда добавляется новая задача обнаружения, нам не нужно заполнять наш существующий набор данных новыми метками. Наш набор данных на 100% актуален и на 100% бесшумен.

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

Построение платформы объединения данных

Имея все это в виду, еще в апреле моей целью было создать структуру для поддержки быстро растущей сложности наших данных. Наша команда в Nauto всегда была агрессивна в отношении постоянного улучшения модели, но в то время мы были особенно активны — готовились интегрировать 3 совершенно новые задачи обнаружения в наш основной детектор объектов.

Будучи владельцем этой модели, я только начал осознавать, как быстро мой существующий рабочий процесс станет немасштабируемым. К тому времени я уже использовал автоматическую маркировку для унификации наших наборов данных и только что собрал свою первую рабочую версию функции частичной потери. Но я все еще делал все вручную. У меня не было формального процесса для отслеживания этих частичных наборов данных, результирующих унифицированных наборов данных или моделей, используемых в качестве автоматических меток. Каждый раз, когда нам нужно было объединить новый пакет размеченных данных, я изо всех сил пытался выяснить, что нужно помечать автоматически, и вручную планировать задачи логического вывода.

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

  • Поддерживается как частичная потеря, так и автоматическая маркировка.
  • Устранена сложность процесса объединения.
  • Отслеживание и поддержка нашей постоянно растущей коллекции наборов данных для конкретных задач, средств автоматической маркировки и унифицированных наборов данных.

То, что я в итоге собрал, выглядело примерно так:

Кратко пройдусь по основным частям:

Наборы данных компонентов

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

Когда новая партия данных PCW поступает из нашей очереди маркировки пешеходов, мы просто регистрируем ее в разделе «Пешеход» файла конфигурации. Теперь он готов к объединению!

Автоматические этикетировщики

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

Обратите внимание, что для каждой задачи, которую мы включаем из наборов данных компонентов, мы также должны выбрать соответствующий автомаркировщик. Например, мы не можем выбрать Транспортное средство:[b6] и Пешеход:[b1–4, b5] в качестве наборов данных, а выбираем только автоматическую маркировку транспортного средства, как Vehicle:b6 не сможет заполнить отсутствующие метки Pedestrian.

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

Скомпилировать данные

Чтобы скомпилировать новый унифицированный набор данных, все, что нужно сделать, это указать желаемые наборы данных компонентов для включения, метод унификации (частичная потеря или автоматическая маркировка), модели, которые будут использоваться в качестве автомаркировки, если выбран этот вариант, и выходной путь. Вот и все! Фреймворк объединит данные и выдаст их в виде TFrecords, готовых к немедленному обучению производственной модели.

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

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

Напротив, автоматическая маркировка немного сложнее из-за ее сложности. Для каждого набора данных компонентов мы должны выполнить вывод с автомаркировкой для всех задач, кроме одной — задачи, которой принадлежит набор данных. к. Скажем, мы хотели бы объединить 5 наборов данных компонентов, охватывающих 4 задачи (и, следовательно, 4 средства автоматической маркировки). Для каждого компонента 3 отдельных автомаркировщика должны выполнить вывод, чтобы сгенерировать отсутствующие метки — всего 5 x 3 задания вывода. В более общем плане рабочая нагрузка логического вывода масштабируется следующим образом:

     #_inference_jobs = (#_component_datasets) x (#_tasks — 1)

Это должно показаться очень знакомым, потому что оно в основном перефразирует уравнение сложности полностью человеческого подхода. Что для меня невероятно, так это то, что даже для моделей, генерирующих 1000 прогнозов в минуту на графических процессорах серверного класса, масштаб этой рабочей нагрузки уже можно ощутить всего на нескольких задачах. Моя последняя компиляция из ~100 тыс. примеров для 5 задач заняла несколько часов GPU. Только представьте, что вы делаете это с помощью человеческой силы!

Полученные результаты

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

С теоретической точки зрения, я был почти уверен, что частичные потери значительно превзойдут автоматическую маркировку. Частичные потери позволяют получить 100% бесшумный набор данных, помеченный человеком, который должен обучать более точную модель, чем модель, испорченная шумными машинными метками. С практической точки зрения он также имеет то преимущество, что компиляция занимает минуты, а не часы.

Вот почему я был так удивлен, обнаружив обратное, чтобы быть правдой:

Первое, на что следует обратить внимание, это то, что использование функции частичной потери дает гораздо лучшую точность, чем потеря по умолчанию при обучении на частично помеченном наборе данных. Это было потрясающе видеть по нескольким причинам. Во-первых, это подтверждает центральную проблему, для решения которой была задумана вся эта структура: наивное обучение на наборе данных с частичными метками неоптимально, поскольку оно несправедливо наказывает правильные прогнозы немаркированных объектов. С практической точки зрения, это также подтверждает мою реализацию функции частичных потерь, которую до сих пор я тестировал лишь минимально.

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

Здесь я рисую «коэффициент автоматической маркировки» для каждого класса — отношение шумных машинных меток к чистым человеческим меткам. Хотя автоматическая маркировка превосходит метод частичной потери во всех классах, ее преимущество постепенно сокращается по мере увеличения доли машинных этикеток. Для Пешеходов, класса с наименьшей долей машинных меток (8%), опережение автоматической маркировки составляет более 2% F1. Но для класса Другое транспортное средство, в котором доля меток машин намного выше (21%), разрыв сокращается до 0,67% F1. Одна из гипотез состоит в том, что чем больше доля автоматических меток, тем меньше преимущества полных меток по сравнению с частичными метками из-за увеличения шума в основной правде. И возникает естественный вопрос: перевернется ли лид? если коэффициент автоматической маркировки достаточно высок.

Оказывается, да! Мне удалось значительно повысить коэффициент автоматической маркировки во всех классах, расширив набор данных с 2 задач до 5. И в новом испытании частичная потеря оказалась лучше, чем автоматическая маркировка во всех классах.

Хотя это открытие добавляет некоторую поддержку нашей гипотезе, оно также поднимает некоторые другие вопросы. Например, почему процент потенциальных клиентов с частичной потерей не увеличивается при более высоких коэффициентах автоматической маркировки? Частичная потеря опережает 4 % для класса Пешеход, в котором наименьший процент автоматических меток, и только 1 % для класса Другое транспортное средство, которое имеет более чем вдвое больший процент автоматических меток. Если наша гипотеза верна, то не должны ли мы также увидеть, что преимущество частичной потери становится более очевидным с увеличением количества зашумленных наборов данных?

Одно из объяснений заключается в том, что мы упускаем из виду важную переменную: сам инструмент автоматической маркировки.Шум в наборе данных с автоматической маркировкой зависит как от точности автоматической маркировки, так и от доли меток, которые он вносит. к общему набору достоверных данных. Имея это в виду, наши результаты имеют немного больше смысла, если учесть, что моя автомаркировка Пешеход была гораздо менее точной, чем моя Автомобиль средство автоматической маркировки. Отчасти это связано с тем, что обнаружение пешеходов — гораздо более новая задача в Nauto, а отчасти с тем, что пешеходов гораздо сложнее локализовать, чем транспортные средства.

Возможно, мы также упускаем из виду еще одну важную переменную: размер набора данных. Первый экспериментальный набор данных был довольно небольшим, при этом Пешеходы имели наименьшую долю меток человека, за которым следовал Транспортное средство, затем Другое транспортное средство. Второй набор данных был намного больше и, что особенно важно, содержал огромное количество ярлыков Pedestrian. Можно возразить, что в первом испытании автоматическая маркировка дала огромное преимущество в задаче Pedestrian с дефицитом меток — увеличила общее количество меток и приблизила ее к конвергенции. Ко второму испытанию это преимущество уменьшилось из-за более существенной базы человеческих меток, и фактически стало перевешиваться растущим количеством шума, создаваемого авто-меткой Pedestrian.

Идти вперед

Теперь должно быть очевидно, что есть еще масса открытых исследовательских вопросов, которые нужно изучить. К счастью, такая структура унификации делает подобные эксперименты безболезненными и отслеживаемыми с течением времени. И самое замечательное то, что, поскольку сам процесс унификации абстрагирован, мы можем работать с различными оптимизациями под капотом, не нарушая и не усложняя рабочий процесс пользователя. Вот лишь несколько улучшений, которые я хотел бы реализовать в будущем:

Мягкая автоматическая маркировка

На самом деле существует 3-й метод объединения. Вместо того, чтобы относиться к любой автоматической метке так же, как к человеческой метке, мы можем взвесить ее по показателю достоверности. Это превращает метки объектов из двоичных [0 или 1] в непрерывные [от 0 до 1], при этом меткам человека всегда будет присваиваться вес 1, а меткам машины попадет в распределение ниже этого.

Интуиция, стоящая за этим подходом, заключается в том, что не все автоматические ярлыки одинаковы. некоторые будут более уверенными и, следовательно, точными, в то время как другие будут менее уверенными и, следовательно, шумными. В то время как «жесткая» автоматическая маркировка по умолчанию будет обрабатывать все машинные прогнозы выше порогового значения одинаково, «мягкий» подход будет масштабироваться. каждый прогноз по его вероятности быть точным. Таким образом, во время обучения модель, которая пропустит автоматически помеченный объект с весом 0,95, будет оштрафована почти так же, как если бы она пропустила объект, помеченный человеком. Модель, которая пропускает автоматическую метку с весом 0,45, напротив, не будет оштрафована почти так же сильно, поскольку существует более высокая вероятность того, что метка ненадежна.

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

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

Кэширование

В настоящее время каждый раз, когда составляется новый унифицированный набор данных, это делается с нуля. Для автоматической маркировки это может быть расточительно, поскольку наши данные постепенно расширяются с течением времени; существует очень большая вероятность того, что большинство запланированных заданий логического вывода для новой компиляции уже выполнялись ранее. Решение состоит в том, чтобы просто кэшировать каждое задание логического вывода как стандартный блок полностью аннотированных данных, используя идентификаторы средств автоматической маркировки и набор данных компонентов в качестве подписи. Если точно такая же сигнатура запланирована в будущем, мы просто извлекаем ее из кеша вместо повторного запуска логического вывода.

За счет хранения нескольких текстовых аннотаций такой кеш мог бы сэкономить нам массу времени, сократив цикл компиляции нового набора данных с автоматической маркировкой с часов до минут.

Распараллеливание

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

Последние мысли

Сначала я начал этот проект только для того, чтобы автоматизировать ручную задачу, которая быстро становилась немасштабируемой: объединение растущего числа наборов данных для конкретных задач в то, на чем можно было бы обучать нашу модель. Оглядываясь назад, я никак не мог бы поддержать запуск 3 новых задач по обнаружению, не инвестируя в такой фреймворк 6 месяцев назад. В этом смысле я уже вижу, что эти инвестиции окупились.

Заглядывая вперед, я надеюсь, что эта структура унификации будет поддерживать механизм активного обучения, который стимулирует нашу способность постоянно улучшать существующие функции и запускать новые. В этом расширенном цикле мы повторяем не только данные, которые мы собираем, и модели, которые мы развертываем, но и процесс, с помощью которого мы объединяем эти данные, и модели, лежащие в основе этого процесса. Главное, чтобы этот процесс был надежным, самодокументируемым и по своей сути итеративным. Nauto должна иметь возможность продолжать расширять свои предложения по обеспечению безопасности и возможности искусственного интеллекта в агрессивном темпе, не сдерживаясь проблемами масштабирования и сложности.

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