第11节 DQN实现走迷宫——《强化学习笔记》
上一节已经详细介绍了DQN的两大利器:REPLAY BUFFER(经验回放机制)和冻结Q-target(目标网络,两个网络中用来估计真实Q值的网络),这里给出DQN的伪代码,方便后面的编程实现。
11.1 主循环
DQN伪代码如下:
主循环的主要代码就是上面的更新过程,其它诸如DQN类等代码后续补充。主循环要注意的是,这里的两个网络和REPLAY BUFFER的用处,都是为了切断马尔可夫序列元素之间的关联性。
对于网络的编写、网络参数更新主函数都不做考虑,中间存储到REPLAY BUFFER之后的内容属于学习Q网络的内容,都在DQN类的learn函数中。
这里还添加了神经网络误差虚线的绘制函数,可以的话,也可以修改代码,输出两个网络的全部LOSS变化情况。
from maze_env import Maze
from DQN import DeepQNetwork
def run_maze():
step = 0 # 记录步数,用来提示学习的时间
for episode in range(300):
# 初始化环境
observation = env.reset()
while True:
env.render() # 渲染一帧环境画面
action = RL.choose_action(observation) # DQN根据当前状态s选择行为a
observation_, reward, done = env.step(action) # 与环境进行交互,获得下一状态s'、奖励R和是否到达终态
RL.store_transition(observation, action, reward, observation_) # 将当前的采样序列存储到RF中(s, a, R, s')
# 200步之后开始学习,每隔5步学习一次,更新Q网络参数(第一个网络)
if (step > 200) and (step % 5 == 0):
RL.learn()
observation = observation_ # 转移至下一状态
if done: # 如果终止, 就跳出循环
break
step += 1 # 总步数 + 1
# 游戏结束
print('game over')
env.destroy()
if __name__ == "__main__":
env = Maze() # 创建环境
RL = DeepQNetwork(env.n_actions, env.n_features,
learning_rate=0.01,
reward_decay=0.9,
e_greedy=0.9,
replace_target_iter=200, # 每 200 步替换一次 target_net 的参数
memory_size=2000, # 记忆上限
# output_graph=True # 是否输出 tensorboard 文件
)
env.after(100, run_maze)
env.mainloop()
RL.plot_cost() # 神经网络的误差曲线
11.2 DeepQNetwork类
DQN与Q学习和SARSA有很大不同,主类的代码包含参数初始化、创建网络、存储记忆、选择动作、学习和绘制学习曲线这几个模块。
参数初始化
具体参数含义在代码注释中。
import tensorflow as tf
import numpy as np
class DeepQNetwork:
def __init__(
self,
n_actions,
n_features,
learning_rate=0.01,
reward_decay=0.9,
e_greedy=0.9,
replace_target_iter=300,
memory_size=500,
batch_size=48,
e_greedy_increment=None,
output_graph=False,
):
self.n_actions = n_actions # 动作空间,有几个动作
self.n_features = n_features # 特征的维度,比如迷宫是在迷宫中的位置(length, height),图像的话有可能是(m*n)大小的图片
self.lr = learning_rate # 学习率,参数更新效率
self.gamma = reward_decay # 奖励衰减因子
self.epsilon_max = e_greedy # epsilon-greedy的参数,数越大随机性越小
self.replace_target_iter = replace_target_iter # 每隔多少步更新目标网络(第二个网络)
self.memory_size = memory_size # 记忆上限
self.batch_size = batch_size # 每次更新从buffer中取出的记忆数目
self.epsilon_increment = e_greedy_increment # epsilon的值随着时间增加,即随机性减小,探索模式参数
self.epsilon = 0 if e_greedy_increment is not None else self.epsilon_max # 是否开启探索模式,
# 并逐步减少探索次数,epsilon为0证明一开始完全随机
self.learn_step_counter = 0 # 记录学习步数,为了更新目标网络参数
# 初始化全 0 记忆 [s, a, r, s_]
self.memory = np.zeros((self.memory_size, n_features * 2