问题定义
黄金点游戏是源于经济学家Richar Thaler构思的在1997年伦敦金融时报进行了一次公开竞猜活动。MSRA-ASE课程的第一次结对编程中,我们写了一个AI Bot来与大家玩儿这个游戏。
问题的定义?
Golden Number的问题实际上是一个多人博弈问题,基于当前对局情况比赛者需要提交一个或两个数来尽可能靠近黄金点,同时自己提交的数又影响着黄金点的大小。实际上随着设定的比赛轮数、人数、提交数,这个游戏的场景非常多变。目光短浅地来看,可以抽象为这样一个问题,从一局比赛开始,当前回合之前所有轮的黄金点、各位玩家提交的数及得分是Bot可以获取的信息,根据这些复杂的信息,Bot需要预测两个数,使得其中有一个尽可能靠近下一轮黄金点。高瞻远瞩一点来看,Bot的目的是所有回合结束后的总分比其他玩家高,而由于奖惩机制的特殊性以及当前的提交数对未来的若干轮次的影响,Bot不一定要每次都采取短浅的策略。
为了不陷入囚徒困境,看上去基于历史黄金点做曲线分析与拟合,或者设置策略,会是一个比较靠谱的选择。
问题的难点?
- 环境未知且随时间变化,加上规则设定使之复杂且特殊。
- 复杂性的来源很多,包括奖惩机制、选手策略的未知性。
- 特殊性在于不同的房间、不同回合数阶段等可能呈现很不一样的状态。
- 短期记忆与长期记忆的重要程度要怎么分配。作为一个时间序列,而且最终目的是总分最多,输一轮最多-2,那么模型的前瞻性是否重要?要如何设计?
方法建模
方法基于一个强大的假设:大家都是正常人,采取常规策略,黄金点趋势应该是正常的,在一定范围内波动。并且大多数情况下,有以下两个很重要的性质:
- 在一定范围内出现的概率类似于正态分布
- 周期性波动,且“周期”随回合进展而改变
(想下去就要晕了,于是决定快刀斩乱麻直接暴力一点)不考虑model的太长期的记忆(鸵鸟策略:),不考虑其他人用什么策略,就只观察一定时间内的黄金点。在一些简单假设和观察之后就可以开始Coding了。
Deep Q Learning
DQN还要从Q-learning说起。Q-learning是一种off-policy的方法,也就是将智能体Agent放在环境Env中,根据某状态下某动作获得的Reward来学习一张State-Action表(Q表)。对于状态空间和动作空间很小的类似于迷宫之类的问题,Q-learning是一个有效的解决方案。
由于问题状态空间的扩大,Q-table的维护逐渐演变为Q值函数近似(Function Approximation)。用值函数Q(s, a)来预测s状态下所有动作的Q值可以缓解这一维度灾难,但是我们需要建立合适的神经网络来拟合这一函数:
\[ q(s, a, \boldsymbol{w}) = q_{\pi}(s, a) \]
从此,Q-table就转变为了Q-network,我们需要除了要在与环境交互过程中了解State, Action, Reward之外,还要注意为network提供训练样本以及设计损失函数。具体算法如下:
基于DQN的算法设计
Model的Pipeline如下图,矩形框中的是test过程,其他的是train过程:
黄金点以下简称为GN。
关于运行的几个问题?
- 记忆重放:学习就是DQN训练的过程,采用的是Memory replay的方式,也就是说训练的样本来源于记忆库而并不是实时的输入。
- 动作选择:动作选择实际上就是利用Network根据当前状态预测动作Q-value的过程,同时也应用了Greedy的策略来增加样本及网络的鲁棒性。
- 权重更新:target_net和eval_net的形状一样,更新方式就是在eval_net更新若干轮(如10轮)之后直接把其参数导入进来就行了,所以更新频率低一些比eval_net滞后一些
- 超参数设置:主要的超参数有记忆库容量\(memory\_capacity\), 随机采样的个数\(batch\_size\),衰减率\(\gamma\),学习率\(\alpha\),贪心策略\(\epsilon\). 除此之外由于游戏的特殊性还有一些关于回合数的其他参数。
State的设定?
基于我们强大的假设,GN应该会在一定范围内波动(0-6),并且呈现出一种正态分布的感觉,靠近中间的数出现概率大一些。由于对手策略未知并且分析难度太大,在经过一些实验之后我们选择了采用前十轮的黄金点作为状态输入,忽略了单个对手的输出与得分(其实这里有一点点欠妥,只能说凭感觉了)。
\[ state = historyGoldenNumberList[-10:] \]
Action的设定与选择?
在模型调整过程中我们发现action的设定是对结果影响非常非常非常大的一环。我们通过预测的Q值,选取其中最大的两个Q对应的action来生成两个数作为本轮的提交数。根据黄金点波动的周期性,总共设置了7个策略(action 0-6),策略不能太多,不然DQN会很难训,效果不好,而且还多余:
| Action | Number |
| ---- | ---- |
| 0 | 前一轮GN |
| 1 | 前两轮GN的均值 |
| 2 | 前三轮GN的均值 |
| 3 | 前十轮中每隔1轮进行采样后的GN的均值 |
| 4 | 前十轮中每隔2轮进行采样后的GN的均值 |
| 5 | 前十轮中最大的三个的GN的均值 |
| 6 | 前十轮中最小的三个的GN的均值 |
Network结构
我们用历史的十轮黄金点为时间序列作为网络输入的一个样本,经过FC层后进入一个GRU再经过attention得到输出。
扰动黄金点曲线
一定轮数之后,模型会给一些很大的数来扰一下曲线,但是这个策略做的比较粗糙,没有好好分析扰动之后若干轮的结果并加以利用。
结果分析
我们组是Bot4,第一次比赛有十个Bot,得了倒数第四(orz),得了几千分。后来把网络各种参数缩小,减少输入的信息量,动作的定义更加明确之后,在11个Bot的激烈大战10000轮下得了第二名,总分13k多一些吧。结果emmmmm其实一开始没有什么预期,感觉就很random。
- 根据训练结果发现,网络渐渐收敛之后,采取最多的动作是0,5,6,其次是1,3。多轮之后动作4几乎很少出现。
- 查看了其他人提交的数和黄金点变化趋势,GN曲线的形状更预想的还是大致相似的,其他一些Bot采取的一些提交很大的数字的策略也获得了很多分,我们虽然采取了但是没有在这上面花很多时间思考与建模,总体上还是保守估计。
模型评估
- 模型评估应该只能看比赛结果吧...而且不同实验环境(参赛选手、回合总数)下相同模型可能存在很大差距,需要很多实验。写Bot的时候由于时间因素,只能跑几百轮或者几千轮,观察action出现的频率、黄金点、预测值的分布和差距、其他Bot的输出值,然后可以调整模型I/O和一些参数。
- 我们的模型适合提交更多个数的情况,并且这样的话可以有更多操作空间。
- 我们的模型也适合更多人的比赛,但是如果有很多捣乱策略出现可能会造成影响,毕竟RL model鲁棒性可能不是那么强。
模型改进
- 模型设置的调整:经过更多次实验之后我们可以根据action出现的频率来进行调整action的设置,根据得分的频率和周期来调整state的的设置,也可以调整其他超参数。
- 扰动策略:制造一些特定情况,然后在特定情况下得分,这种策略如果用好了其实收益很大。
- 策略的周期性变化:有一些简单策略在模型开始的时候非常有用,有一些在很多论之后会厚积薄发。
- 其他策略: 是否可以用Network只预测一个数,另一个数采取简单策略给出作为Network的输入,这样我们就可以让预测的那个数配合另一个数来生成,给出大数的时候说不定有奇效。或者使用policy-gradient的模型做RL,毕竟状态空间无穷,可能比DQN更适合。
- “黄金点游戏”很有意思,而且蕴含丰富的哲理。但是不建议为了改进比赛效果而多想,我觉得这个问题是一个时间黑洞,并且no silver bullet.
Hello partner
队友太强了!她改的Beta版本吊打Alpha版本。之前我们没用过RL的算法,我甚至也没有接触过这中time-based的序列模型,所以network也来源于她的设计。我们面对面讨论的时候还是很高效的,但是由于这次结对编程没有讨论环境所以很多时候只能打字、语音、共享屏幕,所以两个人写代码的时候还是出了很多bug,(强行三明治的话)只能说队友好忙(tql),其实提前协商一下我们的工作时间,要是能多一些时间一起优化的话会更好。