Применение градиента к слоям LSTM с использованием GradientTape вызывает ошибку «Градиенты не предусмотрены ни для одной переменной»

#python #tensorflow #machine-learning #keras #reinforcement-learning

#python #tensorflow #машинное обучение #keras #подкрепление-обучение

Вопрос:

В настоящее время я настраиваю глубоко детерминированный агент градиента политики для взаимодействия со средой криптоторговли. Код работает, когда я использую Dense слои для аппроксиматора функций, но когда я переключаюсь на LSTM или GRU s, эта ошибка появляется при вызове learn метода Agent :

 No gradients provided for any variable: ['actor_network_4/lstm/kernel:0', 'actor_network_4/lstm/recurrent_kernel:0', 'actor_network_4/lstm/bias:0', 'actor_network_4/lstm_1/kernel:0', 'actor_network_4/lstm_1/recurrent_kernel:0', 'actor_network_4/lstm_1/bias:0', 'actor_network_4/dense_8/kernel:0', 'actor_network_4/dense_8/bias:0'].
  

Я использую GradientTape для записи градиентов и optimizer.apply_gradients обновления сетей актеров и критиков. Пожалуйста, найдите фрагменты кода ниже:

 #RNN version
class CriticNetwork(keras.Model):
    def __init__(self, n_actions,name='critic', chkpt_dir='ddpg'):
        super(CriticNetwork, self).__init__()
        self.n_actions = n_actions

        self.model_name = name
        self.checkpoint_dir = chkpt_dir
        self.checkpoint_file = os.path.join(self.checkpoint_dir, self.model_name '_ddpg.h5')

        self.lstm1 = LSTM(128,return_sequences=True,unroll=False)
        self.lstm2 = LSTM(128)
        self.q = Dense(1, activation=None)

    def call(self, state, action):
        action_value = tf.concat([state, action], axis=1)
        action_value = np.reshape(action_value,(32,1,44))
        action_value = self.lstm1(action_value)
        action_value = self.lstm2(action_value)
        q = self.q(action_value)

        return q

class ActorNetwork(keras.Model):
    def __init__(self,n_actions=1, name='actor',chkpt_dir='ddpg'):
        super(ActorNetwork, self).__init__()
        self.n_actions = n_actions

        self.model_name = name
        self.checkpoint_dir = chkpt_dir
        self.checkpoint_file = os.path.join(self.checkpoint_dir, 
                    self.model_name '_ddpg.h5')

        self.lstm1 = LSTM(128,return_sequences=True,unroll=False)
        self.lstm2 = LSTM(128)
        self.mu = Dense(self.n_actions, activation='tanh')

    def call(self, state):
        state = np.reshape(state,(32,1,43))
        prob = self.lstm1(state)
        prob = self.lstm2(prob)
        mu = self.mu(prob)

        return mu

class Agent:
    def __init__(self, alpha=0.001, beta=0.002, input_dims=[33], env=None,
            gamma=0.99, n_actions=1, max_size=3000000, tau=0.005, 
            fc1=128, fc2=128, fc3=64, batch_size=32):
        self.gamma = gamma
        self.tau = tau
        self.memory = ReplayBuffer(max_size, input_dims, n_actions)
        self.batch_size = batch_size
        self.n_actions = n_actions
        self.max_action = 1
        self.min_action = -1
        
        self.actor = ActorNetwork(n_actions=n_actions, name='actor')
        self.critic = CriticNetwork(n_actions=n_actions, name='critic')
        self.target_actor = ActorNetwork(n_actions=n_actions, name='target_actor')
        self.target_critic = CriticNetwork(n_actions=n_actions, name='target_critic')

        self.actor.compile(optimizer=Adam(learning_rate=alpha))
        self.critic.compile(optimizer=Adam(learning_rate=beta))
        self.target_actor.compile(optimizer=Adam(learning_rate=alpha))
        self.target_critic.compile(optimizer=Adam(learning_rate=beta))

        self.update_network_parameters(tau=1)

    def update_network_parameters(self, tau=None):
        if tau is None:
            tau = self.tau

        weights = []
        targets = self.target_actor.weights
        for i, weight in enumerate(self.actor.weights):
            weights.append(weight * tau   targets[i]*(1-tau))
        self.target_actor.set_weights(weights)

        weights = []
        targets = self.target_critic.weights
        for i, weight in enumerate(self.critic.weights):
            weights.append(weight * tau   targets[i]*(1-tau))
        self.target_critic.set_weights(weights)

    def remember(self, state, action, reward, new_state, done):
        self.memory.store_transition(state, action, reward, new_state, done)

    def save_models(self):
        print('... saving models ...')
        self.actor.save_weights(self.actor.checkpoint_file)
        self.target_actor.save_weights(self.target_actor.checkpoint_file)
        self.critic.save_weights(self.critic.checkpoint_file)
        self.target_critic.save_weights(self.target_critic.checkpoint_file)

    def load_models(self):
        print('... loading models ...')
        self.actor.load_weights(self.actor.checkpoint_file)
        self.target_actor.load_weights(self.target_actor.checkpoint_file)
        self.critic.load_weights(self.critic.checkpoint_file)
        self.target_critic.load_weights(self.target_critic.checkpoint_file)

    def choose_action(self, observation, evaluate=False):
        state = tf.convert_to_tensor([observation], dtype=tf.float32)
        actions = self.actor(state)
        if not evaluate:
            actions  = tf.random.normal(shape=[self.n_actions],
                    mean=0.0, stddev=0.05)
            
        actions = tf.clip_by_value(actions, self.min_action, self.max_action)

        return actions

    def learn(self):
        if self.memory.mem_cntr < self.batch_size:
            return

        state, action, reward, new_state, done = 
                self.memory.sample_buffer(self.batch_size)

        states = tf.convert_to_tensor(state, dtype=tf.float32)
        states_ = tf.convert_to_tensor(new_state, dtype=tf.float32)
        rewards = tf.convert_to_tensor(reward, dtype=tf.float32)
        actions = tf.convert_to_tensor(action, dtype=tf.float32)

        with tf.GradientTape() as tape:
            target_actions = self.target_actor(states_)
            critic_value_ = tf.squeeze(self.target_critic(
                                states_, target_actions), 1)
            critic_value = tf.squeeze(self.critic(states, actions), 1)
            target = reward   self.gamma*critic_value_*(1-done)
            critic_loss = keras.losses.MSE(target, critic_value)

        critic_network_gradient = tape.gradient(critic_loss, self.critic.trainable_variables)
        self.critic.optimizer.apply_gradients(zip(critic_network_gradient, self.critic.trainable_variables))

        with tf.GradientTape() as tape:
            new_policy_actions = self.actor(states)
            actor_loss = -self.critic(states, new_policy_actions)
            actor_loss = tf.math.reduce_mean(actor_loss)

        actor_network_gradient = tape.gradient(actor_loss, self.actor.trainable_variables)
        self.actor.optimizer.apply_gradients(zip(actor_network_gradient, self.actor.trainable_variables))

        self.update_network_parameters()
  

Любые советы или помощь в отношении того, как записывать и применять градиент для LSTM, помогут.

Ответ №1:

На самом деле проблема в том, что вы используете операции Numpy для определения логики вычислений (следовательно, градиенты не могут течь с этой точки и далее); однако это должно быть сделано полностью с использованием слоев TF Ops или Keras. В частности, в call методе CriticNetwork , а также ActorNetwork , вместо того, чтобы использовать np.reshape , вы должны либо использовать tf.expand_dims (если вы хотите добавить новую ось размера один к тензору), либо tf.reshape , либо tf.keras.layers.Reshape слой (для более сложной перестройки). Например, используя tf.expand_dims :

 class CriticNetwork(keras.Model):
    #...
    def call(self, state, action):
        action_value = tf.concat([state, action], axis=1)
        action_value = tf.expand_dims(action_value, axis=1)
  

или использование tf.reshape :

 class CriticNetwork(keras.Model):
    #...
    def call(self, state, action):
        action_value = tf.concat([state, action], axis=1)
        action_value = tf.reshape(action_value, (-1, 1, 44))  # Use `-1` for the first axis so that any batch size would be supported
  

или использование Reshape layer:

 class CriticNetwork(keras.Model):
    def __init__(self, n_actions,name='critic', chkpt_dir='ddpg'):
        # ...
        self.reshape = tf.keras.layers.Reshape((1, 44))

    def call(self, state, action):
        action_value = tf.concat([state, action], axis=1)
        action_value = self.reshape(action_value)
  

И вам нужно сделать то же самое для ActorNetwork .


Примечание: я не уверен, является ли это просто демонстрационным кодом или нет, но обратите внимание, что использование слоев RNN для последовательности длиной один (т. Е. Имеющих Только один временной шаг) может оказаться не очень полезным.