Обновление актера DDPG (Tensroflow 2) - PullRequest
0 голосов
/ 23 января 2020

Я столкнулся с большой проблемой с реализацией в tenorflow 2 агента DDPG. Хотя обновление критической c сети ясное и простое (просто сделайте градиентный спуск по потере), обновление актера немного сложнее.

Это моя реализация "actor_update" функция:

def actor_train(self, minibatch):
    s_batch, _, _, _, _ = minibatch
    with tf.GradientTape() as tape1:
        with tf.GradientTape() as tape2:
            mu = self.actor_network(s_batch)
            q = self.critic_network([s_batch, mu])
        mu_grad = tape1.gradient(mu, self.actor_network.trainable_weights)
    q_grad = tape2.gradient(q, self.actor_network.trainable_weights)

    x = np.array(q_grad)*np.array(mu_grad)
    x /= -len(minibatch)
    self.actor_optimizer.apply_gradients(zip(x, self.actor_network.trainable_weights))

Как указано в статье, оптимизация представляет собой произведение двух градиентов: один - градиент функции Q по отношению к действиям, а другой - градиент функции субъекта по весам .

Начиная все сети с весами, взятыми с помощью равномерного распределения между -1e-3 и 1e-3, субъект, похоже, не обновляет его веса. Вместо этого, отображение результата критического анализа c (с использованием MountainCarContinous в качестве тестовой среды) показывает небольшую зависимость от данных.

Это код критического значения c для полноты:

def critic_train(self, minibatch):
    s_batch, a_batch, r_batch, s_1_batch, t_batch = minibatch

    mu_prime = np.array(self.actor_target_network(s_1_batch))
    q_prime = self.critic_target_network([s_1_batch, mu_prime])
    ys = r_batch + self.GAMMA * (1 - t_batch) * q_prime


    with tf.GradientTape() as tape:
        predicted_qs = self.critic_network([s_batch, a_batch])
        loss = tf.keras.losses.MSE(ys, predicted_qs)
        dloss = tape.gradient(loss, self.critic_network.trainable_weights)

    self.critic_optimizer.apply_gradients(zip(dloss, self.critic_network.trainable_weights))

В качестве дополнения, актер, кажется, насыщается после победного эпизода. (Означает, что он застревает на +1 или -1 для каждого входа).

В чем проблема? Функция обновления работает правильно? Или это только проблема настройки гиперпараметров?

Это репо, кто-то хочет иметь лучшее представление о проблеме: Github repo

1 Ответ

1 голос
/ 04 февраля 2020

Я не смотрел в репозитории, но я могу заметить пару вещей в фрагменте кода, который вы разместили:

  1. Сеть Criti c выглядит хорошо с первого взгляда. Это использует потерю MSE все же. Ничего страшного, но в работе используется потеря Хьюбера, и агент будет более устойчивым, если вы это сделаете.
  2. Подача градиентов критических c в актера неверна.

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

Вам нужно будет рассчитать градиенты действий по критическим значениям c и представить их как начальные градиенты для актера. Представьте себе, что градиенты текут сквозь слой за слоем от критического c выхода до актерского входа, как если бы обе сети были связаны друг с другом.

Более конкретно:

  • Расчетные градиенты действий - градиенты критических результатов c относительно (относительно) входов действий. Интуитивно понятно, что эти градиенты говорят о том, как вклады действия в критическую точку c внесли вклад в значение Q. После этого у нас должен быть Tensor / список градиентов формы [batch_size, action_dims]
  • Выход актера также [batch_size, action_dims]. Мы хотим подать эти градиенты в выходной слой актера, чтобы сделать обратное распространение, чтобы изменить выходные данные нашего действия, чтобы максимизировать значение Q.

Таким образом, ваш код выглядит примерно так ( не проверял его на корректность, но вы должны быть в состоянии заставить его работать. В частности, я не слишком знаком с градиентной лентой, поэтому вы можете убедиться, что область действия градиентов действительна):

with tf.GradientTape() as tape1:
    mu = self.actor_network(s_batch)
    with tf.GradientTape() as tape2:
        q = self.critic_network([s_batch, mu])
    q_grad = tape2.gradient(q, mu) # grads of Q output wrt. action inputs [batch_size, action_dims]
mu_grad = tape1.gradient(mu, self.actor_network.trainable_weights, -q_grad) # grads of actions wrt. network vars, feeding in the action grads as initial grads

x = mu_grad / len(minibatch) # gradient() sums over batch dim, so take the mean to apply
self.actor_optimizer.apply_gradients(zip(x, self.actor_network.trainable_weights))

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

...