Я попытался использовать непрерывное пространство действия DDPG для решения следующей проблемы управления.Цель состоит в том, чтобы пройти к первоначально неизвестному положению в двухмерной граничащей области, сказав, как далеко вы находитесь от целевого положения на каждом шаге (аналогично этой детской игре, в которой игрок руководствуется «температурой»).уровни, горячие и холодные ).
В настройках целевая позиция фиксируется, в то время как начальная позиция агента изменяется от эпизода к эпизоду.Цель состоит в том, чтобы выучить политику как можно быстрее идти к целевой позиции.Наблюдение агента состоит только из его текущей позиции.Что касается схемы вознаграждений, я рассмотрел среду Reacher , поскольку она включает в себя аналогичную цель и аналогичным образом использует контрольную награду и награду за дистанцию (см. код ниже).То есть, чем ближе к цели, тем больше вознаграждение, и чем ближе агент, тем больше он должен отдавать предпочтение меньшим действиям.
Для реализации, которую я рассмотрел, был openai / spinningup пакет.Что касается сетевой архитектуры, я полагал, что, если целевая позиция известна, оптимальным действием будет action = target - position
, то есть политика pi(x) -> a
может быть смоделирована как просто один плотный слой, а целевая позиция будет изучена в форметермин смещения: a = W @ x + b
, где после сходимости (в идеале) W = -np.eye(2)
и b = target
.Так как среда накладывает ограничение действия, так что целевая позиция, вероятно, не может быть достигнута за один шаг, я вручную масштабирую вычисленные действия как a = a / tf.norm(a) * action_limit
.Это сохраняет направление к цели и, следовательно, напоминает оптимальное действие.Я использовал эту пользовательскую архитектуру для сети политик, а также стандартную архитектуру MLP с 3 скрытыми слоями (см. Код и результаты ниже).
Результаты
После запуска алгоритма для примерно 400 эпизодовв случае MLP и 700 эпизодов в случае с пользовательской политикой, с 1000 шагов на эпизод, он, кажется, не получил ничего полезного.Во время тестовых прогонов средняя отдача не увеличивалась, и когда я проверял поведение на трех разных стартовых позициях, он всегда шел к (0, 1)
углу области;даже когда он начинается прямо рядом с целевой позицией, он проходит мимо нее, направляясь к углу (0, 1)
.То, что я заметил, - то, что агент архитектуры пользовательской политики привел к намного меньшему стандартному значению.девиациятестового эпизода возвращается.
Вопрос
Я хотел бы понять, почему алгоритм, кажется, ничего не изучает для данной установки и что нужно изменить, чтобы он сходился,Я подозреваю проблему с реализацией или с выбором гиперпараметров, так как не могу обнаружить никаких концептуальных проблем с изучением политики в данной настройке.Однако я не смог точно определить источник проблемы, поэтому я был бы рад, если кто-то может помочь.
Средний результат теста (архитектура пользовательской политики):
(вертикальные черты указывают стандартное отклонение результатов тестового эпизода)
Средний тестовый возврат (архитектура политики MLP):
Контрольные примеры (архитектура настраиваемой политики):
Контрольные примеры (архитектура политики MLP):
Код
import logging
import os
import gym
from gym.wrappers.time_limit import TimeLimit
import numpy as np
from spinup.algos.ddpg.ddpg import core, ddpg
import tensorflow as tf
class TestEnv(gym.Env):
target = np.array([0.7, 0.8])
action_limit = 0.01
observation_space = gym.spaces.Box(low=np.zeros(2), high=np.ones(2), dtype=np.float32)
action_space = gym.spaces.Box(-action_limit * np.ones(2), action_limit * np.ones(2), dtype=np.float32)
def __init__(self):
super().__init__()
self.pos = np.empty(2, dtype=np.float32)
self.reset()
def step(self, action):
self.pos += action
self.pos = np.clip(self.pos, self.observation_space.low, self.observation_space.high)
reward_ctrl = -np.square(action).sum() / self.action_limit**2
reward_dist = -np.linalg.norm(self.pos - self.target)
reward = reward_ctrl + reward_dist
done = abs(reward_dist) < 1e-9
logging.debug('Observation: %s', self.pos)
logging.debug('Reward: %.6f (reward (ctrl): %.6f, reward (dist): %.6f)', reward, reward_ctrl, reward_dist)
return self.pos, reward, done, {}
def reset(self):
self.pos[:] = np.random.uniform(self.observation_space.low, self.observation_space.high, size=2)
logging.info(f'[Reset] New position: {self.pos}')
return self.pos
def render(self, *args, **kwargs):
pass
def mlp_actor_critic(x, a, hidden_sizes, activation=tf.nn.relu, action_space=None):
act_dim = a.shape.as_list()[-1]
act_limit = action_space.high[0]
with tf.variable_scope('pi'):
# pi = core.mlp(x, list(hidden_sizes)+[act_dim], activation, output_activation=None) # The standard way.
pi = tf.layers.dense(x, act_dim, use_bias=True) # Target position should be learned via the bias term.
pi = pi / (tf.norm(pi) + 1e-9) * act_limit # Prevent division by zero.
with tf.variable_scope('q'):
q = tf.squeeze(core.mlp(tf.concat([x,a], axis=-1), list(hidden_sizes)+[1], activation, None), axis=1)
with tf.variable_scope('q', reuse=True):
q_pi = tf.squeeze(core.mlp(tf.concat([x,pi], axis=-1), list(hidden_sizes)+[1], activation, None), axis=1)
return pi, q, q_pi
if __name__ == '__main__':
log_dir = 'spinup-ddpg'
if not os.path.exists(log_dir):
os.mkdir(log_dir)
logging.basicConfig(level=logging.INFO)
ep_length = 1000
ddpg(
lambda: TimeLimit(TestEnv(), ep_length),
mlp_actor_critic,
ac_kwargs=dict(hidden_sizes=(64, 64, 64)),
steps_per_epoch=ep_length,
epochs=1_000,
replay_size=1_000_000,
start_steps=10_000,
act_noise=TestEnv.action_limit/2,
gamma=0.99, # Use large gamma, because of action limit it matters where we walk to early in the episode.
polyak=0.995,
max_ep_len=ep_length,
save_freq=10,
logger_kwargs=dict(output_dir=log_dir)
)