Введение
Статья написана под впечатлением и при поддержке "Большой математической мастерской", проходившей летом 2022 г. в Академгородке на базе НГУ.
Теорема о градиенте стратегии является одним из краеугольных камней современного RL. Удивительно, что некоторые авторы книг, посвященных обучению с подкреплением, стараются обойти тему стороной и неохотно делятся информацией откуда и каким образом у стратегии растут градиенты. В этой заметке, мы разберём детали непростой анатомии стратегий во всех подробностях. За дополнительным сведениями об RL рекомендую обращаться к произведению Р. Саттона и А. Барто “Reinforcement Learning. An Introduction“
Интересно, что теорема о градиенте стратегии располагается в одной из точек пересечения математического анализа c теорией вероятностей и не является тривиальным результатом, поэтому разговор будет происходить с активным применением нотации и определенным уровнем математической строгости. С другой стороны все символы и формулы постараюсь также описать словами, чтобы не терялась нить повествования.
Некоторые определения и задача оптимизации RL
Начнём разговор с описания окружающего ландшафта, для этого сформулируем некоторые важные определения из области обучения с подкреплением.
Траектория – последовательность состояний , между которыми перемещается агент, совершая действияи получая вознаграждение . Конечную траекторию будем также называть эпизодом.
При выборе очередного действия, агент использует стратегию .
Стратегия – отображение пространства состояний на пространство действий. Для каждого состояния стратегия возвращает распределение вероятностей выбора того или иного действия .
В роли стратегии выступает какая-то хитрая функция с набором параметров , покрутив которые можно обучить её нужному поведению. Для удобства рассуждений мы будем считать, что это нейросеть.
Эффективность стратегии определяется вознаграждением, которое агент получил при движении вдоль траектории , порожденной .
Функция вознаграждения представляет собой сумму всех вознаграждений , которые получил агент, начиная с момента времени и до конца эпизода . Коэффициент обесценивания регулирует влияние отдаленных шагов на текущий момент времени
Если траекторию рассматривать целиком, то обозначение слегка упрощается (исчезает индекс ):
В сложных средах траектории могут сильно различаться даже для одной и той же стратегии , поэтому для оценки поведения агента удобнее усреднять вознаграждение по набору траекторий. Возникает
Целевая функция – среднее вознаграждение агента по всем траекториям, порожденным с помощью стратегии .
Возникает естественное желание, изменяя параметры стратегии , максимизировать целевую функцию, чтобы добиться наибольшего вознаграждения для агента.
Т.к. мы договорились считать нейросетью, то набором параметров являются веса сети.
Вычислительные затруднения
В первом приближении карта местности определена и сформулирована понятная цель: увеличить среднее вознаграждение, которое получает агент в процессе взаимодействия со средой, руководствуясь стратегией . Математическим языком эту цель можно выразить в виде задачи оптимизации
Как это можно сделать? Первый ответ, который приходит в голову специалистам по ML, воспользоваться градиентом целевой функции по набору параметров : ! Схему градиентного подъема можно записать вот так
Но как применить её на практике? Посмотрим внимательно на уравнение нашей задачи (1) оптимизации. Ключевым элементом выражения является функция вознаграждения . Что же нам известно о ней? Как она зависит от ?
К сожалению, мы не знаем об почти ничего, она выступает в роли загадочного черного ящика. Выражение не получится продифференцировать по . Но связь между и можно почувствовать, семплируя траектории и вычисляя вдоль них вознаграждение . То есть, нужная информация хранится в распределении и нужно найти способ до неё добраться.
Теорема о градиенте
Такой способ существует и называется теоремой о градиенте стратегии. Для удобства восприятия, мы сперва разберем её в общих обозначениях, а затем подставим интересующие выражения и выведем основной результат.
Некоторые полезные математические преобразования
Пусть определены следующие объекты: переменная , функция , условное (оно же параметризованное) распределение вероятностей и математическое ожидание (то есть интеграл). Будем искать градиент математического ожидания по параметру .
Сперва выпишем определение мат. ожидания, а затем внесём оператор дифференцирования под знак интеграла
не зависит от , поэтому выносим её из-под оператора и домножаем всё подынтегральное выражение на единицу в форме
в подынтегральном выражении появился знакомый паттерн для логарифма, который можно собрать, а затем и снова выписать мат. ожидание
За счет смены порядка операторов дифференцирования и интегрирования, а также пары математических трюков удалось получить тождество, в котором градиент находится под интегралом и действует только на распределение , явным образом зависящее от параметра .
На функцию при этом накладываются минимальные требования: мы хотим, чтобы она была интегрируемой. Тогда интеграл для мат. ожидания можно оценивать численно с помощью выборок .
Градиент стратегии для RL
Пора вернуться обратно к градиенту целевой функции . Подставим в получившееся равенство (2) выражения для стратегии и переместимся в пространство траекторий. Теперь в роли выступает траектория , – это функция вознаграждения для траектории , а таинственное распределение превращается в .
В целом выражение выглядит хорошо, но появился множитель , который выражает вероятность возникновения траектории при условии, что стратегия задана набором параметров . Как его вычислить?
Мы знаем, что для каждого шага вероятность действия определяется стратегией и равна , а вероятность перехода между состояниями и определяется как . Тогда вероятность осуществления всей траектории равна произведению вероятностей этих переходов.
Загадка множителя разгадана, но попробуем ещё немного улучшить уравнение градиента, прологарифмировав выражение (3) и вычислив градиент по . Сперва подставляем (3) и превращаем произведение в сумму, пользуясь свойствами логарифма
затем избавляемся от слагаемых, не зависящих от , т.к. при дифференцировании они дадут ноль
Можно сказать, что в равенстве (4) удалось избавиться от вероятностей, связанных с переходами между состояниями, которые не зависят от стратегии, т.е. агент не может на них повлиять. Собрав все множители под знаком суммы, мы получаем формулировку теоремы о градиенте стратегии
Тонкая настройка вознаграждений
На практике имеет смысл выполнить ещё одно преобразование, хотя оно уже и не будет тождественным. В выражении (5) каждое слагаемое содержит коэффициент , зависящий от полной траектории. Т.е. информация о порядке действий не используется. Это можно исправить, если считать вознаграждения по отдельности для каждого временного шага . Для этого мы заменим на .
Получившиеся выражения (4) и (5) в общем случае не получится аналитически проинтегрировать, но на самом деле этого и не требуется. Основная ценность уравнений градиента заключается в том, что с их помощью можно определять направление, в котором следует изменить стратегию , чтобы увеличить совокупное вознаграждение. Нужно просто собирать информацию о пройденных траекториях и пересчитывать значения суммы для всех шагов.
Последнее замечание касается множителя . Т.к. является по сути нейросетью, возвращающей вероятностные распределения для действий на каждом шаге, значит с вычислением градиента способен справиться любой DL-фреймворк.
Пример алгоритма
В настоящее время немало продвинутых RL-алгоритмов в том или ином виде опираются на градиент стратегии. Для примера взглянем на простейший алгоритм REINFORCE, использующий для обучения только актуальный опыт, то есть текущую траекторию.
def reinforce(env, pi, n_episode, gamma=1.0):
"""
Алгоритм REINFORCE
@param env: имя среды Gym
@param pi: сеть, аппроксимирующая стратегию
@param n_episode: количество эпизодов
@param gamma: коэффициент обесценивания
"""
for episode in range(n_episode):
log_probs = []
rewards = []
state = env.reset()
while True:
action, log_prob = pi.get_action(state)
next_state, reward, is_done, _ = env.step(action)
log_probs.append(log_prob)
rewards.append(reward)
if is_done:
returns = []
Gt, k = 0, 0
for reward in rewards[::-1]:
Gt += gamma ** k * reward
k += 1
returns.append(Gt)
returns = torch.tensor(returns)
pi.update(returns, log_probs)
В качестве упражнения на внимание и смекалку, предлагаю пристально посмотреть на представленный код и соотнести его с разобранными выше формулами.