Существует четыре основных вида машинного обучения:
- Обучение с учителем — когда все наблюдения в наборе данных помечены целевой переменной, что позволяет выполнять регрессию или классификацию для предсказания этих значений.
- Обучение без учителя — когда целевая переменная отсутствует, поэтому применяется кластеризация для сегментации и группировки данных.
- Полунадзираемое обучение — когда целевая переменная неполная, и модель должна научиться предсказывать неразмеченные данные. В таких случаях комбинируются подходы с учителем и без него.
- Обучение с подкреплением — когда вместо целевой переменной используется вознаграждение, и оптимальное решение неизвестно заранее, поэтому процесс строится на пробах и ошибках для достижения цели.
Точнее говоря, обучение с подкреплением изучает, как искусственный интеллект принимает решения в интерактивной среде, чтобы максимизировать вознаграждение. В отличие от обучения с учителем, где правильный ответ известен заранее (целевой переменной), и модель подстраивается под него, в задачах RL оптимальный ответ неизвестен изначально. Единственный способ его выявить — это совершать действия и получать отклик в виде вознаграждения, поэтому модель осваивает навыки через исследование и накопление ошибок.
Обучение с подкреплением активно применяется для обучения роботов. Яркий пример — автономный пылесос: при прохождении по запыленной поверхности он получает положительное вознаграждение (+1), а при столкновении со стеной — наказание (-1). Таким образом, робот определяет подходящие действия и избегает нежелательных.
В этой статье рассматривается, как создавать пользовательские 3D-среды для обучения роботов с использованием различных алгоритмов обучения с подкреплением. Приводится полезный код на Python, который можно легко адаптировать для аналогичных задач (скопировать, вставить, запустить), с подробными комментариями к каждой строке для удобства воспроизведения примера.
Настройка
В отличие от задач с учителем, требующих целевой переменной и обучающего набора, проблема RL включает:
- Среда — окружение агента, которое назначает вознаграждения за действия и предоставляет новое состояние в результате принятого решения. По сути, это пространство, с которым взаимодействует ИИ (в примере с пылесосом — комната для уборки).
- Действие — множество возможных действий агента в среде. Пространство действий может быть дискретным (фиксированное число вариантов, как в шахматах) или непрерывным (бесконечные состояния, как в вождении автомобиля или торговле).
- Вознаграждение — последствие действия (+1/-1).
- Агент — ИИ, который изучает оптимальный план действий в среде для максимизации вознаграждения.
Среди популярных 3D-симуляторов физики выделяются: PyBullet (для новичков), Webots (средний уровень), MuJoCo (продвинутый) и Gazebo (профессиональный). Их можно применять как самостоятельное ПО или интегрировать через Gym, библиотеку от OpenAI для разработки алгоритмов RL, построенную на различных физических движках.
В примере используется Gym (pip install gymnasium) для загрузки одной из стандартных сред на базе MuJoCo (Multi-Joint dynamics with Contact, pip install mujoco).
import gymnasium as gym
env = gym.make("Ant-v4")
obs, info = env.reset()
print(f"--- INFO: {len(info)} ---")
print(info, "\n")
print(f"--- OBS: {obs.shape} ---")
print(obs, "\n")
print(f"--- ACTIONS: {env.action_space} ---")
print(env.action_space.sample(), "\n")
print(f"--- REWARD ---")
obs, reward, terminated, truncated, info = env.step(
env.action_space.sample()
)
print(reward, "\n")

Робот Ant представляет собой 3D-агента в виде четвероногого существа с туловищем и четырьмя конечностями. Каждая конечность состоит из двух сегментов, итого 8 суставов (гибкие элементы) и 9 звеньев (жесткие части). Цель среды — применять силы (толчки/тяги) и крутящие моменты (вращения) для перемещения робота в заданном направлении.
Протестируем среду, запустив один эпизод с случайными действиями робота (эпизод — это полная сессия взаимодействия агента со средой от начала до завершения).
import time
env = gym.make("Ant-v4", render_mode="human")
obs, info = env.reset()
reset = False # reset if the episode ends
episode = 1
total_reward, step = 0, 0
for _ in range(240): # action
step += 1
action = env.action_space.sample() # random action
obs, reward, terminated, truncated, info = env.step(action)
# reward
total_reward += reward
# render
env.render() # render physics step (CPU speed = 0.1 seconds)
time.sleep(1/240) # slow down to real-time (240 steps × 1/240 second sleep = 1 second)
if (step == 1) or (step % 100 == 0): # print first step and every 100 steps
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
# reset
if reset:
if terminated or truncated: # print the last step
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
Пользовательская среда
Обычно среды обладают стандартными характеристиками:
- Сброс — для возврата к начальному состоянию или случайной точке в данных.
- Визуализация — для отображения происходящего.
- Шаг — для выполнения выбранного агентом действия и изменения состояния.
- Расчет вознаграждения — для присвоения подходящего вознаграждения или штрафа после действия.
- Получение информации — для сбора данных о сессии после действия.
- Завершение или обрезка — для определения, закончился ли эпизод после действия (провал или успех).
Стандартные среды в Gym удобны, но не всегда соответствуют требованиям проекта. Часто требуется разработка кастомной среды, адаптированной под задачу. Это ключевой этап в RL, поскольку качество модели напрямую зависит от дизайна среды.
Существует несколько подходов к созданию собственной среды:
- Создание с нуля: проектирование всех аспектов (физики, корпуса, окружения). Полный контроль, но самый сложный метод, так как начинается с пустоты.
- Изменение существующего XML-файла: каждый симулируемый агент описан XML-файлом. Можно корректировать физические параметры (например, увеличить высоту или массу робота), сохраняя логику.
- Изменение существующего Python-класса: оставить агента и физику без изменений, но обновить правила (новые вознаграждения, условия завершения). Можно даже преобразовать непрерывную среду в дискретное пространство действий.
В примере адаптируется стандартная среда Ant для прыжков робота. Изменяются физические свойства в XML и функция вознаграждения в Python-классе. Достаточно усилить ноги и ввести вознаграждение за прыжки.
Сначала найдем XML-файл, скопируем и отредактируем его.
import os
print(os.path.join(os.path.dirname(gym.__file__), "envs/mujoco/assets/ant.xml"))Чтобы сделать Ant более "прыгущим", уменьшим плотность тела для снижения массы...

...и добавим силу к ногам для большего подъема (гравитация в симуляторе остается неизменной).

Полный отредактированный XML-файл доступен в репозитории.
Далее обновим функцию вознаграждения в среде Gym. Для кастомной среды создается новый класс, наследующий оригинальный, с переопределением нужных методов (в данном случае — расчета вознаграждения). После регистрации новая среда используется как стандартная.
from gymnasium.envs.mujoco.ant_v4 import AntEnv
from gymnasium.envs.registration import register
import numpy as np
## modify the class
class CustomAntEnv(AntEnv):
def __init__(self, **kwargs):
super().__init__(xml_file=os.getcwd()+"/assets/custom_ant.xml", **kwargs) # specify xml_file only if modified
def CUSTOM_REWARD(self, action, info):
torso_height = float(self.data.qpos[2]) # torso z-coordinate = how high it is
reward = np.clip(a=torso_height-0.6, a_min=0, a_max=1) *10 # when the torso is high
terminated = bool(torso_height < 0.2 ) # if torso close to the ground
info["torso_height"] = torso_height # add info for logging
return reward, terminated, info
def step(self, action):
obs, reward, terminated, truncated, info = super().step(action) # override original step()
new_reward, new_terminated, new_info = self.CUSTOM_REWARD(action, info)
return obs, new_reward, new_terminated, truncated, new_info # must return the same things
def reset_model(self):
return super().reset_model() # keeping the reset as it is
## register the new env
register(id="CustomAntEnv-v1", entry_point="__main__:CustomAntEnv")
## test
env = gym.make("CustomAntEnv-v1", render_mode="human")
obs, info = env.reset()
for _ in range(1000):
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
if terminated or truncated:
obs, info = env.reset()
env.close()
Если 3D-мир и его правила спроектированы грамотно, достаточно эффективной RL-модели, и робот будет стремиться к максимуму вознаграждения. В RL доминируют две группы моделей: Q-Learning (оптимальны для дискретных пространств действий) и Actor-Critic (для непрерывных). Кроме них появляются новые экспериментальные методы, такие как эволюционные алгоритмы и обучение имитацией.
Q-Learning
Q-Learning — базовая форма обучения с подкреплением, использующая Q-значения ("Q" означает "качество") для оценки полезности действия в получении будущего вознаграждения. Проще говоря, если в конце игры агент получает вознаграждение после последовательности действий, начальное Q-значение — это дисконтированное будущее вознаграждение.

По мере исследования и получения откликов агент обновляет Q-значения в Q-матрице (уравнение Беллмана). Цель — выучить оптимальные Q-значения для каждого состояния и действия, чтобы принимать лучшие решения и максимизировать ожидаемое будущее вознаграждение для конкретного действия в конкретном состоянии.
В процессе обучения агент балансирует между исследованием и эксплуатацией. Сначала он исследует среду случайными действиями, собирая опыт (информацию о вознаграждениях для разных действий и состояний). По мере обучения и снижения уровня исследования он переходит к эксплуатации знаний, выбирая действия с наивысшими Q-значениями для каждого состояния.
Обратите внимание, что Q-матрица может быть многомерной и сложной. Например, в алгоритме торговли:

В 2013 году произошел прорыв в RL, когда Google представил Deep Q-Network (DQN), способный играть в Atari-игры по сырым пикселям, объединяя глубокое обучение и Q-Learning. Проще говоря, глубокое обучение аппроксимирует Q-значения вместо хранения в таблице. Это достигается с помощью нейронной сети, обученной предсказывать Q-значения для каждого возможного действия на основе текущего состояния среды.

Семейство Q-Learning предназначено в основном для дискретных сред, поэтому оно не подходит напрямую для робота Ant. Альтернатива — дискретизация среды (хотя это не самый эффективный способ для непрерывных задач). Нужно создать обертку для Python-класса, ожидающую дискретное действие (например, "двигаться вперед"), и преобразующую его в силы на суставы.
class DiscreteEnvWrapper(gym.Env):
def __init__(self, render_mode=None):
super().__init__()
self.env = gym.make("CustomAntEnv-v1", render_mode=render_mode)
self.action_space = gym.spaces.Discrete(5) # will have 5 actions
self.observation_space = self.env.observation_space # same observation space
n_joints = self.env.action_space.shape[0]
self.action_map = [
# action 0 = stand still
np.zeros(n_joints),
# action 1 = push all forward
0.5*np.ones(n_joints),
# action 2 = push all backward
-0.5*np.ones(n_joints),
# action 3 = front legs forward + back legs backward
0.5*np.concatenate([np.ones(n_joints//2), -np.ones(n_joints//2)]),
# action 4 = front legs backward + back legs forward
0.5*np.concatenate([-np.ones(n_joints//2), np.ones(n_joints//2)])
]
def step(self, discrete_action):
assert self.action_space.contains(discrete_action)
continuous_action = self.action_map[discrete_action]
obs, reward, terminated, truncated, info = self.env.step(continuous_action)
return obs, reward, terminated, truncated, info
def reset(self, **kwargs):
obs, info = self.env.reset(**kwargs)
return obs, info
def render(self):
return self.env.render()
def close(self):
self.env.close()
## test
env = DiscreteEnvWrapper()
obs, info = env.reset()
print(f"--- INFO: {len(info)} ---")
print(info, "\n")
print(f"--- OBS: {obs.shape} ---")
print(obs, "\n")
print(f"--- ACTIONS: {env.action_space} ---")
discrete_action = env.action_space.sample()
continuous_action = env.action_map[discrete_action]
print("discrete:", discrete_action, "-> continuous:", continuous_action, "\n")
print(f"--- REWARD ---")
obs, reward, terminated, truncated, info = env.step(
discrete_action
)
print(reward, "\n")
Теперь среда с 5 возможными действиями совместима с DQN. В Python самый простой способ применять глубокие RL-алгоритмы — через Stable Baselines3 (pip install stable-baselines3), коллекцию готовых моделей на PyTorch (pip install torch). Полезно отслеживать прогресс обучения в TensorBoard (pip install tensorboard). Создается папка "logs", и запускается tensorboard --logdir=logs/ для локального дашборда (http://localhost:6006/).
import stable_baselines3 as sb
from stable_baselines3.common.vec_env import DummyVecEnv
# TRAIN
env = DiscreteEnvWrapper(render_mode=None) # no rendering to speed up
env = DummyVecEnv([lambda:env])
model_name = "ant_dqn"
print("Training START")
model = sb.DQN(
policy="MlpPolicy",
env=env,
verbose=0,
learning_rate=0.005,
exploration_fraction=0.2,
exploration_final_eps=0.05, # eps decays linearly from 1 to 0.05
tensorboard_log="logs/"
) # >tensorboard --logdir=logs/
model.learn(
total_timesteps=1_000_000, # 20min
tb_log_name=model_name,
log_interval=10
)
print("Training DONE")
model.save(model_name)После завершения обучения модель загружается и тестируется в визуализированной среде. Агент больше не обновляет предпочтения действий, а использует обученную модель для предсказания следующего оптимального шага на основе текущего состояния.
# TEST
env = DiscreteEnvWrapper(render_mode="human")
model = sb.DQN.load(path=model_name, env=env)
obs, info = env.reset()
reset = False # reset if episode ends
episode = 1
total_reward, step = 0, 0
for _ in range(1000):
# action
step += 1
action, _ = model.predict(obs)
obs, reward, terminated, truncated, info = env.step(action)
# reward
total_reward += reward
# render
env.render()
time.sleep(1/240)
if (step == 1) or (step % 100 == 0): # print first step and every 100 steps
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
# reset
if reset:
if terminated or truncated: # print the last step
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
Как видно, робот усвоил, что оптимальная стратегия — прыжки, но движения не плавные, поскольку модель не предназначена для непрерывных действий.
Actor-Critic
На практике алгоритмы Actor-Critic наиболее востребованы, так как идеально подходят для непрерывных сред. Основная концепция — два cooperating системы: функция политики ("Актер") для выбора действий и функция ценности ("Критик") для оценки ожидаемого вознаграждения. Модель корректирует принятие решений, сравнивая реальные вознаграждения с предсказаниями.
Первый стабильный алгоритм глубокого обучения — Advantage Actor-Critic (A2C) от OpenAI в 2016 году. Он минимизирует разницу между реальным вознаграждением после действия Актера и оценкой Критика. Нейронная сеть имеет общий входной слой для обоих, но выдает два выхода: Q-значения действий (как в DQN) и предсказанное вознаграждение (добавление A2C).

Со временем AC-алгоритмы эволюционировали в более стабильные варианты, такие как Proximal Policy Optimization (PPO) и Soft Actor-Critic (SAC). Последний использует не одну, а две сети Критиков для дополнительной оценки. Эти модели применяются напрямую в непрерывных средах.
# TRAIN
env_name, model_name = "CustomAntEnv-v1", "ant_sac"
env = gym.make(env_name) # no rendering to speed up
env = DummyVecEnv([lambda:env])
print("Training START")
model = sb.SAC(
policy="MlpPolicy",
env=env,
verbose=0,
learning_rate=0.005,
ent_coef=0.005, # exploration
tensorboard_log="logs/"
) # >tensorboard --logdir=logs/
model.learn(
total_timesteps=100_000, # 3h
tb_log_name=model_name,
log_interval=10
)
print("Training DONE")
# save
model.save(model_name)Обучение SAC занимает больше времени, но дает значительно лучшие результаты.
# TEST
env = gym.make(env_name, render_mode="human")
model = sb.SAC.load(path=model_name, env=env)
obs, info = env.reset()
reset = False # reset if the episode ends
episode = 1
total_reward, step = 0, 0
for _ in range(1000):
# action
step += 1
action, _ = model.predict(obs)
obs, reward, terminated, truncated, info = env.step(action)
# reward
total_reward += reward
# render
env.render()
time.sleep(1/240)
if (step == 1) or (step % 100 == 0): # print first step and every 100 steps
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
# reset
if reset:
if terminated or truncated: # print the last step
print(f"EPISODE {episode} - Step:{step}, Reward:{reward:.1f}, Total:{total_reward:.1f}")
obs, info = env.reset()
episode += 1
total_reward, step = 0, 0
print("------------------------------------------")
env.close()
Учитывая популярность Q-Learning и Actor-Critic, появились гибридные варианты, сочетающие подходы и расширяющие DQN на непрерывные пространства. Например, Deep Deterministic Policy Gradient (DDPG) и Twin Delayed DDPG (TD3). Однако чем сложнее модель, тем труднее обучение.
Экспериментальные модели
Помимо основных семейств (Q и AC) существуют менее распространенные, но интересные модели. Они особенно полезны для задач с редкими вознаграждениями, которые сложно спроектировать. Например:
- Эволюционные алгоритмы развивают политики через мутации и отбор, а не градиенты. Вдохновленные эволюцией Дарвина, они надежны, но требуют больших вычислительных ресурсов.
- Обучение имитацией пропускает исследование и учит агентов копировать демонстрации экспертов. Основано на "клонировании поведения", сочетая обучение с учителем и идеи RL.
Для экспериментов рассмотрим первый вариант с EvoTorch, открытым инструментарием для нейроэволюции, совместимым с PyTorch и Gym (pip install evotorch).
Лучший эволюционный алгоритм для RL — Policy Gradients with Parameter Exploration (PGPE). Он не обучает одну нейронную сеть напрямую, а строит вероятностное распределение (гауссово) по всем возможным весам (μ — среднее множество весов, σ — разброс для исследования). В каждой генерации PGPE семплирует из популяции весов, начиная со случайной политики. Затем модель корректирует среднее и дисперсию на основе вознаграждения (эволюция популяции). PGPE считается параллелизованным RL, поскольку, в отличие от классических методов вроде Q и AC, обновляющих одну политику батчами, он параллельно семплирует множество вариаций политик.
Перед обучением определяется "проблема" — задача оптимизации (наша среда).
from evotorch.neuroevolution import GymNE
from evotorch.algorithms import PGPE
from evotorch.logging import StdOutLogger
## problem
train = GymNE(
env=CustomAntEnv, # directly the class because it's custom env
env_config={"render_mode":None}, # no rendering to speed up
network="Linear(obs_length, act_length)", # linear policy
observation_normalization=True,
decrease_rewards_by=1, # normalization trick to stabilize evolution
episode_length=200, # steps per episode
num_actors="max" # use all available CPU cores
)
## model
model = PGPE(
problem=train,
popsize=20,
stdev_init=0.1, # keep it small
center_learning_rate=0.005,
stdev_learning_rate=0.1,
optimizer_config={"max_speed":0.015}
)
## train
StdOutLogger(searcher=model, interval=20)
model.run(num_generations=100)
Для тестирования создается визуализирующая "проблема". Затем извлекается лучший набор весов из центра распределения (во время обучения гауссиана смещается к улучшенным областям пространства политик).
## visualization problem
test = GymNE(
env=CustomAntEnv,
env_config={"render_mode":"human"},
network="Linear(obs_length, act_length)",
observation_normalization=True,
decrease_rewards_by=1,
num_actors=1 # only need 1 for visualization
)
## test best policy
population_center = model.status["center"]
policy = test.to_policy(population_center)
## render
test.visualize(policy)
Заключение
Эта статья служит руководством по применению обучения с подкреплением в робототехнике. Рассматривается создание 3D-симуляций с Gym и MuJoCo, адаптация среды и выбор подходящих RL-алгоритмов для разных сценариев. В будущем ожидаются руководства по более сложным роботам.