DQN算法可以用于解决离散的动作问题,而FlappyBird的操作正好是离散的。
FlappyBird的游戏状态一般可以通过图像加卷积神经网络(CNN)来进行强化学习。但是通过图像分析会比较麻烦,因为每次只输入一帧图像可能是不够的,因为一帧的图像分析不出小鸟的速度信息,所以通常需要几帧图像一起送入CNN分析。这样的方法麻烦又复杂,仔细观察一下游戏。
发现有三个信息对于小鸟能够顺利飞过水管比较重要:
- 小鸟的水平速度。
- 小鸟与它前面一对水管中下面那根水管的水平距离。
- 小鸟与它前面一对水管中下面那根水管的顶端的垂直距离。
而其他的信息,比如:已经飞过了的水管等信息就不重要了。
好了,思路已经有了,开始准备环境。
一、安装FlappyBird
ntasfi/PyGame-Learning-Environmentgithub.com提供了一个FlappyBird游戏的实现,按照着文档中Installation部分的提示安装就可以了。安装完了后还不能直接使用,原因如下:
- 需要根据游戏的状态提取上面提到的那三个重要的状态信息。
- 封装成和gym环境类似的接口,使用起来也方便一下。
封装好后的核心代码及注释如下:
def _get_obs(self):
# 获取游戏的状态
state = self.game.getGameState()
# 小鸟与它前面一对水管中下面那根水管的水平距离
dist_to_pipe_horz = state["next_pipe_dist_to_player"]
# 小鸟与它前面一对水管中下面那根水管的顶端的垂直距离
dist_to_pipe_bottom = state["player_y"] - state["next_pipe_top_y"]
# 获取小鸟的水平速度
velocity = state['player_vel']
# 将这些信息封装成一个数据返回
return np.array([dist_to_pipe_horz, dist_to_pipe_bottom, velocity])
完整FlappyBird状态获取封装的代码在:
flappybird_wrapper.pygithub.com二、实现DQN算法
因为最终实现的代码比较长,这里只贴算法实现中最核心的learn代码及注释:
def learn(self, writer):
# 根据经验进行学习
# 确保经验池里的经验以及足够多时,才进行学习
assert WARMUP_SIZE >= BATCH_SIZE
if len(self.memory) < WARMUP_SIZE:
return
# 从经验池中选取BATCH_SIZE条经验出来
batch = random.sample(self.memory, BATCH_SIZE)
batch = Transition(*(zip(*batch)))
# 把这些经验都转换位Tensor
s0 = torch.FloatTensor(batch.state).to(device)
a0 = torch.LongTensor(batch.action).to(device).unsqueeze(1)
r1 = torch.FloatTensor(batch.reward).to(device)
s1 = torch.FloatTensor(batch.next_state).to(device)
d1 = torch.LongTensor(batch.done).to(device)
# DQN算法
q_pred = self.model(s0).gather(1, a0).squeeze()
with torch.no_grad():
if USE_DBQN:
acts = self.model(s1).max(1)[1].unsqueeze(1)
q_target = self.target_model(s1).gather(1, acts).squeeze(1)
else:
q_target = self.target_model(s1).max(1)[0]
q_target = r1 + GAMMA * (1 - d1) * q_target
loss = self.loss_func(q_pred, q_target)
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
# 记录每次学习的loss
writer.add_scalar('train/value_loss', loss.item(), self.update_count)
self.update_count += 1
# 每MODEL_SYNC_COUNT同步一下目标模型
if self.update_count % MODEL_SYNC_COUNT == 0:
self.target_model.load_state_dict(self.model.state_dict())
在这段代码中,可以通过设置变量USE_DBQN为true和false来控制是否使用DQN的变种算法Double-Q Learning来训练模型。同时,代码还借助tensorboard来记录在训练过程中:完成步骤数(finish_step)、每局获得奖励的总数(total_reward)以及每次学习时loss的值(value_loss)来观察训练的过程。
从tensorboard的截图中可以看出,随着训练的进行,每局游戏结束时的步骤数(finish_step)越来越多,表明算法在正确的进行训练和提高。
最后,所有的代码都可以在github中查看(包括tensorboard的记录数据):
dqn_flappybirdgithub.com