> 文章列表 > Python-DQN代码阅读(6)

Python-DQN代码阅读(6)

Python-DQN代码阅读(6)

目录

1.代码

(1)导入所需要的包

(2)设置游戏并选择有效的操作

(3)设置模式(train/test)和开始迭代

(4)创建环境

代码总括:

代码分解:

(5)创建存储检查点文件的路径和目录

代码总括:

代码分解:

(6)定义deep_q_learning()函数

代码总括

代码分解

 (7)使用遇到的初始随机操作经验填充重放内存


1.代码

(1)导入所需要的包

# OpenAI Gym库,用于构建强化学习环境
import gym
# Python标准库,用于生成迭代器
import itertools
# 数值计算库,用于处理矩阵和数组
import numpy as np
# Python标准库,用于操作文件和目录
import os
# Python标准库,用于生成随机数
import random
# Python标准库,用于与Python解释器进行交互
import sys
# Matplotlib库的子模块,用于绘制图表
import matplotlib.pyplot as plt
# Google的机器学习框架,用于构建深度神经网络
import tensorflow as tf
# deque:Python标准库,用于实现双端队列  namedtuple:Python标准库,用于创建具名元组
from collections import deque, namedtuple# 自定义的 model 和 funcs 模块,用于实现模型和辅助功能的函数
from model import *
from funcs import *

(2)设置游戏并选择有效的操作

GAME = "BreakoutDeterministic-v4" # "BreakoutDeterministic-v0"# Atari Breakout actions: 0 (noop), 1 (fire), 2 (left) and 3 (right) 
VALID_ACTIONS = [0, 1, 2, 3]

这段代码定义了 Atari Breakout 游戏的名称和有效的动作列表:

  • GAME: 指定 Atari Breakout 游戏的环境名称,用于创建 Gym 环境对象。在这里可以选择不同的版本,例如 "BreakoutDeterministic-v4" 或 "BreakoutDeterministic-v0"。
  • VALID_ACTIONS: 定义了有效的动作列表,包含了可以在游戏中执行的动作的标识符。在 Atari Breakout 游戏中,有效的动作有 4 个,分别对应 "noop" (无操作), "fire" (发射球), "left" (向左移动板) 和 "right" (向右移动板)。

(3)设置模式(train/test)和开始迭代

train_or_test = 'train' #'test' #'train'
train_from_scratch = True
start_iter = 0
start_episode = 0
epsilon_start = 1.0
  • train_or_test: 指定是训练还是测试。可以设置为 'train' 进行训练,'test' 进行测试。
  • train_from_scratch: 指定是否从头开始训练模型。如果设置为 True,则从零开始训练模型;如果设置为 False,则从上一次保存的模型继续训练。
  • start_iter: 指定训练的起始迭代次数。如果从头开始训练,通常设置为 0。
  • start_episode: 指定训练的起始回合数。如果从头开始训练,通常设置为 0。
  • epsilon_start: 指定初始的 epsilon 值,用于 epsilon-greedy 探索策略。epsilon 用于控制在训练过程中随机选择动作的概率,初始值通常设置为较大的值,逐渐衰减以便在训练过程中逐渐减少探索并增加利用。

(4)创建环境

代码总括:

env = gym.envs.make(GAME)print("Action space size: {}".format(env.action_space.n))
observation = env.reset()
print("Observation space shape: {}".format(observation.shape))plt.figure()
plt.imsave("Atari_Breakout1.png", env.render(mode='rgb_array'))env.step(2)
env.step(1)plt.figure()
plt.imsave("Atari_Breakout2.png", env.render(mode='rgb_array'))
env.close() env.step(2)
env.step(0)plt.figure()
plt.imsave("Atari_Breakout3.png", env.render(mode='rgb_array'))
env.close()

这段代码演示了如何创建 Atari Breakout 游戏环境,并在游戏中执行一些动作并保存游戏画面。

  • env: 创建 Gym 环境对象,使用 gym.envs.make() 函数传入游戏名称 GAME
  • env.action_space.n: 打印游戏环境中可执行动作的数量。
  • env.reset(): 重置游戏环境并获取初始观察值。
  • env.render(mode='rgb_array'): 渲染游戏画面并以 RGB 图像格式返回。
  • plt.figure(): 创建一个新的图像窗口。
  • plt.imsave(): 将当前游戏画面保存为 PNG 图像文件。
  • env.step(action): 在游戏环境中执行指定的动作 action
  • env.close(): 关闭游戏环境。

代码分解:

print("Action space size: {}".format(env.action_space.n))这行代码用于打印游戏环境的动作空间(action space)的大小。env.action_space: 游戏环境的动作空间,通常是一个包含可选动作信息的对象。
env.action_space.n: 获取动作空间的大小,即可选动作的数量。
format(env.action_space.n): 将动作空间的大小格式化为字符串。
print("Action space size: {}".format(env.action_space.n)): 打印动作空间的大小信息,并将大小信息插入到字符串中。例如,如果动作空间的大小为 4,则输出类似于 "Action space size: 4" 的信息。
print("Observation space shape: {}".format(observation.shape))这行代码用于打印游戏环境的观察空间(observation space)的形状。observation: 游戏环境返回的观察值,通常是一个包含游戏状态信息的多维数组。
observation.shape: 获取观察值的形状,即数组的维度信息。
format(observation.shape): 将观察值的形状信息格式化为字符串。
print("Observation space shape: {}".format(observation.shape)): 打印观察空间的形状信息,并将形状信息插入到字符串中。例如,如果观察空间的形状为 (84, 84, 4),则输出类似于 "Observation space shape: (84, 84, 4)" 的信息。
plt.imsave("Atari_Breakout1.png", env.render(mode='rgb_array'))这行代码用于保存游戏环境的当前渲染图像为PNG格式的文件。env.render(mode='rgb_array'): 调用游戏环境的 render() 方法,并设置参数 mode='rgb_array',以获取当前渲染图像的RGB数组表示。
plt.imsave("Atari_Breakout1.png", env.render(mode='rgb_array')): 将获取到的RGB数组保存为PNG格式的文件,并命名为 "Atari_Breakout1.png"。这行代码会在当前工作目录下创建一个新的PNG图像文件,并将当前渲染图像保存到该文件中。
env.step(2)
env.step(1)这两行代码执行了游戏环境的两个动作。env.step(2): 执行动作2,对应于向左移动。该动作会让游戏环境中的游戏角色向左移动一步。
env.step(1): 执行动作1,对应于发射火球。该动作会让游戏环境中的游戏角色发射一个火球。
这两个动作的具体效果会根据游戏规则和当前游戏状态而有所不同。
plt.figure()
plt.imsave("Atari_Breakout3.png", env.render(mode='rgb_array'))
env.close()这段代码生成并保存了一张游戏环境的截图。env.render(mode='rgb_array'): 渲染当前游戏环境,并返回一个RGB数组,表示当前游戏画面。
plt.figure(): 创建一个新的图像窗口。
plt.imsave("Atari_Breakout3.png", env.render(mode='rgb_array')): 将RGB数组保存为PNG格式的图像文件,文件名为 "Atari_Breakout3.png"。
env.close(): 关闭游戏环境。在保存截图后,通常需要关闭游戏环境以释放资源。

(5)创建存储检查点文件的路径和目录

代码总括:

# 创建实验目录,用于保存实验结果和模型检查点
# 根据游戏环境的ID生成实验目录的绝对路径
# 例如,如果游戏环境ID为 "BreakoutDeterministic-v4",则实验目录路径为 "./experiments/BreakoutDeterministic-v4"
experiment_dir = os.path.abspath("./experiments/{}".format(env.spec.id))# 创建模型检查点目录
# 在实验目录下创建一个名为 "ckpt" 的目录,用于保存模型检查点
checkpoint_dir = os.path.join(experiment_dir, "ckpt")# 创建模型检查点文件的路径
# 将模型检查点文件保存在 checkpoint_dir 目录下的名为 "model" 的文件中
checkpoint_path = os.path.join(checkpoint_dir, "model")# 如果模型检查点目录不存在,则创建该目录
if not os.path.exists(checkpoint_dir):os.makedirs(checkpoint_dir)

以上代码段用于创建实验目录和模型检查点目录,以便后续保存实验结果和模型参数。

  • os.path.abspath("./experiments/{}".format(env.spec.id)): 根据游戏环境的ID创建一个实验目录的绝对路径。env.spec.id返回当前游戏环境的ID,例如 "BreakoutDeterministic-v4"。
  • checkpoint_dir = os.path.join(experiment_dir, "ckpt"): 在实验目录下创建一个名为 "ckpt" 的目录,用于保存模型检查点。
  • checkpoint_path = os.path.join(checkpoint_dir, "model"): 创建模型检查点文件的完整路径,包括目录和文件名。
  • os.makedirs(checkpoint_dir): 如果模型检查点目录不存在,则创建该目录。

代码分解:

experiment_dir = os.path.abspath("./experiments/{}".format(env.spec.id))这行代码用于生成实验目录的绝对路径。
env.spec.id 是 Gym 环境的 ID,例如 "BreakoutDeterministic-v4"。
通过使用字符串格式化操作,将实验目录的路径设置为当前工作目录下的 "experiments" 目录下,根据 Gym 环境的 ID 创建的子目录。
这样可以将实验结果和模型保存到不同的实验目录中,便于管理和组织。os.path.abspath() 函数用于获取绝对路径,确保路径的正确性。
checkpoint_dir = os.path.join(experiment_dir, "ckpt")这行代码用于生成保存模型检查点的目录的路径。
experiment_dir 是上面生成的实验目录的绝对路径,"ckpt" 是检查点目录的名称。
os.path.join() 函数用于将实验目录路径和检查点目录名称拼接在一起,生成完整的检查点目录路径。
这样可以将模型的检查点文件保存在实验目录下的 "ckpt" 子目录中,便于管理和加载模型。

(6)定义deep_q_learning()函数

代码总括

def deep_q_learning(sess, env, q_net, target_net, state_processor, num_episodes, train_or_test='train', train_from_scratch=True,start_iter=0, start_episode=0, replay_memory_size=250000, replay_memory_init_size=50000, update_target_net_every=10000,gamma=0.99, epsilon_start=1.0, epsilon_end=[0.1,0.01], epsilon_decay_steps=[1e6,1e6], batch_size=32):Transition = namedtuple("Transition", ["state", "action", "reward", "next_state", "done"])# 定义 Transition 元组用于存储状态转换信息,包括当前状态、动作、奖励、下一个状态和完成状态# policy policy = epsilon_greedy_policy(q_net, len(VALID_ACTIONS))# 根据 epsilon-greedy 策略选择动作的函数,接受 q_net 模型和有效动作数量作为参数

代码分解

def deep_q_learning(sess, env, q_net, target_net, state_processor, num_episodes, train_or_test='train', train_from_scratch=True,start_iter=0, start_episode=0, replay_memory_size=250000, replay_memory_init_size=50000, update_target_net_every=10000,gamma=0.99, epsilon_start=1.0, epsilon_end=[0.1,0.01], epsilon_decay_steps=[1e6,1e6], batch_size=32):

这是一个 Deep Q-Learning (DQL) 算法的实现函数,用于训练或测试一个在 Gym 环境中玩 Atari 游戏的智能体。以下是函数参数的详细解释:

  • sess: TensorFlow 会话,用于执行计算图。
  • env: Gym 环境对象,表示待解决的 Atari 游戏环境。
  • q_net: Q 网络,用于估计 Q 值函数的神经网络。
  • target_net: 目标网络,用于生成目标 Q 值的神经网络。
  • state_processor: 状态处理器,用于对环境观察进行预处理。
  • num_episodes: 训练或测试的总轮数(即总的游戏局数)。
  • train_or_test: 指定训练模式还是测试模式,可选值为 'train''test'
  • train_from_scratch: 是否从头开始训练,如果为 True,则 Q 网络的参数将随机初始化,否则将从预训练模型加载参数。
  • start_iter: 开始训练的迭代次数,用于从指定的迭代次数开始训练。
  • start_episode: 开始训练的游戏局数,用于从指定的游戏局数开始训练。
  • replay_memory_size: 回放内存的大小,用于存储训练样本的经验回放缓冲区的大小。
  • replay_memory_init_size: 初始回放内存大小,用于指定在训练开始前先填充回放内存的经验样本数。
  • update_target_net_every: 更新目标网络的频率,表示每隔多少次训练更新一次目标网络。
  • gamma: 折扣因子,用于计算目标 Q 值的折扣累积奖励。
  • epsilon_start: 初始 epsilon 值,用于指定 epsilon-greedy 探索策略中的随机探索概率。
  • epsilon_end: epsilon 最终值的列表,用于在训练过程中动态调整 epsilon 的最终值。
  • epsilon_decay_steps: epsilon 衰减步数的列表,用于指定在训练过程中每个阶段的衰减步数。
  • batch_size: 每次训练时的批次大小,用于指定从回放内存中抽样的样本数量。

这个函数实现了 DQL 算法的主要逻辑,包括训练过程中的经验回放、Q 值更新、目标网络更新等。在训练模式下,函数会根据指定的参数和超参数进行训练,更新 Q 网络和目标网络的参数,保存训练模型到指定的检查点目录。

replay_memory_size=250000, replay_memory_init_size=50000replay_memory_size 是回放缓存(Replay Memory)的最大容量,用于存储训练过程中的经验数据(Experience Data)。
经验数据是由环境产生的状态、动作、奖励和下一个状态等信息组成的元组,用于训练深度 Q 网络。replay_memory_init_size 是回放缓存的初始大小,表示在训练开始前需要先向回放缓存中添加多少个经验数据。这样做的目的是为了使初始经验数据更加丰富,增加模型的训练稳定性和效果。例如,在这个代码中,replay_memory_size=250000 表示回放缓存的最大容量为 250,000 个经验数据,replay_memory_init_size=50000 表示在训练开始前向回放缓存中添加 50,000 个经验数据。这样,在训练开始时,回放缓存已经包含了一定数量的经验数据,有助于提供丰富的样本数据供深度 Q 网络进行训练。随着训练的进行,环境产生的新经验数据会被添加到回放缓存中,并用于更新深度 Q 网络。
update_target_net_every=10000update_target_net_every 是用于控制目标网络(Target Network)更新频率的参数。
在深度 Q 网络中,目标网络是用于计算目标 Q 值的网络,其参数在训练过程中相对固定,而主网络(Q 网络)则通过训练不断地更新参数。为了增加训练的稳定性,常常会采用目标网络的延迟更新策略,即每隔一定的训练步数或训练周期,更新目标网络的参数。
update_target_net_every 参数就是用来控制目标网络更新的频率,表示经过多少个训练步数或训练周期后进行一次目标网络的参数更新。例如,在这个代码中,update_target_net_every=10000 表示每经过 10,000 个训练步数,就进行一次目标网络的参数更新。这个值可以根据具体问题和模型的性能进行调整,以获得最佳的训练效果。较大的值可能会减少目标网络的更新频率,从而降低训练的速度但提高训练的稳定性;较小的值可能会增加目标网络的更新频率,从而加快训练的速度但降低训练的稳定性。
epsilon_end=[0.1,0.01]epsilon_end 是一个列表,用于指定训练过程中 epsilon 的最终值。
在 epsilon-greedy 探索策略中,epsilon 控制着在训练过程中随机选择动作的概率,从而平衡探索和利用的权衡。epsilon_end 列表中包含两个值,分别表示在训练过程的两个阶段的 epsilon 最终值。
具体而言,第一个值表示在训练的前半部分阶段的 epsilon 最终值,第二个值表示在训练的后半部分阶段的 epsilon 最终值。
这样设计的目的是在训练初期更加侧重于探索,随着训练的进行,逐渐减小探索概率,增加利用概率。例如,在这个代码中,epsilon_end=[0.1,0.01] 表示在训练的前半部分阶段,epsilon 最终值为 0.1,而在训练的后半部分阶段,epsilon 最终值为 0.01。这意味着在训练初期,随机选择动作的概率较高(10%),随着训练的进行,随机选择动作的概率逐渐减小,增加选择 Q 值最高的动作的概率,从而更加倾向于利用已学到的知识。
epsilon_decay_steps=[1e6,1e6]epsilon_decay_steps 是一个列表,用于指定训练过程中 epsilon 的衰减步数。
在 epsilon-greedy 探索策略中,epsilon 控制着在训练过程中随机选择动作的概率,从而平衡探索和利用的权衡。epsilon_decay_steps 列表中包含两个值,分别表示在训练过程的两个阶段的 epsilon 衰减步数。
具体而言,第一个值表示在训练的前半部分阶段的 epsilon 衰减步数,第二个值表示在训练的后半部分阶段的 epsilon 衰减步数。
这样设计的目的是在训练初期更加侧重于探索,随着训练的进行,逐渐减小探索概率,增加利用概率。例如,在这个代码中,epsilon_decay_steps=[1e6,1e6] 表示在训练的前半部分阶段,epsilon 衰减步数为 1,000,000 步,而在训练的后半部分阶段,epsilon 衰减步数仍然为 1,000,000 步。这意味着在训练初期,随机选择动作的概率会随着步数的增加而减小,增加选择 Q 值最高的动作的概率,从而更加倾向于利用已学到的知识。在训练后期,随机选择动作的概率将保持较低水平,以便更多地利用已学到的知识。
batch_size=32batch_size 是用于指定每次训练时从经验回放缓冲区(replay memory)中抽取的样本数的参数。
在深度 Q 网络的训练过程中,通常使用经验回放(Experience Replay)技术,将智能体在环境中的历史经验存储在缓冲区中,然后从中随机抽取一定数量的样本用于训练。batch_size 参数决定了每次训练时从经验回放缓冲区中抽取的样本数。较大的 batch_size 值可以加速训练过程,因为每次更新都使用了更多的样本信息,但也会增加计算和内存开销。较小的 batch_size 值可以减小计算和内存开销,但可能导致训练过程中的随机性较大。一般来说,batch_size 的取值应根据具体问题和模型的性能进行调整,以获得最佳的训练效果。常见的取值范围通常在几十到几百之间,具体取值可根据硬件资源和训练效果进行调优。
Transition = namedtuple("Transition", ["state", "action", "reward", "next_state", "done"])这行代码定义了一个命名元组(namedtuple)的类型,名为 Transition,并指定了其包含的字段。Transition 类型用于表示深度 Q 网络在训练过程中的经验回放缓冲区(replay memory)中的每个经验样本。命名元组是一种类似于普通元组的数据结构,但可以通过字段名进行访问,使得代码更加具有可读性和可维护性。在这里,Transition 类型包含了五个字段:state: 当前状态(state)的表示,用于输入到深度 Q 网络进行训练。
action: 当前状态下选择的动作(action)的索引或标识。
reward: 在当前状态下执行动作后获得的奖励(reward)。
next_state: 在执行当前动作后,进入的下一个状态(next state)的表示。
done: 表示当前状态是否为终止状态(done),即是否结束了一个回合。
通过使用命名元组 Transition,我们可以在深度 Q 网络的训练过程中将每个经验样本表示为一个具有字段名的对象,从而使得代码更加清晰和易于理解。
policy = epsilon_greedy_policy(q_net, len(VALID_ACTIONS))这行代码定义了一个 epsilon-greedy(epsilon-greedy policy)用于在深度 Q 网络训练过程中选择动作。epsilon-greedy策略是一种用于在探索和利用之间平衡的策略。
在每个时间步,epsilon-greedy策略会以概率 ε 选择一个随机动作(探索),以概率 1-ε 选择一个基于当前 Q 值估计的最优动作(利用)。epsilon_greedy_policy 函数接受两个参数:q_net: 深度 Q 网络,用于估计 Q 值。
len(VALID_ACTIONS): 动作空间的大小,即有效动作的数量。
该函数返回一个函数对象,该函数可以接受当前状态的 Q 值估计作为输入,并返回根据 epsilon-greedy策略选择的动作的索引或标识。在深度 Q 网络的训练过程中,可以调用该函数来选择动作并执行在当前状态下选择的动作。

(7)使用遇到的初始随机操作经验填充重放内存

# populate replay memory
if (train_or_test == 'train'):print("populating replay memory")replay_memory = populate_replay_mem(sess, env, state_processor, replay_memory_init_size, policy, epsilon_start, epsilon_end[0], epsilon_decay_steps[0], VALID_ACTIONS, Transition)

这段代码用于在训练模式下填充回放内存(replay memory)。

回放内存是一种用于存储过去的状态、动作、奖励和下一个状态的数据结构,用于训练深度 Q 网络。在强化学习中,使用回放内存来存储和重用过去的经验,从而可以更好地训练深度 Q 网络,并提高其学习效率和稳定性。

populate_replay_mem 函数接受以下参数:

  • sess: TensorFlow 会话对象。
  • env: OpenAI Gym 环境对象。
  • state_processor: 状态处理器对象,用于对状态进行预处理。
  • replay_memory_init_size: 回放内存的初始大小,即在开始训练前需要填充的经验数量。
  • policy: 用于选择动作的策略函数。
  • epsilon_start: 初始 epsilon 值,用于 epsilon-greedy策略。
  • epsilon_end[0]: 最终 epsilon 值,用于 epsilon-greedy策略。
  • epsilon_decay_steps[0]: epsilon 值的衰减步数,用于 epsilon-greedy策略
  • VALID_ACTIONS: 有效动作的列表。
  • Transition: 回放内存中存储的经验数据的命名元组。

在调用 populate_replay_mem 函数时,会根据给定的参数生成一些随机的初始经验,并将其添加到回放内存中,直到回放内存的大小达到设定的初始大小。这样,深度 Q 网络在训练之前就可以从回放内存中获取一些经验数据用于学习。

epsilon_end[0], epsilon_decay_steps[0]在代码中,epsilon_end[0] 和 epsilon_decay_steps[0] 表示列表 epsilon_end 和 epsilon_decay_steps 中的第一个值。这是因为这两个列表都包含了两个值,分别表示训练初期和训练后期的参数。例如,epsilon_end = [0.1, 0.01] 表示 epsilon_end 列表中的第一个值为 0.1,第二个值为 0.01。
而 epsilon_decay_steps = [1e6, 1e6] 则表示 epsilon_decay_steps 列表中的第一个值为 100 万,第二个值也为 100 万。通过使用索引 [0],可以访问这两个列表中的第一个值,即 epsilon_end[0] 表示训练初期的最终 epsilon 值,epsilon_decay_steps[0] 表示训练初期的 epsilon 衰减步数。