自学内容网 自学内容网

24/12/8 算法笔记<强化学习> DDPG

DDPG 深度确定性策略梯度算法,基于 Actor - Critic 架构。

DDPG也是解决连续控制型问题的一个算法,不过和PPO不一样,PPO输出的是一个策略,也就是一个概率分布1,而DPPG输出的直接是一个1动作。

DDPG更接近DQN,是用一个actor去弥补DQN不能处理连续控制性问题的缺点

DDPG呢是用magic解决DQN不能解决的连续控制型问题,也就是说,我们期待我们输入状态S,magic函数返回我们动作action的取值,这个取值能够让q值最大,这个就是DDPG中的Actor的功能。

Actor的任务就是进来给出使得critic最大Q值的动作,所以DDPG并不是PG,并没有做带权重的梯度更新,而是梯度上升,在寻找Q的最大值。

总的来说

        对于Critic网络,Q值的计算结合了Actor和Critic目标网络,公式为:其中

是 Actor 的目标网络。即先通过 Actor 的目标网络得到下一个状态下的目标动作,再通过 Critic 的目标网络得到目标 Q 值。

        对于 Actor 网络,更新目标是最大化 Critic 网络对当前生成动作所给出的 Q 值。通过计算 Critic 网络输出的 Q 值关于 Actor 网络生成的动作的梯度,来指导 Actor 网络调整其策略,使得生成的动作能朝着提升 Q 值的方向发展。

更新策略

        DDPG也用经验回放缓冲区来存储和采样经验数据用于网络更新,但是包含Actor和Critic两个网络,并且两个网络相互关联,所以更新更为复杂。

        在一次更新过程中,先更新 Critic 网络,再基于 Critic 网络的梯度信息更新 Actor 网络。并且,DDPG 中的目标网络(Actor 和 Critic 的目标网络)采用软更新(Soft - Update)的方式,在每次训练步骤中都以较小的幅度更新目标网络的参数,使其更平滑地跟踪主网络的变化,而不是像 DQN 那样定期完全复制主网络参数。这种软更新方式使得 DDPG 在训练过程中能够更稳定地收敛。

我们来看下代码

经验回放缓冲区类

class ReplayBuffer:
    def __init__(self, capacity):
        self.capacity = capacity
        self.buffer = []
        self.position = 0

    def push(self, state, action, reward, next_state, done):
        if len(self.buffer) < self.capacity:
            self.buffer.append(None)
        self.buffer[self.position] = (state, action, reward, next_state, done)
        self.position = (self.position + 1) % self.capacity

    def sample(self, batch_size):
        batch = random.sample(self.buffer, batch_size)
        states, actions, rewards, next_states, dones = zip(*batch)
        return np.array(states), np.array(actions), np.array(rewards), np.array(next_states), np.array(dones)

    def __len__(self):
        return len(self.buffer)

 定义 Actor 网络

class ActorNetwork(tf.keras.Model):
    def __init__(self, state_dim, action_dim, action_bound):
        super(ActorNetwork, self).__init__()
        self.fc1 = tf.keras.layers.Dense(400, activation='relu', kernel_initializer='he_normal')
        self.fc2 = tf.keras.layers.Dense(300, activation='relu', kernel_initializer='he_normal')
        self.fc3 = tf.keras.layers.Dense(action_dim, activation='tanh', kernel_initializer='he_normal')
        self.action_bound = action_bound

    def call(self, state):
        x = self.fc1(state)
        x = self.fc2(x)
        x = self.fc3(x)
        return x * self.action_bound

定义Critic网络

class CriticNetwork(tf.keras.Model):
    def __init__(self, state_dim, action_dim):
        super(CriticNetwork, self).__init__()
        self.fc1_s = tf.keras.layers.Dense(400, activation='relu', kernel_initializer='he_normal')
        self.fc1_a = tf.keras.layers.Dense(400, activation='relu', kernel_initializer='he_normal')
        self.fc2 = tf.keras.layers.Dense(300, activation='relu', kernel_initializer='he_normal')
        self.fc3 = tf.keras.layers.Dense(1, kernel_initializer='he_normal')

    def call(self, state, action):
        s = self.fc1_s(state)
        a = self.fc1_a(action)
        x = tf.keras.layers.concatenate([s, a])
        x = self.fc2(x)
        return self.fc3(x)

choose_action 方法

def choose_action(self, state):
        state = np.expand_dims(state, axis=0).astype(np.float32)
        action = self.actor(tf.convert_to_tensor(state, dtype=tf.float32)).numpy()[0]
        noise = np.random.normal(0, self.action_bound * self.epsilon, size=self.action_dim)
        action = np.clip(action + noise, -self.action_bound, self.action_bound)
        return action

将经验存储到经验回放缓冲区

def store_transition(self, state, action, reward, next_state, done):
        self.memory.push(state, action, reward, next_state, done)

learn方法

def learn(self):
        if len(self.memory) < BATCH_SIZE:
            return

        states, actions, rewards, next_states, dones = self.memory.sample(BATCH_SIZE)
        states = tf.convert_to_tensor(states, dtype=tf.float32)
        actions = tf.convert_to_tensor(actions, dtype=tf.float32)
        rewards = tf.convert_to_tensor(rewards, dtype=tf.float32)
        next_states = tf.convert_to_tensor(next_states, dtype=tf.float32)
        dones = tf.convert_to_tensor(dones, dtype=tf.float32)

        # 更新Critic网络
        with tf.GradientTape() as tape:
            target_actions = self.actor_target(next_states)
            target_q = self.critic_target(next_states, target_actions)
            target_q = rewards + (1 - dones) * GAMMA * target_q
            current_q = self.critic(states, actions)
            critic_loss = tf.reduce_mean(tf.square(target_q - current_q))

        critic_gradients = tape.gradient(critic_loss, self.critic.trainable_variables)
        self.critic_optimizer.apply_gradients(zip(critic_gradients, self.critic.trainable_variables))

        # 更新Actor网络
        with tf.GradientTape() as tape:
            actions = self.actor(states)
            q_value = self.critic(states, actions)
            actor_loss = -tf.reduce_mean(q_value)

        actor_gradients = tape.gradient(actor_loss, self.actor.trainable_variables)
        self.actor_optimizer.apply_gradients(zip(actor_gradients, self.actor.trainable_variables))

        # 软更新目标网络
        self._soft_update(self.actor_target, self.actor, TAU)
        self._soft_update(self.critic_target, self.critic, TAU)

        # 更新探索噪声比例
        self.epsilon = max(self.min_epsilon, self.epsilon * self.epsilon_decay)

DDPG里核心特殊的一点就是基于Critic网络指导Actor网络更新

# 更新Critic网络
with tf.GradientTape() as tape:
    target_actions = self.actor_target(next_states)
    target_q = self.critic_target(next_states, target_actions)
    target_q = rewards + (1 - dones) * GAMMA * target_q
    current_q = self.critic(states, actions)
    critic_loss = tf.reduce_mean(tf.square(target_q - current_q))

critic_gradients = tape.gradient(critic_loss, self.critic.trainable_variables)
self.critic_optimizer.apply_gradients(zip(critic_gradients, self.critic.trainable_variables))

# 更新Actor网络
with tf.GradientTape() as tape:
    actions = self.actor(states)
    q_value = self.critic(states, actions)
    actor_loss = -tf.reduce_mean(q_value)

actor_gradients = tape.gradient(actor_loss, self.actor.trainable_variables)
self.actor_optimizer.apply_gradients(zip(actor_gradients, self.actor.trainable_variables))

先基于目标网络计算出目标 Q 值(结合目标 Actor 网络生成的动作等信息),然后通过最小化目标 Q 值与当前 Critic 网络输出的 Q 值之间的均方误差来更新 Critic 网络。而对于 Actor 网络的更新,其目标是最大化 Critic 网络对当前生成动作所给出的 Q 值,所以通过对 Critic 网络输出的 Q 值关于 Actor 网络生成的动作求梯度(q_value = self.critic(states, actions) 及后续求梯度相关操作),以这个梯度信息来指导 Actor 网络调整其策略(即更新网络参数),使得 Actor 网络生成的动作能朝着提升 Q 值、获取更高长期奖励的方向发展。

软更新

def _soft_update(self, target_network, current_network, tau):
        for target_param, param in zip(target_network.trainable_variables, current_network.trainable_variables):
            target_param.assign(target_param * (1 - tau) + param * tau)

软更新也是DDPG中特殊的一种方法,具体的更新方式是按照软更新的规则,将目标网络当前变量值 target_param 乘以 (1 - tau),再加上当前网络对应变量值 param 乘以 tau,得到新的值并赋值给目标网络的该变量。通过这样逐个变量的更新,使得目标网络整体的参数朝着当前网络参数的方向进行了一个小幅度的调整,实现了软更新的效果。

以上就是大概的DDPG代码,接下来我们来看看它的优劣势

优劣势

优势

DDPG适用于连续动作空间,既吸收了策略梯度方法能够直接优化策略网络的优点,有融合了A学习中通过估计Q值来评估动作价值的思想。使得算法在学习过程中能够更全面地考虑策略和价值的信息,从而更好地找到最优策略.

劣势

对超参数敏感,探索能力有限,DDPG输出的是确定的动作,可能会陷入局部最优解,虽然可以加入噪声来优化,但是噪声策略和噪声强度也是问题。

高估Q值:DDPG可能会高估Q值使得算法在寻来你过程中过度乐观地估计某些动作的价值。


原文地址:https://blog.csdn.net/yyyy2711/article/details/144327832

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!