在 DDPG(Deep Deterministic Policy Gradient)中加入 ε-greedy 探索 也是一种增加智能体探索性的策略,尽管 ε-greedy 策略通常更适用于离散动作空间。然而,在 DDPG 的连续动作空间中,也可以通过 ε-greedy 策略实现探索。以下是如何将 ε-greedy 应用于 DDPG 的方法及其原因。
ε-greedy
1. 什么是 ε-greedy 探索策略?
ε − g r e e d y ε-greedy ε−greedy 策略是一种简单而有效的探索方法,定义如下:
- 在每个时间步中,以 ε 的概率进行随机探索,即选择一个随机动作;
- 以 (1 - ε) 的概率选择当前策略网络输出的最优动作,即 Actor 网络的确定性输出。
在离散动作空间中,ε-greedy 的随机探索通常是从离散动作集中随机选择一个动作。而在连续动作空间中,我们可以稍作修改,使 ε-greedy 策略与 DDPG 的确定性策略结合。
2. 在 DDPG 中实现 ε-greedy 探索
在 DDPG 中,可以通过以下方式引入 ε-greedy 策略:
1 ) 随机选择一个动作:
- 以 ε 的概率选择一个完全随机的动作,从动作空间中均匀采样。
2 ) 选择当前策略输出的最优动作:
- 以 (1 - ε) 的概率选择 Actor 网络输出的确定性动作(最优动作)。
在连续动作空间中,具体实现如下:
import numpy as np
def select_action(state, actor, action_dim, epsilon):
# 以 epsilon 的概率进行随机探索
if np.random.rand() < epsilon:
# 随机生成一个动作
action = np.random.uniform(low=-1.0, high=1.0, size=action_dim)
else:
# 否则选择当前策略的最优动作
action = actor(state).detach().cpu().numpy()
return action
3. ε 的值及其调整
- 初始值设置:在训练开始时,可以将 ε 设置得较高(如 0.1 至 0.3),这样可以让智能体在初期进行更多的探索。
- 逐步减少 ε:随着训练的进行,逐渐减小 ε 的值,使得智能体逐渐从探索过渡到利用。
epsilon = max(epsilon_min, epsilon * decay_rate)
4. 为什么在 DDPG 中加入 ε-greedy 探索?
在 DDPG 中使用 ε-greedy 探索有以下几点优势:
1 ) 探索与利用平衡:
- 通过 ε-greedy 策略,智能体在训练初期能够进行更多随机探索,逐渐减少探索以便更专注于最优策略。
- 这种机制有助于在确定性策略中增加随机性,以避免过度依赖当前策略网络输出。
2 ) 简单且有效:
- 相比于加入连续噪声(如高斯噪声或 Ornstein-Uhlenbeck 噪声),ε-greedy 策略的实现更加直接且便于调节。
- 这种方法尤其适合对噪声分布不敏感的任务,能够以更简单的方式实现探索。
3 ) 适合在特定场景下使用:
- 对于需要更强的探索性或环境具有较多随机性时,ε-greedy 可以确保一定的随机探索,有助于跳出局部最优解。
5. ε-greedy 与噪声探索的对比
在 DDPG 中,加入 ε-greedy 探索和噪声探索各有优势,具体区别如下:
特性 | ε-greedy 探索 | 噪声探索(如 Ornstein-Uhlenbeck 噪声) |
---|---|---|
适用性 | 更适合需要随机选择动作的任务 | 更适合需要连续平滑动作的任务 |
探索方式 | ε 概率选择随机动作,(1 - ε) 概率选择最优动作 | 每个时间步中均在确定性动作上加入噪声 |
复杂性 | 实现简单、调节直观 | 实现稍复杂,可能需调整噪声参数 |
效果 | 增强探索性,适合具有较多随机性环境的任务 | 平滑且时间相关的探索,适合物理系统或需要平滑动作的任务 |
6.实践中的应用
在 DDPG 中,通常会优先使用噪声(如高斯噪声或 Ornstein-Uhlenbeck 噪声)进行探索。然而,在某些任务中,ε-greedy 探索也可以提供有效的探索方式,尤其是在探索多样性、随机性方面有较高要求的任务中。
def allocate(self, prev_action, users, curren_episode, current_step):
self.prev_qualities = prev_action
self.users = users
self.curren_episode = curren_episode
self.curren_step = current_step
# input state
state_vector = self.get_state_vector(self.prev_qualities, self.users)
# 增加noise探索
epsilon = np.interp(x=self.curren_episode * self.num_step + self.curren_step,
xp=[0, self.epsilon_decay],
fp=[self.epsilon_start, self.epsilon_end])
random_sample = random.random()
if random_sample <= epsilon:
# 探索:选择一个随机离散动作
actions = np.random.choice(range(1, self.max_quality_level + 1), size=self.action_dim)
else:
# 利用:使用 Actor 网络选择动作
actions = self.ddpg_agent.select_action(state_vector)
return actions
这段代码实现了基于 ε-greedy 策略的探索与利用机制。它通过插值计算逐步减少的 ε 值来控制探索的概率,使得在训练早期更偏向探索,后期则逐渐更偏向利用 Actor 网络输出的最优动作。以下是每一行的解释:
6.1 代码详细解释
for step_i in range(NUM_STEP):
- 这段代码运行一个循环
NUM_STEP
次(假设是一个训练过程中需要执行的步数),每次循环都代表一个时间步。 step_i
表示当前时间步的索引。
epsilon = np.interp(x=episode_i * NUM_STEP + step_i, xp=[0, epsilon_decay], fp=[epsilon_start, epsilon_end])
- 这一行使用
np.interp
函数对epsilon
进行线性插值,使epsilon
从epsilon_start
(初始探索概率)逐步减小到epsilon_end
(最终探索概率)。 np.interp
的参数含义:x=episode_i * NUM_STEP + step_i
:表示当前训练的步数。episode_i
是当前的回合数,乘以NUM_STEP
后表示所有时间步的数量,再加上当前步step_i
。xp=[0, epsilon_decay]
:定义了插值的两个参考点,即从步数0
到epsilon_decay
之间,epsilon
会逐渐变化。fp=[epsilon_start, epsilon_end]
:指定插值的起始值和结束值,即epsilon
从epsilon_start
逐渐减小到epsilon_end
。
- 这种插值方法让
epsilon
随着步数逐渐减小,初期探索概率较高,训练后期则逐渐减少探索概率。
random_sample = random.random()
- 生成一个 0 到 1 之间的随机浮点数
random_sample
,用于决定当前时间步是执行探索还是利用。 random.random()
返回一个均匀分布的随机数,介于[0, 1)
之间。
if random_sample <= epsilon:
# 探索:选择一个随机动作
action = np.random.uniform(-2, 2, size=ACTION_DIM)
- 如果
random_sample
小于等于当前的epsilon
值,表示以epsilon
概率进行探索,即选择一个随机动作。 np.random.uniform(-2, 2, size=ACTION_DIM)
:在动作空间中随机生成一个动作,范围为[-2, 2]
,动作的维度是ACTION_DIM
。- 这种探索方式允许智能体尝试不同的动作,从而在训练初期积累更多多样性的经验。
else:
# 利用:使用 Actor 网络选择动作
action = agent.select_action(state)
- 如果
random_sample
大于epsilon
值,表示以 1 − ϵ 1 - \epsilon 1−ϵ 的概率执行利用,即使用 Actor 网络来选择动作。 agent.select_action(state)
:调用agent
的select_action
方法,用 Actor 网络基于当前状态state
输出确定性动作。
6.2 代码总结
- 这段代码实现了 ε-greedy 探索策略,在每个时间步中以 ϵ \epsilon ϵ 的概率执行探索(选择随机动作),以 1 − ϵ 1 - \epsilon 1−ϵ 的概率执行利用(选择最优动作)。
epsilon
通过插值逐渐减小,使得智能体在训练早期更偏向探索,后期则逐渐更偏向利用 Actor 网络的输出,从而逐步收敛到更稳定的策略。
6.3 参数解释
参数 xp=[0, epsilon_decay]
是用来定义 epsilon
的衰减过程的步数范围。它指定了 epsilon
从初始值逐渐衰减到终止值的步数区间。
在 np.interp
函数中:
xp
表示插值过程的参考点的步数范围。在[0, epsilon_decay]
之间,epsilon
从初始值epsilon_start
逐渐减小到终值epsilon_end
。epsilon_decay
通常是一个整数,表示经过多少步后,epsilon
应该从epsilon_start
减小到epsilon_end
。
6.4 如何设定 epsilon_decay
1 ) 任务复杂度:
- 对于较复杂的任务,可能需要更长时间的探索。因此,
epsilon_decay
可以设定得较大,让智能体更长时间保持较高的探索概率,从而积累足够的经验。 - 对于简单的任务,
epsilon_decay
可以设定得小一些,让智能体快速过渡到利用阶段。
2 ) 环境动态性:
- 如果环境较为动态,可能需要较长时间的探索。此时,可以增加
epsilon_decay
值。 - 如果环境稳定,可以更快地衰减
epsilon
,因此可以设置较小的epsilon_decay
值。
3 ) 训练时长:
- 假设训练的总步数为
total_steps
,可以设置epsilon_decay
使探索持续到总步数的10%-20%
,即epsilon_decay ≈ total_steps * 0.1
或0.2
。 - 例如,若总步数为
100000
,可以将epsilon_decay
设为10000
或20000
,这样前10000-20000
步中epsilon
会逐渐减小。
6.5 示例
假设我们有如下参数:
total_steps = 100000
(总训练步数)- 希望在前
10%
的步数中衰减epsilon
,那么:epsilon_decay = int(total_steps * 0.1) # 10% 的步数 xp = [0, epsilon_decay] # 衰减范围
这样,epsilon
会在前 10000
步内从 epsilon_start
逐渐衰减到 epsilon_end
。