📢本篇文章是博主强化学习(RL)领域学习时,用于个人学习、研究或者欣赏使用,并基于博主对相关等领域的一些理解而记录的学习摘录和笔记,若有不当和侵权之处,指出后将会立即改正,还望谅解。文章分类在👉**强化学习**专栏: 【强化学习】(36)---《基于好奇心驱动的自监督探索机制(ICM算法)》
基于好奇心驱动的自监督探索机制(ICM算法)
1. 引言
** Intrinsic Curiosity Module** (ICM) 是一种用于强化学习的内在奖励机制,旨在解决传统强化学习中在稀疏奖励场景下,智能体难以学习有效策略的问题。该算法由 *Deepak Pathak* 等人在论文《Curiosity-driven Exploration by Self-supervised Prediction》中提出。ICM 是通过引入“好奇心”作为一种内在动机,帮助智能体在缺乏外部奖励的情况下探索环境并获取经验,从而提高强化学习的效率。
ICM 模块的核心思想是利用智能体对环境状态变化的预测误差来产生内在奖励,以鼓励智能体进行探索。这种内在奖励机制与任务的外部奖励无关,因此在奖励稀疏或完全没有外部奖励的情况下,ICM 也能够引导智能体继续学习。下图展示了ICM是如何作用的。
状态通过执行操作与环境交互,从其当前策略中抽样一个动作,并最终进入下一状态。策略经过训练以优化由环境和基于好奇心内在奖励信号的外在奖励总和基于好奇心的内在奖励信号由ICM生成的。
ICM 对状态进行编码st,st+1变为特征φ(st),φ(st+1),经过训练以预测一个动作at(即逆动力学模型)。前向模型作为输入φ(st)和at并预测下一状态的特征φ^(st+1)。特征空间中的预测误差用作基于好奇心的内在奖励信号。由于编码任何不能影响或不受代理行为影响的环境特征φ(st)是没有激励,因此智能体的学习探索策略对环境的不可控方面是稳健的。
2. Intrinsic Curiosity Module 的结构
ICM 算法由两个关键部分组成:
2.1 逆向模型(Inverse Model):
公式表示为:
其中,是逆模型的函数,( 是模型的参数。
该模型负责通过观察连续的两个状态 ![( s_t )](https://latex.csdn.net/eq?%28%20s_t%20%29)和![( s_{t+1} )](https://latex.csdn.net/eq?%28%20s_%7Bt+1%7D%20%29),预测智能体采取的动作 ![( a_t )](https://latex.csdn.net/eq?%28%20a_t%20%29)。
换句话说,给定状态 ![( s_t )](https://latex.csdn.net/eq?%28%20s_t%20%29)和 ![( s_{t+1} )](https://latex.csdn.net/eq?%28%20s_%7Bt+1%7D%20%29),逆模型尝试推断出使得状态从![( s_t )](https://latex.csdn.net/eq?%28%20s_t%20%29)转移到 ![( s_{t+1} )](https://latex.csdn.net/eq?%28%20s_%7Bt+1%7D%20%29)的动作![( a_t )](https://latex.csdn.net/eq?%28%20a_t%20%29)。逆模型的作用是捕捉环境中与智能体行为有关的重要特征。
2.2 前向模型(Forward Model):
公式表示为:
其中,是前向模型的函数,是模型的参数。
前向模型根据当前状态 ![( s_t )](https://latex.csdn.net/eq?%28%20s_t%20%29)和动作![( a_t )](https://latex.csdn.net/eq?%28%20a_t%20%29),预测下一时刻的状态 ![( \hat{s}_{t+1} )](https://latex.csdn.net/eq?%28%20%5Chat%7Bs%7D_%7Bt+1%7D%20%29)。
通过最小化前向模型的预测误差(即实际状态![( s_{t+1} )](https://latex.csdn.net/eq?%28%20s_%7Bt+1%7D%20%29)与预测状态![( \hat{s}_{t+1} )](https://latex.csdn.net/eq?%28%20%5Chat%7Bs%7D_%7Bt+1%7D%20%29)之间的差异),来计算内在奖励。
前向模型的预测误差可以看作是智能体对环境变化的“好奇心”,误差越大,说明智能体对环境的变化感到越“好奇”,从而激励智能体去探索新的状态。
3. 内在奖励的计算
ICM 中的内在奖励是通过前向模型的预测误差来计算的。具体来说,内在奖励是智能体无法精确预测下一状态时产生的误差。这个误差反映了当前环境变化的不可预测性,即智能体“好奇”的程度。公式如下:
其中:
- 是时刻的内在奖励;
- 是通过状态的特征表示,通常是使用卷积神经网络(CNN)提取的高维特征;
- 是前向模型预测的特征;
- 表示二范数的平方,用于衡量特征之间的差异。
该内在奖励会被添加到外部奖励中,使得智能体在稀疏外部奖励场景下依然能够保持探索行为。
4. ICM 与强化学习框架的结合
ICM 算法通常结合传统的强化学习算法(如 A3C 或 DQN)一起使用。在这种组合中,ICM 负责生成内在奖励,而标准的强化学习算法通过同时利用外部奖励和内在奖励来更新策略。
完整的强化学习过程可以概括为以下步骤:
智能体在环境中执行动作,得到当前状态 、动作 和下一状态 。
ICM 通过逆模型预测动作,通过前向模型预测下一状态特征。
计算前向模型的预测误差,产生内在奖励 。
将内在奖励和外部奖励结合,更新强化学习算法中的策略。
通过这种方式,智能体在外部奖励稀疏的情况下,也能通过内在奖励的引导不断探索,最终学得更加有效的策略。
[Python] ICM算法实现
基于ICM(Intrinsic Curiosity Module)结合DQN算法在CartPole环境中实现的PyTorch代码。ICM主要负责生成内在奖励,而DQN则利用外部奖励和内在奖励进行训练。
🔥若是下面代码复现困难或者有问题,**欢迎评论区留言**;需要以整个项目形式的代码,请**在评论区留下您的邮箱**📌,以便于及时分享给您(私信难以及时回复)。
**参数设置: **
"""《ICM算法实现简单例程》
时间:2024.10.26
环境:cartpole
作者:不去幼儿园
"""
import gym
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import numpy as np
from collections import deque
import random
# 超参数设置
gamma = 0.99 # 外部奖励的折扣因子
epsilon_start = 1.0 # ε-greedy 策略中 ε 的初始值
epsilon_end = 0.01 # ε 的最小值
epsilon_decay = 0.995 # ε 的衰减率
lr = 1e-3 # DQN和ICM的学习率
batch_size = 64
icm_beta = 0.2 # ICM内在奖励的系数
icm_lambda = 0.1 # 内在奖励的缩放因子
buffer_size = 10000 # 经验回放池的容量
target_update_freq = 10 # 目标网络更新频率
max_steps = 500 # 每个episode中的最大步数
算法配置:
# DQN网络
class DQN(nn.Module):
def __init__(self, state_dim, action_dim):
super(DQN, self).__init__()
self.fc1 = nn.Linear(state_dim, 128)
self.fc2 = nn.Linear(128, 128)
self.fc3 = nn.Linear(128, action_dim)
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
return self.fc3(x)
# ICM(内在好奇心模块)
class ICM(nn.Module):
def __init__(self, state_dim, action_dim):
super(ICM, self).__init__()
# 状态编码器
self.state_encoder = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, 128)
)
# 逆模型:根据状态变化预测动作
self.inverse_model = nn.Sequential(
nn.Linear(128 * 2, 256),
nn.ReLU(),
nn.Linear(256, action_dim)
)
# 前向模型:根据状态和动作预测下一个状态
self.forward_model = nn.Sequential(
nn.Linear(128 + action_dim, 256),
nn.ReLU(),
nn.Linear(256, 128)
)
def forward(self, state, next_state, action):
# 状态编码
encoded_state = self.state_encoder(state)
encoded_next_state = self.state_encoder(next_state)
# 预测动作和下一个状态
predicted_action = self.inverse_model(torch.cat([encoded_state, encoded_next_state], dim=1))
predicted_next_state = self.forward_model(torch.cat([encoded_state, action], dim=1))
return predicted_action, predicted_next_state, encoded_next_state
# 经验回放池
class ReplayBuffer:
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity)
def push(self, state, action, reward, next_state, done):
self.buffer.append((state, action, reward, next_state, done))
def sample(self, batch_size):
batch = random.sample(self.buffer, batch_size)
states, actions, rewards, next_states, dones = zip(*batch)
return states, actions, rewards, next_states, dones
def __len__(self):
return len(self.buffer)
# 带ICM的DQN智能体
class Agent:
def __init__(self, state_dim, action_dim):
self.dqn = DQN(state_dim, action_dim)
self.dqn_target = DQN(state_dim, action_dim)
self.icm = ICM(state_dim, action_dim)
self.optimizer_dqn = optim.Adam(self.dqn.parameters(), lr=lr)
self.optimizer_icm = optim.Adam(self.icm.parameters(), lr=lr)
self.buffer = ReplayBuffer(buffer_size)
self.epsilon = epsilon_start
self.action_dim = action_dim
def select_action(self, state):
# 根据ε-greedy策略选择动作
if random.random() < self.epsilon:
return random.randint(0, self.action_dim - 1)
state = torch.FloatTensor(state).unsqueeze(0)
with torch.no_grad():
return self.dqn(state).argmax().item()
def train(self, batch_size):
if len(self.buffer) < batch_size:
return
# 从回放池中采样
states, actions, rewards, next_states, dones = self.buffer.sample(batch_size)
states = torch.FloatTensor(np.array(states))
actions = torch.LongTensor(np.array(actions)).unsqueeze(1)
rewards = torch.FloatTensor(np.array(rewards)).unsqueeze(1)
next_states = torch.FloatTensor(np.array(next_states))
dones = torch.FloatTensor(np.array(dones)).unsqueeze(1)
# 计算DQN损失
q_values = self.dqn(states).gather(1, actions)
with torch.no_grad():
q_next = self.dqn_target(next_states).max(1)[0].unsqueeze(1)
target_q_values = rewards + gamma * q_next * (1 - dones)
dqn_loss = F.mse_loss(q_values, target_q_values)
# 计算ICM损失和内在奖励
one_hot_actions = F.one_hot(actions.squeeze(), self.action_dim).float()
predicted_action, predicted_next_state, encoded_next_state = self.icm(states, next_states, one_hot_actions)
forward_loss = F.mse_loss(predicted_next_state, encoded_next_state.detach())
inverse_loss = F.cross_entropy(predicted_action, actions.squeeze())
icm_loss = (1 - icm_beta) * inverse_loss + icm_beta * forward_loss
intrinsic_reward = icm_lambda * forward_loss.detach().sum()
# 外部奖励和内在奖励结合
total_reward = rewards + intrinsic_reward
# 优化DQN和ICM模型
self.optimizer_dqn.zero_grad()
dqn_loss.backward()
self.optimizer_dqn.step()
self.optimizer_icm.zero_grad()
icm_loss.backward()
self.optimizer_icm.step()
def update_target(self):
# 更新目标网络
self.dqn_target.load_state_dict(self.dqn.state_dict())
** 算法训练:**
# 训练阶段
env = gym.make('CartPole-v1')
state_dim = env.observation_space.shape[0]
action_dim = env.action_space.n
agent = Agent(state_dim, action_dim)
num_episodes = 100
for episode in range(num_episodes):
state, _ = env.reset()
total_reward = 0
for step in range(max_steps):
action = agent.select_action(state)
next_state, reward, done, _, _ = env.step(action)
agent.buffer.push(state, action, reward, next_state, done)
state = next_state
total_reward += reward
agent.train(batch_size)
if done:
break
# 每隔一段时间更新目标网络
if episode % target_update_freq == 0:
agent.update_target()
# 逐渐减小ε的值,减少随机探索
agent.epsilon = max(epsilon_end, agent.epsilon * epsilon_decay)
print(f"Episode {episode}: Total Reward = {total_reward}")
env.close()
算法测试:
# 测试阶段显示动画
def test_agent(agent, env, num_episodes=5):
for episode in range(num_episodes):
state, _ = env.reset()
total_reward = 0
done = False
while not done:
env.render() # 显示动画
action = agent.select_action(state)
state, reward, done, _, _ = env.step(action)
total_reward += reward
print(f"Test Episode {episode}: Total Reward = {total_reward}")
env.close()
# 测试模型
env_test = gym.make('CartPole-v1', render_mode='human')
test_agent(agent, env_test)
[Notice] 代码结构说明
- DQN:使用经典的三层全连接网络来估计状态的Q值。
- ICM:使用了两部分模型: - 逆模型:从状态变化预测动作。- 前向模型:从当前状态和动作预测下一个状态的特征表示。- 前向模型的误差被用来生成内在奖励。
- Replay Buffer:存储环境交互的经验,以便在训练时进行采样。
- Agent:结合了DQN和ICM,DQN利用环境外部奖励和ICM生成的内在奖励来更新策略。
训练流程
- 在每个episode中,智能体在环境中执行动作并存储经验。
- 同时利用DQN和ICM模型对状态-动作对进行训练,生成内在奖励并通过外在奖励一起用于Q值更新。
- 定期更新目标网络,随着时间的推移减少ε的值,逐步减少随机动作的探索。
由于博文主要为了介绍相关**算法的原理**和**应用的方法**,缺乏对于实际效果的关注,算法可能在上述环境中的效果不佳,一是算法不适配上述环境,二是算法未调参和优化,三是等等。上述代码用于**了解和学习**算法足够了,但若是想直接将上面代码应用于实际项目中,还需要进行修改。
5. ICM 的优势与局限
优势:
- 应对稀疏奖励问题:在许多强化学习任务中,外部奖励往往稀疏且难以获得。ICM 通过引入内在奖励,鼓励智能体探索更多的状态空间,帮助其发现潜在的奖励源。
- 自监督学习:ICM 是自监督的,不依赖任何外部标签或人为设计的奖励信号,只需要通过状态变化进行自我学习。这种特性使得它在复杂、未知环境中表现出色。
- 广泛适用性:由于 ICM 并不依赖于具体的任务目标,它可以用于广泛的强化学习任务,如游戏、机器人控制等。
局限:
- 计算复杂度:ICM 需要同时训练逆模型和前向模型,增加了计算成本。特别是在复杂环境中,前向模型的预测误差计算可能较为耗时。
- 过度探索:有时,智能体可能因为过度依赖内在奖励而忽视外部奖励,从而陷入无关紧要的状态探索中,这在某些特定任务中可能导致学习效率低下。
- 内在奖励的平衡:如何在内在奖励和外部奖励之间取得平衡,是一个需要仔细调节的超参数。在某些任务中,过高的内在奖励可能会抑制外部奖励的影响。
6. 实验结果与效果验证
在论文中,ICM 算法被应用于多种基准测试环境中,包括经典的 Atari 游戏和 3D 迷宫游戏。实验结果表明,与仅依赖外部奖励的传统算法相比,ICM 在稀疏奖励环境中能够显著提高智能体的表现。特别是在某些游戏中,即使完全没有外部奖励,ICM 也能通过内在奖励引导智能体学会有效的探索策略,从而最终取得不错的成绩。
例如,在经典的 Atari 游戏 *Montezuma's Revenge* 中,由于游戏中外部奖励的分布极其稀疏,传统的强化学习算法通常难以学到有效的策略。而引入 ICM 后,智能体能够通过对环境变化的预测误差产生内在奖励,进而学会探索更多的房间和道具,从而显著提高了得分。
7. 总结
Intrinsic Curiosity Module(ICM)为强化学习带来了一种新颖且有效的内在奖励机制,解决了稀疏奖励问题。它通过前向模型和逆模型的结合,使得智能体能够利用好奇心驱动探索,特别适合那些外部奖励稀少或缺失的环境。然而,ICM 在计算复杂度、内外奖励平衡等方面仍然面临一些挑战。随着技术的不断进步,未来可以期待进一步的优化和扩展。
参考论文: Curiosity-driven Exploration by Self-supervised Prediction, ICML 2017.
文章若有不当和不正确之处,还望理解与指出。由于部分文字、图片等来源于互联网,无法核实真实出处,如涉及相关争议,请联系博主删除。如有错误、疑问和侵权,欢迎评论留言联系作者,或者关注VX公众号:**Rain21321,**联系作者。✨
版权归原作者 不去幼儿园 所有, 如有侵权,请联系我们删除。