Spinning Up: алгоритмы VPG и PPO
Перевод описаний двух алгоритмов оптимизации политики из документации Spinning Up: простой градиент политики (VPG) и PPO-Clip — формулы, разбор клиппинга по случаям, псевдокод, параметры реализации на PyTorch и ключевые строки её кода.
Vanilla Policy Gradient (простой градиент политики)
Заголовок раздела «Vanilla Policy Gradient (простой градиент политики)»(Раньше: Введение в RL, часть 3)
Ключевая идея градиентов политики — повышать вероятности действий, которые ведут к большей отдаче, и понижать вероятности действий, которые ведут к меньшей отдаче, пока не придём к оптимальной политике.
Коротко
Заголовок раздела «Коротко»- VPG — алгоритм с обучением по текущей политикеon-policy (обучение на данных текущей политики)EN on-policyПротивоположность — off-policy, обучение на данных другой политики. (on-policy).
- VPG можно применять в средах как с дискретным, так и с непрерывным пространством действий.
- Реализация VPG в Spinning Up поддерживает распараллеливание через MPI.
Ключевые формулы
Заголовок раздела «Ключевые формулы»Пусть — политика с параметрами , а — ожидаемая недисконтированная отдача политики на конечном горизонте. Градиент равен
где — траектория, а — функция преимуществапреимущество (advantage)EN advantageA = r − b или A(s, a) = Q(s, a) − V(s): насколько действие лучше среднего в этой ситуации. для текущей политики.
Словами: проходим по всем шагам траектории; на каждом шаге берём направление, в котором растёт логарифм вероятности выбранного действия, и умножаем его на преимущество — насколько это действие оказалось лучше среднего. Хорошие действия тянут параметры в свою сторону, плохие (с отрицательным ) — от себя.
Алгоритм градиента политики обновляет параметры политики стохастическим градиентным подъёмом по качеству политики:
Реализации градиента политики обычно оценивают функцию преимущества по бесконечно-горизонтной дисконтированной отдаче, хотя в остальном используют формулу градиента политики для конечного горизонта без дисконта.
Исследование и использование
Заголовок раздела «Исследование и использование»VPG обучает стохастическую политику on-policy. Это значит, что она исследует среду, выбирая действия по самой свежей версии своей стохастической политики. Насколько случаен выбор действий, зависит и от начальных условий, и от процедуры обучения. По ходу обучения политика обычно становится всё менее случайной, потому что правило обновления подталкивает её использовать награды, которые она уже нашла. Из-за этого политика может застрять в локальном оптимуме.
Псевдокод
Заголовок раздела «Псевдокод»Алгоритм 1. Vanilla Policy Gradient
- Вход: начальные параметры политики , начальные параметры функции ценности .
- Для :
- Собрать набор траекторий , запуская в среде политику .
- Посчитать reward-to-go .
- Посчитать оценки преимущества (любым методом оценки преимущества) на основе текущей функции ценности .
- Оценить градиент политики:
- Обновить политику — либо обычным градиентным подъёмом, либо другим алгоритмом градиентного подъёма, например Adam.
- Подогнать функцию ценности регрессией по среднеквадратичной ошибке: обычно каким-нибудь алгоритмом градиентного спуска.
Документация
Заголовок раздела «Документация»Документация: версия на PyTorch
Заголовок раздела «Документация: версия на PyTorch»spinup.vpg_pytorch(env_fn, actor_critic=<MLPActorCritic>, ac_kwargs={}, seed=0, steps_per_epoch=4000, epochs=50, gamma=0.99, pi_lr=0.0003, vf_lr=0.001, train_v_iters=80, lam=0.97, max_ep_len=1000, logger_kwargs={}, save_freq=10)Vanilla Policy Gradient (с GAE-Lambda для оценки преимущества).
Параметры:
-
env_fn — функция, которая создаёт копию среды. Среда должна удовлетворять API OpenAI Gym.
-
actor_critic — конструктор модуля PyTorch с методами
stepиact, модулемpiи модулемv. Методstepдолжен принимать батч наблюдений и возвращать:Символ Форма Описание a(batch, act_dim) массив NumPy с действиями для каждого наблюдения v(batch,) массив NumPy с оценками ценности для поданных наблюдений logp_a(batch,) массив NumPy с логарифмами вероятностей действий из aМетод
actведёт себя так же, какstep, но возвращает толькоa.Прямой проход модуля
piдолжен принимать батч наблюдений и, необязательно, батч действий, и возвращать:Символ Форма Описание pi— объект Distribution из PyTorch с батчем распределений, описывающих политику для поданных наблюдений logp_a(batch,) необязательно (возвращается, только если подан батч действий): тензор с логарифмами вероятностей поданных действий по политике. Если действия не поданы, здесь будет NoneПрямой проход модуля
vдолжен принимать батч наблюдений и возвращать:Символ Форма Описание v(batch,) тензор с оценками ценности для поданных наблюдений. (Критично: не забудьте сделать его плоским!) -
ac_kwargs (dict) — любые именованные аргументы для объекта ActorCritic, который вы передали в VPG.
-
seed (int) — сид генераторов случайных чисел.
-
steps_per_epoch (int) — число шагов взаимодействия (пар «состояние — действие») агента со средой за эпоху.
-
epochs (int) — число эпох взаимодействия (равно числу обновлений политики).
-
gamma (float) — коэффициент дисконтирования. (Всегда между 0 и 1.)
-
pi_lr (float) — скорость обучения оптимизатора политики.
-
vf_lr (float) — скорость обучения оптимизатора функции ценности.
-
train_v_iters (int) — число шагов градиентного спуска по функции ценности за эпоху.
-
lam (float) — лямбда для GAE-Lambda. (Всегда между 0 и 1, близко к 1.)
-
max_ep_len (int) — максимальная длина траектории / эпизода / роллаута.
-
logger_kwargs (dict) — именованные аргументы для EpochLogger.
-
save_freq (int) — как часто (через сколько эпох) сохранять текущие политику и функцию ценности.
Содержимое сохранённой модели: версия на PyTorch
Заголовок раздела «Содержимое сохранённой модели: версия на PyTorch»Сохранённую модель PyTorch можно загрузить командой ac = torch.load('path/to/model.pt') — получится объект
актор-критика (ac) со свойствами, описанными в docstring функции vpg_pytorch.
Действия из этой модели можно получить так:
actions = ac.act(torch.as_tensor(obs, dtype=torch.float32))Документация: версия на TensorFlow
Заголовок раздела «Документация: версия на TensorFlow»Функция spinup.vpg_tf1 принимает те же параметры, только вместо модуля PyTorch actor_critic — функция, которая
строит граф вычислений актор-критика в TensorFlow 1.
Содержимое сохранённой модели: версия на TensorFlow
Заголовок раздела «Содержимое сохранённой модели: версия на TensorFlow»Граф вычислений, который сохраняет логгер, содержит:
| Ключ | Значение |
|---|---|
x | placeholder TensorFlow для входного состояния |
pi | выбирает (сэмплирует) действие агента при состояниях из x |
v | даёт оценку ценности для состояний из x |
К сохранённой модели можно обратиться
- запустив обученную политику инструментом test_policy.py,
- или загрузив весь сохранённый граф в программу функцией restore_tf_graph.
Статьи по теме
Заголовок раздела «Статьи по теме»- Policy Gradient Methods for Reinforcement Learning with Function Approximation, Sutton et al. 2000
- Optimizing Expectations: From Deep Reinforcement Learning to Stochastic Computation Graphs, Schulman 2016(a)
- Benchmarking Deep Reinforcement Learning for Continuous Control, Duan et al. 2016
- High Dimensional Continuous Control Using Generalized Advantage Estimation, Schulman et al. 2016(b)
Почему эти статьи?
Заголовок раздела «Почему эти статьи?»Sutton 2000 включена, потому что это вневременная классика теории обучения с подкреплением, и в ней есть ссылки на более ранние работы, которые привели к современным градиентам политики. Schulman 2016(a) включена, потому что её глава 2 — ясное введение в теорию алгоритмов градиента политики, с псевдокодом. Duan 2016 — понятная недавняя статья-бенчмарк, которая показывает, как простой градиент политики в глубоком RL (то есть с нейросетевыми политиками и оптимизатором Adam) выглядит на фоне других алгоритмов глубокого RL. Schulman 2016(b) включена, потому что наша реализация VPG использует Generalized Advantage Estimation для вычисления градиента политики.
Другие открытые реализации
Заголовок раздела «Другие открытые реализации»Proximal Policy Optimization (проксимальная оптимизация политики)
Заголовок раздела «Proximal Policy Optimization (проксимальная оптимизация политики)»(Раньше: основы TRPO)
PPOпроксимальная оптимизация политики (PPO)EN proximal policy optimization (PPO)Schulman и др., 2017. Ограничивает шаг обновления «клиппингом» отношения вероятностей. отвечает на тот же вопрос, что и TRPOTRPO (оптимизация политики в доверительной области)EN trust region policy optimization (TRPO)Schulman и др., 2015 — шаг политики с ограничением на KL к старой политике; предшественник PPO.: как сделать самый большой возможный шаг улучшения политики на тех данных, что у нас есть, и не шагнуть так далеко, чтобы случайно обрушить качество? TRPO решает эту задачу сложным методом второго порядка, а PPO — это семейство методов первого порядка, которые с помощью нескольких других приёмов держат новую политику рядом со старой. Методы PPO заметно проще реализовать, а на практике они, похоже, работают не хуже TRPO.
Есть два основных варианта PPO: PPO-Penalty и PPO-Clip.
PPO-Penalty приближённо решает задачу обновления с ограничением на KL, как TRPO, но вместо жёсткого ограничения штрафует KL-дивергенциюKL-дивергенция (дивергенция Кульбака — Лейблера)EN Kullback–Leibler divergenceKL(p ‖ q) = Σ p log(p / q) = H(p, q) − H(p) ≥ 0. Несимметрична, поэтому не настоящее расстояние. в целевой функции и по ходу обучения автоматически подстраивает коэффициент штрафа, чтобы он был нужного масштаба.
PPO-Clip не содержит в целевой функции слагаемого с KL-дивергенцией и вообще не имеет ограничения. Вместо этого он полагается на особую обрезку (clipping) в целевой функции, которая убирает у новой политики стимул уходить далеко от старой.
Здесь мы сосредоточимся только на PPO-Clip (основном варианте, который используют в OpenAI).
Коротко
Заголовок раздела «Коротко»- PPO — алгоритм с обучением по текущей политике (on-policy).
- PPO можно применять в средах как с дискретным, так и с непрерывным пространством действий.
- Реализация PPO в Spinning Up поддерживает распараллеливание через MPI.
Ключевые формулы
Заголовок раздела «Ключевые формулы»PPO-Clip обновляет политику так:
обычно делая несколько шагов (как правило, минибатчевого) SGD для максимизации цели. Здесь задаётся так:
где — (маленький) гиперпараметр, который грубо говоря задаёт, насколько далеко новой политике разрешено уйти от старой.
Словами: — отношение вероятностей: во сколько раз новая политика выбирает действие чаще старой (при оно равно 1). Первое слагаемое под минимумом — обычная «суррогатная» цель, второе — то же самое, но с отношением, зажатым в коридор . Минимум из двух выбирает более осторожную, пессимистичную оценку.
Выражение довольно сложное, и с первого взгляда трудно понять, что оно делает и как помогает держать новую политику рядом со старой. Оказывается, у этой цели есть заметно упрощённая версия 1, с которой легче разобраться (и именно её мы реализуем в коде):
где
Чтобы понять, какую интуицию отсюда вынести, рассмотрим одну пару «состояние — действие» и разберём случаи.
Преимущество положительное. Пусть преимущество для этой пары положительно; тогда её вклад в цель сводится к
Раз преимущество положительно, цель растёт, если действие становится вероятнее — то есть если увеличивается. Но минимум в этом слагаемом ограничивает, насколько цель может вырасти. Как только , срабатывает минимум, и слагаемое упирается в потолок . Итак: новой политике невыгодно уходить далеко от старой.
Преимущество отрицательное. Пусть преимущество для этой пары отрицательно; тогда её вклад в цель сводится к
Раз преимущество отрицательно, цель растёт, если действие становится менее вероятным — то есть если уменьшается. Но максимум в этом слагаемом ограничивает, насколько цель может вырасти. Как только , срабатывает максимум, и слагаемое упирается в потолок . И снова: новой политике невыгодно уходить далеко от старой.
Итак, мы увидели, что обрезка работает как регуляризатор: она убирает у политики стимул резко меняться, а гиперпараметр соответствует тому, насколько далеко новая политика может уйти от старой, всё ещё выигрывая по цели.
Исследование и использование
Заголовок раздела «Исследование и использование»PPO обучает стохастическую политику on-policy. Это значит, что он исследует среду, выбирая действия по самой свежей версии своей стохастической политики. Насколько случаен выбор действий, зависит и от начальных условий, и от процедуры обучения. По ходу обучения политика обычно становится всё менее случайной, потому что правило обновления подталкивает её использовать награды, которые она уже нашла. Из-за этого политика может застрять в локальном оптимуме.
Псевдокод
Заголовок раздела «Псевдокод»Алгоритм 1. PPO-Clip
- Вход: начальные параметры политики , начальные параметры функции ценности .
- Для :
- Собрать набор траекторий , запуская в среде политику .
- Посчитать reward-to-go .
- Посчитать оценки преимущества (любым методом оценки преимущества) на основе текущей функции ценности .
- Обновить политику, максимизируя цель PPO-Clip: обычно стохастическим градиентным подъёмом с Adam.
- Подогнать функцию ценности регрессией по среднеквадратичной ошибке: обычно каким-нибудь алгоритмом градиентного спуска.
Документация
Заголовок раздела «Документация»Документация: версия на PyTorch
Заголовок раздела «Документация: версия на PyTorch»spinup.ppo_pytorch(env_fn, actor_critic=<MLPActorCritic>, ac_kwargs={}, seed=0, steps_per_epoch=4000, epochs=50, gamma=0.99, clip_ratio=0.2, pi_lr=0.0003, vf_lr=0.001, train_pi_iters=80, train_v_iters=80, lam=0.97, max_ep_len=1000, target_kl=0.01, logger_kwargs={}, save_freq=10)Proximal Policy Optimization (с обрезкой), с ранней остановкой по приближённой KL.
Параметры:
- env_fn — функция, которая создаёт копию среды. Среда должна удовлетворять API OpenAI Gym.
- actor_critic — конструктор модуля PyTorch с методами
stepиact, модулемpiи модулемv. Требования кstep,act,piиvте же, что у VPG (см. таблицы выше):stepвозвращаетa,v,logp_a;piвозвращает распределение и, если поданы действия, их логарифмы вероятностей;vвозвращает плоский тензор оценок ценности. - ac_kwargs (dict) — любые именованные аргументы для объекта ActorCritic, который вы передали в PPO.
- seed (int) — сид генераторов случайных чисел.
- steps_per_epoch (int) — число шагов взаимодействия (пар «состояние — действие») агента со средой за эпоху.
- epochs (int) — число эпох взаимодействия (равно числу обновлений политики).
- gamma (float) — коэффициент дисконтирования. (Всегда между 0 и 1.)
- clip_ratio (float) — гиперпараметр обрезки в цели политики. Грубо говоря: насколько далеко новая политика может
уйти от старой, всё ещё выигрывая (улучшая целевую функцию)? Новая политика может уйти и дальше, чем говорит
clip_ratio, но цели это больше не помогает. (Обычно маленький, от 0,1 до 0,3.) Обычно обозначается . - pi_lr (float) — скорость обучения оптимизатора политики.
- vf_lr (float) — скорость обучения оптимизатора функции ценности.
- train_pi_iters (int) — максимальное число шагов градиентного спуска по функции потерь политики за эпоху. (Из-за ранней остановки оптимизатор может сделать меньше.)
- train_v_iters (int) — число шагов градиентного спуска по функции ценности за эпоху.
- lam (float) — лямбда для GAE-Lambda. (Всегда между 0 и 1, близко к 1.)
- max_ep_len (int) — максимальная длина траектории / эпизода / роллаута.
- target_kl (float) — грубо говоря, какую KL-дивергенцию между новой и старой политикой после обновления мы считаем допустимой. Используется для ранней остановки. (Обычно маленькая, 0,01 или 0,05.)
- logger_kwargs (dict) — именованные аргументы для EpochLogger.
- save_freq (int) — как часто (через сколько эпох) сохранять текущие политику и функцию ценности.
Содержимое сохранённой модели: версия на PyTorch
Заголовок раздела «Содержимое сохранённой модели: версия на PyTorch»Сохранённую модель PyTorch можно загрузить командой ac = torch.load('path/to/model.pt') — получится объект
актор-критика (ac) со свойствами, описанными в docstring функции ppo_pytorch.
Действия из этой модели можно получить так:
actions = ac.act(torch.as_tensor(obs, dtype=torch.float32))Документация и сохранённая модель: версия на TensorFlow
Заголовок раздела «Документация и сохранённая модель: версия на TensorFlow»Функция spinup.ppo_tf1 принимает те же параметры (с функцией построения графа вместо модуля PyTorch). Граф,
который сохраняет логгер, содержит те же ключи, что у VPG: x (placeholder для состояния), pi (сэмплирует
действие), v (оценка ценности). Запускать его можно через test_policy.py или загружать через restore_tf_graph.
Статьи по теме
Заголовок раздела «Статьи по теме»- Proximal Policy Optimization Algorithms, Schulman et al. 2017
- High Dimensional Continuous Control Using Generalized Advantage Estimation, Schulman et al. 2016
- Emergence of Locomotion Behaviours in Rich Environments, Heess et al. 2017
Почему эти статьи?
Заголовок раздела «Почему эти статьи?»Schulman 2017 включена, потому что это исходная статья с описанием PPO. Schulman 2016 включена, потому что наша реализация PPO использует Generalized Advantage Estimation для вычисления градиента политики. Heess 2017 включена, потому что в ней есть крупный эмпирический анализ поведения, которому учатся агенты PPO в сложных средах (хотя там используется PPO-Penalty, а не PPO-Clip).
Другие открытые реализации
Заголовок раздела «Другие открытые реализации»- Baselines
- ModularRL (Осторожно: здесь реализован PPO-Penalty, а не PPO-Clip.)
- rllab (Осторожно: здесь реализован PPO-Penalty, а не PPO-Clip.)
- rllib (Ray)
Ключевые строки кода PPO (добавлено переводчиком)
Заголовок раздела «Ключевые строки кода PPO (добавлено переводчиком)»Чтобы связать формулы с кодом, ниже — три фрагмента из файла
spinup/algos/pytorch/ppo/ppo.py
(MIT). Код приведён как в оригинале, русские комментарии добавлены переводчиком. Целиком файл запускать не нужно:
он рассчитан на старый Gym и MPI. Его полезно читать.
1. GAE и reward-to-go в конце траектории
Заголовок раздела «1. GAE и reward-to-go в конце траектории»def finish_path(self, last_val=0): # срез буфера с текущей траекторией path_slice = slice(self.path_start_idx, self.ptr) # last_val = 0, если эпизод закончился «смертью», иначе V(s_T) — # так мы достраиваем отдачу за пределами обрезанного эпизода (бутстрэп) rews = np.append(self.rew_buf[path_slice], last_val) vals = np.append(self.val_buf[path_slice], last_val)
# следующие две строки — оценка преимущества GAE-Lambda: # δ_t = r_t + γ·V(s_{t+1}) − V(s_t), затем A_t = Σ (γλ)^l · δ_{t+l} deltas = rews[:-1] + self.gamma * vals[1:] - vals[:-1] self.adv_buf[path_slice] = core.discount_cumsum(deltas, self.gamma * self.lam)
# reward-to-go R̂_t = Σ γ^l · r_{t+l} — цель для регрессии функции ценности self.ret_buf[path_slice] = core.discount_cumsum(rews, self.gamma)[:-1]
self.path_start_idx = self.ptrПеред обновлением преимущества ещё и нормируются: из них вычитают среднее и делят на стандартное отклонение
(«трюк нормализации преимущества» в методе get).
2. Функция потерь политики PPO-Clip
Заголовок раздела «2. Функция потерь политики PPO-Clip»def compute_loss_pi(data): obs, act, adv, logp_old = data['obs'], data['act'], data['adv'], data['logp']
# Потери политики pi, logp = ac.pi(obs, act) ratio = torch.exp(logp - logp_old) # π_θ(a|s) / π_θk(a|s) через разность логарифмов clip_adv = torch.clamp(ratio, 1-clip_ratio, 1+clip_ratio) * adv loss_pi = -(torch.min(ratio * adv, clip_adv)).mean() # минус: оптимизатор минимизирует
# Полезная дополнительная информация approx_kl = (logp_old - logp).mean().item() # грубая оценка KL(старая || новая) ent = pi.entropy().mean().item() # энтропия политики clipped = ratio.gt(1+clip_ratio) | ratio.lt(1-clip_ratio) clipfrac = torch.as_tensor(clipped, dtype=torch.float32).mean().item() # доля обрезанных примеров pi_info = dict(kl=approx_kl, ent=ent, cf=clipfrac)
return loss_pi, pi_infoОбратите внимание: здесь используется полная форма с clamp, а не функция . Обе дают одно и то же
значение; упрощённая форма нужна, чтобы проще рассуждать.
3. Обновление: много шагов по одним и тем же данным и ранняя остановка
Заголовок раздела «3. Обновление: много шагов по одним и тем же данным и ранняя остановка»# Обучаем политику несколькими шагами градиентного спускаfor i in range(train_pi_iters): pi_optimizer.zero_grad() loss_pi, pi_info = compute_loss_pi(data) kl = mpi_avg(pi_info['kl']) if kl > 1.5 * target_kl: # политика ушла слишком далеко — стоп logger.log('Early stopping at step %d due to reaching max kl.'%i) break loss_pi.backward() mpi_avg_grads(ac.pi) # усредняем градиенты между процессами MPI pi_optimizer.step()
# Обучение функции ценности: регрессия V(s) на reward-to-gofor i in range(train_v_iters): vf_optimizer.zero_grad() loss_v = compute_loss_v(data) # ((ac.v(obs) - ret)**2).mean() loss_v.backward() mpi_avg_grads(ac.v) vf_optimizer.step()Что дальше
Заголовок раздела «Что дальше»- TRPO, DDPG, TD3, SAC — остальные четыре алгоритма Spinning Up.
- Упражнения: упражнение 1.2 — написать гауссову политику для этого PPO.
- Ключевые статьи — откуда что взялось.
Footnotes
Заголовок раздела «Footnotes»-
Вывод упрощённой формы цели PPO-Clip — в этой заметке. ↩