DDPG算法求解月球着陆游戏
DDPG算法,即Deep Deterministic Policy Gradient算法,是一种专门用于解决连续控制问题的深度强化学习算法。DDPG算法是由DeepMind团队提出的,用于处理连续动作空间的强化学习问题。它结合了确定性策略梯度方法和深度神经网络,旨在学习连续动作空间中的高性能策略。
核心特点:
- 确定性策略:与传统的策略梯度方法不同,DDPG使用确定性策略,直接输出动作值而不是动作的概率分布。这有助于在连续动作空间中更好地学习策略。
- Actor-Critic架构:DDPG算法基于Actor-Critic框架,其中Actor负责学习确定性策略,Critic负责学习值函数,评估当前状态的价值。
- 经验回放:为了解决样本相关性和稳定性问题,DDPG引入了经验回放机制,将Agent与环境交互得到的经验存储在经验回放缓冲区中,然后从中随机采样进行训练。
- 目标网络:为了稳定训练,DDPG使用目标网络来估计目标Q值和目标策略。目标网络的参数是通过软更新的方式从主网络的参数逐渐更新得到的。
- 噪声探索:由于确定性策略输出的动作为确定性动作,缺乏对环境的探索。在训练阶段,给Actor网络输出的动作加入噪声,以便进行更好的探索。
注意:本文用到了PyTorch库,gym强化学习环境库,需要提前安装。
- gym环境安装:https://github.com/Farama-Foundation/Gymnasium
- gym环境介绍文档:https://gymnasium.farama.org/environments/classic_control/mountain_car/
- pytorch官网:https://pytorch.org/
本文所使用的python版本为3.11.8
step1:月球着陆(Lunar Lander)游戏介绍
官方地址:https://gymnasium.farama.org/environments/box2d/lunar_lander/
Lunar Lander是GYM库中的一个经典环境,它模拟了一个航天器在月球表面着陆的场景。在这个环境中,智能体的目标是控制航天器安全且准确地着陆在指定的着陆点上。
该环境是Box2D环境的一部分:
属性 | 说明 |
---|---|
环境名称 | LunarLander-v2 |
导入语句 | gymnasium.make(“LunarLander-v2”) |
动作空间 | Discrete(4) |
观测空间 | Box([-1.5, -1.5, -5., -5., -3.1415927, -5., -0., -0.], [1.5, 1.5, 5., 5., 3.1415927, 5., 1., 1.], (8,), float32) |
import gymnasium as gym # 导入gym包
env = gym.make("LunarLander-v3") # 创建LunarLander游戏环境
observation, info = env.reset() # 初始化环境
for _ in range(1):
action = env.action_space.sample() # 随机选择一个动作
observation, reward, terminated, truncated, info = env.step(action) # 执行动作
if terminated or truncated: # 当达到终点时,重置环境
observation, info = env.reset()
env.close() # 关闭环境
action, observation
(0,
array([-0.00377369, 1.4026645 , -0.1908573 , -0.19629022, 0.0043321 ,
0.04278683, 0. , 0. ], dtype=float32))
动作空间有四个可用的离散动作:
- 0:不做任何事情
- 1:开启左舵发动机
- 2:开启主发动机
- 3:开启右舵发动机
观测空间状态是一个8维向量:
分别为着陆器在x和y方向上的坐标、x和y方向上的线速度、其角度、角速度,以及两个布尔值,分别代表每条腿是否与地面接触。
奖励:每一步都会获得奖励。一个回合的总奖励是该回合内所有步骤的奖励之和。
对于每一步,奖励:
- 着陆器离着陆台越近/越远,奖励增加/减少。
- 着陆器移动得越慢/越快,奖励增加/减少。
- 着陆器倾斜越多(角度不水平),奖励减少。
- 每条与地面接触的腿增加10分。
- 侧发动机每帧发射时减少0.03分。
- 主发动机每帧发射时减少0.3分。
- 如果发生撞机或安全着陆,该回合将分别获得额外的-100或+100分奖励。
如果一个回合的得分至少为200分,则该回合被视为解决方案。
初始状态:
着陆器从视口的顶部中心开始,其质心受到随机的初始力。
回合终止,回合在以下情况下结束:
- 着陆器坠毁(着陆器机体与月球接触);
- 着陆器移出视口(x坐标大于1);
- 着陆器未处于活动状态。
step2:DDPG算法的搭建
DDPG算法结构如下图所示:
DDPG(Deep Deterministic Policy Gradient)算法是一种用于连续动作空间的强化学习算法,它结合了DQN(Deep Q-Network)和Actor-Critic方法的优点,能够处理连续的动作输出。DDPG算法包含两个主要网络:Actor网络和Critic网络,每个网络又分别有一个当前网络和一个目标网络。Actor网络负责根据当前状态输出动作,而Critic网络则评估采取的动作给出奖励预估值。目标网络用于稳定学习过程,其参数定期从当前网络复制而来。
假定Actor网络可表示为$a = \mu_\theta (s)
,其中
, 其中
,其中s
是状态,
是状态,
是状态,\theta
是网络参数,
是网络参数,
是网络参数,\mu_\theta (s)
是确定性策略。
C
r
i
t
i
c
网络可表示为
是确定性策略。Critic网络可表示为
是确定性策略。Critic网络可表示为Q(s,a) = Q_\omega (s,a)
,其中
,其中
,其中\omega
是网络参数,
是网络参数,
是网络参数,Q_\omega (s,a)$是Q值函数。则Actor网络和Critic网络的更新公式可表示为:
ω
t
+
1
=
ω
t
−
α
∇
ω
[
Q
ω
(
s
t
,
a
t
)
−
(
r
t
+
γ
Q
ω
(
s
t
+
1
,
μ
θ
(
s
t
+
1
)
)
)
]
2
\omega_{t+1} = \omega_t - \alpha \nabla_\omega [Q_\omega (s_t, a_t) - (r_t + \gamma Q_\omega (s_{t+1}, \mu_\theta (s_{t+1})))]^2
ωt+1=ωt−α∇ω[Qω(st,at)−(rt+γQω(st+1,μθ(st+1)))]2
θ
t
+
1
=
θ
t
+
α
∇
a
[
Q
ω
(
s
t
,
μ
θ
(
s
t
)
)
]
∇
θ
μ
θ
(
s
t
)
\theta_{t+1} = \theta_t + \alpha \nabla_a [Q_\omega (s_t, \mu_\theta (s_t))]\nabla_\theta \mu_\theta (s_t)
θt+1=θt+α∇a[Qω(st,μθ(st))]∇θμθ(st)
其中
α
\alpha
α是学习率。
为什么要这样更新呢?对于Critic网络,其更新原理是时序差分(Temporal Difference,TD)算法,详见:https://hrl.boyuai.com/chapter/1/%E6%97%B6%E5%BA%8F%E5%B7%AE%E5%88%86%E7%AE%97%E6%B3%95 , 时序差分算法中,对于SARSA过程,有公式:
Q
(
s
t
,
a
t
)
=
r
t
+
γ
Q
(
s
t
+
1
,
a
t
+
1
)
Q(s_t,a_t) = r_t + \gamma Q(s_{t+1},a_{t+1})
Q(st,at)=rt+γQ(st+1,at+1)
则对于Critic网络,由于在
s
s
s状态下,由Actor输出的动作
a
a
a是确定的,因此对于Critic网络,其输出需满足:
Q
(
s
t
,
a
t
)
=
r
t
+
γ
Q
(
s
t
+
1
,
μ
θ
(
s
t
+
1
)
)
Q(s_t,a_t) = r_t + \gamma Q(s_{t+1},\mu_\theta (s_{t+1}))
Q(st,at)=rt+γQ(st+1,μθ(st+1))
进而可得到Critic网络的更新公式。
对于Actor网络,其更新原理是策略梯度(Policy Gradient)算法,详见:https://hrl.boyuai.com/chapter/2/%E7%AD%96%E7%95%A5%E6%A2%AF%E5%BA%A6%E7%AE%97%E6%B3%95 。有公式:
∇
θ
J
(
π
θ
)
=
E
s
∼
ν
π
β
[
∇
θ
μ
θ
(
s
)
∇
a
Q
ω
μ
(
s
,
a
)
∣
a
=
μ
θ
(
s
)
]
\nabla_\theta J\left(\pi_\theta\right)=\mathbb{E}_{s \sim \nu^{\pi_\beta}}\left[\left.\nabla_\theta \mu_\theta(s) \nabla_a Q_\omega^\mu(s, a)\right|_{a=\mu_\theta(s)}\right]
∇θJ(πθ)=Es∼νπβ[∇θμθ(s)∇aQωμ(s,a)∣a=μθ(s)]
其中, π \pi π 是用来收集数据的行为策略。我们可以这样理解这个定理:假设现在已经有函数 Q ( s , a ) Q(s, a) Q(s,a),给定一个状态 s s s,但由于现在动作空间是无限的,无法通过遍历所有动作 a a a 来得到值最大的动作,因此我们想用策略 π \pi π 找到使 Q ( s , a ) Q(s, a) Q(s,a) 值最大的动作 a a a,即 max a Q ( s , a ) \max_a Q(s, a) maxaQ(s,a)。此时, Q ( s , a ) Q(s, a) Q(s,a) 就是 Critic, π \pi π 就是 Actor,这是一个 Actor-Critic 的框架。那如何得到这个 max a Q ( s , a ) \max_a Q(s, a) maxaQ(s,a) 呢?首先用 π \pi π 对 Q ( s , a ) Q(s, a) Q(s,a) 求导,其中会用到梯度的链式法则,先对 a a a 求导,再对 π \pi π 求导。然后通过梯度上升的方法来最大化 Q ( s , a ) Q(s, a) Q(s,a) 函数,得到值最大的动作 a a a。
DDPG算法实现的一些细节:
DDPG 要用到4个神经网络,其中 Actor 和 Critic 各用一个网络,此外它们都各自有一个目标网络;
在深度确定性策略梯度(Deep Deterministic Policy Gradient,DDPG)算法中,为了稳定学习过程并减少学习过程中的震荡,目标网络(target network)的更新并非直接复制当前网络(也称为行为网络或在线网络)的参数,而是采用了一种被称为软更新(soft update)的策略。这种策略的核心思想是使目标网络的参数缓慢而平滑地向当前网络的参数靠近,从而在保证学习稳定性的同时,也使得策略能够持续学习和改进。具体地,软更新的公式如下:
θ
target
=
τ
θ
online
+
(
1
−
τ
)
θ
target
\theta_{\text{target}} = \tau \theta_{\text{online}} + (1 - \tau)\theta_{\text{target}}
θtarget=τθonline+(1−τ)θtarget
其中,
θ
target
\theta_{\text{target}}
θtarget 代表目标网络的参数,
θ
online
\theta_{\text{online}}
θonline 代表当前网络(在线网络)的参数,
τ
\tau
τ 是一个远小于1的正数(通常取值很小,如0.001),它决定了目标网络参数更新的速度。通过这种更新方式,目标网络的参数会缓慢地向在线网络的参数靠拢,从而实现了软更新的效果。这种更新机制有助于减少学习过程中的不稳定性,使策略学习更加平滑和稳健。
由于函数存在值过高估计的问题,DDPG(深度确定性策略梯度)采用了 Double DQN(双深度Q网络)中的双网络估计技术来更新网络,以提高值函数估计的准确性。然而,尽管这种技术有助于解决值过高估计的问题,但由于DDPG采用的是确定性策略,其本身的探索能力仍然受限。在强化学习中,探索与利用之间的平衡是至关重要的。在DQN(深度Q网络)算法中,这种平衡主要通过ε-贪婪策略来实现,即在大多数情况下选择具有最高Q值的动作(利用),但有时也随机选择一个动作(探索)。通过这种方式,DQN能够在探索新状态和动作的同时,也有效地利用已学到的知识。作为一种离线策略的算法,DDPG同样需要解决探索与利用的平衡问题。然而,由于其使用的是确定性策略,即对于给定的状态,策略总是输出相同的动作,这使得探索变得困难。为了增强DDPG的探索能力,算法在行为策略上引入了一个随机噪声。具体来说,在执行动作时,DDPG会将一个随机生成的噪声添加到确定性策略输出的动作上,从而产生新的、可能是之前未探索过的动作。数学上,我们可以这样表示DDPG的探索策略:
a
=
μ
(
s
;
θ
)
+
N
a = \mu(s; \theta) + \mathcal{N}
a=μ(s;θ)+N
其中,
a
a
a 是实际执行的动作,
μ
(
s
;
θ
)
\mu(s; \theta)
μ(s;θ) 是确定性策略网络输出的动作,
N
\mathcal{N}
N 是添加的随机噪声,通常可以选择为正态分布或其他合适的分布。通过这种方式,DDPG能够在保持确定性策略优点的同时,增强探索能力,更好地适应复杂和动态的环境。
深度确定性策略梯度(DDPG)算法流程
-
初始化阶段
- 随机噪声可以用 N \mathcal{N} N 来表示。
- 用随机的网络参数 w w w 和 θ \theta θ 分别初始化 Critic 网络 Q w ( s , a ) Q_w(s, a) Qw(s,a) 和 Actor 网络 μ θ ( s ) \mu_\theta(s) μθ(s)。
- 复制相同的参数 w ′ ← w w' \leftarrow w w′←w 和 θ ′ ← θ \theta' \leftarrow \theta θ′←θ,分别初始化目标网络 Q w ′ Q_{w'} Qw′ 和 μ θ ′ \mu_{\theta'} μθ′。
- 初始化经验回放池 R \mathcal{R} R。
-
主循环
对于序列 e = 1 → E e = 1 \rightarrow E e=1→E 执行以下步骤:
- 初始化随机过程 N \mathcal{N} N 用于动作探索。
- 获取环境初始状态 s 1 s_1 s1。
对于时间步 t = 1 → T t = 1 \rightarrow T t=1→T 执行以下步骤:
- 根据当前策略和噪声选择动作 a t = μ θ ( s t ) + N a_t = \mu_\theta(s_t) + \mathcal{N} at=μθ(st)+N。
- 执行动作 a t a_t at,获得奖励 r t r_t rt,环境状态变为 s t + 1 s_{t+1} st+1。
- 将 ( s t , a t , r t , s t + 1 ) (s_t, a_t, r_t, s_{t+1}) (st,at,rt,st+1) 存储进回放池 R \mathcal{R} R。
- 从 R \mathcal{R} R 中采样 N N N 个元组 { ( s i , a i , r i , s i + 1 ) } i = 1 N \{(s_i, a_i, r_i, s_{i+1})\}_{i=1}^N {(si,ai,ri,si+1)}i=1N。
- 对每个元组,用目标网络计算 y i = r i + γ Q w ′ ( s i + 1 , μ θ ′ ( s i + 1 ) ) y_i = r_i + \gamma Q_{w'}(s_{i+1}, \mu_{\theta'}(s_{i+1})) yi=ri+γQw′(si+1,μθ′(si+1))。
- 最小化目标损失 L = 1 N ∑ i = 1 N ( y i − Q w ( s i , a i ) ) 2 L = \frac{1}{N} \sum_{i=1}^{N} (y_i - Q_w(s_i, a_i))^2 L=N1∑i=1N(yi−Qw(si,ai))2,以此更新当前 Critic 网络。
- 计算采样的策略梯度,以此更新当前 Actor 网络。
- 更新目标网络:
w ′ ← τ w + ( 1 − τ ) w ′ w' \leftarrow \tau w + (1 - \tau) w' w′←τw+(1−τ)w′
θ ′ ← τ θ + ( 1 − τ ) θ ′ \theta' \leftarrow \tau \theta + (1 - \tau) \theta' θ′←τθ+(1−τ)θ′
其中, τ \tau τ 是一个远小于 1 的正数。
# 初始化网络
import torch
import torch.nn as nn
import torch.nn.functional as F
class Actor(nn.Module):
def __init__(self, state_dim, action_dim):
'''
动作网络u(s):
'''
super(Actor, self).__init__()
self.l1 = nn.Linear(state_dim, 400)
self.l2 = nn.Linear(400, 300)
self.l3 = nn.Linear(300, action_dim)
self.softmax = nn.Softmax(dim=1)
def forward(self, state):
a = F.relu(self.l1(state))
a = F.relu(self.l2(a))
return self.softmax(self.l3(a)) # 此处是由于动作空间是一个离散值,所以使用softmax函数
class Critic(nn.Module):
def __init__(self, state_dim, action_dim):
'''
价值网络Q(s,a):
'''
super(Critic, self).__init__()
self.l1 = nn.Linear(state_dim + action_dim, 400)
self.l2 = nn.Linear(400, 300)
self.l3 = nn.Linear(300, 1)
def forward(self, state, action):
q = F.relu(self.l1(torch.cat([state, action], 1)))
q = F.relu(self.l2(q))
return self.l3(q)
接下来,让我们设计一个DDPG算法,它将使用上述定义的Actor和Critic网络。DDPG算法的主要步骤包括:初始化网络、选择动作、执行动作、存储经验、更新网络和目标网络。我们将使用PyTorch来实现这些步骤。
import numpy as np
import random
# 设计一个队列结构,用于存储经验回放池中的经验
class Queue_ReplayBuffer:
def __init__(self):
self.queue = []
def enqueue(self, item):
self.queue.append(item)
def dequeue(self):
if self.queue:
self.queue.pop(0)
def random_sample(self, k):
if k <= len(self.queue):
return random.sample(self.queue, k)
else:
raise ValueError("Sample size k is larger than the queue size.")
def size(self):
return len(self.queue)
class DDPG:
def __init__(self, gamma, state_dim, action_dim, device):
self.device = device # 定义训练设备,可以是CPU或GPU
self.actor = Actor(state_dim, action_dim).to(device) # 初始化Actor网络
self.actor_target = Actor(state_dim, action_dim).to(device) # 初始化Actor目标网络
self.actor_target.load_state_dict(self.actor.state_dict()) # 加载Actor网络的参数到Actor目标网络
self.actor_optimizer = torch.optim.Adam(self.actor.parameters()) # 定义Actor网络的优化器
self.critic = Critic(state_dim, action_dim).to(device) # 初始化Critic网络
self.critic_target = Critic(state_dim, action_dim).to(device) # 初始化Critic目标网络
self.critic_target.load_state_dict(self.critic.state_dict()) # 加载Critic网络的参数到Critic目标网络
self.critic_optimizer = torch.optim.Adam(self.critic.parameters()) # 定义Critic网络的优化器
self.replay_buffer = Queue_ReplayBuffer() # 初始化经验回放池
self.gamma = gamma # 定义折扣因子
def take_action(self, state):
'''
根据当前状态选择确定性动作
'''
state = torch.FloatTensor(state.reshape(1, -1)).to(self.device)
return self.actor(state)
def take_noise_action(self, state, noise_scale=0.1):
'''
根据当前状态,添加随机噪声,选择动作
'''
state = torch.FloatTensor(state.reshape(1, -1)).to(self.device)
return self.actor(state) + torch.randn_like(self.actor(state)) * noise_scale
def take_random_action(self, action_dim):
'''
随机选择动作
'''
return np.random.randint(action_dim)
def update(self, batch_size=100, tau=0.005):
'''
更新网络参数
'''
if self.replay_buffer.size() < batch_size:
batch = self.replay_buffer.random_sample(self.replay_buffer.size()) # 从经验回放池中随机采样
else:
batch = self.replay_buffer.random_sample(batch_size)
state_batch, action_batch, reward_batch, next_state_batch, done_batch = zip(*batch) # 解压batch数据
state_batch = torch.FloatTensor(state_batch).to(self.device)
action_batch = torch.FloatTensor(action_batch).to(self.device)
reward_batch = torch.FloatTensor(reward_batch).to(self.device)
next_state_batch = torch.FloatTensor(next_state_batch).to(self.device)
done_batch = torch.FloatTensor(np.float32(done_batch)).to(self.device)
# 更新Critic网络
next_action_batch = self.actor_target(next_state_batch)
target_q = reward_batch + (1 - done_batch) * self.gamma * self.critic_target(next_state_batch, next_action_batch)
current_q = self.critic(state_batch, action_batch)
critic_loss = F.mse_loss(current_q, target_q.detach())
self.critic_optimizer.zero_grad()
critic_loss.backward()
self.critic_optimizer.step()
# 更新Actor网络
actor_loss = -self.critic(state_batch, self.actor(state_batch)).mean()
self.actor_optimizer.zero_grad()
actor_loss.backward()
self.actor_optimizer.step()
# 更新目标网络
for param, target_param in zip(self.critic.parameters(), self.critic_target.parameters()):
target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data)
for param, target_param in zip(self.actor.parameters(), self.actor_target.parameters()):
target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data)
接下来,让我们设计一个训练流程,进行智能体训练:
import numpy as np
from tqdm import tqdm
num_episodes = 30000 # 总的训练轮数
hidden_dim = 32 # 隐含层数量
gamma = 0.99 # 折扣因子
env = gym.make("LunarLander-v3")
state_dim = 8 # 状态维度
action_dim = env.action_space.n # 动作维度
MAXSIZE_replaybuffer = 500 # 经验回放池的最大容量
thold_random = 0.2 # 随机动作的比例阈值
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
agent = DDPG(gamma, state_dim, action_dim, device)
for i in range(10):
with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
return_list = [] # 记录每一轮的回报
for i_episode in range(int(num_episodes / 10)):
episode_return = 0
state, info = env.reset() # 初始化环境
done = False
while not done:
if np.random.rand() < thold_random: # 随机动作
action = agent.take_random_action(action_dim)
else:
action = agent.take_action(state) # 选择动作
# 选择最大的动作值下标
if type(action) == torch.Tensor:
action_index = np.argmax(action.cpu().detach().numpy())
action = action.cpu().view(-1).detach().numpy() # 将动作值转换为numpy数组
else:
action_index = action
action = np.zeros(action_dim)
action[action_index] = 1. # 将动作值转换为one-hot编码
next_state, reward, terminated, truncated, info = env.step(action_index) # 执行动作
done = terminated or truncated # 判断是否结束
# 选择性的将经验存入回放池
if agent.replay_buffer.size() < MAXSIZE_replaybuffer:
agent.replay_buffer.enqueue((state, action, reward, next_state, done))
else:
agent.replay_buffer.dequeue()
agent.replay_buffer.enqueue((state, action, reward, next_state, done))
state = next_state
episode_return += reward
return_list.append(episode_return)
agent.update(batch_size=32, tau=0.005) # 更新网络参数
if (i_episode + 1) % 10 == 0:
pbar.set_postfix({
'episode':
'%d' % (num_episodes / 10 * i + i_episode + 1),
'return':
'%.3f' % np.mean(return_list)
})
pbar.update(1)
print('all mean return: %.3f' % np.mean(return_list))
Iteration 0: 0%| | 0/3000 [00:00<?, ?it/s]/tmp/ipykernel_21179/4096501834.py:82: UserWarning: Using a target size (torch.Size([32, 32])) that is different to the input size (torch.Size([32, 1])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.
critic_loss = F.mse_loss(current_q, target_q.detach())
Iteration 0: 100%|██████████| 3000/3000 [02:44<00:00, 18.27it/s, episode=3000, return=-202.343]
all mean return: -202.343
Iteration 1: 100%|██████████| 3000/3000 [02:46<00:00, 18.04it/s, episode=6000, return=-480.301]
all mean return: -480.301
Iteration 2: 100%|██████████| 3000/3000 [03:33<00:00, 14.02it/s, episode=9000, return=-686.308]
all mean return: -686.308
Iteration 3: 100%|██████████| 3000/3000 [03:54<00:00, 12.80it/s, episode=12000, return=-292.234]
all mean return: -292.234
Iteration 4: 100%|██████████| 3000/3000 [03:10<00:00, 15.77it/s, episode=15000, return=-98.950]
all mean return: -98.950
Iteration 5: 100%|██████████| 3000/3000 [02:46<00:00, 18.05it/s, episode=18000, return=-82.063]
all mean return: -82.063
Iteration 6: 100%|██████████| 3000/3000 [02:38<00:00, 18.90it/s, episode=21000, return=-60.661]
all mean return: -60.661
Iteration 7: 100%|██████████| 3000/3000 [03:15<00:00, 15.36it/s, episode=24000, return=-76.051]
all mean return: -76.051
Iteration 8: 100%|██████████| 3000/3000 [04:36<00:00, 10.87it/s, episode=27000, return=-88.352]
all mean return: -88.352
Iteration 9: 100%|██████████| 3000/3000 [07:26<00:00, 6.71it/s, episode=30000, return=-61.495]all mean return: -61.495
可以看到智能体能够收敛,让我们看看智能体在游戏上的表现吧!
import matplotlib.pyplot as plt
%matplotlib inline
from IPython import display
import time
def show_state(env, step=0, info=""):
plt.figure(3)
plt.clf()
plt.imshow(env.render())
plt.axis('off')
display.clear_output(wait=True)
display.display(plt.gcf())
env = gym.make('LunarLander-v3', render_mode='rgb_array')
state, info = env.reset()
for _ in range(1000):
action = agent.take_action(state)
action_index = np.argmax(action.cpu().detach().numpy())
state, reward, terminated, truncated, info = env.step(action_index)
done = truncated or terminated
show_state(env, action, info)
time.sleep(0.05)
if done:
state, info = env.reset()
env.close()
可以看到在共30000轮的强化学习训练后,智能体虽然能够控制飞船在月球表面着陆,但着陆点并不精确,并且飞船在着陆过程中可能会出现翻滚。这表明智能体在训练过程中可能没有完全掌握着陆的技巧,或者训练的轮数还不够多。读者可以尝试以下技术手段,以进一步提高智能体的表现:
- 增加训练轮数:训练轮数越多,智能体能够获得的经验就越多,从而能够更好地掌握着陆的技巧。
- 调整超参数:超参数包括学习率、折扣因子、探索率等,这些参数的调整可以影响智能体的学习效果。读者可以尝试不同的超参数组合,以找到最优的参数设置。
- 使用更复杂的网络结构:智能体的网络结构可以影响其学习效果。读者可以尝试使用更复杂的网络结构,如循环神经网络(RNN)等,以更好地适应环境。
- 使用更高级、更复杂的强化学习算法:除了DDPG算法外,还有许多其他高级的强化学习算法,如PPO、SAC等。这些算法在处理连续动作空间和连续状态空间方面具有更好的性能。读者可以尝试使用并改进算法,以进一步提高智能体的表现。