13.2 DDPG算法
DDPG(Deep Deterministic Policy Gradient)是一种用于解决连续动作空间中的强化学习问题的算法。它结合了深度神经网络和确定性策略梯度方法,旨在学习连续动作空间中的高性能策略。
13.2.1 DDPG算法的特点
DDPG是一种通用算法,可用于多种强化学习任务,如机器人控制、自动驾驶和游戏玩法。DDPG算法的特点如下所示:
- 确定性策略:DDPG使用确定性策略,这意味着代理直接输出连续动作值,而不是输出动作的概率分布。确定性策略通常更容易优化和稳定。
- 深度神经网络:DDPG使用深度神经网络来表示策略(行动者网络)和值函数(评论者网络)。这些神经网络可以是多层前馈神经网络,用于近似复杂的策略和值函数。
- 经验回放缓冲区:为了提高样本的利用效率和稳定性,DDPG使用经验回放缓冲区来存储之前的经验样本。在训练时,它从缓冲区中随机采样样本进行学习,以减少样本间的相关性,并允许代理重复使用以前的经验。
- 目标网络:DDPG使用目标网络(Target Networks)来稳定训练。这包括目标策略网络和目标值函数网络,它们是用于计算目标值的副本网络,通常与原始网络之间采用软更新(soft update)来保持一定的平滑度。
- 策略梯度方法:DDPG使用策略梯度方法来更新策略网络,通过最大化累积奖励的期望值,代理可以逐渐改进策略。策略网络的梯度由值函数网络计算。
13.2.2 DDPG算法在连续动作空间中的优势
DDPG算法在连续动作空间中具有多个优势,使其成为解决连续动作控制问题的有效工具。这些优势的具体说明如下:
- 适用于高维度状态空间和动作空间:DDPG可以轻松应对高维度状态和动作空间,因为它使用深度神经网络来表示策略和值函数,这些网络能够处理大量输入和输出。
- 连续动作的直接建模:DDPG使用确定性策略,直接输出连续动作值,这比使用概率性策略(如REINFORCE算法)更容易优化。这使得DDPG在连续动作空间中更加高效。
- 经验回放缓冲区:DDPG使用经验回放缓冲区来存储以前的经验样本,这提高了样本的重复使用率和训练稳定性。这有助于减少样本间的相关性,改善学习的效率。
- 目标网络:DDPG引入了目标网络来稳定训练。目标网络的软更新使得训练更加平滑,减少了训练中的振荡和发散问题。
- 策略梯度方法:DDPG使用策略梯度方法来更新策略网络,这意味着它可以处理高度不连续和嘈杂的动作空间。它在学习复杂、高维度的策略中表现出色。
- 通用性:DDPG是一种通用算法,可以应用于多种连续动作控制问题,如机器人控制、自动驾驶、游戏玩法等。这种通用性使得它成为解决各种现实世界问题的有力工具。
- 稳定性:相对于一些其他深度强化学习方法,DDPG在训练过程中更加稳定。这使得它更容易实现,并且对于复杂的任务也可以获得较好的性能。
注意:尽管DDPG在许多方面具有优势,但它也有一些限制,如对超参数的敏感性和需要大量的样本和计算资源。此外,对于某些问题,可能有更适合的算法。然而,在处理连续动作控制问题时,DDPG仍然是一个值得考虑的重要算法之一。
13.2.3 DDPG算法的实现步骤与网络结构
在实际应用中,DDPG算法的实现步骤如下所示。
(1)定义环境:首先,定义强化学习问题中的环境,包括状态空间、动作空间、奖励函数等。
构建神经网络:构建两个深度神经网络,一个用于表示策略(行动者网络),另一个用于表示值函数(评论者网络)。
(2)初始化网络参数:随机初始化神经网络的权重和偏差。
(3)设置超参数:设置学习率、折扣因子(discount factor)、经验回放缓冲区的大小、目标网络软更新率等超参数。
(4)定义经验回放缓冲区:创建一个经验回放缓冲区,用于存储代理与环境互动时的经验元组,通常包括(状态,动作,奖励,下一个状态)。
(5)定义目标网络:为了稳定训练,创建目标策略网络和目标值函数网络,它们是原始网络的副本。这些目标网络的参数在训练过程中会慢慢地与原始网络的参数同步。
(6)定义噪声过程:为策略网络引入噪声,以促进探索。通常使用高斯噪声或其他随机过程来添加噪声。
(7)开始训练循环,具体步骤如下:
- 从环境中获取初始状态。
- 使用策略网络选择动作,通常是加入噪声的动作。
- 执行动作,观察奖励和下一个状态。
- 将经验元组存储在经验回放缓冲区中。
- 从经验回放缓冲区中采样一批样本,用于更新策略和值函数网络。
- 使用目标网络计算目标值,并根据目标值更新值函数网络。
- 使用策略梯度方法计算策略的梯度,并根据梯度更新策略网络。
- 周期性地将目标网络的参数软更新为原始网络的参数。
(8)终止条件:训练循环可以根据收敛条件、固定的迭代次数或其他终止条件来结束。
DDPG算法的网络结构如下所示:
- 行动者网络(Actor Network):用于表示策略,它接收当前状态作为输入,输出一个连续动作值。通常采用多层前馈神经网络结构,激活函数可以是ReLU或tanh等。最后一层的输出层通常不使用激活函数,以保证输出范围在动作空间内。
- 评论者网络(Critic Network):用于表示值函数,接收当前状态和动作作为输入,输出一个估计的状态值或动作值。也是多层前馈神经网络结构,通常与行动者网络共享一些层(共享参数),以加速训练。最后一层的输出层通常只有一个神经元,表示值函数的估计。
- 目标网络(Target Networks):目标策略网络和目标值函数网络是原始网络的副本,它们的参数慢慢地与原始网络的参数同步。这有助于稳定训练。
注意:上面介绍的是DDPG算法的一般实现步骤和网络结构,在实际应用中,需要根据具体问题和任务的特性进行调整和优化,并且需要仔细选择合适的超参数和训练策略,以获得最佳的性能。
请看下面的例子,功能是使用深度强化学习方法(DDPG,Deep Deterministic Policy Gradient)来解决连续动作空间问题的示例代码。这个例子演示了一个自定义环境(CustomEnv)中的DDPG算法,该算法包括Actor-Critic网络架构和经验回放缓冲区,用于训练智能体在连续状态空间中执行动作以最大化累积奖励。
实例13-1:在自定义环境中实现DDPG算法(源码路径:daima\13\ddpg01.py)
实例文件ddpg01.py的具体实现代码如下所示:
import numpy as np
import tensorflow as tf
from collections import deque
import random
# 在需要的地方生成随机数
random_number = random.random() # 生成一个0到1之间的随机浮点数
# 环境定义
class CustomEnv:
def __init__(self):
self.state_dim = 2
self.action_dim = 1
self.max_action = 2.0
self.goal = np.array([5.0, 5.0])
self.state = None
def reset(self):
self.state = np.random.rand(self.state_dim) * 10.0 # 随机初始化状态
return self.state
def step(self, action):
self.state += action # 执行动作,更新状态
reward = -np.linalg.norm(self.state - self.goal) # 奖励是到目标的负距离
done = np.linalg.norm(self.state - self.goal) < 0.1 # 判断是否到达目标
return self.state, reward, done, {}
# Actor网络定义
class ActorNetwork(tf.keras.Model):
def __init__(self, action_dim):
super(ActorNetwork, self).__init__()
self.dense1 = tf.keras.layers.Dense(64, activation='relu')
self.dense2 = tf.keras.layers.Dense(32, activation='relu')
self.output_layer = tf.keras.layers.Dense(action_dim, activation='tanh')
def call(self, inputs):
x = self.dense1(inputs)
x = self.dense2(x)
return self.output_layer(x)
# Critic网络定义
class CriticNetwork(tf.keras.Model):
def __init__(self):
super(CriticNetwork, self).__init__()
self.dense1 = tf.keras.layers.Dense(64, activation='relu')
self.dense2 = tf.keras.layers.Dense(32, activation='relu')
self.output_layer = tf.keras.layers.Dense(1, activation=None)
def call(self, inputs, training=None, mask=None):
states, actions = inputs
x = tf.concat([states, actions], axis=-1) # 合并状态和动作
x = self.dense1(x)
x = self.dense2(x)
return self.output_layer(x)
# 噪声生成
class Noise:
def __init__(self, action_dim, mu=0, theta=0.15, sigma=0.2):
self.action_dim = action_dim
self.mu = mu
self.theta = theta
self.sigma = sigma
self.state = np.ones(self.action_dim) * self.mu
self.reset()
def reset(self):
self.state = np.ones(self.action_dim) * self.mu
def sample(self):
x = self.state
dx = self.theta * (self.mu - x) + self.sigma * np.random.randn(self.action_dim)
self.state = x + dx
return self.state
# DDPG算法
class DDPG:
def __init__(self, state_dim, action_dim, max_action):
self.state_dim = state_dim
self.action_dim = action_dim
self.max_action = max_action
self.actor = ActorNetwork(action_dim)
self.target_actor = ActorNetwork(action_dim)
self.actor_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
self.critic = CriticNetwork()
self.target_critic = CriticNetwork()
self.critic_optimizer = tf.keras.optimizers.Adam(learning_rate=0.002)
self.target_actor.set_weights(self.actor.get_weights())
self.target_critic.set_weights(self.critic.get_weights())
self.replay_buffer = deque(maxlen=1000000)
self.noise = Noise(action_dim)
def select_action(self, state):
state = np.expand_dims(state, axis=0).astype(np.float32)
action = self.actor(state)[0]
action += self.noise.sample()
return np.clip(action, -self.max_action, self.max_action)
def train(self, batch_size=64):
if len(self.replay_buffer) < batch_size:
return
minibatch = random.sample(self.replay_buffer, batch_size)
states, actions, rewards, next_states = zip(*minibatch)
states = np.vstack(states).astype(np.float32)
actions = np.vstack(actions).astype(np.float32)
rewards = np.vstack(rewards).astype(np.float32)
next_states = np.vstack(next_states).astype(np.float32)
with tf.GradientTape() as actor_tape, tf.GradientTape() as critic_tape:
# 计算Critic的损失
target_actions = self.target_actor(next_states)
target_q_values = self.target_critic([next_states, target_actions])
target_q_values = rewards + discount_factor * target_q_values
q_values = self.critic([states, actions])
critic_loss = tf.reduce_mean(tf.square(target_q_values - q_values))
# 计算Actor的梯度
actor_actions = self.actor(states)
actor_loss = -tf.reduce_mean(self.critic([states, actor_actions]))
actor_gradients = actor_tape.gradient(actor_loss, self.actor.trainable_variables)
critic_gradients = critic_tape.gradient(critic_loss, self.critic.trainable_variables)
self.actor_optimizer.apply_gradients(zip(actor_gradients, self.actor.trainable_variables))
self.critic_optimizer.apply_gradients(zip(critic_gradients, self.critic.trainable_variables))
self.soft_update_target_networks()
def soft_update_target_networks(self):
tau = 0.005
for t, e in zip(self.target_actor.trainable_variables, self.actor.trainable_variables):
t.assign(t * (1 - tau) + e * tau)
for t, e in zip(self.target_critic.trainable_variables, self.critic.trainable_variables):
t.assign(t * (1 - tau) + e * tau)
# 参数设置
num_episodes = 1000
max_steps_per_episode = 500
discount_factor = 0.99
batch_size = 64
# 创建环境
env = CustomEnv()
state_dim = env.state_dim
action_dim = env.action_dim
max_action = env.max_action
# 创建DDPG代理
agent = DDPG(state_dim, action_dim, max_action)
# 训练循环
for episode in range(num_episodes):
state = env.reset()
episode_reward = 0
for step in range(max_steps_per_episode):
action = agent.select_action(state)
next_state, reward, done, _ = env.step(action)
agent.replay_buffer.append((state, action, reward, next_state))
agent.train(batch_size)
state = next_state
episode_reward += reward
if done:
break
print(f"Episode: {episode + 1}, Reward: {episode_reward}")
# 训练完成后,您可以使用训练好的DDPG代理来在环境中执行动作并测试其性能。
上述代码的实现流程如下:
- 首先,导入所需的库,包括NumPy、TensorFlow和collections。
- 定义一个随机数生成器来生成随机数。
- 创建自定义环境(CustomEnv),其中包括状态空间、动作空间、状态初始化、动作执行、奖励函数等。
- 定义Actor网络和Critic网络,它们分别用于近似策略和值函数。
- 创建噪声生成器(Noise),用于在策略网络中引入噪声以促进探索。
- 创建DDPG代理,该代理包括Actor网络、Critic网络、目标网络、优化器以及经验回放缓冲区。
- 实现了选择动作的方法(select_action)和训练方法(train),其中使用了目标网络和经验回放来训练Actor和Critic网络。
- 定义训练循环,代理与环境互动,收集经验并进行训练。
- 最后,打印输出每个回合的奖励:
Episode: 1, Reward: -163947.52511222256
Episode: 2, Reward: -119372.47314215968
Episode: 3, Reward: -10607.124598505221
Episode: 4, Reward: -16607.03150308046
Episode: 5, Reward: -22438.776618015836
Episode: 6, Reward: -23654.293122714527
Episode: 7, Reward: -29209.026893293543
Episode: 8, Reward: -51482.17951051141
……
上述代码可以用于解决连续动作空间问题,代理将学会在给定环境下选择最佳的动作以最大化累积奖励。本实例包括环境创建、DDPG代理的构建和训练,以及训练完成后的性能测试。
13.2.4 DDPG算法中的经验回放与探索策略
在DDPG算法中,经验回放和探索策略是两个关键的组成部分,它们共同帮助解决了连续动作空间中的探索问题。
1. 经验回放
经验回放是一种存储代理与环境互动的经验样本并随机采样这些样本的方法。这个经验缓冲区的主要目的是减少样本的相关性,提高样本的利用效率,并增加训练的稳定性。在DDPG中,经验回放的操作如下:
- 代理与环境互动,生成经验元组(状态,动作,奖励,下一个状态)。
- 将这些经验元组存储在经验回放缓冲区中。
- 在训练时,从经验回放缓冲区中随机采样一批样本,用于更新策略和值函数网络。
经验回放的优势如下所示:
- 提高训练的稳定性,减少样本间的相关性,有助于防止训练中的不稳定性。
- 可以重复使用以前的经验,使得代理可以从旧的经验中学到更多信息,而不仅仅依赖于最新的样本。
- 允许随机性地选择样本,这有助于探索更广泛的状态空间。
2. 探索策略
在DDPG中,探索策略通常是通过向策略网络输出的动作中添加噪声来实现的。噪声可以是高斯噪声或其他类型的噪声,它们增加了动作的随机性。添加噪声的主要目的是促进探索,使代理能够尝试新的动作,而不仅仅是根据当前的最佳动作来行动。
添加噪声的方法通常包括:
- 确定性策略中的高斯噪声:对策略网络输出的动作值添加高斯噪声,例如,通过从高斯分布中采样噪声并将其加到动作中,可以使代理在探索中更具随机性。
- 参数化噪声策略:使用参数化的噪声策略,例如使用确定性策略网络预测噪声参数,并根据这些参数生成动作噪声。
控制噪声的强度和随时间的衰减是调整探索策略的重要因素,适度的探索策略可以平衡探索和利用,可以帮助代理学习到更好的策略。例如下面是一个使用Python演示DDPG算法中的经验回放和探索策略的用法的例子。在这个示例中,将创建一个简化的连续环境,然后使用DDPG算法来训练智能体。
实例13-2:使用DDPG算法训练智能体(源码路径:daima\13\jing.py)
实例文件jing.py的具体实现代码如下所示:
# 创建简化的自定义环境
class CustomEnv:
def __init__(self):
self.state_dim = 2
self.action_dim = 1
self.max_action = 2.0
self.goal = np.array([5.0, 5.0])
self.state = None
def reset(self):
self.state = np.random.rand(self.state_dim) * 10.0 # 随机初始化状态
return self.state
def step(self, action):
self.state += action # 执行动作,更新状态
reward = -np.linalg.norm(self.state - self.goal) # 奖励是到目标的负距离
done = np.linalg.norm(self.state - self.goal) < 0.1 # 判断是否到达目标
return self.state, reward, done, {}
# 定义经验回放缓冲区
class ReplayBuffer:
def __init__(self, buffer_size):
self.buffer = deque(maxlen=buffer_size)
def append(self, experience):
self.buffer.append(experience)
def sample(self, batch_size):
batch = random.sample(self.buffer, batch_size)
states, actions, rewards, next_states = zip(*batch)
return np.vstack(states), np.vstack(actions), np.vstack(rewards), np.vstack(next_states)
# 定义Actor网络
class ActorNetwork(tf.keras.Model):
def __init__(self, action_dim):
super(ActorNetwork, self).__init__()
self.dense1 = tf.keras.layers.Dense(64, activation='relu')
self.dense2 = tf.keras.layers.Dense(32, activation='relu')
self.output_layer = tf.keras.layers.Dense(action_dim, activation='tanh')
def call(self, inputs):
x = self.dense1(inputs)
x = self.dense2(x)
return self.output_layer(x)
# 定义Critic网络
class CriticNetwork(tf.keras.Model):
def __init__(self):
super(CriticNetwork, self).__init__()
self.dense1 = tf.keras.layers.Dense(64, activation='relu')
self.dense2 = tf.keras.layers.Dense(32, activation='relu')
self.output_layer = tf.keras.layers.Dense(1, activation=None)
def call(self, inputs):
states, actions = inputs
x = tf.concat([states, actions], axis=-1)
x = self.dense1(x)
x = self.dense2(x)
return self.output_layer(x)
# 定义噪声生成器
class Noise:
def __init__(self, action_dim, mu=0, theta=0.15, sigma=0.2):
self.action_dim = action_dim
self.mu = mu
self.theta = theta
self.sigma = sigma
self.state = np.ones(self.action_dim) * self.mu
self.reset()
def reset(self):
self.state = np.ones(self.action_dim) * self.mu
def sample(self):
x = self.state
dx = self.theta * (self.mu - x) + self.sigma * np.random.randn(self.action_dim)
self.state = x + dx
return self.state
# 定义DDPG算法
class DDPG:
def __init__(self, state_dim, action_dim, max_action, buffer_size=10000):
self.state_dim = state_dim
self.action_dim = action_dim
self.max_action = max_action
self.actor = ActorNetwork(action_dim)
self.target_actor = ActorNetwork(action_dim)
self.actor_optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
self.critic = CriticNetwork()
self.target_critic = CriticNetwork()
self.critic_optimizer = tf.keras.optimizers.Adam(learning_rate=0.002)
self.target_actor.set_weights(self.actor.get_weights())
self.target_critic.set_weights(self.critic.get_weights())
self.replay_buffer = ReplayBuffer(buffer_size)
self.noise = Noise(action_dim)
def select_action(self, state):
state = np.expand_dims(state, axis=0).astype(np.float32)
action = self.actor(state)[0]
action += self.noise.sample()
return np.clip(action, -self.max_action, self.max_action)
def train(self, batch_size=64):
if len(self.replay_buffer.buffer) < batch_size:
return
states, actions, rewards, next_states = self.replay_buffer.sample(batch_size)
with tf.GradientTape() as actor_tape, tf.GradientTape() as critic_tape:
# 计算Critic的损失
target_actions = self.target_actor(next_states)
target_q_values = self.target_critic([next_states, target_actions])
target_q_values = rewards + 0.99 * target_q_values
q_values = self.critic([states, actions])
critic_loss = tf.reduce_mean(tf.square(target_q_values - q_values))
# 计算Actor的梯度
actor_actions = self.actor(states)
actor_loss = -tf.reduce_mean(self.critic([states, actor_actions]))
actor_gradients = actor_tape.gradient(actor_loss, self.actor.trainable_variables)
critic_gradients = critic_tape.gradient(critic_loss, self.critic.trainable_variables)
self.actor_optimizer.apply_gradients(zip(actor_gradients, self.actor.trainable_variables))
self.critic_optimizer.apply_gradients(zip(critic_gradients, self.critic.trainable_variables))
self.soft_update_target_networks()
def soft_update_target_networks(self):
tau = 0.005
for t, e in zip(self.target_actor.trainable_variables, self.actor.trainable_variables):
t.assign(t * (1 - tau) + e * tau)
for t, e in zip(self.target_critic.trainable_variables, self.critic.trainable_variables):
t.assign(t * (1 - tau) + e * tau)
# 参数设置
num_episodes = 1000
max_steps_per_episode = 500
batch_size = 64
# 创建环境
env = CustomEnv()
state_dim = env.state_dim
action_dim = env.action_dim
max_action = env.max_action
# 创建DDPG代理
agent = DDPG(state_dim, action_dim, max_action)
# 训练循环
for episode in range(num_episodes):
state = env.reset()
episode_reward = 0
for step in range(max_steps_per_episode):
action = agent.select_action(state)
next_state, reward, done, _ = env.step(action)
agent.replay_buffer.append((state, action, reward, next_state))
agent.train(batch_size)
state = next_state
episode_reward += reward
if done:
break
print(f"Episode: {episode + 1}, Reward: {episode_reward}")
在上述代码创中建了一个简单的自定义环境,并在其中应用了DDPG算法。它演示了如何使用经验回放缓冲区来存储和采样经验,以及如何使用噪声策略来促进探索。在训练循环中,每个回合的奖励将被打印出来,以监测DDPG算法的性能。这个示例可以帮助您理解DDPG算法中经验回放和探索策略的实际应用。
综合来说,DDPG通过经验回放缓冲区和探索策略来有效地解决连续动作空间中的探索问题。经验回放提高了样本的利用效率和训练的稳定性,而探索策略引入了随机性,鼓励代理进行探索并学习到最优策略。这两个组成部分协同工作,使DDPG在复杂的连续动作控制问题中表现出色。