少女祈祷中...

前两天折腾了一下DQN, 但是很杂乱. 今天打算干干净净地把DQN给做了.

DQN

[!question]
DQN是什么?

具体参照之前的Blog RL 第七讲 深度强化学习

简单来说, DQN全名深度Q网络(Deep Q Network), 即引入了深度神经网络进行近似的Q学习.

关于Q-learning:

  • Q学习是价值迭代的方法, 它通过一个行动策略进行采样, 然后对目标策略进行优化(这些策略并不显式给出, 而是隐含在Q-function中. ). Q-Learning的方法不需要重要性采样. 而且它是离线的学习方法(收集数据的策略和优化的目标策略不同)
  • 详细见 RL 第四讲 无模型控制方法
    关于DQN:
  • 它使用神经网络来近似Q-function
  • 为了避免样本不满足独立分布, 它加入了一个buffer. 在buffer中进行采样.
  • 为了减小参数的更新频率, 引入了双网络结构(实际上在原本的Q-learning当中, 天生就支持双网络, 只要采样策略具有随机性即可. 但是一般采用同一个Q函数生成的两个不同策略作为采样策略和优化目标. )

同时, 我们还可以

  • 引入优先经验回放, 加上重要性采样, 使得算法的表现更优.
  • 引入Double DQN, 做max操作时用上两个网络. (更改训练步骤)
  • 引入Dueling DQN, 在建模Q函数的时候采用A和V的组合完成. (更改初始网络建模)

于是, 算法的架构为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...  # 初始化建模神经网络
# 若是Dueling DQN, 则建模神经网络时引入A-function
# 若是正常DQN, 则建模神经网络时正常建模Q-function
states, actions, rewards, next_states, dones = buffer.samples(batch_size)
q_values = self.action_network(states)
q_values_next = self.target_network(next_states)
target_q = rewards + gamma * np.max(q_values_next, axis=1)
# 如果是Double DQN
# q_values_next = self.action_network(next_states) # 选择动作, 通过action
# target_q = rewards + gamma * self.target_network(np.argmax(q_values_next, axis=1)) # 估值动作, 通过target
errors = target_q - q_values
loss = np.square(errors)
# 若引入优先级采样和重要性采样
# weights = ...
# loss = np.mean(weights * np.square(errors))
# new_priorities = ...
# pool.add(new_samples) # 将样本和新计算出的priorities添加到样本池中

# step
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
...

# 定时将两个网络的参数进行同步

DPG, DDPG

Actor-Critic模式加上深度神经网络的结果

具体可参见 RL 第六讲: 价值和策略近似逼近方法

AC模式已经是神经网络近似的方法了.
对于Critic:

Qαr+γQQ \leftarrow ^\alpha r + \gamma Q'

Q不断拟合当前分布π\pi下的Q; π\pi不断拟合当前Q下的最优策略.

对于DDPG, 它是面对复杂问题时, 添加了各种优化组件的DPG. 各种组件包括

  • 经验重放 (离线策略)
  • 目标网络
  • 在动作输入前批标准化Q网络
  • 添加连续噪声

算法:

  1. 四个网络, 分别为行动估值QQ, 行动策略π\pi, 目标估值QQ, 目标策略π\pi
  2. 执行动作时, 始终有一个高斯噪声
  3. 从buffer中采样得到minibatch
  4. 按照目标网络进行目标估值 y=r+γQ(s,a)y = r + \gamma Q(s, a)
  5. 按照上面的目标更新行动QQ网络
  6. 按照策略梯度更新行动策略π\pi 网络
  7. 也许可以考虑加上 priority pool. 但是优先级的值貌似不太好确认.

相关的一些杂项知识

通过一组下标, 访问对应元素

在 PyTorch 中,如果你有一个数组和一组下标,可以使用 torch.gather 或直接索引操作来获取对应元素。以下是两种方法:

方法 1:直接索引操作

如果你有一个一维数组和一组下标,可以直接使用下标列表或张量进行索引:

1
2
3
4
5
6
7
import torch

arr = torch.tensor([10, 20, 30, 40, 50])
indices = torch.tensor([0, 2, 4])

result = arr[indices]
print(result) # 输出:tensor([10, 30, 50])

方法 2:torch.gather(用于多维张量)

torch.gather 可以在多维张量中使用,适用于复杂情况。

1
2
3
4
5
6
arr = torch.tensor([[10, 20, 30], [40, 50, 60]])
indices = torch.tensor([[0, 2, 1], [1, 0, 2]])

# 注意:需要指定维度 dim 参数
result = torch.gather(arr, dim=1, index=indices)
print(result) # 输出:tensor([[10, 30, 20], [50, 40, 60]])

在这种情况下,indices 的形状要与目标结果的形状一致,并指定 dim 参数来指明在第几维度进行索引。