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

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

Мне, как стажеру группы Data & ML Platform в KeepTruckin, было поручено разработать систему, которая решает проблемы, связанные с данными обучения для моделей. Вот как (и почему) это было сделано.

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

Отсутствие должной автоматизации на этом фронте может привести к сложным ручным процессам, часто включающим хранение промежуточных результатов в файлах и отслеживание результатов в электронных таблицах. Эти так называемые трубопроводные джунгли неустойчивы, и их следует избегать любой ценой. Кроме того, базовая инфраструктура машинного обучения должна обеспечивать гибкость и воспроизводимость экспериментов: мы обсудим это подробнее позже в этой публикации. Чтобы узнать больше о таких проблемах, которые возникают в производственных системах машинного обучения, ознакомьтесь с одной из исследовательских работ Google за 2015 год: Скрытый технический долг в системах машинного обучения (этот документ дал начало концепции MLOps) .

Получение данных обучения

Эта часть процесса может быть специфичной для компании и удивительно сложной. На высоком уровне вот как этот процесс может выглядеть для моделей видения KeepTruckin:

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

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

У меня есть данные о тренировках; что теперь?

Предположим, у нас есть данные для обучения, хранящиеся где-то в облаке, скажем, в S3. Теперь мы хотим сохранить его где-нибудь в более структурированном, доступном для запросов формате. Мы также хотим отслеживать наборы для обучения, тестирования и проверки, которые инженеры машинного обучения используют для обучения (подробнее об этом позже). Наконец, мы хотим постоянно обновлять это хранилище данных обучения последними данными обучения. В KeepTruckin мы создали хранилище обучающих данных с базой данных Postgres; вот как это работает:

В нашем хранилище данных хранятся три объекта: примеры, наборы данных и моментальные снимки.

  • Пример соответствует одному фрагменту обучающих данных: мы сохраняем местоположение кадра в S3, расположение его аннотации в S3 и некоторые теги, связанные с кадром (они могут включать время дня, дорогу условия, тип камеры и т. д.).
  • Набор данных - это логическая группа примеров, при которой данная модель использует обучающие данные только из одного набора данных. Это может быть что-то вроде road_facing_data или driver_facing_data.
  • Снимок - это неизменяемый разделение примеров для обучения / тестирования / проверки, используемых для учебных заданий. Снимок можно создавать произвольными способами; то есть вы можете запросить примеры в вашем снимке любым удобным для вас способом.

Подробнее о снимках

Определяющим свойством снимков является неизменность: после создания снимка его содержимое никогда не изменяется. Это означает, что если вы добавите новые обучающие примеры в набор данных, это не повлияет на какие-либо моментальные снимки. Аналогичным образом, если в какой-то момент мы изменим метку изображения, мы не сможем изменить существующую запись example; вместо этого мы должны добавить новый. В результате, запрашивая примеры для моментального снимка, мы должны всегда брать самый последний.

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

Как мы получаем самые свежие данные о тренировках?

Чтобы наши исследователи машинного обучения могли повторно обучать свои модели с использованием новейших обучающих данных по мере их поступления, мы создали API поверх Airflow, чтобы инженеры машинного обучения могли легко создавать конвейеры данных для своих обучающих данных. Для построения конвейеров мы позаимствовали паттерн извлечение-преобразование-загрузка (ETL) из области инженерии данных. Вот как работает этот процесс:

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

Затем в самом хранилище данных происходит еще одно действие: мы назначаем новые примеры их соответствующему набору данных.

Наконец, выполняется еще один процесс ETL для создания снимка:

  1. Мы запрашиваем примеры, которые хотим включить в снимок. Это этап извлечения.
  2. Далее следуют несколько преобразований: сначала мы дедуплицируем выходные данные этого запроса. Если мы встречаем какие-либо примеры с повторными аннотациями, мы берем только последний. Затем мы случайным образом разбиваем примеры в соответствии с соотношением разделения поезд / тест / проверка. Мы также создаем идентификатор и настраиваемое описание снимка в зависимости от того, как он был создан.
  3. Для каждого разбиения мы загружаем текстовый файл, содержащий идентификаторы примеров в разбиении, в S3 и сохраняем путь к этому файлу вместе со всеми другими метаданными снимка. Возможно, это этап загрузки.

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

А теперь самое интересное:

Как мы это построили

В качестве планировщика задач мы использовали Apache Airflow, при этом задачи выполнялись в модулях Kubernetes.

Напомним, что у нас есть три части этого конвейера: первая извлекает примеры, следующая обновляет набор данных, а последняя создает моментальный снимок. Каждый из них соответствует образу докера с именами run_example, run_dataset и run_snapshot соответственно.

Затем мы создаем файл DAG Airflow, который представляет собой файл Python, который сообщает Airflow, что делать. Для каждого компонента мы определяем Оператор Kubernetes Pod. Это оператор, созданный Airflow, который запускает Kubernetes Pod. Мы говорим оператору сделать внутри модуля следующее:

  • Вытащите соответствующий Docker image.
  • Запустите изображение с заданными аргументами (start_time, end_time и аргументы для конкретного компонента, такие как имя компонента и т. Д.)

В этом файле DAG мы указываем порядок выполнения: сначала run_example, затем run_dataset и, наконец, run_snapshot.

Обратите внимание, что все это абстрагируется от пользователя: мы позволяем пользователю объявлять каждый пример, набор данных или моментальный снимок как простой экземпляр объекта Python. У каждого из этих объектов есть .update() метод, который принимает соответствующие параметры и выполняет требуемую задачу. По сути, это то, что запускается в образах докеров. Мы разрешаем пользователю объявить TrainingDataPipeline объект с такими параметрами, как расписание CRON и соответствующий пример, набор данных и снимок объекты как зависимые задачи. У объекта конвейера есть метод generate_dag_file(), который, как вы уже догадались, выполняет всю тяжелую работу за кулисами и генерирует файл DAG, который Airflow может использовать.

Об инкрементальности и воспроизводимости

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

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

Заключение

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

Присоединяйся к нам!

Ознакомьтесь с нашими последними возможностями KeepTruckin на нашей странице Карьера и посетите страницу Перед тем, как подать заявку, чтобы узнать больше о нашей команде инженеров Rad.