这段时间没怎么更新专栏,倒不是什么事情都没做,只是觉得做的东西都没什么意思,加上强化学习确实很玄学(或者说我的知识水平有限),很多运行出来的结果我都没办法合理解释,所以没什么更新的动力。
从上周开始我打算试试看使用强化学习去解决2020年数学建模比赛的B题,于是我遇到的第一个问题就是去描述水以及食物的购买。我的想法是每次在智能体决定买水或者食物时,在调用一个专门的智能体来判断要买多少水和食物,最终返回的奖励为整局游戏的收益。这样的话,如果动作空间直接采用连续的,那最后就要取整,而且还要编写掩码函数来判断状态是否违背了约束条件,实现起来比较麻烦,而且这个方案听起来就很不靠谱。如果动作空间直接采用离散的话,那就有上万个动作,就更不现实了。
最终,我打算通过把上万个动作拆成若干步只有几个动作的决策过程,具体的实现类似于:选取初始状态为(x,y)=(0,0),动作0:(x,y)保持不变,结算收益,动作1:(x,y)变为(x+1,y),(单步的)奖励为0,如果使用动作后超过了约束条件,则尽量使得状态符合约束条件并结算,动作2:(x,y)变为(x,y+1),描述和1类似,然后算法选择任意一个DQN方法,这里我选择的是DDQN。
为了验证这个方案是可行的,我打算先测试一些简单的例子,于是我先定义了如下环境:假设水的价格/重量为5/3,食物的价格/重量为10/2,资金为10000,负重上限为1200,最终的收益为
输入为当前水的数量以及食物的数量
输出动作为
动作1:买20箱水
动作2:买10箱水
动作3:买5箱水
动作4:买1箱水
动作5:买20箱食物
动作6:买10箱食物
动作7:买5箱食物
动作8:买1箱食物
动作9:买50箱水
动作10:买50箱食物
动作11:结算
可是最后结果是糟糕的,智能体总会在不断增加试探次数后,忽然陷入什么都不买的摆烂状态,如下图所示
我对这个结果感到很困惑,于是决定先考虑一个简单的情况,可是这个简单的情况仍然出现了这种现象,我调了好久的参数结果都是一样的,于是我上了知乎提问:
https://www.zhihu.com/question/664217058
,虽然不知道是不是这个奖励函数有助于智能体把游戏继续下去,探索未知的状态这种原因,总之这次这个简单的情况确实可以求得最优了。
可是我同样的修改上面的目标函数后并限制了最大的步数后,智能体在训练结束后总会选择一个很差的解(比如连续五次买一箱水然后结算)或者干脆像之前一样摆烂。
,我希望通过这种方式让智能体优先选取变动较大的动作。为了避免后者,我加大了buffer_size
这样修改后,运行结果类似于下图
虽然步数还是在不断上升,忽然摆烂的情况仍然存在,但是因为buffer_size的增加,智能体仍然可以从之前的情况去学习,不至于一摆到底。
最后,因为迭代的结果不是很稳定,所以我改写了xuance自带的DDQN_Agent类,在训练固定的步数后就进行测试以记录最佳的成绩,如下图所示
一开始我以为训练的大幅度波动是ε贪心策略随机选取动作导致的,从测试结果看来就算动作选取是固定的,迭代仍然存在很严重的波动。
,而是选取了,这样智能体就必须采取+50以外的动作才能达到最优,选取初始状态为(0,0),最后智能体返回的状态为(174,215),我认为这已经是一个相对可以接受的结果了。即使不选择(0,0)作为初始状态,而是选取比较小的状态作为初始状态,智能体最后也能返回一个相对可以接受的结果,所以我认为虽然训练的波动大的离谱,但这个训练还算是成功的。
代码不复杂,如果有人希望复现可以使用我贴在下面的代码
实现购买模型的代码:
在训练固定的步数后就进行测试以记录最佳的成绩DDQN:
看起来很长,然而只是照抄了xuance的源码,然后自己做了一些小改动
训练的代码:
其中配置文件BestRecordTest.yaml如下
使用这段代码,在我的电脑(i7,RTX4070)上运行大概半个小时就可以完成训练
通过下面的代码进行测试: