Введение
Последний пост посвящен принципам проектирования, изложенным в статье Riselabs из Беркли для новой структуры, необходимой для развивающегося класса приложений ИИ. Этот пост будет основан на этих принципах дизайна и сделает глубокое погружение в каркас Ray, построенный в соответствии с этими принципами. Я настоятельно рекомендую ознакомиться с принципами дизайна, прежде чем переходить к этому. Этот пост будет основан на большом количестве материала, который был рассмотрен в предыдущем посте.
Подведем итоги последнего поста: на высоком уровне структура для нового класса приложений API должна будет поддерживать низкую задержку планирования, высокую пропускную способность для миллионов задач, динамическое создание задач, гетерогенные задачи, произвольные сложные зависимости потоков данных. . Ray поддерживает эти требования и, в свою очередь, обеспечивает новый класс приложений искусственного интеллекта, использующих обучение с подкреплением (RL), которые нуждаются в поддержке распределенного обучения, обслуживания моделей и запуска многих симуляций. Ray специализируется на выполнении легких задач без сохранения состояния, а также на длительных операциях с отслеживанием состояния, необходимых для обучения.
Типичный алгоритм RL
Типичный пример взаимодействия агента RL с окружающей средой проходит через следующий итерационный алгоритм:
- Начни с какой-нибудь модели
- Заставить политику (из модели) взаимодействовать со средой и выполнить рекомендованное действие (обслуживание модели)
- Получите награду, взаимодействуя с смоделированной средой (Симуляторы)
- Повторяйте шаги 2–3 до завершения моделирования.
- Используя результаты последнего шага, переобучите политику. (Обучение)
- Повторяйте шаги 1–5, пока результаты не сойдутся и вознаграждение не будет максимальным.
Как видно из этого алгоритма, эта структура должна поддерживать обслуживание моделей, моделирование задач и обучение на больших данных. Обслуживание модели ориентировано на быстрое отображение политики - быстрое принятие множества решений / выводов и сокращение задержки за счет эффективной балансировки нагрузки. Моделирование использует политику для оценки ее эффективности в данной среде, поскольку текущие алгоритмы недостаточно эффективны для работы с физическим миром в реальном времени. Обучение модели основано на массово распределенном стохастическом градиентном спуске. Это сильно отличается от обучения с учителем, где обучение модели и обслуживание / вывод модели не связаны. Обучение происходит в автономном режиме, а вывод - в режиме онлайн (в реальном времени).
Примитивы Ray API
Об этом рассказывалось в посте о принципах дизайна. Но вот краткое изложение, чтобы освежить это:
Задачи и актеры
Задачи (первые три строки), упомянутые в последнем разделе, представляют собой удаленные функции, которые полагаются на внешний ввод и не имеют состояния. Думайте о них как о функциях, которые могут выполняться удаленно на любом узле, где находится ввод. Задачи легко восстановить - это можно сделать, просто выполнив их повторно. Для небольших обновлений задачи неэффективны.
Акторы - это сущности с состоянием, которые несут с собой состояние. Они полезны для итеративных режимов, таких как обучение или серверы параметров, которые зависят от внутреннего состояния и их непрерывного обновления. У участников есть методы, которые могут выполняться удаленно, как задача, но для их выполнения требуется «работник с отслеживанием состояния». Акторы также упрощают упаковку сторонних пакетов, которые нельзя легко десериализовать в качестве входных данных для задачи. Актеры также полезны для небольших обновлений, в отличие от массовых обновлений, для которых предназначены чистые задачи.
Графики задач
Рэй создает динамический граф на основе программы, написанной пользователем. Этот граф формирует структуру зависимостей, и следующие задачи в графе выполняются системой, когда становятся доступными входные данные. Граф состоит из узлов, которые представляют объекты данных, удаленные вызовы функций (задачи). У графа тоже есть ребра. Границы потока данных фиксируют зависимость между задачами и объектами данных. Границы управления захватывают вложенные задачи или функции, которые создают дальнейшие удаленные функции. Пока мы не обсуждали Актеров в этой модели. Актеры снова похожи на задачи. Единственное отличие состоит в том, чтобы уловить состояние. Это делается путем добавления края с отслеживанием состояния. Когда Actor вызывает два метода M1 и M2, между двумя узлами задач, представленными M1 и M2, добавляется граница с отслеживанием состояния. Это означает, что цепочка ребер с сохранением состояния формируется для всех методов, вызываемых одним и тем же субъектом, а также помогает в построении линии передачи, что полезно для восстановления после сбоя.
Архитектура
Системная архитектура Ray состоит из прикладного и системного уровней. Давайте посмотрим, как работают два слоя.
Прикладной уровень
Как видите, уровень приложения состоит из драйвера, исполнителя и участников. Драйверы выполняют прикладные программы. Рабочие выполняют задачи или функции, присутствующие в системе. Всякий раз, когда объявляется удаленная функция, она передается работнику. Драйвер и локальный планировщик запрашивают у работника выполнение задач. Актер похож на worker в том, что он выполняет задачи, но он работает с состоянием предыдущего метода и вызывает только методы, принадлежащие субъекту.
Системный уровень
Системный уровень состоит из трех основных компонентов, которые включают глобальное состояние управления (GCS), глобальный планировщик и распределенное хранилище в памяти + локальный планировщик.
Глобальное хранилище управления (GCS) используется для хранения такой информации, как все таблицы удаленных функций, журналы событий, таблицы объектов. По сути, это хранилище ключей и значений, дополненное функциональностью pub-sub. В этой таблице также хранится информация о том, где расположены объекты, чтобы ее можно было использовать для эффективного размещения задач и данных. Поскольку GCS поддерживает все состояние, он помогает масштабировать планировщик. Кроме того, GCS можно использовать для восстановления после сбоев. Грубое восстановление решается с помощью таких архитектур, как RDD, но для мелкозернистого восстановления требуется другой механизм, особенно для динамических задач, которые добавляются во время моделирования. Для решения этой проблемы необходимо, чтобы GCS сохранял информацию о происхождении для восстановления. Это позволяет всем другим компонентам системы не иметь состояния и не беспокоиться о восстановлении после сбоев.
Глобальный планировщик. Глобальный планировщик - это действительно гибридный планировщик, работающий снизу вверх, в котором задачи передаются локальному планировщику. Локальный планировщик сначала пытается запланировать данную задачу на том же узле. Если это невозможно из-за ограничений (отсутствие графического процессора) или из-за высокой нагрузки на локальный узел, он отправит задачу в глобальный планировщик. Глобальный планировщик осведомлен об общем использовании системы на всех узлах и потребностях в данных для различных задач через GCS. Затем эти задачи могут быть отправлены локальному планировщику на узле, выбранном глобальным планировщиком. Глобальный планировщик может найти несколько узлов, которые удовлетворяют потребности в емкости для этой задачи. Поскольку задачи в Ray очень чувствительны к задержкам, планировщик выбирает узел, который минимизирует время ожидания для задачи. Время ожидания определяется как сумма «времени, проведенного в очередях на выбранном узле» и «времени, затраченного на передачу входных данных на этот узел». GCS использует тактовые импульсы для получения информации о пропускной способности передачи данных, а также о размерах очередей на каждом узле. Кроме того, если планировщик становится узким местом, то можно масштабировать планировщик, используя GCS в качестве источника данных.
См. Рисунок ниже, который упрощает рабочий процесс планирования задач.
Хранилище распределенных объектов в памяти присутствует на каждом узле системы и используется для хранения входных и выходных данных задач. Для любых входных данных, которых нет на узле, хранилище объектов будет реплицировать их на локальный узел. Это также помогает с узкими местами, связанными с горячими объектами. Хранилище объектов ограничено неизменяемыми данными, поэтому согласованность не является большой проблемой. Кроме того, только поддерживаемые объекты могут поместиться на одном узле.
Выполнение удаленной задачи в Ray
Давайте рассмотрим последовательность шагов, необходимых для выполнения функции в Ray, и шаги, необходимые для получения результатов. Представьте, что в системе есть два узла N1 и N2. Объявляется удаленная функция, она регистрируется в GCS и затем передается рабочим на N1 и N2 (шаг 0 на рисунке выше). Приложение работает на N1 и хочет выполнить операцию «добавления» для двух объектов a и b. Эта операция возвращает будущий «id», а затем приложение на N1 вызывает get () для будущего «id», что является блокирующим вызовом. Теперь сначала давайте рассмотрим, что происходит в системе при выполнении операции добавления. Это показано на рисунке (а).
- Задача добавления отправляется локальному планировщику на N1.
- Локальный планировщик понимает, что у него есть только объект «а», доступный локально. Таким образом, он отправляет задачу в глобальный планировщик.
- Глобальный планировщик смотрит в GCS, где присутствуют объекты «a» и «b», а затем решает запланировать это на N2.
- Локальный планировщик на N2 получает информацию о «a» из локального хранилища объектов. «B» доступно локально.
- Поскольку в хранилище объектов нет «а», его необходимо получить из узла, в котором он присутствует.
- Хранилище объектов на N2 узнает от GCS, что «a» находится на N1.
- «A» реплицируется на N2 через репликацию хранилища объектов с N1
- Локальный планировщик планирует добавление задач с помощью локальных работников
- Локальный работник использует хранилище объектов для извлечения «a» и «b» для выполнения добавления.
Вторая часть приложения - получить результат сложения с использованием будущего «id», на которое ссылается приложение на N2. Это показано на рисунке (b).
- Локальный планировщик на N1 выполняет переход на будущий «id». Он проверяет хранилище объектов локально, в котором еще нет записи для id.
- Локальное хранилище объектов запрашивает у GCS «идентификатор». Поскольку в GCS этого нет, обратный вызов регистрируется в GCS.
- Тем временем добавление завершается на узле N2. Данные хранятся в хранилище объектов на N2.
- Хранилище объектов N2 сообщает GCS о наличии результатов «id». Это зарегистрировано в таблице объектов.
- GCS вызывает обратный вызов на N1, указывая на доступность «id» на N2.
- Хранилище объектов на N1 копирует «id» из N2.
- Теперь задача на N1, ожидающая future-get (ray.get), разблокирована.
Оценка приложений RL
В этой статье также сравнивается производительность платформы Ray с двумя алгоритмами RL и их эталонными реализациями. Эти два алгоритма - стратегии эволюции и ближайшая оптимизация политики. В обоих случаях алгоритм на основе лучей работает намного лучше по сравнению с одноразовыми решениями, созданными для этих проблем. Имейте в виду, что создание таких одноразовых решений довольно сложно. Для ES эталонное решение не масштабируется за пределы 1024 узлов, в то время как лучи масштабируются до 8192 узлов. Точно так же для PPO луч превосходит реализацию OpenMPI по гораздо более низкой цене (что улучшает использование кластера) и требует гораздо меньшего количества графических процессоров. Это стало возможным, потому что задачи и субъекты могут запрашивать определенные разнородные ресурсы и лучше использовать локальность данных при закреплении этих ресурсов.
Вывод
Как принципы проектирования, так и сам документ по фреймворку делают интересным чтение о фреймворках машинного обучения нового поколения, которые необходимо развить для поддержки методов RL. Структуры, которые могут поддерживать гибридное планирование и комбинацию подходов, основанных на задачах / участниках, кажутся подходящими для этого проблемного пространства.
Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик