深度强化学习与双Q学习

引言

  • Q学习的问题:流行的Q学习算法在某些条件下会过度估计动作值。
  • DQN算法:首先展示最近的DQN算法,该算法将Q学习与深度神经网络相结合,在Atari 2600领域的一些游戏中遭受了大量的过度估计。
  • 双Q学习算法:然后展示,最初在表格设置中引入的双Q学习算法背后的思想可以推广到与大规模函数逼近一起工作。提出了对DQN算法的特定适应,并展示了得到的算法不仅减少了观察到的过度估计,而且这也导致了在几个游戏上的性能大大提高。

背景

  • 最优值的定义:为了解决顺序决策问题,可以学习每个动作的最优值的估计,定义为在采取该动作并随后遵循最优策略时的未来奖励的期望总和。
  • 在给定策略pi下,动作a在状态s的真实值为Q^pi(s;a) = E[R_1 + gamma R_2 + dots | S_0=s, A_0=a, pi] 其中gamma in [0, 1]是一个折扣因子,用于权衡即时和后续奖励的重要性。然后最优值为Q^*(s;a) = max_pi Q^pi(s;a)。从最优值中可以轻易地导出最优策略,即在每个状态中选择最高值的动作。

Q学习和深度Q网络(DQN)

  • Q学习:最优动作值的估计可以使用Q学习(一种时间差分学习的形式)来学习。大多数问题都太大,无法在所有状态中分别学习所有动作值。
    • 可以学习一个参数化的值函数Q(s;a;theta_t)。在采取动作A_t在状态S_t并观察到即时奖励R_{t+1}和结果状态S_{t+1}之后
    • 参数的标准Q学习更新为theta_{t+1} = theta_t + alpha(Y^{Q}_t – Q(S_t;A_t;theta_t))nabla_{theta_t}Q(S_t;A_t;theta_t),其中alpha是标量步长,目标Y^{Q}_t定义为Y^{Q}_t = R_{t+1} + gamma max_a Q(S_{t+1};a;theta_t)
  • 深度Q网络(DQN):深度Q网络(DQN)是一个多层神经网络,对于给定的状态$s$,输出一个动作值向量Q(s;cdot;theta),其中theta是网络的参数。对于一个$n$维的状态空间和一个包含$m$个动作的动作空间,神经网络是一个从mathbb{R}^nmathbb{R}^m的函数。DQN算法的两个重要成分是使用目标网络和经验回放。
    • 目标网络,其参数为theta',与在线网络相同,只是其参数每隔tau步从在线网络复制一次,然后在所有其他步骤中保持固定。DQN使用的目标是Y^{DQN}_t = R_{t+1} + gamma max_a Q(S_{t+1};a;theta'_t)
    • 经验回放,观察到的转换存储一段时间,并从这个记忆库中均匀采样以更新网络。目标网络和经验回放都显著提高了算法的性能。

双Q学习

  • 双Q学习的思想:标准Q学习和DQN中的最大操作符在选择和评估动作时使用相同的值。这使得更可能选择过度估计的值,导致过于乐观的值估计。为了防止这种情况,可以将选择从评估中解耦。这就是双Q学习的思想。
  • 双Q学习算法
    • 在原始的双Q学习算法中,通过随机地将每个经验分配给更新两个值函数中的一个,从而学习两个值函数,因此有两组权重,thetatheta'
    • 对于每次更新,一组权重用于确定贪婪策略,另一组用于确定其值。
    • 可以首先在Q学习中解开选择和评估,并将其目标重写为Y^{Q}_t = R_{t+1} + gamma Q(S_{t+1};argmax_a Q(S_{t+1};a;theta_t);theta_t)
    • 双Q学习的误差可以写为Y^{DoubleQ}_t = R_{t+1} + gamma Q(S_{t+1};argmax_a Q(S_{t+1};a;theta_t);theta'_t)
    • 动作的选择仍然由在线权重theta_t决定, 和在Q学习中一样,仍然在估计根据当前值(由theta_t定义)的贪婪策略的值。
    • 第二组权重theta'_t公平地评估这个策略的值。这第二组权重可以通过交换thetatheta'的角色来对称地更新。

实证结果

  • 实证结果:在本节中,分析了DQN的过度估计,并显示双DQN在值准确性和策略质量方面都优于DQN。为了进一步测试方法的稳健性,还按照Nair等人(2015)的建议,使用从专家人类轨迹生成的随机开始来评估算法。
    • 测试平台是Atari 2600游戏,使用Arcade Learning Environment(Bellemare等人,2013)。
    • 目标是让一个算法,使用固定的超参数集,学习从交互中只给出屏幕像素作为输入的每个游戏。
    • 这是一个苛刻的测试平台:不仅输入是高维的,游戏视觉和游戏机制在游戏之间变化很大。因此,好的解决方案必须严重依赖学习算法——仅仅依赖调优是不切实际的。紧密遵循Mnih等人(2015)概述的实验设置和网络架构。
    • 网络架构是一个卷积神经网络(Fukushima,1988;LeCun等人,1998),有3个卷积层和一个全连接隐藏层(总共约150万个参数)。
    • 网络将最后四帧作为输入,并输出每个动作的动作值。在每个游戏上,网络在单个GPU上训练了200M帧,或者大约1周。
  • 过度估计的实证研究:图中的顶部和中间行显示了DQN(橙色)和双DQN(蓝色)在六个Atari游戏上的值估计。
    • 这些结果是通过运行DQN和双DQN,使用Mnih等人(2015)使用的超参数,以6个不同的随机种子得到的。
    • 较深的线显示了种子的中位数,平均两个极值以获得阴影区域(即,10%和90%的分位数,线性插值)。
    • 顶行的直线橙色(对于DQN)和蓝色(对于双DQN)线是通过在学习结束后运行相应的代理,并平均每个访问状态获得的实际折现回报来计算的。如果没有偏差,这些直线将与图的右侧的学习曲线匹配。
    • 中间行显示了两个游戏的值估计(以对数尺度),在这两个游戏中,DQN的过度乐观性非常极端。
    • 底行显示了这对代理在训练期间评估时获得的分数的有害影响:当过度估计开始时,分数下降。使用双DQN的学习更稳定。地面真实平均值是通过运行最佳学习策略的几个情节并计算实际累积奖励获得的。

过度估计的实验

学习策略的质量

  • 学习策略的质量:过度乐观并不总是对学习策略的质量产生负面影响。例如,尽管DQN略微高估了策略值,但在Pong游戏中仍然达到了最优行为。然而,减少过度估计可以显著提高学习的稳定性;在图3中看到了这一点的明显例子。现在通过在DQN测试过的所有49个游戏上评估,更一般地评估双DQN在策略质量方面的帮助程度。如Mnih等人(2015)所述,每个评估情节开始时,执行一个特殊的无操作动作,这个动作不影响环境,最多执行30次,以为代理提供不同的起点。评估期间的一些探索提供了额外的随机化。对于双DQN,使用了与DQN完全相同的超参数。
  • 归一化性能:表2总结了在49个游戏上,使用人类开始的30分钟游戏时间内的归一化性能。DQN的结果来自Nair等人(2015)。使用公式(5)来获取每个游戏的归一化分数: text{score}_{text{normalized}} = frac{text{score}_{text{agent}} – text{score}_{text{random}}}{text{score}_{text{human}} – text{score}_{text{random}}} 其中,“random”和“human”分数与Mnih等人(2015)使用的相同,附录中给出了具体数值。表1中的“no ops”下显示,总体来看,双DQN明显优于DQN。详细比较(见附录)显示,有几个游戏中,双DQN大大改善了DQN的性能。值得注意的例子包括Road Runner(从233%提高到617%)、Asterix(从70%提高到180%)、Zaxxon(从54%提高到111%)和Double Dunk(从17%提高到397%)。

Normalized score

讨论

  • 讨论:本文有五个贡献。
    • 首先,展示了为什么Q学习在大规模问题中可能过度乐观,即使这些问题是确定性的,也会因为学习的内在估计误差而如此。
    • 其次,通过分析Atari游戏的价值估计,展示了这些过度估计在实践中比以前认识到的更常见和严重。
    • 第三,展示了双Q学习可以成功地用于减少这种过度乐观,从而使学习更稳定和可靠。
    • 第四,提出了一种特定的实现,称为双DQN,它使用了DQN算法的现有架构和深度神经网络,而无需额外的网络或参数。
    • 最后,展示了双DQN可以找到更好的策略,在Atari 2600领域获得了新的最先进的结果。

附录

  • 定理1:考虑一个状态s,其中所有真实的最优动作值在Q^*(s;a) =V^*(s)处相等。设Q_t是任意的值估计,总体上是无偏的,即sum_a(Q_t(s;a)-V^*(s)) = 0,但并非所有的值都是零,使得frac{1}{m}sum_a(Q_t(s;a)-V^*(s))^2=C对于某个C > 0,其中$m$是状态s的动作数量。在这些条件下,max_a Q_t(s;a)-V^*(s) +sqrt{frac{C}{m-1}}。这个下界是紧的。在相同的条件下,双Q学习估计的绝对误差的下界是零。
  • 定理2:考虑一个状态s,其中所有真实的最优动作值在Q^*(s;a) =V^*(s)处相等。假设估计误差Q_t(s;a)-Q^*(s;a)是独立分布的,均匀随机在[-1,1]中。那么,E[max_a Q_t(s;a)-V^*(s)] =frac{m-1}{m+1}

Atari 2600领域的实验细节

  • Atari 2600领域的实验细节:选择了49个游戏,以匹配Mnih等人(2015)使用的列表,下表给出了完整的列表。每个代理步骤由四个帧组成(在这些帧期间重复最后选择的动作),奖励值(从Arcade Learning Environment(Bellemare等人,2013)获得)被剪切在-1和1之间。

网络架构

  • 网络架构:实验中使用的卷积网络与Mnih等人(2015)提出的完全一致,在此只为了完整性提供细节。
    • 网络的输入是一个84x84x4的张量,包含最后四帧的重新缩放和灰度版本。
    • 第一个卷积层用32个大小为8(步长4)的滤波器对输入进行卷积
    • 第二层有64个大小为4(步长2)的层
    • 最后的卷积层有64个大小为3(步长1)的滤波器。
    • 这之后是一个包含512个单元的全连接隐藏层。
    • 所有这些层之间都由Rectifier Linear Units(ReLu)分隔。
    • 最后,一个全连接线性层将投影到网络的输出,即Q值。
    • 用于训练网络的优化是RMSProp(动量参数0.95)。

超参数

  • 超参数:在所有实验中,折扣设为γ=0.99,学习率设为α=0.00025。目标网络更新之间的步数设为τ=10,000。训练在5000万步(即2亿帧)内完成。
    • 每100万步评估一次代理,并保留这些评估中的最佳策略作为学习过程的输出。
    • 经验重播记忆的大小是100万个元组。每4步对记忆进行采样以更新网络,使用32的小批量。
    • 使用的简单探索策略是ε-贪婪策略,其中ε在100万步内线性减少从1到0.1。

Double DQN 算法

  1. 环境设置:选择一个环境,在这个例子中,使用Atari 2600游戏环境。
  2. 初始化:初始化两个相同的神经网络,一个是Q网络,另一个是目标网络。这两个网络都有相同的架构,包括三个卷积层和一个全连接隐藏层,最后是一个全连接线性层。
  3. 经验回放:初始化一个经验回放缓冲区,用于存储代理的经验,包括状态、动作、奖励和新状态。
  4. 策略选择:使用ε-贪婪策略选择动作。在训练初期,代理主要探索环境(即,随机选择动作),随着训练的进行,代理逐渐转向利用其经验(即,根据Q网络选择动作)。
  5. 训练:对于每一步,代理选择一个动作并执行,然后观察奖励和新状态,这些都存储在经验回放缓冲区中。然后,从经验回放缓冲区中随机抽取一批经验,并使用Q网络和目标网络计算Q值和目标Q值,然后更新Q网络以最小化这两者之间的差异。
  6. 更新目标网络:每隔一定的步数(例如,10,000步),将Q网络的权重复制到目标网络。
  7. 评估:每隔一定的步数(例如,100万步),评估代理的性能,并保存最佳策略。
  8. 重复:重复步骤5-7,直到满足终止条件,例如达到预定的训练步数。

# 导入必要的库
import gym
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from collections import deque
import random

# 定义Q网络
class QNetwork(nn.Module):
def __init__(self, state_size, action_size):
super(QNetwork, self).__init__()
self.fc1 = nn.Linear(state_size, 64)
self.fc2 = nn.Linear(64, 64)
self.fc3 = nn.Linear(64, action_size)

def forward(self, state):
x = torch.relu(self.fc1(state))
x = torch.relu(self.fc2(x))
return self.fc3(x)

# 定义经验回放
class ReplayBuffer:
def __init__(self, buffer_size):
self.buffer_size = buffer_size
self.buffer = deque(maxlen=buffer_size)

def add(self, state, action, reward, next_state, done):
self.buffer.append((state, action, reward, next_state, done))

def sample(self, batch_size):
samples = random.sample(self.buffer, batch_size)
return map(list, zip(*samples))

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

# 定义双DQN代理
class DoubleDQNAgent:
def __init__(self, state_size, action_size):
self.state_size = state_size
self.action_size = action_size
self.network = QNetwork(state_size, action_size)
self.target_network = QNetwork(state_size, action_size)
self.buffer = ReplayBuffer(10000)
self.optimizer = optim.Adam(self.network.parameters())
self.criterion = nn.MSELoss()

def get_action(self, state, epsilon):
if np.random.random() < epsilon:
return np.random.randint(self.action_size)
else:
state = torch.FloatTensor(state)
q_values = self.network(state)
return torch.argmax(q_values).item()

def update(self, batch_size):
states, actions, rewards, next_states, dones = self.buffer.sample(batch_size)
states = torch.FloatTensor(states)
actions = torch.LongTensor(actions)
rewards = torch.FloatTensor(rewards)
next_states = torch.FloatTensor(next_states)
dones = torch.FloatTensor(dones)

current_q = self.network(states).gather(1, actions.unsqueeze(1))
next_q = self.target_network(next_states).max(1)[0]
target_q = rewards + (1 - dones) * 0.99 * next_q

loss = self.criterion(current_q, target_q.unsqueeze(1))
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()

def update_target_network(self):
self.target_network.load_state_dict(self.network.state_dict())

def save_model(self, path):
torch.save(self.network.state_dict(), path)

def load_model(self, path):
if os.path.exists(path):
self.network.load_state_dict(torch.load(path))
self.target_network.load_state_dict(torch.load(path))

# 定义训练函数
def train(agent, env, episodes, batch_size, epsilon):
for episode in range(episodes):
state = env.reset()
done = False
episode_reward = 0
while not done:
action = agent.get_action(state, epsilon)
next_state, reward, done, _ = env.step(action)
agent.buffer.add(state, action, reward, next_state, done)
state = next_state
episode_reward += reward
if len(agent.buffer) > batch_size:
agent.update(batch_size)
if episode % 10 == 0:
agent.update_target_network()
agent.save_model(f"model_episode_{episode}.pth")
print(f"Episode: {episode}, Reward: {episode_reward}, Epsilon: {epsilon}")

# 定义主函数
def main():
env = gym.make('CartPole-v1')
state_size = env.observation_space.shape[0]
action_size = env.action_space.n
agent = DoubleDQNAgent(state_size, action_size)
agent.load_model("pretrained_model.pth")
train(agent, env, 1000, 64, 0.1)

if __name__ == "__main__":
main()

<< · Back Index ·>>

发表回复

相关推荐

炎炎夏日,萌寵也躁動!警惕貓抓病!

貓抓病(CSD)又稱貓抓熱,通常指被貓抓傷、咬傷或密切接觸貓後,主要由漢塞巴爾通體(B.henselae)感染引起的一種自限性感染性...

· 55秒前

阿斯塔纳 | 这个你名字都没听说过的地方,有中亚最魔幻的建筑

阿斯塔纳,哈萨克斯坦首都,也是这个世界上最年轻的首都之一,所有的一切都从零开始。在被赋予了成为首都的使命后,这座城市 ...

· 55秒前

《铁血战士》编年史:电影+漫画+游戏剧情,一网打尽

这一期,半只猫将完整复盘铁血战士系列编年史,有电影,有漫画,有游戏,有异形大战铁血战士,一定有你不知道的内容在等着你。

· 2分钟前

这些来自世界各地的奇花异草,你见过几种?

植物世界很奇妙,有很多奇花异草!下面这些来自世界各地大自然的神作,你一定没见过几种,肯定满足你的好奇心,一起来看看吧!

· 2分钟前

米蟲:我歷經九九八十一難來看你,你卻處心積慮想殺我

我們常說“民以食為天”,對於熱愛當屯屯鼠的國人來說,傢裡怎麼著也得屯上那麼一袋大米。但大米屯的時間久瞭,特別是夏天,裡...

· 3分钟前