Autonomous Learning Library - это библиотека на основе PyTorch, предназначенная для быстрого внедрения новых агентов обучения с подкреплением. В этой статье описывается базовая философия проектирования и архитектура библиотеки, чтобы вы могли начать создание собственных агентов.
Если эта статья вам знакома, возможно, вы читали документацию. Это также может быть связано с тем, что вы видели сообщение Синхронизировано, в котором использовались изображения из документации. Спасибо Synced за привет! Без дальнейших церемоний…
Агент-ориентированный дизайн
Одна из основных философий autonomous-learning-library
заключается в том, что RL должен быть основан на агентах, а не на алгоритмах. Чтобы понять, что мы имеем в виду, ознакомьтесь с реализацией DQN в OpenAI Baselines. Есть гигантская функция learn
, которая принимает окружение и большое количество гиперпараметров. В основе этой функции лежит большой контур управления, который вызывает множество различных функций. Какая часть этой learn
функции является агентом? Какая часть окружающей среды? Какая часть что-то еще? Мы называем эту реализацию алгоритмической, потому что центральной абстракцией является функция с именем learn
, которая обеспечивает полную спецификацию алгоритма.
Как тогда будет выглядеть реализация на основе агентов? Нам не нужно смотреть дальше следующей известной диаграммы:
Определение Agent
простое. Он принимает состояние и награду и возвращает действие. Вот и все. Все остальное - детали реализации. Вот интерфейс Agent
в библиотеке автономного обучения:
class Agent(ABC): @abstractmethod def act(self, state, reward): pass
Вот и все. Когда и как он тренируется, никого не касается, кроме самого Agent
. Когда Agent
разрешено действовать, определяется контуром управления. Как может выглядеть реализация этого? Вот функция act из нашей реализации DQN:
def act(self, state, reward): self._store_transition(self._state, self._action, reward, state) self._train() self._state = state self._action = self.policy(state) return self.action
Вот и все. _store_transition()
и _train()
- частные вспомогательные методы. У контура управления нет причин знать что-либо об этих деталях. Такой подход упрощает как нашу Agent
реализацию, так и сам контур управления.
Отделение логики контура управления от логики Agent
обеспечивает большую гибкость в использовании агентов. Фактически, Agent
полностью отделен от интерфейса Environment
. Это означает, что наши агенты могут использоваться вне стандартных исследовательских сред, например, будучи частью REST API, многоагентной системы и т. Д. Любой код, передающий State
и вознаграждение, совместим с нашими агентами.
Аппроксимация функций
Почти все, что делает агент глубокого обучения с подкреплением, основано на аппроксимации функции.
По этой причине одной из центральных абстракций в autonomous-learning-library
является Approximation
. Создавая агенты, которые полагаются на Approximation
абстракцию, а не напрямую взаимодействуют с объектами PyTorch Module
и Optimizer
, мы можем добавлять или изменять функциональность Agent
без изменения его исходного кода (это известно как принцип открытости-закрытости). Объект Approximation
по умолчанию позволяет нам достичь высокого уровня повторного использования кода, инкапсулируя общие функции, такие как ведение журнала, контрольные точки модели, целевые сети, графики скорости обучения и отсечение градиента. Объект Approximation
, в свою очередь, полагается на набор абстракций, которые позволяют пользователям изменять его поведение. Давайте посмотрим на простое использование Approximation
для решения очень простой задачи контролируемого обучения:
import torch from torch import nn, optim from all.approximation import Approximation # create a pytorch module model = nn.Linear(16, 1) # create an associated pytorch optimizer optimizer = optim.Adam(model.parameters(), lr=1e-2) # create the function approximator f = Approximation(model, optimizer) for _ in range(200): # Generate some arbitrary data. # We'll approximate a very simple function: # the sum of the input features. x = torch.randn((16, 16)) y = x.sum(1, keepdim=True) # forward pass y_hat = f(x) # compute loss loss = nn.functional.mse_loss(y_hat, y) # backward pass f.reinforce(loss)
Легкий! Теперь давайте посмотрим на функцию _train () для нашего агента DQN:
def _train(self): if self._should_train(): (states, actions, rewards, next_states, _) = self.replay_buffer.sample(self.minibatch_size) # forward pass values = self.q(states, actions) targets = rewards + self.discount_factor * torch.max(self.q.target(next_states), dim=1)[0] # compute loss loss = mse_loss(values, targets) # backward pass self.q.reinforce(loss)
Так же просто! Агенту не нужно ничего знать о сетевой архитектуре, журналировании, регуляризации и т. Д. Все это обрабатывается с помощью соответствующей конфигурации Approximation
. Вместо этого реализация Agent
может сосредоточиться исключительно на своей единственной цели: определении самого алгоритма RL. Инкапсулируя эти детали в Approximation
, мы можем следовать принципу единой ответственности.
Еще несколько замечаний: f.eval(x)
выполняет прямой проход в torch.no_grad()
. f.target(x)
вызывает целевую сеть (расширенная концепция, используемая в алгоритмах, таких как DQN), связанная с Approximation.
. autonomous-learning-library
также предоставляет несколько тонких оболочек над Approximation
для определенных целей, таких как QNetwork
, VNetwork
, FeatureNetwork
и несколько Policy
реализации.
Среды
Само собой разумеется, что важность Environment
в обучении с подкреплением. В autonomous-learning-library
предварительно упакованные среды представляют собой просто оболочки для OpenAI Gym, стандартной библиотеки де-факто для сред RL.
Мы добавляем несколько дополнительных функций:
gym
в основном используетnumpy.array
для представления состояний и действий. Мы автоматически конвертируем вtorch.Tensor
объекты и обратно, так что реализация агента не должна учитывать разницу.- Мы добавляем свойства в среду для
state
,reward
и т. Д. Это упрощает цикл управления и обычно полезно. - Мы применяем общие препроцессоры, такие как несколько стандартных оболочек Atari. Однако, где это возможно, мы предпочитаем выполнять предварительную обработку с использованием
Body
объектов, чтобы максимизировать гибкость агентов.
Ниже мы покажем, как можно создать несколько различных типов сред:
from all.environments import AtariEnvironment, GymEnvironment # create an Atari environment on the gpu env = AtariEnvironment('Breakout', device='cuda') # create a classic control environment on the compute env = GymEnvironment('CartPole-v0') # create a PyBullet environment on the cpu import pybullet_envs env = GymEnvironment('HalfCheetahBulletEnv-v0')
Теперь мы можем написать наш первый цикл управления:
# initialize the environment env.reset() # Loop for some arbitrary number of timesteps. for timesteps in range(1000000): env.render() action = agent.act(env.state, env.reward) env.step(action) if env.done: # terminal update agent.act(env.state, env.reward) # reset the environment env.reset()
Конечно, этот контур управления не совсем многофункциональный. Как правило, лучше использовать Experiment
API, описанный ниже.
Пресеты
В autonomous-learning-library
агенты являются композиционными, что означает, что поведение данного Agent
зависит от поведения нескольких других объектов. Пользователи могут составлять агентов с определенным поведением, передавая соответствующие объекты в конструкторы высокоуровневых алгоритмов, содержащихся в all.agents
. Библиотека предоставляет ряд функций, которые создают эти объекты определенным образом, чтобы они подходили для данного набора среды. Мы называем такую функцию preset
, и несколько таких предустановок содержатся в пакете all.presets
. (Это пример более общего паттерна фабричного метода).
Например, all.agents.vqn
содержит высокоуровневое описание стандартного алгоритма Q-обучения. Чтобы фактически применить этот агент к проблеме, например, классической задаче управления, мы могли бы определить следующую предустановку:
# The outer function signature contains the set of hyperparameters def vqn( # Common settings device="cpu", # Hyperparameters discount_factor=0.99, lr=1e-2, exploration=0.1, ): # The inner function creates a closure over the hyperparameters passed into the outer function. # It accepts an "env" object which is passed right before the Experiment begins, as well as # the writer created by the Experiment which defines the logging parameters. def _vqn(env, writer=DummyWriter()): # create a pytorch model model = nn.Sequential( nn.Linear(env.state_space.shape[0], 64), nn.ReLU(), nn.Linear(64, env.action_space.n), ).to(device) # create a pytorch optimizer for the model optimizer = Adam(model.parameters(), lr=lr) # create an Approximation of the Q-function q = QNetwork(model, optimizer, writer=writer) # create a Policy object derived from the Q-function policy = GreedyPolicy(q, env.action_space.n, epsilon=exploration) # instansiate the agent return VQN(q, policy, discount_factor=discount_factor) # return the inner function return _vqn
Обратите внимание на то, что есть «внешняя функция» и «внутренняя» функция. Этот подход позволяет разделить конфигурацию и создание экземпляра. Хотя это может показаться излишним, иногда это может быть полезно. Например, предположим, что мы хотим запустить один и тот же агент в нескольких средах. Это можно сделать следующим образом:
agent = vqn() some_custom_runner(agent(), GymEnvironment('CartPole-v0')) some_custom_runner(agent(), GymEnvironment('MountainCar-v0'))
Теперь каждый вызов some_custom_runner
получает уникальный экземпляр агента. Иногда это достигается в других библиотеках с помощью функции «сброса». Мы считаем, что наш подход позволяет нам поддерживать Agent
интерфейс в чистоте и в целом является более элегантным и менее подверженным ошибкам.
Эксперимент
Наконец, у нас есть все компоненты, необходимые для представления объекта Experiment
. Experiment
- это встроенный контур управления для проведения эксперимента по обучению с подкреплением. Он создает собственный объект Writer
, который затем передается каждому из агентов, и запускает каждого агента в каждой переданной ему среде в течение некоторого количества временных шагов (кадров) или эпизодов. Вот краткий пример:
from gym import envs from all.experiments import Experiment from all.presets import atari from all.environments import AtariEnvironment agents = [ atari.dqn(), atari.ddqn(), atari.c51(), atari.rainbow(), atari.a2c(), atari.ppo(), ] envs = [AtariEnvironment(env, device='cuda') for env in ['BeamRider', 'Breakout', 'Pong', 'Qbert', 'SpaceInvaders']] Experiment(agents, envs, frames=10e6)
Вышеупомянутый блок выполняет каждый запуск последовательно. Это может занять очень много времени даже на быстром GPU! Если у вас есть доступ к кластеру, в котором запущен Slurm, вы можете заменить Experiment
на SlurmExperiment
, чтобы существенно ускорить процесс (магия отправки заданий осуществляется за кулисами).
По умолчанию Experiment
записывает результаты в ./runs
. Вы можете просмотреть результаты в tensorboard
, выполнив следующую команду:
tensorboard --logdir runs
Помимо tensorboard
журналов, каждые 100 эпизодов в runs/[agent]/[env]/returns100.csv
записываются среднее значение и стандартное отклонение предыдущих 100 эпизодов. Этот формат намного быстрее читается и строится, чем собственный формат Tensorboard. Библиотека содержит утилиту автоматического построения графиков, которая генерирует соответствующие графики для всего runs
каталога следующим образом:
from all.experiments import plot_returns_100 plot_returns_100('./runs')
Это сгенерирует график, который выглядит следующим образом (после настройки пробелов через matplotlib
пользовательский интерфейс):
Вы также можете передать Experiment
необязательные параметры, чтобы изменить его поведение. Вместо указания frames
вы можете указать максимальное количество episodes
. Вы можете настроить render=True
для наблюдения за агентом во время обучения (обычно не рекомендуется: это значительно замедляет работу агента!). Вы можете настроить quiet=True
на отключение вывода командной строки. Наконец, вы можете установить write_loss=False
, чтобы отключить запись отладочной информации в tensorboard
. Эти файлы могут становиться большими, поэтому рекомендуется, если у вас ограниченное хранилище!
Структура проекта
В предыдущем разделе мы обсудили основные компоненты autonomous-learning-library
. Хотя библиотека содержит набор предустановленных агентов, основная цель библиотеки - быть инструментом для создания ваших собственных агентов. С этой целью мы предоставили примерный проект, содержащий новый вариант прогнозирующего управления моделью DQN, чтобы продемонстрировать гибкость библиотеки. Вкратце, при создании собственного агента вы обычно будете иметь следующие компоненты:
- Файл
agent.py
, содержащий высокоуровневую реализациюAgent
. model.py
файл, содержащий модели PyTorch, соответствующие выбранному вами домену.- Файл
preset.py
, составляющий вашAgent
с использованием соответствующей модели и других объектов. main.py
или аналогичный файл, который запускает ваш агент и любыеautonomous-learning-library
пресеты, с которыми вы хотите сравнить.
Хотя нет необходимости следовать этой структуре, мы считаем, что в целом она поможет вам использовать autonomous-learning-library
по назначению и обеспечит понимание вашего кода другими пользователями библиотеки.
Резюме
В центре autonomous-learning-library
находится Agent
. Agent
состоит из Approximation
и других объектов и может быть настроен и настроен для конкретного Environment
путем определения preset
. Experiment
API позволяет обучать preset
агента на Environment
и автоматически генерирует всевозможные журналы и другую информацию.
Вот и все! А теперь займись чем-нибудь крутым!