Designing Reinforcement Learning Systems
Minimal MDP formalization + training loop with Stable-Baselines3:
Pythonimport gymnasium as gym from stable_baselines3 import PPO from stable_baselines3.common.monitor import Monitor from stable_baselines3.common.callbacks import EvalCallback # 1. Define or wrap environment env = Monitor(gym.make("Pendulum-v1")) eval_env = Monitor(gym.make("Pendulum-v1")) # 2. Choose algorithm by action space type # Discrete -> DQN/PPO; Continuous -> PPO/SAC/TD3 model = PPO("MlpPolicy", env, verbose=1, tensorboard_log="./logs") # 3. Train with monitoring eval_cb = EvalCallback(eval_env, eval_freq=5000, n_eval_episodes=10, best_model_save_path="./best_model") model.learn(total_timesteps=200_000, callback=eval_cb) # 4. Validate mean_reward, std_reward = evaluate_policy(model, eval_env, n_eval_episodes=20) print(f"Reward: {mean_reward:.2f} ± {std_reward:.2f}")
Progress:
- Step 1: Формализовать задачу как MDP (состояние, действие, награда, переходы, горизонт)
- Step 2: Спроектировать среду (Gymnasium API или кастомная
gym.Env) - Step 3: Спроектировать функцию вознаграждения и проверить на вырожденные стратегии
- Step 4: Выбрать алгоритм под тип пространства действий и режим обучения
- Step 5: Обучить модель с логированием (W&B/TensorBoard), мониторить кривую наград
- Step 6: Оценить на тестовых эпизодах, отдельных от обучающих seed'ов
- Step 7 (опционально): Перенести политику в реальную среду (domain randomization, fine-tuning)
Step 1 — Формализация MDP
Явно зафиксировать перед кодом:
- State: что наблюдает агент; достаточно ли информации для марковости (иначе — добавить историю/LSTM)
- Action: дискретное/непрерывное/гибридное пространство
- Reward: dense vs sparse; единицы измерения; диапазон значений
- Episode: условия терминации, max_steps, timeout vs terminal (различать
terminatedиtruncated)
Step 2 — Проектирование среды
Каркас кастомной среды:
Pythonimport gymnasium as gym from gymnasium import spaces import numpy as np class CustomEnv(gym.Env): metadata = {"render_modes": ["human"]} def __init__(self): super().__init__() self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(obs_dim,), dtype=np.float32) self.action_space = spaces.Box(low=-1, high=1, shape=(act_dim,), dtype=np.float32) def reset(self, seed=None, options=None): super().reset(seed=seed) obs = self._get_obs() return obs, {} def step(self, action): obs = self._get_obs() reward = self._compute_reward() terminated = self._check_terminal() truncated = False info = {} return obs, reward, terminated, truncated, info
Нормализовать наблюдения и действия (VecNormalize) — ускоряет сходимость почти всегда.
Step 3 — Проектирование reward
- Начинать с простой dense-награды, затем добавлять shaping-термы
- Избегать reward hacking: явно тестировать, какую тривиальную стратегию максимизирует текущая функция
- Разделять компоненты награды в логах (progress, penalty, bonus) для диагностики
- Для sparse-задач рассматривать: curriculum, HER (Hindsight Experience Replay), intrinsic motivation
Step 4 — Выбор алгоритма
| Пространство действий | Режим | Алгоритм |
|---|---|---|
| Discrete | off-policy, sample-efficient | DQN (+ Double/Dueling/PER) |
| Continuous | стабильность, parallelizable | PPO |
| Continuous | sample-efficient, off-policy | SAC |
| Continuous | детерминированная политика | TD3 |
PPO — безопасный дефолт при неопределённости в выборе алгоритма.
Step 5 — Обучение и мониторинг
Обязательные метрики в логах: средняя награда за эпизод (скользящее окно), длина эпизода, entropy/exploration, value loss, KL-дивергенция (для PPO), % использования буфера (off-policy).
Признаки проблем:
- Награда растёт, потом коллапсирует → слишком высокий LR или KL target
- Награда плоская с первых шагов → проверить reward scale, нормализацию, баг в среде
- Высокая дисперсия между seed'ами → увеличить n_envs, усреднять по ≥5 seed
Step 6 — Оценка
- Минимум 20–50 эпизодов, отдельные seed'ы от обучения
- Деterministic policy evaluation отдельно от stochastic
- Отчёт: mean ± std, success rate, worst-case (p5 percentile), а не только средняя награда
Step 7 — Sim-to-real
- Domain randomization: варьировать физические параметры (масса, трение, задержки, шум сенсоров) во время обучения
- Action/observation noise injection для устойчивости к реальным шумам
- Постепенный перенос: sim → sim с шумом → реальная среда с safety limits и человеческим override
- Fine-tuning на реальных данных с меньшим LR, замороженными нижними слоями при необходимости
Example 1: Input: Задача — робот-манипулятор должен подбирать объект с произвольным положением на столе. Output: State = координаты эффектора + положение объекта (из камеры/датчика); Action = continuous delta-позиция (3D) + захват (discrete); Reward = -distance_to_object + grasp_success_bonus(100) - time_penalty(0.01/step); Algorithm = SAC (sample-efficient для реального робота); перенос через domain randomization трения/массы и fine-tuning на 50 реальных эпизодах.
Example 2: Input: Награда растёт первые 100k шагов, затем резко падает к нулю и не восстанавливается. Output: Диагноз — вероятный коллапс политики из-за слишком агрессивного LR или потери exploration. Действия: снизить learning_rate в 2-5 раз, проверить entropy coefficient (увеличить для PPO/SAC), включить early stopping по best_model из EvalCallback, проверить не стала ли функция награды источником deceptive gradient (reward hacking).
- Логировать компоненты reward отдельно — суммарная награда скрывает, что именно "хакнул" агент
- Фиксировать seed и версии библиотек для воспроизводимости экспериментов
- Использовать VecEnv с параллельными средами (n_envs ≥ 8) для PPO — резко ускоряет и стабилизирует обучение
- Сохранять checkpoint'ы по лучшей eval-награде, не по последнему шагу обучения
- Для sim-to-real всегда добавлять safety wrapper (clipping действий, emergency stop) до запуска на реальном железе
- Путать
terminated(конец эпизода по правилам задачи) иtruncated(обрыв по timeout) — ломает bootstrapping value function - Нормализовывать reward/observation по статистике, включающей тестовые данные — утечка
- Судить о качестве политики по одному seed — RL крайне чувствителен к случайности инициализации
- Проектировать reward без проверки на дегенеративные стратегии (агент находит way to "фармить" награду без решения задачи)
- Переносить политику в реальный мир без domain randomization и без safety-ограничений на действия