正在学习基础强化学习,心血来潮写了一下这个,算是学习心路历程的记录,比较简单和粗糙。效果也不是很好,有很多问题
整个2048的游戏环境是我自己自己实现的,游戏的基础逻辑实现在Game.py中,直接运行Game.py即可游玩,用w、a、s、d分别代表上下左右来游玩吧!
在游戏环境的基础上实现了强化学习的环境,在Env.py中,继承了2048的Game类,加上了step和奖励的设置,我设计的奖励机制是每一步智能体合成的方块点数的log2+移动后(在移动后和游戏随机生成棋子之前)棋盘上空位置数量的$ \frac{1}{4} $,注释中还保留了很多奖励的尝试,但是感觉效果其实都差不多。另外,加上了无效移动的惩罚,无效移动会直接将这一次移动的奖励设置为-1,游戏结束时的惩罚由棋盘上最大的棋子数决定。
actor和critic网络都建立在一个基础网络BaseNet之上,BaseNet的输出经过PolicyNet和ValueNet之后分别输出一个$ batch_size \times 4
PPO的实现没什么好说的,就是借鉴别人的代码然后做一些修改。 但是在智能体采取行动的代码上我想做一些修改,在我PPO代码中我一开始设计的采取行动的代码是这样的:
def take_action(self, state):
try:
state = torch.tensor([state], dtype=torch.float).to(self.device)
probs = self.actor(state)
action_dist = torch.distributions.Categorical(probs)
action = action_dist.sample()
except:
print("NaN detected in probs, taking fallback action")
action = torch.tensor([torch.randint(0, 4, (1,))])
return action.item()但是这样训练出来的模型在实际的测试中只会选择某两个特定的方法,经过老师的指点我顿悟,智能体在一开始中采取了某两个方向的动作后由于获得了奖励因此会增大采取这两个动作的概率,由于我categorical的建模方式,会导致采取这两个动作的概率越来越大,导致最后不会采取别的动作了,因此我在utils中实现了一个epsilon-greedy策略中的epsilon迭代器,保证模型在迭代一段时间后还能有一定概率采取随机行动,但是具体的迭代策略还没想好,因此还没投入训练,后续有精力了也许会更新吧。
def take_action_with_random_choice(self, state):
epsilon = self.epsilon_optimizer.get_epsilon()
if random.random() < epsilon:
action = torch.tensor([torch.randint(0, 4, (1,))])
else:
try:
state = torch.tensor(
[state], dtype=torch.float).to(self.device)
probs = self.actor(state)
action_dist = torch.distributions.Categorical(probs)
action = action_dist.sample()
except:
print("NaN detected in probs, taking fallback action")
action = torch.tensor([torch.randint(0, 4, (1,))])
return action.item()训练参数都在PPO_CONFIG.py中给出了。
直接运行train.py就可以了,会自动保存和加载已经存在的checkpoints
其实有一个令我很震惊的东西,我一开始训练的一个版本中看到了比较好的效果,但是后来发现网络架构除了一点问题,我在CNN输出$ batch_size \times 12 \times 4 \times 4
这是第一版我觉得网络有问题的训练过程
这是第二版我将网络修改之后的训练过程,这完全就是没有在训练嘛。
有点不是太理解这是什么原因。