Авторы: Сэмми Сидху, Цяньгуи (Джек) Хуанг, Рэй Гао - Lyft Level 5

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

В своей разработке мы используем большое количество разнообразных алгоритмов машинного обучения для управления нашими беспилотными автомобилями, решая проблемы в картографировании, восприятии, прогнозировании и планировании. Для разработки этих моделей мы обучаем и проверяем их на миллионах изображений и плотных облаках точек LiDAR / RADAR, а также на многих других типах входных данных, таких как траектории агентов или видеопоследовательности. Затем эти модели развертываются на платформе Lyft для автономных транспортных средств (AV), где им необходимо генерировать логические выводы, такие как ограничивающие прямоугольники, состояния светофора и траектории движения транспортных средств с точностью до миллисекунд.

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

Создание правильной системы машинного обучения для самостоятельного вождения

Мы считаем, что быстрое повторение и адаптация являются ключом к успеху на уровне 5. Этот принцип применим как к нашим моделям машинного обучения (ML), так и к нашим инструментам ML. Когда мы запустили Lyft Level 5 в 2017 году, мы обучили некоторым базовым моделям компьютерного зрения на наших настольных компьютерах. Всего через несколько месяцев мы создали нашу первую внутреннюю структуру обучения, которая позволяет нам расширяться. С помощью этой структуры мы развернули около дюжины моделей AV, но вскоре поняли, что нам нужна смена парадигмы с упором на следующие ключевые принципы:

  • Итерация моделей в часах. Обучение наших первых производственных моделей занимало несколько дней из-за увеличения объемов данных и размеров моделей. Мы хотели, чтобы наши инженеры и исследователи могли начать с новой идеи, реализовать модель и увидеть результаты по качеству продукции всего за часов, даже несмотря на то, что размеры наших наборов данных продолжают расти.
  • Беспрепятственное развертывание модели. Наш первоначальный процесс развертывания был сложным, и разработчикам моделей пришлось преодолеть множество трудностей, чтобы превратить обученную модель в развертываемую модель, готовую к развертыванию AV, то есть ее можно запускать в наших автомобилях, в среда выполнения C ++ с низкой задержкой и джиттером. Мы хотели сделать приоритетной проблему скорости вывода и возможности развертывания. Нам также нужно было, чтобы инженеры могли легко добавлять новые настраиваемые слои как для обучения, так и для вывода.
  • Унифицированные эксперименты и исследования. Часто существует разрыв между идеальной средой разработки для инженеров и исследователей. В Lyft мы увидели возможность устранить этот разрыв как один из ключей к быстрому развитию наших систем машинного обучения; нам не нужны две стопки. Такие вещи, как среды сборки, облачные вычислительные ресурсы, ведение журналов и т. Д., Должны быть под ключ и работать для всех.
  • Оптимизация ресурсов для оборудования. Судя по низкой загрузке графического процессора и процессора, наша первоначальная обучающая среда не могла полностью использовать оборудование. По мере роста размеров набора данных и нашей команды это привело к увеличению времени обучения разработчиков моделей и потенциальной экономии средств. Мы хотели сделать наш фреймворк аппаратно-ориентированным, чтобы добиться максимальной производительности, как для ускорения обучения, так и для снижения затрат.

Рассмотрев эти принципы, мы решили создать решение, которое включило бы PyTorch в основу нашей инфраструктуры машинного обучения следующего поколения. За 6 месяцев мы построили прототип, перенесли более десятка производственных моделей AV из нашей последней платформы, приняли на борт более 40 инженеров по машинному обучению и создали единую структуру машинного обучения для всех уровней 5.

Создание Proof-of-Concept

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

Задачей кандидата на моделирование, которую мы выбрали для сквозной реализации, была сегментация LiDAR, которая представляет собой задачу, которая использует облако точек 3D-LiDAR и классифицирует каждую точку по классу, например, земля, автомобиль, человек и т. Д. (См. Рисунок 1).

Мы начали с написания класса PyTorch DataSet, который работал с двоичными файлами аннотированных облаков точек. Затем нам пришлось написать настраиваемую функцию сопоставления для объединения различных элементов данных в пакет для использования PyTorch DataLoader.

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

  1. Возьмите разреженное облако точек + метаданные для каждой точки
  2. Украсьте каждую точку
  3. Облако точек с функцией Voxelize
  4. Запускать плотные свертки в пространстве вокселей
  5. Сопоставление вокселей с заданной точкой (деВокселизация)
  6. Классифицируйте каждую точку

Некоторые из этих этапов, такие как (де) вокселизация в нашей предыдущей реализации, были написаны от руки на CUDA и заняли недели инженерного времени. Мы обнаружили, что их реализация в собственном PyTorch с такими примитивами, как scatter_add и index_select, дает нам аналогичную производительность без необходимости прибегать к написанию вручную ядер, что позволяет нам создавать те же модели за несколько дней.

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

Теперь, когда у нас был способ создать обученную модель для нашего набора данных, осталось только заставить ее работать в нашем самоуправляемом стеке C ++. Во время этого Proof-of-Concept (PyTorch 1.3) мы нашли два варианта того, как экспортировать и запустить «замороженную модель» в среде выполнения C ++:

  1. Экспортируйте нашу модель Torch в ONNX и запустите модель ONNX в TensorRT или ONNX Runtime.
  2. Создайте наши модели с помощью TorchScript и запустите сохраненные сериализованные модели в LibTorch

Уроки из нашей прошлой структуры: хотя ONNX и TensorRT существуют дольше и в некоторых случаях более оптимизированы для скорости вывода, мы оценили возможность быстрого развертывания моделей и значительно более легкий конвейер (меньше внешних библиотек и зависимостей), что позволяет нам быстро экспериментируйте, не ограничиваясь различными ограничениями ONNX и не создавая собственных плагинов TensorRT. Мы понимаем, что нам всегда нужно писать собственные ядра и операции, но мы бы предпочли писать расширения LibTorch (подробнее см. В разделе ниже), чем добавлять дополнительные внешние слои черного ящика. По этим причинам мы решили оценить TorchScript для вывода.

Последним этапом оценки была интеграция нашей модели TorchScript в наш стек самоуправления. В качестве отправной точки мы использовали сборку общей библиотеки LibTorch, предоставленную PyTorch, и интегрировали ее в нашу сборку. Затем мы смогли использовать LibTorch C ++ API для интеграции модели в наш стек LiDAR. Мы обнаружили, что API удобен для пользователя Python PyTorch API, но на C ++.

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

В целом, мы почувствовали, что наше Proof-of-Concept прошло очень хорошо, и мы решили продолжить работу над производством PyTorch на Lyft Level 5 как для обучения, так и для развертывания.

Создание производственной среды PyTorch

В рамках разработки PyTorch и для расширения возможностей наших инженеров по машинному обучению мы создали среду Lyft-Internal под названием Jadoo (см. рисунок 2). В отличие от некоторых фреймворков, наша цель состояла в том, чтобы предоставить надежную экосистему, которая упрощает среду выполнения и вычислительную инфраструктуру, позволяя инженеру и исследователю ML выполнять итерацию и развертывать код быстро, вместо того, чтобы пытаться сделать ML «проще» для неспециалистов и абстрагироваться. все замечательные возможности PyTorch.

Некоторые основные функции Jadoo включают:

  • Все вакансии распределяются. Jadoo распространяется с самого начала; все задания - это изначально распределенные задания с базовым случаем один-графический процессор-один-узел. Инженер, создающий модель локально, может затем обучать работе в облаке с сотнями графических процессоров с помощью одного изменения аргумента командной строки. Независимо от того, выполняется ли эксперимент на одном графическом процессоре, одном узле или десятках узлов, мы поддерживаем один и тот же путь кода и один и тот же образ Docker, что позволяет нам избежать каких-либо сюрпризов при локальном / облачном обучении. Мы также предоставляем пользователям инструменты, позволяющие быстро обнаруживать проблемы в распределенной среде, такие как использование графического процессора, узкие места в сети и ведение журнала нескольких узлов.
  • Вывод является приоритетом. В Jadoo все модели профилированы для выполнения и предназначены для развертывания для AV. Мы фиксируем все количество операций для всех уровней, измеряем задержку логического вывода и сохраняем эту информацию, чтобы пользователи могли выбирать оптимальные по Парето компромиссы по скорости и точности. Мы также гарантируем, что каждая обученная модель может быть развернута с помощью TorchScript для развертываний как на C ++, так и на Python, и не требует специальных конвейеров для преобразования ее в модель вывода.
  • Сочетайте скорость итерации исследований с качеством продукции. Jadoo стремится дать исследователям свободу экспериментировать и повторять, но в то же время применяет передовые инженерные практики. Весь код, проверенный в Jadoo, проходит через CI (непрерывную интеграцию) на базе графического процессора и проверяет наличие модульных тестов, статической проверки типов, линтинга стилей, проверок документации, детерминизма и профилирования и т. Д. Чтобы обеспечить быстрое экспериментирование, мы используем код как- конфигурации и устранения больших и громоздких файлов json / yaml и автоматического извлечения параметров эксперимента из кода пользователя. Мы обеспечиваем строгую воспроизводимость для всех заданий путем управления версиями и записи данных, кода эксперимента и артефактов. Наконец, мы предоставляем экосистему инструментов для регистрации, визуализации и отслеживания заданий, которая позволяет пользователям быстро отлаживать и анализировать свою работу.

Распределенное обучение

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

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

Логический вывод с помощью LibTorch и TorchScript

В Jadoo мы хотим расставить приоритеты для моделей построения, которые могут эффективно работать в AV со средой выполнения C ++. Мы обнаружили, что LibTorch позволяет нам легко развертывать обученные модели с помощью TorchScript, а C ++ API делает его действительно простым в использовании. C ++ API особенно полезен, когда для одной из наших развернутых моделей требуется предварительная или постобработка, поскольку API следует за знакомым PyTorch.

Следует отметить, что, хотя мы начали с PyTorch, предоставившего сборки LibTorch для Proof-of-Concept, мы обнаружили, что большой статической связанной библиотекой было трудно управлять. Чтобы облегчить это, мы компилируем LibTorch из исходного кода с нашими собственными зависимостями, связывая зависимости LibTorch через общие библиотеки. Сделав это, мы смогли на порядок уменьшить размер двоичного файла LibTorch.

Чтобы пользователи могли легко развернуть свои обученные модели, Jadoo проверяет, можно ли преобразовать модели в TorchScript во время обучения, и если да, то периодически генерирует контрольные точки, которые содержат модель TorchScript, а также любые дополнительные метаданные, которые позволяют проследить модель до ее источника. Эти метаданные включают такую ​​информацию, как пробег, GitSHA, имя пользователя и любые другие метаданные, которые пользователь выбирает для отслеживания. Кроме того, Jadoo автоматически профилирует задержку этой модели TorchScript, а также ее MAC-адреса (умножение-накопление) и количество параметров.

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

Мы пришли к выводу, что это лучшая практика, поскольку пользователи создают свои модели с учетом TorchScript. Это позволяет избежать проблем, связанных с наличием сложной модели, а при развертывании вы обнаружите, что большую часть API-интерфейсов модели необходимо изменить из-за несовместимого синтаксиса TorchScript. Мы также обнаружили, что модульные тесты - это хороший способ убедиться, что модели и модули совместимы с TorchScript. Например, часто один разработчик может изменить общий модуль, используемый другим разработчиком, и добавить синтаксис, который не поддерживается TorchScript, это будет обнаружено на ранней стадии в CI (непрерывная интеграция) и никогда не будет запущено в производство.

Комбинированные исследования и разработки

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

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

  • Пользователи могут начать работу менее чем за 5 секунд
  • Работа с сотнями графических процессоров запускается за считанные минуты
  • Мы значительно переработали наши конвейеры загрузки данных с учетом аппаратного обеспечения, чтобы распределенные задания никогда не ограничивались вводом-выводом, процессором и пропускной способностью сети.
  • Среднее время обучения работе составляет ~ 1 час. Для типичных моделей обнаружения на основе изображений / лидаров, развернутых на AV (см. Рисунок 4.).

Обеспечение простой воспроизводимости и сопоставимости экспериментов

  • Мы настраиваем модель и конфигурацию эксперимента как код Python, который регистрируется в git и в наших инструментах отслеживания экспериментов, что устраняет необходимость в управлении бесчисленными файлами моделей json и yaml.
  • Мы также используем интроспекцию кода, чтобы отслеживать и регистрировать константы в коде, чтобы упростить удаление и сравнение входных данных и результатов экспериментов.
  • Мы автоматически регистрируем и записываем наборы данных, используемые в эксперименте, удаленные Git SHA, а также локальные изменения кода, чтобы сделать любой эксперимент на 100% воспроизводимым в любое время. Это очень важно для тщательного экспериментирования.

Соблюдение высоких стандартов кодирования

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

Результаты и взгляд в будущее

Мы считаем, что добились огромных успехов в наших усилиях по машинному обучению, приняв PyTorch и создав Jadoo. Всего за несколько месяцев работы мы смогли переместить около десятка моделей из нашей старой инфраструктуры в PyTorch и развернуть их с помощью TorchScript + LibTorch. Мы также смогли сократить среднее время обучения для тяжелых производственных работ, таких как 2D- и 3D-детекторы и сегментаторы, с дней примерно до 1 час (см. рисунок 3), позволяющий пользователям указать, сколько вычислений и времени им нужно. Это позволяет нашим пользователям выполнять множество итераций в день для разработки модели, что раньше было невозможно. Мы считаем, что мы также создали уникальную структуру, которая действительно позволяет нашим инженерам и исследователям машинного обучения делать больше за значительно меньшее время, позволяя им быстро перерабатывать идеи и развертывать их на автомобильной платформе без каких-либо попыток демократизации и упрощения машинного обучения. .

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

Благодарности

Мы хотели бы поблагодарить команду PyTorch, включая Майкла Суо, Дмитрия Джулгакова, Гиту Чаухан, Ву Ким и Сумит Чинтала, за их помощь и полезные предложения за последние несколько месяцев.