Непрерывная DDPG, похоже, не сходится к двумерной задаче пространственного поиска («Охота на наперсток») - PullRequest
2 голосов
/ 11 мая 2019

Я попытался использовать непрерывное пространство действия 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).То, что я заметил, - то, что агент архитектуры пользовательской политики привел к намного меньшему стандартному значению.девиациятестового эпизода возвращается.

Вопрос

Я хотел бы понять, почему алгоритм, кажется, ничего не изучает для данной установки и что нужно изменить, чтобы он сходился,Я подозреваю проблему с реализацией или с выбором гиперпараметров, так как не могу обнаружить никаких концептуальных проблем с изучением политики в данной настройке.Однако я не смог точно определить источник проблемы, поэтому я был бы рад, если кто-то может помочь.


Средний результат теста (архитектура пользовательской политики):

Average test return (custom policy architecture)

(вертикальные черты указывают стандартное отклонение результатов тестового эпизода)

Средний тестовый возврат (архитектура политики MLP):

Average test return (MLP policy architecture)

Контрольные примеры (архитектура настраиваемой политики):

Test cases (custom policy architecture)

Контрольные примеры (архитектура политики MLP):

Test cases (MLP policy architecture)

Код

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)
    )

1 Ответ

0 голосов
/ 12 мая 2019

Вы используете ОГРОМНУЮ сеть (64x64x64) для очень маленькой проблемы.Это само по себе может быть большой проблемой.Вы также сохраняете 1M сэмплов в своей памяти, и, опять же, для очень простой проблемы это может быть вредным и медленным сходимостью.Сначала попробуйте гораздо более простую настройку (32x32 нетто и 100 000 памяти, или даже линейный аппроксиматор с полиномиальными характеристиками).Кроме того, как вы обновляете свою целевую сеть?Что такое polyak?Наконец, нормализация такого действия не может быть хорошей идеей.Лучше просто обрезать его или использовать желтоватый слой в конце.

...