Модуль 5. Обучение по фиксированным данным: имитация, глубокие Q-методы и офлайн-RL
Сначала попробуйте решить упражнение. Ответы, подробные решения и проектные ориентиры раскрываются отдельно и сохраняют своё состояние при переключении RU/EN.
Скачать файлы курса (.zip) 382,1 КБАрхив содержит файлы на обоих языках и вспомогательные материалы с сохранением структуры каталогов.
Когда SFT действительно является BC
Для каждого случая укажите, можно ли буквально считать токеновый SFT поведенческим копированием. Назовите состояние, действие и маску функции потерь.
-
Модель получает запрос и экспертный ответ; потери считаются только по токенам ответа.
-
Потери считаются по запросу пользователя и по ответу ассистента.
-
Один «шаг» в задаче определяется как весь ответ целиком, но оптимизируется токеновая отрицательная логарифмическая правдоподобность.
-
Для каждого запроса есть несколько демонстраций, и модель максимизирует их совместное правдоподобие.
Объясните, почему фраза «SFT всегда тождествен BC» без указания единицы действия и маски слишком сильна.
Показать ответ и решение
-
Да, буквально в токеновой абстракции. Состояние , действие , маска равна единице только на токенах ответа. Максимизация правдоподобия демонстрационных действий и есть BC.
-
Это всё ещё максимум правдоподобия последовательности, но более широкий процесс. Модель копирует также токены пользователя. Называть это BC политики ассистента без оговорки нельзя, потому что часть «действий» не принадлежит ассистенту.
-
Да, при согласованных масках и агрегировании. На уровне полного ответа состояние — , действие — . Тождество делает сумму токеновых NLL точной BC-целью для этого действия, включая EOS при одной и той же конвенции. Обратное распространение через факторизацию не меняет цель. Усреднение каждого ответа по собственной длине меняет веса примеров; деление общей суммы на фиксированное число токенов лишь масштабирует цель пакета.
-
Да. Несколько демонстраций задают эмпирическое распределение экспертных действий. Максимум их правдоподобия остаётся BC, хотя эксперт уже не детерминирован.
Фраза без условий скрывает единицу действия, способ формирования состояния и маску. Именно эти элементы определяют, какую политику мы копируем.
Два распределения состояний
Пусть ошибка политики на состояниях эксперта равна 1%. Ответьте:
-
Почему из этого не следует 1% ошибок при развёртывании?
-
Когда расхождение и может оказаться небольшим?
-
Приведите пример обратимой и необратимой ошибки.
-
Какое дополнительное измерение качества нужно помимо точности действий на демонстрациях?
Показать ответ и решение
-
Ошибка 1% измерена при . После собственной ошибки политика может перейти в состояния из , которых почти нет в демонстрациях, и там частота ошибок может быть иной.
-
Распределения остаются близкими, если ошибки редки и обратимы, динамика устойчива, демонстрации покрывают восстановление или модель хорошо обобщает на соседние состояния.
-
Обратимая ошибка: лишний шаг в коридоре, после которого можно вернуться. Необратимая: падение с обрыва или отправка необратимой транзакции.
-
Нужна оценка политики в замкнутом контуре: ожидаемая накопленная награда, вероятность успеха, стоимость отказа, качество восстановления или иной показатель на траекториях самой политики.
DAgger, RLHF и формат обратной связи
Сопоставьте три протокола:
-
эксперт сообщает правильное действие для состояния, посещённого текущей политикой;
-
человек выбирает лучший из двух полных ответов;
-
автоматическая проверяющая программа выдаёт скалярную награду за полный ответ.
Какие из них соответствуют DAgger, обучению по предпочтениям и RL с проверяемой наградой? Что общего у протоколов и почему их целевые функции не следует смешивать?
Показать ответ и решение
-
Это DAgger: эксперт даёт целевое действие в конкретном состоянии.
-
Это обучение по предпочтениям; пары могут затем использоваться в модели награды, DPO или другой цели.
-
Это RL с проверяемой наградой: программа задаёт скалярный сигнал за результат.
Общее состоит в том, что обратная связь собирается на поведении текущей политики, а не только на статических демонстрациях. Но действие эксперта, бинарное предпочтение и скалярная награда имеют разные пространства наблюдений и приводят к разным оценивателям и функциям потерь.
Буфер опыта и фиксированный журнал
Сравните обычный DQN с буфером опыта и офлайн-RL.
-
Почему оба алгоритма могут обновляться по данным другой политики?
-
Почему буфер DQN не делает задачу офлайн-RL?
-
Что происходит, если текущая политика DQN обнаруживает новую полезную область состояний?
-
Почему в фиксированном журнале та же ошибка покрытия принципиально опаснее?
Показать ответ и решение
-
В обоих случаях целевая политика может отличаться от политики, собравшей переходы.
-
DQN продолжает взаимодействовать со средой и обновляет буфер. Набор данных не фиксирован на весь период обучения.
-
Новые переходы попадают в буфер; Q-функция может переоценить прежние выводы по реальному опыту.
-
В офлайн-RL гипотеза о ненаблюдавшемся действии не проверяется. Ошибка значения может быть выбрана политикой и усилена бутстрэппингом без корректирующего опыта.
Целевая сеть и базовая политика
Целевая сеть DQN и базовая политика в RLHF могут быть замороженными копиями предыдущей модели. Для каждой конструкции укажите:
-
какую величину она определяет;
-
входит ли она в бутстрэппинговую цель;
-
как и когда обновляется;
-
почему внешнее сходство реализации не означает равенства математических ролей.
Показать ответ и решение
Целевая сеть DQN: задаёт значение следующего состояния в цели Беллмана, непосредственно участвует в бутстрэппинге и периодически копируется или медленно обновляется из основной сети.
Базовая политика RLHF: задаёт опорное распределение для регуляризации по дивергенции Кульбака—Лейблера (KL) или отношения вероятностей. Она не является оценкой будущей накопленной награды и обычно остаётся фиксированной в пределах запуска.
Обе могут быть копиями, но первая стабилизирует движущуюся численную цель значения, а вторая определяет стоимость ухода от поведения базы.
Невозможность без покрытия
В журнале наблюдалось только действие с наградой 0; действие отсутствует.
-
Постройте два мира, согласованных с журналом, но имеющих разные оптимальные действия.
-
Почему ни один алгоритм не может по этому журналу гарантированно выбрать оптимальное действие в обоих мирах?
-
Какое дополнительное предположение могло бы сделать вывод возможным?
-
Почему большая нейросеть не устраняет неидентифицируемость?
Показать ответ и решение
-
Мир A: , . Мир B: , .
-
Журнал одинаков, значит алгоритм получает один и тот же вход и выдаёт один и тот же результат. Одно действие не может быть оптимальным в обоих мирах.
-
Возможны предположения о гладкости награды по признакам, известная модель динамики, структурная связь между действиями, причинная модель или новое взаимодействие.
-
Большая сеть может выразить больше функций, но данные не выбирают между двумя совместимыми мирами. Дополнительная ёмкость не создаёт отсутствующего наблюдения.
Четыре защиты в офлайн-RL
Сопоставьте методы с основным механизмом:
- Q-обучение с ограничением на пакет данных (
Batch-Constrained deep Q-learning, BCQ); - TD3+BC (
Twin Delayed Deep Deterministic Policy Gradient plus BC); - CQL;
- IQL.
Механизмы:
-
ограничить кандидатов действиями, похожими на журнал;
-
добавить поведенческий штраф к обновлению актора;
-
занизить значения широкого множества действий относительно действий журнала;
-
обучать ценность через действия данных и извлекать политику взвешенным BC.
Для каждого метода назовите один возможный изъян при слишком сильной консервативности.
Показать ответ и решение
- BCQ → 1: ограничивает кандидатов действиями, похожими на журнал. Слишком сильное ограничение не позволит выбрать редкое, но полезное улучшение.
- TD3+BC → 2: сочетает значение и близость к действию данных. Большой коэффициент BC сведёт метод к копированию поведения.
- CQL → 3: понижает относительные значения широкого набора действий. Сильный штраф может систематически недооценивать хорошие действия.
- IQL → 4: обучает ценность по действиям данных и извлекает политику взвешенным BC. Неудачные , или ограничение веса могут либо не улучшить политику, либо чрезмерно сосредоточить её на малой части данных.
DT, DPO и ILQL
Для каждой постановки укажите основные объекты обучения и наличие бутстрэппинга Беллмана.
-
Decision Transformer;
-
прямая оптимизация предпочтений (
Direct Preference Optimization, DPO); -
неявное языковое Q-обучение (
Implicit Language Q-Learning, ILQL).
Почему все три метода могут использовать фиксированные данные, но не являются одной и той же разновидностью офлайн-RL?
Показать ответ и решение
- DT: обучает условное распределение действий по состоянию, истории и оставшейся награде; явного бутстрэппинга Беллмана нет.
- DPO: обучает политику по парным предпочтениям и отношению к базовой политике; Q-функция и цель Беллмана отсутствуют.
- ILQL: обучает токеновые и по награждённым траекториям; использует последовательную ценностную рекурсию и затем смещает политику преимуществом.
Фиксированность данных описывает режим доступа к опыту, но не определяет единственную математическую цель. Тип записи — демонстрация, предпочтение или переход с наградой — меняет доступные контрфакты и объект обучения.
Маскированная потеря BC/SFT
Даны логарифмы вероятностей демонстрационных токенов
и маска
Вычислите сумму отрицательных логарифмических правдоподобий и среднее по активным токенам. Почему деление на размер всей матрицы дало бы другую целевую функцию?
Показать ответ и решение
Активны три элемента: , и . Поэтому
Деление на 6 дало бы и сделало масштаб градиента зависимым от числа замаскированных позиций. Обычно среднее берут по токенам, которые действительно входят в цель.
Канат без асимптотики
Для и вычислите:
-
точный ;
-
точный регрет;
-
приближение ;
-
нормированный регрет .
Объясните, почему приближение уже заметно отличается от точного ответа.
Показать ответ и решение
При :
Точный регрет:
Первый порядок:
Нормированный регрет:
Здесь уже не очень мало, поэтому члены порядка заметны и линейное по приближение завышает потерю.
Цели DQN и терминальная маска
Даны
и значения целевой сети в следующих состояниях:
Вычислите три цели DQN. Почему значение 100 не входит во вторую цель?
Показать ответ и решение
Максимумы равны . С учётом маски:
Во втором переходе : продолжения задачи нет, поэтому значение следующего состояния не бутстрэппится.
DQN против Double DQN
Пусть
Награды , оба перехода нетерминальны, .
-
Вычислите обычные цели DQN.
-
Какие действия выберет основная сеть?
-
Вычислите цели Double DQN.
-
Как пример иллюстрирует разделение выбора и оценивания?
Показать ответ и решение
Обычный DQN использует максимумы целевой сети: и :
Основная сеть выбирает действия . Целевая сеть оценивает именно их значениями :
Пример намеренно создаёт сильное расхождение ранжирований. DQN выбирает и оценивает максимум одной таблицы, а Double DQN выбирает по одной и оценивает по другой.
CQL и экспектильная потеря IQL
Для одного состояния значения действий равны
а в журнале наблюдалось действие 0.
- Вычислите дискретный штраф CQL
- Для остатка IQL и вычислите поэлементную экспектильную потерю
и её среднее.
- Объясните, почему положительные остатки получают больший вес при .
Показать ответ и решение
Штраф CQL:
Для отрицательный остаток получает вес , положительные — :
Среднее:
Больший вес положительных остатков заставляет двигаться к верхней части распределения значений действий из данных.
Оставшаяся награда и условие DT
Для наград
-
вычислите недисконтированную оставшуюся награду;
-
начиная с желаемого значения 5, обновляйте цель после каждой награды;
-
для одного шага с , и вычислите следующую дисконтированную цель;
-
объясните, почему большое желаемое значение не гарантирует большую фактическую накопленную награду.
Показать ответ и решение
Обратные суммы:
При начальной цели 5 недисконтированное обновление даёт
Для :
Цель — входное условие модели. Если данных для столь высокой награды нет, модель вынуждена экстраполировать; условие не меняет динамику среды и не является гарантией результата.
Маскированная отрицательная логарифмическая правдоподобность
Реализуйте:
import numpy as np
def masked_bc_nll(
selected_log_probs: np.ndarray,
mask: np.ndarray,
reduction: str = "mean",
) -> float:
"""Вернуть среднюю или сумму -log pi на активных позициях."""
...Требования: формы должны совпадать; маска содержит только 0 и 1; пустая маска вызывает ValueError; reduction принимает только "mean" или "sum".
Открытые проверки:
logprobs = np.array([[-0.1, -0.2, -1.0], [-0.3, -0.7, -0.4]])
mask = np.array([[0, 1, 1], [0, 0, 1]])
assert abs(masked_bc_nll(logprobs, mask, reduction="sum") - 1.6) < 1e-12
assert abs(masked_bc_nll(logprobs, mask) - 1.6 / 3) < 1e-12Показать ответ и решение
import numpy as np
def masked_bc_nll(
selected_log_probs: np.ndarray,
mask: np.ndarray,
reduction: str = "mean",
) -> float:
lp = np.asarray(selected_log_probs, dtype=np.float64)
m = np.asarray(mask)
if lp.shape != m.shape:
raise ValueError("shapes must match")
if np.any((m != 0) & (m != 1)):
raise ValueError("mask must be binary")
active_count = int(m.sum())
if active_count == 0:
raise ValueError("empty active mask")
total = float(np.where(m.astype(bool), -lp, 0.0).sum())
if reduction == "sum":
return total
if reduction == "mean":
return total / active_count
raise ValueError("reduction must be 'mean' or 'sum'")Точный расчёт каната
Реализуйте устойчивую замкнутую форму:
def tightrope_return(epsilon: float, horizon: int) -> float:
...
def tightrope_regret(epsilon: float, horizon: int) -> float:
...Обработайте , и ошибочные входы.
Открытые проверки:
assert tightrope_return(0.0, 20) == 20.0
assert tightrope_regret(0.0, 20) == 0.0Показать ответ и решение
def tightrope_return(epsilon: float, horizon: int) -> float:
if not 0.0 <= epsilon <= 1.0 or horizon < 0:
raise ValueError
if horizon == 0:
return 0.0
if epsilon == 0.0:
return float(horizon)
q = 1.0 - epsilon
return q * (1.0 - q ** horizon) / epsilon
def tightrope_regret(epsilon: float, horizon: int) -> float:
return float(horizon) - tightrope_return(epsilon, horizon)DQN и Double DQN
Реализуйте векторизованные функции:
import numpy as np
def dqn_targets(rewards, terminated, next_q_target, gamma=0.99):
...
def double_dqn_targets(rewards, terminated, next_q_online, next_q_target, gamma=0.99):
...Форма значений — [B, A]; терминальные переходы не бутстрэппятся.
Открытые проверки:
result = dqn_targets(
np.array([1.0, -2.0, 0.5]),
np.array([0, 1, 0]),
np.array([[2.0, 4.0], [100.0, 100.0], [-1.0, 3.0]]),
gamma=0.9,
)
np.testing.assert_allclose(result, [4.6, -2.0, 3.2])Показать ответ и решение
import numpy as np
def dqn_targets(rewards, terminated, next_q_target, gamma=0.99):
r = np.asarray(rewards, dtype=float)
term = np.asarray(terminated, dtype=bool)
q = np.asarray(next_q_target, dtype=float)
return r + gamma * (~term) * q.max(axis=1)
def double_dqn_targets(rewards, terminated, next_q_online, next_q_target, gamma=0.99):
r = np.asarray(rewards, dtype=float)
term = np.asarray(terminated, dtype=bool)
online = np.asarray(next_q_online, dtype=float)
target = np.asarray(next_q_target, dtype=float)
actions = online.argmax(axis=1)
selected = target[np.arange(len(r)), actions]
return r + gamma * (~term) * selectedВ рабочем коде нужно дополнительно проверять формы и диапазон ; это сделано в module_5_reference.py.
Простейший пессимистический выбор
Для оценённых средних наград и числа наблюдений реализуйте учебный нижний доверительный балл
Действия с должны получать . Объясните, почему это лишь иллюстрация покрытия, а не универсальный алгоритм офлайн-RL.
Сигнатура:
def pessimistic_values(
empirical_means: np.ndarray,
counts: np.ndarray,
*,
radius_scale: float = 1.0,
) -> np.ndarray:
...Открытые проверки:
values = pessimistic_values(np.array([0.8, 0.5]), np.array([4, 0]))
assert abs(values[0] - 0.3) < 1e-12
assert values[1] == -np.inf
wide = pessimistic_values(np.array([0.8]), np.array([1]))
assert wide[0] < 0.3Показать ответ и решение
import numpy as np
def pessimistic_values(
empirical_means: np.ndarray,
counts: np.ndarray,
*,
radius_scale: float = 1.0,
) -> np.ndarray:
means = np.asarray(empirical_means, dtype=np.float64)
n = np.asarray(counts, dtype=np.float64)
if means.shape != n.shape or np.any(n < 0) or not np.isfinite(radius_scale) or radius_scale < 0:
raise ValueError("invalid shapes, counts, or radius_scale")
out = np.full_like(means, -np.inf)
seen = n > 0
out[seen] = means[seen] - radius_scale / np.sqrt(n[seen])
return outПравило использует только число наблюдений и не учитывает последовательную динамику, качество представления, систематический сдвиг данных или корректную статистическую границу. Это иллюстрация идеи, а не общий метод.
CQL, экспектиль и веса IQL
Реализуйте:
def cql_penalty(q_values, data_actions):
...
def expectile_loss(residuals, tau):
...
def iql_weights(advantages, beta, max_weight=100.0):
...cql_penalty должен использовать устойчивый logsumexp, а веса IQL — ограничиваться сверху.
Открытые проверки:
q = np.array([2.0, 0.0, -1.0])
assert abs(cql_penalty(q, 0) - (np.log(np.exp(q).sum()) - 2.0)) < 1e-9
assert cql_penalty(q, 0) > 0.0
assert abs(cql_penalty(q, 0) - 0.1698460) < 1e-6Показать ответ и решение
import numpy as np
def _logsumexp(x, axis=-1):
x = np.asarray(x, dtype=float)
m = np.max(x, axis=axis, keepdims=True)
return np.squeeze(m + np.log(np.exp(x - m).sum(axis=axis, keepdims=True)), axis=axis)
def cql_penalty(q_values, data_actions):
q = np.asarray(q_values, dtype=float)
if q.ndim == 1:
q = q[None, :]
a = np.asarray(data_actions, dtype=int)
if a.ndim == 0:
a = np.full(q.shape[0], int(a))
return float(np.mean(_logsumexp(q) - q[np.arange(q.shape[0]), a]))
def expectile_loss(residuals, tau):
u = np.asarray(residuals, dtype=float)
if not 0 < tau < 1:
raise ValueError
return np.abs(tau - (u < 0)) * u ** 2
def iql_weights(advantages, beta, max_weight=100.0):
a = np.asarray(advantages, dtype=float)
return np.minimum(np.exp(np.minimum(beta * a, np.log(max_weight))), max_weight)Оставшаяся награда и обновление цели
Реализуйте:
def return_to_go(rewards, gamma=1.0):
...
def update_target_return(current_target, reward, gamma=1.0):
...Вторая функция использует и должна отклонять .
Открытые проверки:
np.testing.assert_allclose(return_to_go([1.0, 0.0, -2.0, 4.0]), [3.0, 2.0, 2.0, 4.0])Показать ответ и решение
import numpy as np
def return_to_go(rewards, gamma=1.0):
r = np.asarray(rewards, dtype=float)
out = np.empty_like(r)
running = 0.0
for t in range(len(r) - 1, -1, -1):
running = r[t] + gamma * running
out[t] = running
return out
def update_target_return(current_target, reward, gamma=1.0):
if not 0.0 < gamma <= 1.0:
raise ValueError("gamma must be in (0, 1]")
return (float(current_target) - float(reward)) / gamma