Представьте, как обучить робота посадке дрона без детального программирования каждого движения. Это достигается через создание игры, в которой виртуальный дрон самостоятельно осваивает посадку на платформу методом проб и ошибок, подобно тому, как человек учится ездить на велосипеде.
Такое обучение называется обучением с подкреплением (RL) и существенно отличается от других подходов в машинном обучении. Вместо предоставления тысяч примеров правильных посадок системе дается обратная связь: положительная за удачные попытки и отрицательная за неудачи. Через множество итераций система определяет эффективные стратегии.
В этой статье описывается путь от основ RL до создания работающей системы, которая обучает дрон посадке. Рассматриваются достижения, ошибки и необычные поведения, требующие отладки.
1. Обучение с подкреплением: Обзор
Концепция связана с экспериментами Павлова с собакой и Скиннера с крысой. Субъект получает вознаграждение за желаемое действие (положительное подкрепление) и штраф за нежелаемое (отрицательное подкрепление). Через повторения субъект учится ассоциировать действия с исходами, как крыса Скиннера, нажимавшая рычаг для получения пищи.

Аналогично, система стремится к выполнению задач, максимизируя вознаграждение и минимизируя штрафы. Важно отметить акцент на максимизации вознаграждения, что проявится далее.
1.1 Основные концепции
Для программной реализации на компьютерах ключевые идеи формализуются следующим образом:
- Агент (или актер): Это субъект из предыдущего описания, такой как собака, робот в заводе или NPC в видеоигре.
- Среда (или мир): Это пространство, симуляция с ограничениями или виртуальный мир игры. Среда ограничивает существование агента; внешние изменения воспринимаются агентом как воля высшей силы.
- Политика: Определяет действия в зависимости от ситуации, подобно правилам в организациях.
- Состояние: То, что агент наблюдает о текущей ситуации, как мгновенный снимок реальности — цвет светофора, скорость и расстояние для водителя.
- Действие: Реакция агента на состояние для достижения цели, например, встать с постели, чтобы приготовить кофе.
- Вознаграждение: Изменение в среде после действия. За неудачное падение — отрицательное вознаграждение, за успешное получение кофе — положительное.

Большинство ключевых компонентов адаптируются под конкретную задачу.
2. Gym
После освоения основ возникает вопрос: как реализовать такую систему? Рассмотрим созданную игру для обучения агента.
Разработана специальная видеоигра, доступная для тренировки агентов машинного обучения.
Информация о запуске игры содержится в репозитории кода.
Delivery Drone
В игре Delivery Drone цель — посадить дрон (с грузом) на платформу. Для успешной посадки требуются:
- Близость к платформе.
- Низкая скорость.
- Вертикальная ориентация (перевернутая посадка приравнивается к аварии).
Все детали запуска доступны в репозитории.
Внешний вид игры:

Если дрон улетает за экран или касается земли, это считается аварией и приводит к неудаче.
State description
Дрон наблюдает 15 непрерывных значений, описывающих ситуацию:

Критерии успешной посадки: Дрон должен одновременно удовлетворить:
- Горизонтальное выравнивание: в пределах платформы (|dx| < 0.0625).
- Безопасная скорость подхода: менее 0.3.
- Горизонтальная ориентация: наклон менее 20° (|angle| < 0.111).
- Правильная высота: нижняя часть дрона касается верха платформы.
Это аналогично параллельной парковке: требуется точное положение, угол и низкая скорость.
How can someone design a policy?
Политику можно проектировать различными способами: байесовской (с распределениями вероятностей), таблицей поиска для дискретных состояний, правилами ("если расстояние < 10, то тормозить"), деревом решений или нейронной сетью, обучаемой градиентным спуском.
Политика принимает состояние, обрабатывает его и выдает рекомендуемое действие.
Deep Learning to build a policy?
Для обработки непрерывных состояний (как точные координаты дрона) и сложных поведений используются нейронные сети. Нейронные сети позволяют работать с вероятностями действий: "Какое действие наиболее вероятно в текущем состоянии?".
Сеть принимает состояние как вектор или набор векторов. Для игры с дроном состояние:
Вектор состояния (из 2D-игры с дроном)
Дрон наблюдает абсолютное положение, скорости, ориентацию, топливо, положение платформы и производные метрики. Непрерывное состояние:

Каждый компонент представляет:

Все компоненты нормализованы в диапазоны [0,1] или [-1,1] для стабильного обучения сети.
Пространство действий (три независимых бинарных ускорителя)
Каждый ускоритель обрабатывается отдельно:
- Основной ускоритель (вверх).
- Левый ускоритель (по часовой стрелке).
- Правый ускоритель (против часовой стрелки).
Каждое действие сэмплируется из биномиального распределения, давая 3 независимых бинарных решения на шаг.
Политика нейронной сети (вероятностная с биномиальным сэмплированием)
Пусть fθ(s) — выходы сети после сигмоидной активации. Политика использует независимые биномиальные распределения:

Минимальный эскиз на Python (из реализации):
# Формирование вектора состояния из DroneState
s = np.array([
state.drone_x, state.drone_y,
state.drone_vx, state.drone_vy,
state.drone_angle, state.drone_angular_vel,
state.drone_fuel,
state.platform_x, state.platform_y,
state.distance_to_platform,
state.dx_to_platform, state.dy_to_platform,
state.speed,
float(state.landed), float(state.crashed)
])
# Выходы сети — вероятности для каждого ускорителя (после сигмоиды)
action_probs = policy(torch.tensor(s, dtype=torch.float32))
# Форма: (3,)
# Сэмплирование каждого ускорителя независимо из биномиального
dist = Bernoulli(probs=action_probs)
action = dist.sample()
# Форма: (3,), напр. [1, 0, 1] — основной + правый ускорителиЭто демонстрирует преобразование физических наблюдений игры в 15-мерный нормализованный вектор состояния и генерацию независимых бинарных решений для ускорителей.
Code setup (part 1): Imports and game socket setup
Сначала запускается сокетный слушатель игры. В директории delivery_drone репозитория выполняется:
pip install -r requirements.txt # Запускать один раз для установки модулей
python socket_server.py --render human --port 5555 --num-games 1 # Запускать для игры в сокетном режимеПРИМЕЧАНИЕ: Для кода требуется PyTorch. Убедитесь в его установке заранее.
import os
import torch
import torch.nn as nn
import math
import numpy as np
from torch.distributions import Bernoulli
# Импорт сокетного клиента игры
from delivery_drone.game.socket_client import DroneGameClient, DroneState
# Настройка клиента и подключение
client = DroneGameClient()
client.connect()How to design a reward function?
Что делает функцию вознаграждения эффективной? Это наиболее сложная часть RL, требующая значительного времени на отладку.
Функция вознаграждения — суть любой реализации RL. Она определяет желаемое и нежелаемое поведение. Каждое действие оценивается накопленным вознаграждением за проявленные черты. Для мягкой посадки дрона положительные баллы за близость к платформе и низкую скорость, отрицательные за аварии или расход топлива — агент максимизирует сумму.
Преимущество: Лучший способ оценки эффективного вознаграждения
При обучении политики важно не только наличие вознаграждения, но и его относительная ценность. Это основа преимущества.
Преимущество указывает: "Было ли это действие лучше или хуже ожидаемого?"

В реализации:
- Сбор нескольких эпизодов и расчет их возвратов (дисконтированных сумм вознаграждений).
- Вычисление базовой линии как среднего возврата по эпизодам.
- Расчет преимущества = возврат – базовая линия для каждого шага.
- Нормализация преимуществ (среднее=0, std=1) для стабильности.
Почему это помогает:
- Действия с положительным преимуществом — лучше среднего — повышают вероятность.
- Действия с отрицательным — хуже среднего — снижают вероятность.
- Снижает дисперсию градиентных обновлений (стабильное обучение).
Простая базовая линия значительно улучшает обучение по сравнению с сырыми возвратами. Она взвешивает последовательность действий относительно исходов (авария или посадка), помогая политике выбирать действия с лучшим преимуществом.
После множества экспериментов разработана функция вознаграждения. Ключ — условное вознаграждение на близость И вертикальное положение: положительные баллы только над платформой, предотвращая эксплуатацию вроде зависания снизу.

Краткая заметка о обратно пропорциональном (нелинейном) масштабировании вознаграждения
Для вознаграждения, обратно пропорционального значению состояния (например, расстоянию от 0 до ~1.41), используются функции: высокое вознаграждение при нулевом расстоянии, низкое при большом.

Примеры других полезных функций масштабирования
Вспомогательные функции:
def inverse_quadratic(x, decay=20, scaler=10, shifter=0):
"""Вознаграждение квадратично уменьшается с расстоянием"""
return scaler / (1 + decay * (x - shifter)**2)
def scaled_shifted_negative_sigmoid(x, scaler=10, shift=0, steepness=10):
"""Масштабированная и сдвинутая сигмоидная функция"""
return scaler / (1 + np.exp(steepness * (x - shift)))
def calc_velocity_alignment(state: DroneState):
"""Расчет выравнивания скорости дрона с оптимальным направлением к платформе.
Возвращает косинусное сходство: 1.0 = идеальное, -1.0 = противоположное"""
# Оптимальное направление: от дрона к платформе
optimal_dx = -state.dx_to_platform
optimal_dy = -state.dy_to_platform
optimal_norm = math.sqrt(optimal_dx**2 + optimal_dy**2)
if optimal_norm < 1e-6: # Уже на платформе
return 1.0
optimal_dx /= optimal_norm
optimal_dy /= optimal_norm
# Направление текущей скорости
velocity_norm = state.speed
if velocity_norm < 1e-6: # Не движется
return 0.0
velocity_dx = state.drone_vx / velocity_norm
velocity_dy = state.drone_vy / velocity_norm
# Косинусное сходство
return velocity_dx * optimal_dx + velocity_dy * optimal_dyКод текущей функции вознаграждения:
def calc_reward(state: DroneState):
rewards = {}
total_reward = 0
# 1. Штраф за время — на основе расстояния (больше при удаленности)
minimum_time_penalty = 0.3
maximum_time_penalty = 1.0
rewards['time_penalty'] = -inverse_quadratic(
state.distance_to_platform, decay=50, scaler=maximum_time_penalty - minimum_time_penalty
) - minimum_time_penalty
total_reward += rewards['time_penalty']
# 2. Расстояние и выравнивание скорости — ТОЛЬКО над платформой
velocity_alignment = calc_velocity_alignment(state)
dist = state.distance_to_platform
rewards['distance'] = 0
rewards['velocity_alignment'] = 0
# Ключевое условие: дрон над платформой (dy > 0) для положительных вознаграждений
if dist > 0.065 and state.dy_to_platform > 0:
# Вознаграждение за движение к платформе при выравнивании
if velocity_alignment > 0:
rewards['distance'] = state.speed * scaled_shifted_negative_sigmoid(dist, scaler=4.5)
rewards['velocity_alignment'] = 0.5
total_reward += rewards['distance']
total_reward += rewards['velocity_alignment']
# 3. Штраф за угол — на основе расстояния
abs_angle = abs(state.drone_angle)
max_angle = 0.20
max_permissible_angle = ((max_angle - 0.111) * dist) + 0.111
excess = abs_angle - max_permissible_angle
rewards['angle'] = -max(excess, 0)
total_reward += rewards['angle']
# 4. Штраф за скорость — за чрезмерную
rewards['speed'] = 0
speed = state.speed
max_speed = 0.4
if dist < 1:
rewards['speed'] = -2 * max(speed - 0.1, 0)
else:
rewards['speed'] = -1 * max(speed - max_speed, 0)
total_reward += rewards['speed']
# 5. Штраф за вертикальное положение — за нахождение ниже платформы
rewards['vertical_position'] = 0
if state.dy_to_platform > 0:
# Дрон над платформой (ХОРОШО)
rewards['vertical_position'] = 0
else:
# Дрон ниже платформы (ПЛОХО!)
rewards['vertical_position'] = state.dy_to_platform * 4.0 # Отрицательный штраф
total_reward += rewards['vertical_position']
# 6. Терминальные вознаграждения
rewards['terminal'] = 0
if state.landed:
rewards['terminal'] = 500.0 + state.drone_fuel * 100.0
elif state.crashed:
rewards['terminal'] = -200.0
# Дополнительный штраф за аварию вдали от цели
if state.distance_to_platform > 0.3:
rewards['terminal'] -= 100.0
total_reward += rewards['terminal']
rewards['total'] = total_reward
return rewardsЧисла вроде 4.5, 0.065 и 4.0 получены через многочисленные эксперименты. В RL настройка гиперпараметров сочетает искусство, науку и удачу.
def compute_returns(rewards, gamma=0.99):
"""Расчет дисконтированных возвратов (G_t) для каждого шага по уравнению Беллмана
G_t = r_t + γ*r_{t+1} + γ²*r_{t+2} + ...
"""
returns = []
G = 0 # Вычисление задом наперед (эффективнее)
for r in reversed(rewards):
G = r + gamma * G
returns.insert(0, G)
return returnsФункции вознаграждения требуют тщательного тестирования. Ошибка может привести к оптимизации нежелаемых стратегий, вызывая reward hacking.
Reward hacking
Reward hacking — когда агент находит непреднамеренный способ максимизировать вознаграждение, не решая задачу. Агент следует инструкциям буквально, но не так, как задумано.
Классический пример: Робот-пылесос, вознаграждаемый за отсутствие грязи, может отключить камеру вместо уборки.
Пример из опыта: В ранней версии функции вознаграждения за стабильность и низкую скорость недалеко от платформы дрон за 50 эпизодов научился зависать на месте, накапливая баллы, но не садился. Зависание длилось 5 минут.
Проблемный код:
# НЕ КОПИРОВАТЬ!
# Если дрон над платформой (|dx| < 0.0625) и близко (distance < 0.25):
corridor_reward = inverse_quadratic(distance, decay=20, scaler=15)
# До 15 баллов за стабильность и низкую скорость:
corridor_reward += 10 # Дополнительные 10 баллов!
# Итого возможно: 25 балла за шаг!Пример reward hacking:


Making a policy network
Как указано, нейронная сеть служит политикой для агента. Простая реализация принимает вектор состояния и вычисляет распределение вероятностей для 3 независимых действий:
- Активация основного ускорителя.
- Активация левого ускорителя.
- Активация правого ускорителя.
def state_to_array(state):
"""Вспомогательная функция для преобразования DroneState в numpy-массив"""
data = np.array([
state.drone_x, state.drone_y,
state.drone_vx, state.drone_vy,
state.drone_angle, state.drone_angular_vel,
state.drone_fuel,
state.platform_x, state.platform_y,
state.distance_to_platform,
state.dx_to_platform, state.dy_to_platform,
state.speed,
float(state.landed), float(state.crashed)
])
return torch.tensor(data, dtype=torch.float32)
class DroneGamerBoi(nn.Module):
def __init__(self, state_dim=15):
super().__init__()
self.network = nn.Sequential(
nn.Linear(state_dim, 128),
nn.LayerNorm(128),
nn.ReLU(),
nn.Linear(128, 128),
nn.LayerNorm(128),
nn.ReLU(),
nn.Linear(128, 64),
nn.LayerNorm(64),
nn.ReLU(),
nn.Linear(64, 3),
nn.Sigmoid()
)
def forward(self, state):
if isinstance(state, DroneState):
state = state_to_array(state)
return self.network(state)Вместо 23=8 комбинаций пространство действий сведено к независимым решениям по ускорителям с биномиальным сэмплированием. Это упрощает оптимизацию, обрабатывая ускорители отдельно.
Training a policy with policy gradients
Learning Strategies: When Should We Update?
Вопрос: обновлять политику после каждого действия или после полного эпизода? Выбор критичен.
Оптимизация только по немедленному вознаграждению приводит к высокой дисперсии (шумные градиенты, случайные направления). Для одного действия градиент может меняться противоположно в похожих состояниях, замедляя обучение.
Три способа обновления политики:
Обучение после каждого действия (обновления на шаг)
Дрон активирует ускоритель, получает вознаграждение и сразу обновляет стратегию. Это чрезмерно реактивно, как корректировка броска в баскетболе после каждого. Случайное вознаграждение не отражает качество.
Первая попытка: Такой подход приводил к случайным движениям, переобучению на удачные шаги и повторным авариям.
Обучение после полного эпизода (обновления на эпизод)
Лучше: дрон завершает попытку, оценивает исход и обновляется. Видны полные последствия, но один эпизод — слабая статистика.
Обучение на множестве эпизодов (пакетные обновления)
Оптимально: несколько (6) попыток параллельно, обновление по среднему. Усреднение снижает шум. Требует ресурсов, но эффективнее. Прост в реализации, хотя существуют лучшие методы.
Код сбора нескольких эпизодов:
def collect_episodes(client: DroneGameClient, policy: nn.Module, max_steps=300):
"""Сбор эпизодов с ранней остановкой
Args:
client: Сокетный клиент игры
policy: Модуль PyTorch
max_steps: Максимум шагов на эпизод (по умолчанию: 300)
"""
num_games = client.num_games
# Инициализация хранилища
all_episodes = [{'states': [], 'actions': [], 'log_probs': [], 'rewards': [], 'done': False} for _ in range(num_games)]
# Сброс всех игр
game_states = [client.reset(game_id) for game_id in range(num_games)]
step_counts = [0] * num_games # Счетчики шагов
while not all(ep['done'] for ep in all_episodes):
# Пакет активных игр
batch_states = []
active_game_ids = []
for game_id in range(num_games):
if not all_episodes[game_id]['done']:
batch_states.append(state_to_array(game_states[game_id]))
active_game_ids.append(game_id)
if len(batch_states) == 0:
break
# Пакетный вывод
batch_states_tensor = torch.stack(batch_states)
batch_action_probs = policy(batch_states_tensor)
batch_dist = Bernoulli(probs=batch_action_probs)
batch_actions = batch_dist.sample()
batch_log_probs = batch_dist.log_prob(batch_actions).sum(dim=1)
# Выполнение действий
for i, game_id in enumerate(active_game_ids):
action = batch_actions[i]
log_prob = batch_log_probs[i]
next_state, _, done, _ = client.step({
"main_thrust": int(action[0]),
"left_thrust": int(action[1]),
"right_thrust": int(action[2])
}, game_id)
reward = calc_reward(next_state)
# Хранение данных
all_episodes[game_id]['states'].append(batch_states[i])
all_episodes[game_id]['actions'].append(action)
all_episodes[game_id]['log_probs'].append(log_prob)
all_episodes[game_id]['rewards'].append(reward['total'])
# Обновление состояния и счетчика
game_states[game_id] = next_state
step_counts[game_id] += 1
# Проверка завершения
if done or step_counts[game_id] >= max_steps:
# Штраф за таймаут без посадки
if step_counts[game_id] >= max_steps and not next_state.landed:
all_episodes[game_id]['rewards'][-1] -= 500
all_episodes[game_id]['done'] = True
# Возврат эпизодов
return [(ep['states'], ep['actions'], ep['log_probs'], ep['rewards']) for ep in all_episodes]The Maximization-Minimization Puzzle
В типичном глубоком обучении (надзором) минимизируется функция потерь:

Цель — спуск к меньшим потерям (лучшим предсказаниям).
В RL цель — максимизировать общее вознаграждение:

Проблема: Фреймворки для минимизации. Как превратить максимизацию в минимизацию?
Простой трюк: Максимизировать J(θ) = Минимизировать -J(θ).
Функция потерь:

Градиентный спуск поднимается по вознаграждению (градиентный подъем по отрицанию).
The REINFORCE Algorithm (Policy Gradient)
Теорема градиента политики (Williams, 1992) описывает градиент ожидаемого вознаграждения:

(Формула сложна, но элегантна в сути.)
Где:

Простыми словами:
- Если действие at привело к высокому возврату Gt, повысьте вероятность.
- Если к низкому — снизьте.
- Градиент указывает корректировку весов сети.
Adding a Baseline (Variance Reduction)
Сырые возвраты Gt вызывают высокую дисперсию. Улучшение — вычитание базовой линии b(st):

Простейшая базовая линия — средний возврат:

Это дает преимущество: At=Gt-b.
- Положительное преимущество — лучше среднего — повышает вероятность.
- Отрицательное — хуже — снижает.
Почему помогает: Относительная оценка clearer, чем абсолютная (100 баллов при среднем 50 — отлично).
Our Implementation
В коде посадки дрона используется REINFORCE с базовой линией:
# 1. Сбор эпизодов и расчет возвратов
returns = compute_returns(rewards, gamma=0.99) # G_t с дисконтированием
# 2. Расчет базовой линии (среднее возвратов)
baseline = returns_tensor.mean()
# 3. Расчет преимуществ
advantages = returns_tensor - baseline
# 4. Нормализация преимуществ (дополнительное снижение дисперсии)
advantages = (advantages - advantages.mean()) / (advantages.std() + 1e-8)
# 5. Расчет потерь (отрицательный знак!)
loss = -(log_probs_tensor * advantages).mean()
# 6. Градиентный спуск
optimizer.zero_grad()
loss.backward()
optimizer.step()Цикл повторяется до освоения посадки. Подробный код в ноутбуке.
Current Results (reward function is still quite flawed)
После длительной настройки вознаграждений, гиперпараметров и наблюдения за авариями система заработала (в основном). Несмотря на неидеальную функцию вознаграждения, она обучает политику. Пример успешной посадки:

Интересно, но фрустрирующе...
The persistent hovering problem: A fundamental limitation
Даже с улучшенной функцией, условной на вертикальное положение (dy_to_platform > 0), политика проявляет нежелательное поведение: при промахе дрон опускается к платформе, но зависает снизу вместо посадки.
Анализ графиков вознаграждений выявил причину: агент рационально эксплуатирует структуру.
Что происходит?
В эпизоде с зависанием снизу накопленные вознаграждения:


Графики показывают:
- Вознаграждение за расстояние (оранжевый): ~+70 рано, затем плато.
- Выравнивание скорости (зеленый): ~+30 рано, плато.
- Штраф за время (синий): Накопление до ~-250.
- Вертикальное положение (коричневый): До ~-200 (штраф за низ).
- Общее вознаграждение: -400 до -600 (после таймаута).
Ключевой инсайт: Дрон опускается сверху (накапливая положительные), проходит высоту платформы и зависает снизу. Положительные прекращаются (плато на 50-60 шаге), но накапливаются штрафы. Стратегия viable, т.к. посадка рискует -200 за аварию, а зависание — -400/-600 за эпизод.
Почему так?
Функция r(s', a) видит только текущее состояние, не траекторию. На шаге не отличает:
- Прогресс к посадке (подход сверху с контролем).
- Эксплуатацию (колебания для фарминга).
Оба могут иметь dy_to_platform > 0, получая одинаковые баллы. Агент оптимизирует заданное.
Как исправить?
Вознаграждения должны зависеть от переходов состояний: r(s, a, s'). Это позволит:
- Прогресс: Вознаграждение, если
distance(s') < distance(s)(реальное сближение). - Вертикальное улучшение: Только за последовательное движение вверх относительно платформы.
- Стабильность траектории: Штраф за быстрые изменения направления.
Это принципиальнее, чем патчи штрафами (что пробовалось без успеха). Проблема в отсутствии информации о траектории.
В следующей статье рассмотрены методы Actor-Critic и техники с временной информацией против эксплуатации. Следите за обновлениями!
Это завершает пост о простом способе глубокого обучения с подкреплением.
Next on the list
- Системы Actor-Critic.
- DQL.
- PPO и GRPO.
- Применение к системам с зрением 👀.
References
Foundational Stuff
- Turing, A. M. (1950). “Computing Machinery and Intelligence.”.
- Оригинальная статья о тесте Тьюринга.
- Williams, R. J. (1992). “Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning.” Machine Learning.
- Алгоритм REINFORCE.
- Sutton, R. S., & Barto, A. G. (2018). Reinforcement Learning: An Introduction. MIT Press.
- Фундаментальный учебник.
- Бесплатно онлайн: http://incompleteideas.net/book/the-book-2nd.html.
Classical Conditioning & Behavioral Psychology
- Pavlov, I. P. (1927). Conditioned Reflexes: An Investigation of the Physiological Activity of the Cerebral Cortex. Oxford University Press.
- Эксперименты по классическому обусловливанию.
- Skinner, B. F. (1938). The Behavior of Organisms: An Experimental Analysis. Appleton-Century-Crofts.
- Оперантное обусловливание и коробка Скиннера.
Policy Gradient Methods
- Sutton, R. S., McAllester, D., Singh, S., & Mansour, Y. (1999). “Policy Gradient Methods for Reinforcement Learning with Function Approximation.” Advances in Neural Information Processing Systems.
- Теоретические основы градиентов политики.
- Schulman, J., Moritz, P., Levine, S., Jordan, M., & Abbeel, P. (2015). “High-Dimensional Continuous Control Using Generalized Advantage Estimation.” arXiv preprint arXiv:1506.02438.
Neural Networks & Deep Learning
- Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.
- Справочник по основам нейронных сетей.
- Онлайн: https://www.deeplearningbook.org/.
Online Resources
- Karpathy, A. “Deep Reinforcement Learning: Pong from Pixels.”
- Блог: http://karpathy.github.io/2016/05/31/rl/.
- Влиятельный образовательный ресурс.
- Spinning Up in Deep RL by OpenAI
- Ресурс: https://spinningup.openai.com/.
- Отличные объяснения градиентов политики.
Code Repository
- Jumle, V. “Reinforcement Learning 101: Delivery Drone Landing.”
Все изображения — сгенерированы ИИ (Gemini), созданы лично или скриншоты/графики.