Траектория «Обучение с подкреплением» · конспект 2 из 12

Многорукие бандиты

О чём эта тема
Простейшая модель обучения с подкреплением: состояний нет, награда приходит сразу, — но центральный конфликт разведки и использования уже здесь. Вводятся ценность действия, regret — накопленные потери относительно оптимума — с доказательством его разложения, жадные и ε-жадные стратегии, инкрементальная оценка ценностей — формула, из которой позже вырастет всё TD-обучение. По лекции курса «RL: от бандитов до RLHF» (мехмат МГУ) и конспекту преподавателя.
Аннотация
Конспект начинается с постановки задачи k-рукого бандита и определения истинной ценности действия. Затем вводится мера качества алгоритма — regret — и доказывается его фундаментальное разложение через разрывы ценностей и числа выборов неоптимальных действий. Далее анализируются жадная стратегия с примером «застревания» и ε-жадная стратегия, для которой выводится нижняя оценка вероятности оптимального действия и объясняется линейный рост regret при фиксированном ε. Центральная техническая часть — оценка ценностей по выборке: доказываются несмещённость и скорость убывания дисперсии выборочного среднего, выводится инкрементальная формула обновления и её обобщение с постоянным шагом, дающее экспоненциальное сглаживание для нестационарных задач. Завершается конспект реализацией на numpy и воспроизведением классического эксперимента Саттона–Барто — сначала в коде, а затем живьём в интерактивном тренажёре.
Пререквизиты
Конспект 1 (агент, действие, награда, возврат). Из теории вероятностей: математическое ожидание, условное ожидание, дисперсия, закон больших чисел, нормальное распределение.
Мотивация
Задача бандита — не игрушка: выбор рекламного объявления с наибольшей вероятностью клика, выбор рекомендательного блока, A/B-тест вариантов интерфейса — всё это bandit-постановки, в которых каждый показ либо приносит информацию о вариантах, либо эксплуатирует лучший найденный. Каждый лишний показ заведомо худшего варианта — прямые потери, и их сумму можно измерить точно: это и есть regret.

1. Постановка задачи

k-рукий бандит — задача последовательного выбора действий из конечного множества \(\mathcal{A} = \{1, 2, \ldots, k\}\): на шаге \(t = 1, 2, \ldots\) агент выбирает действие \(A_t \in \mathcal{A}\) («дёргает за рычаг» одного из \(k\) игровых автоматов) и получает случайную награду \(R_t\), распределение которой \(\nu_a\) своё у каждого действия и агенту неизвестно. В стационарной постановке распределения не зависят от времени.

По сравнению с полной задачей RL из конспекта 1 здесь всё упрощено: состояний нет, каждое действие немедленно даёт наблюдаемую награду, и текущее решение не меняет устройство среды. Тем не менее центральная проблема уже возникает: чтобы узнать, какое действие лучшее, нужно пробовать разные, но чтобы максимизировать награду — надо чаще выбирать те, что уже кажутся хорошими. Это конфликт разведки и использования (англ. exploration vs exploitation).

Истинной ценностью действия \(a\) называется математическое ожидание награды при его выборе:

\[ q(a) = \bigl\langle R_t \,\big|\, A_t = a \bigr\rangle. \tag{2.1}\]

Если бы значения (2.1) были известны, задача была бы тривиальной — всегда выбирать

\[ a^* \in \arg\max_{a \in \mathcal{A}} q(a), \qquad q^* = \max_{a \in \mathcal{A}} q(a). \tag{2.2}\]

На практике \(q(a)\) неизвестны, и агент вынужден оценивать их по наблюдаемым наградам.

2. Regret: цена неоптимальных действий

Качество алгоритма естественно измерять накопленными потерями относительно гипотетического агента, который всегда выбирает оптимальное действие. Эта величина называется regret (в строгой терминологии — pseudo-regret, поскольку сравниваются ожидаемые награды); за \(T\) шагов:

\[ R_T = T\,q^* - \sum_{t=1}^{T} q(A_t). \tag{2.3}\]

Введём число выборов каждого действия и разрыв (англ. gap) каждого действия до оптимума:

\[ N_T(a) = \sum_{t=1}^{T} \mathbb{1}\{A_t = a\}, \qquad \Delta_a = q^* - q(a) \;\ge\; 0. \tag{2.4}\]

Утверждение (разложение regret). Для любого алгоритма

\[ \bigl\langle R_T \bigr\rangle = \sum_{a \in \mathcal{A}} \Delta_a \,\bigl\langle N_T(a) \bigr\rangle. \tag{2.5}\]

Доказательство. Каждый шаг выбирает ровно одно действие, поэтому \(\sum_a N_T(a) = T\). Сгруппируем сумму наград по действиям: \(\sum_{t=1}^{T} q(A_t) = \sum_a q(a)\, N_T(a)\). Подставляя оба равенства в (2.3):

\[ R_T = q^* \sum_a N_T(a) - \sum_a q(a)\, N_T(a) = \sum_a \bigl(q^* - q(a)\bigr) N_T(a) = \sum_a \Delta_a N_T(a), \]

и взятие математического ожидания обеих частей даёт (2.5). ∎

Формула (2.5) фундаментальна: минимизировать regret — значит ограничить число выборов неоптимальных действий, причём каждый выбор действия \(a\) стоит ровно \(\Delta_a\). Хороший алгоритм имеет \(\langle R_T \rangle = O(\log T)\): тогда regret на шаг убывает как \(O(\log T / T) \to 0\) — алгоритм всё реже ошибается.

3. Жадная и ε-жадная стратегии

Пусть \(Q_t(a)\) — оценка ценности действия \(a\), построенная по наградам, наблюдённым до шага \(t\) (как именно строить — раздел 4). Действие называется жадным, если его оценка максимальна: \(a \in \arg\max_b Q_t(b)\). Стратегия «всегда выбирать жадное действие» (чистая жадная, англ. pure greedy) выглядит естественно, но может застрять навсегда.

Пример. Два автомата, \(q(1) = 1\), \(q(2) = 0{,}9\). Пусть первые наблюдения оказались нетипичными: первый автомат выдал низкую награду, второй — высокую. Жадный алгоритм объявит лучшим второй автомат и почти перестанет выбирать первый — оценка \(Q(1)\) так и останется заниженной, и исправить её будет некому: по разложению (2.5) regret растёт линейно со скоростью \(\Delta_2 = 0{,}1\) за шаг.

Лекарство — принудительная разведка. ε-жадная стратегия:

\[ A_t = \begin{cases} \text{жадное действие из } \arg\max_a Q_t(a), & \text{с вероятностью } 1 - \varepsilon,\\[2pt] \text{случайное действие из } \mathcal{A}, & \text{с вероятностью } \varepsilon. \end{cases} \tag{2.6}\]

При равенстве оценок жадное действие выбирается случайно из максимальных. Если оптимальное действие уже распознаётся как жадное, вероятность выбрать его на шаге равна не менее чем

\[ 1 - \varepsilon + \frac{\varepsilon}{k}, \tag{2.7}\]

поскольку и в фазе разведки оптимальный рычаг выпадает с вероятностью \(1/k\). Обратная сторона: при фиксированном \(\varepsilon\) разведка не выключается никогда, среднее число случайных неоптимальных выборов пропорционально \(T\), и по (2.5) regret растёт линейно: \(\langle R_T \rangle \ge c\,T\) для некоторой константы \(c > 0\). Поэтому на практике \(\varepsilon\) уменьшают по мере стабилизации оценок \(Q\) — либо по расписанию, либо переключаясь на более умные стратегии.

Типичная ошибка Оставляют фиксированное ε на весь горизонт и удивляются, что средняя награда выходит на плато ниже оптимума: ε-доля шагов тратится на случайные действия вечно. Ошибка в обратную сторону — выключить разведку слишком рано (ε = 0 с самого начала): по примеру выше алгоритм рискует навсегда застрять на неоптимальном рычаге из-за раннего шума.

4. Оценка ценностей по выборке

Естественная оценка ценности — выборочное среднее наград, полученных при выборе действия \(a\):

\[ Q_t(a) = \frac{\sum_{i=1}^{t-1} R_i\, \mathbb{1}\{A_i = a\}}{N_t(a)}, \qquad N_t(a) > 0 \tag{2.8}\]

(ещё не выбиравшимся действиям ставят начальную оценку, например \(Q_1(a) = 0\)). Два свойства этой оценки доказываются в одну строку каждое. Пусть награды действия \(a\) независимы, одинаково распределены, с ожиданием \(q(a)\) и дисперсией \(\sigma_a^2\), и действие выбрано \(n\) раз. Несмещённость: по линейности ожидания

\[ \bigl\langle Q_n(a) \bigr\rangle = \frac{1}{n} \sum_{i=1}^{n} \bigl\langle R_i \bigr\rangle = q(a). \tag{2.9}\]

Скорость сходимости: по независимости дисперсия среднего

\[ \operatorname{Var}\bigl(Q_n(a)\bigr) = \frac{1}{n^2} \sum_{i=1}^{n} \operatorname{Var}(R_i) = \frac{\sigma_a^2}{n} \;\xrightarrow[n \to \infty]{}\; 0, \tag{2.10}\]

а по усиленному закону больших чисел \(Q_n(a) \to q(a)\) почти наверное. Оценка честная — вопрос лишь в том, чтобы каждое действие выбиралось достаточно часто (для чего и нужна разведка).

4.1. Инкрементальное обновление

Формула (2.8) в лоб требует хранить все прошлые награды. Выведем пересчёт «на лету». Пусть действие выбрано \(n\) раз с наградами \(r_1, \ldots, r_n\) и \(Q_n = \frac{1}{n}\sum_i r_i\). После награды \(r_{n+1}\):

\[ Q_{n+1} = \frac{1}{n+1} \sum_{i=1}^{n+1} r_i = \frac{1}{n+1} \Bigl( \sum_{i=1}^{n} r_i + r_{n+1} \Bigr) = \frac{n}{n+1} Q_n + \frac{1}{n+1}\, r_{n+1} = Q_n + \frac{1}{n+1} \bigl( r_{n+1} - Q_n \bigr). \tag{2.11}\]

Хранить нужно только текущее \(Q_n\) и счётчик \(n\). Структура результата важнее самой формулы:

\[ Q \;\leftarrow\; Q + \alpha\, \bigl( R - Q \bigr) \qquad \text{«оценка += шаг · (цель − оценка)»}. \tag{2.12}\]

Это общая схема обучения оценок, с которой мы будем встречаться до конца траектории: в (2.11) шаг \(\alpha_n = \frac{1}{n+1}\), в TD-обучении (конспект 4) целью станет «награда + оценка следующего состояния», в Q-обучении (конспект 5) — максимум по действиям. Величину \(R - Q\) — расхождение цели и текущей оценки — там будут называть TD-ошибкой.

4.2. Постоянный шаг и нестационарность

Что если взять в (2.12) постоянный шаг \(\alpha \in (0, 1)\) вместо \(1/n\)? Развернём рекуррентность:

\[ Q_{n+1} = Q_n + \alpha (R_n - Q_n) = (1-\alpha) Q_n + \alpha R_n = (1-\alpha)^n Q_1 + \sum_{i=1}^{n} \alpha (1-\alpha)^{n-i} R_i \tag{2.13}\]

(второе равенство получается подстановкой такого же выражения для \(Q_n\), и так \(n\) раз; строгое обоснование — индукция по \(n\)). Веса наблюдений убывают геометрически с давностью: свежая награда весит \(\alpha\), награда \(m\) шагов назад — \(\alpha(1-\alpha)^m\). Это экспоненциальное сглаживание: оценка «забывает» старое, поэтому постоянный шаг — правильный выбор для нестационарных задач, где ценности рычагов дрейфуют со временем (реклама, рекомендации). Платой служит то, что дисперсия оценки уже не убывает до нуля, как в (2.10), а стабилизируется на уровне порядка \(\alpha \sigma_a^2 / (2 - \alpha)\).

Типичная ошибка Используют выборочное среднее (шаг 1/n) в нестационарной среде. Старые награды весят столько же, сколько свежие, и оценка бесконечно тянется за устаревшим прошлым: после смены ценностей рычагов алгоритм адаптируется всё медленнее, потому что n уже велико и шаг 1/n ничтожен.

5. Реализация: ε-жадный агент на numpy

Псевдокод из лекции переносится в numpy дословно: инициализировать \(Q\) и \(N\) нулями, на каждом шаге выбрать действие по (2.6), получить награду, обновить счётчик и оценку по (2.11). Тестовая среда — гауссовский 10-armed testbed Саттона–Барто: истинные ценности разыгрываются один раз из \(N(0,1)\), награды шумят вокруг них с единичной дисперсией:

\[ q(a) \sim N(0, 1), \qquad R_t \,\big|\, A_t = a \;\sim\; N\bigl(q(a),\, 1\bigr). \tag{2.14}\]
import numpy as np

def run_bandit(eps, T=1000, k=10, rng=None):
    rng = rng or np.random.default_rng()
    q = rng.normal(0, 1, k)          # истинные ценности — формула (2.14)
    a_star = np.argmax(q)            # оптимальный рычаг (агенту неизвестен)

    Q = np.zeros(k)                  # оценки ценностей
    N = np.zeros(k)                  # счётчики выборов
    rewards, optimal = [], []

    for t in range(T):
        if rng.random() < eps:                    # разведка — формула (2.6)
            a = rng.integers(k)
        else:                                     # использование
            a = np.argmax(Q)
        r = q[a] + rng.normal()                   # награда — формула (2.14)
        N[a] += 1
        Q[a] += (r - Q[a]) / N[a]                 # инкрементальное обновление (2.11)
        rewards.append(r)
        optimal.append(a == a_star)
    return np.array(rewards), np.array(optimal)

# усреднение по 200 независимым прогонам для каждой стратегии
for eps in [0.0, 0.01, 0.1]:
    R = np.mean([run_bandit(eps)[0] for _ in range(200)], axis=0)
    print(eps, R[-100:].mean())     # средняя награда в конце обучения

Одиночный прогон почти ни о чём не говорит — кривые шумные, и какая-нибудь стратегия может случайно выиграть. Сравнивают средние по сотням прогонов кривые двух метрик: средняя награда на шаге и доля выборов оптимального действия.

Типичная ошибка Сравнивают стратегии по одной реализации эксперимента. Награды случайны, разыгранные ценности рычагов — тоже: в одиночном прогоне жадная стратегия может обогнать ε-жадную просто по везению. Выводы делаются только по среднему многих независимых прогонов (в классическом эксперименте — 2000).

6. Эксперимент Саттона–Барто

Ожидаемая картина из лекции: жадная стратегия стартует неплохо, но застревает на субоптимальных рычагах (~1.0 средней награды, оптимальное действие лишь в трети случаев); ε = 0.1 быстро находит оптимум, но 10 % шагов вечно тратит на разведку; ε = 0.01 разгоняется медленнее, зато на длинном горизонте её плато выше:

График средней награды на шаге для стратегий с epsilon 0, 0.01 и 0.1: жадная выходит на низкое плато, epsilon 0.1 растёт быстрее всех
Средняя награда на шаге
График доли выборов оптимального действия: жадная стратегия около 35 процентов, epsilon 0.1 выше 80 процентов
Доля выборов оптимального действия

Классические кривые 10-armed testbed. Из лекции курса «RL: от бандитов до RLHF»

Тренажёр 1: побудьте ε-жадным алгоритмом

Три автомата с неизвестными вам ценностями (данные иллюстративные; награды гауссовские). Оценки Q считаются инкрементальной формулой (2.11) по вашим же попыткам. Попробуйте набрать максимум за 20–30 попыток, затем раскройте истинные ценности: regret (2.3) покажет цену каждой попытки, потраченной на неоптимальный автомат, — и цену слишком ранней уверенности.

Тренажёр 2: 10-armed testbed в браузере
средняя награда на шаге
доля оптимальных действий, %

Точное воспроизведение эксперимента из раздела 5 по формулам (2.6), (2.11), (2.14): серая кривая — жадная стратегия (ε = 0), синяя — ε = 0.01, оранжевая — ε = 0.1. Генератор случайных чисел фиксирован, поэтому результат воспроизводим; сравните итоговые числа и форму кривых с классическими графиками выше.

Контрольные вопросы