再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(8) —— 2021年9月SOTA的TDL算法——《Optimistic Temporal Difference Learning for 2048》——完结篇
《2048》游戏在线试玩地址:
如何解决《2048》游戏源于外网的一个讨论帖子,而这个帖子则是讨论如何解决该游戏的最早开始,可谓是“缘起”:
What is the optimal algorithm for the game 2048?
关于该游戏的相关内容前面已经写过一些内容:
再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(1) —— Firefox浏览器下自动运行游戏篇
=====================================================
之前曾经写过一篇《2048》游戏TD解法的SOTA论文的博客:
再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(4) —— state-of-the-art
不过,后来发现上面这个博客的论文:
Mastering 2048 with Delayed Temporal Coherence Learning, Multi-Stage Weight Promotion, Redundant Encoding and Carousel Shaping
在2021年9月被论文《Optimistic Temporal Difference Learning for 2048》所超越,其实超越的幅度并不大,在游戏达到2**15=32768方块数值的回合数episodes上高出了2%,而这个成绩的获得也伴随着更多的训练episodes,总的来看训练的时间应该增加了几倍到几十倍的区间。
其实不管是上个SOTA的论文《Mastering 2048 with Delayed Temporal Coherence Learning, Multi-Stage Weight Promotion, Redundant Encoding and Carousel Shaping》
,还是现在的SOTA论文《Optimistic Temporal Difference Learning for 2048》,与最早的《2048》游戏的TD解法论文《Temporal difference learning of N tuple networks for the game 2048》其实改动并不大,就与现在SOTA的论文《Optimistic Temporal Difference Learning for 2048》为例,其实最大的改动就是对N-Tuple网络初始化时不设置为0而是设置为一个较大的数值,但是即使如此这两个论文的代码都难以复现和重新运行,当然最原始的论文代码还是可以很好运行的,其python版本个人还做了复现,见:再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(7) —— Python版本实现的《2048》游戏的TDL算法。
论文代码的难以运行说明:
之所以现在SOTA的实现都难以运行,主要原因就是运算周期太长,这里以现在的SOTA论文《Optimistic Temporal Difference Learning for 2048》为例,其官方实现见:https://github.com/moporgic/TDL2048
可以看到,该代码的实现基本是C++然后加上各种并行和宏指令,代码难度较大,而且按照论文中的200M或300M个episodes的运行,其耗时是极为巨大的。根据官方的代码,使用i7-10700k cpu在5.0Ghz频率下单核运行1000k个episodes耗时25分钟,那么运行200M个episodes需要耗时(25*200)/60/24=3.472天,当然论文中指出在E5-2698 v4的CPU上运行66 小时就可以,但是这里需要知道,这是需要在企业级CPU上多核心并行运行而且不考虑内存不够用的前提下,然后需要66小时,而如果你是拿一个家用级别的CPU然后单核心运行,那么你就需要至少一个月到两个月的时间才可以运行完,而且注意,这里只是运行单次的时间。而且该论文的官方代码基本优化到了就差上汇编指令了,基本上可以认为是优化到了最快速度了,至于以后能不能用量子计算机来提速就不知道是否可行了。不得不说搞AI这么久,这么耗时的程序除了“阿拉法狗”以外这是我遇到的最耗时的了。

但是不得不说,官方给出的代码确实是优化得很好了,不过这么长的运行时间基本可以劝退其他人了。
学术价值上的思考:
《2048》游戏的解法最早是一群IT开发的程序员所提出并进行了大量讨论,最后还给出了启发式的解法。而后期的Reinforcement learning解法都是基于TD+n-tuple net的,可以说学术价值不大,并没有什么太多的学术问题存在,即使是现在SOTA的论文也是改动很小,基本就是把已经成形的算法移过来用上,但是即使是算法逻辑简单,但是工程实现方面却要求很大。以我实现的最原始的TD解法:再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(7) —— Python版本实现的《2048》游戏的TDL算法来看,我运行100k个episodes的用时为5个小时,当然这是python版本实现的,运行效率自然不会太高,但是如果用这个版本就像200M个episodes的运行,那么需要时长为:(5*2000)/24=416.66天,即10000小时,这个运行时长基本可以用年来计算了,而作者给出的官方C++版本深度指令优化版本在企业级服务器并行运行需要66小时,其差距为151.5倍,当然C++版本的比python版本的快100倍也能理解,但是这个时长是真的难以接受,即使你用官方的优化后代码然后并行运行在企业家服务器上也是至少需要66小时。当然这个算法这么耗时主要原因就是在进行《2048》游戏时交互后游戏执行所耗费的时间,即使使用Intel cpu专属的位移指令最后的耗时也是难以接受的,200M个episodes再怎么优化这耗时也难以降的下来,不过这也是少见的一种需要如此多训练episodes的TD算法应用了。由于《2048》游戏的TD解法学术性不高并且高效C++版本实现如此困难再加上如此长的运行耗时,这个《2048》游戏的最优解法可能也就进展到这了,再继续探究这个好像意义价值也确实不大了。
关于TD+n-tuple net 的注意点:
在《2048》游戏的TD解法中,都是使用afterstate状态,也就是一次移动后游戏环境没有自动添加tile的那个状态,用这个状态来进行估计estimation。
由于使用的是n-tuple net,也就是查表法,不同于函数的近似表示,对于一条episode中的任意一个afterstate状态的更新对于其他afterstate状态的估计和更新影响不大,因此在所有的解法中都是获得整条episode的数据后再从最后的afterstate状态开始向前更新对应的n-tuple net中的数值的。从最后的afterstate状态开始向前更新,要比从第一个afterstate状态开始向后更新效率更高,因为最后一个afterstate的估计值为0,这样计算误差更低并且可以快速的向前修正整个episode中的afterstate的估计,整个update过程会更加高效。而之所以函数近似的TD算法都是使用一次估计然后执行,然后更新,这样是因为使用函数近似来表示状态数值的话对任意一个afterstae的更新都会对所有afterstate的估计产生影响,因此这样的更新方式更高效,而这里我们使用的是n-tuple net查表法,每个afterstate之间的关联度都不大,由于n-tuple net的规模很大,即使在一条episode中有些afterstate有共用的value,其影响度也极为有限。因此,在TD+N-Tuple net的配置中,很重要的一点就是搜集完整条episode数据后从后向前更新afterstate状态的估计值,这一点关键。
借鉴意义:
即使前面说了《2048》游戏最优解法的学术意义不大,继续研究和探讨的意义不大,不过这个问题所引起的一系列的讨论和解法确实还是蛮有趣的,而且对我们也有蛮的启发和借鉴意义的。
论文《Optimistic Temporal Difference Learning for 2048》的主要内容介绍:
主要工作:
1. 对n-tuple net的权重不再初始化为0或随机,而是初始化为一个较大的数值。由于我们都是使用episode数据从后向前更新afterstate估计,因此这样的初始化可以使算法对于没有执行过的状态给与更大的被选择机会。
2. 引入TD算法的优化学习率的TC算法,构成混合算法,也就是TD算法执行10%个episode后执行TC算法,TC算法的初始学习率为1。
3. 使用多阶段的方式,即分层强化学习,比如100M个episodes为一个阶段,使用两个阶段,即200M个episodes。第一个阶段,大数值初始化N-tuple net,TD算法执行10%个episode(100M*10%)后执行TC算法;第二个阶段,从棋盘最大tile数值为2**14=16384的游戏状态开始,大数值初始化N-tuple net,TD算法执行10%个episode(100M*10%)后执行TC算法。
4. 在训练完成后测试时加入Expectimax Search,也就是加入树搜索,在每次树搜索时使用Tile-Downgrading方法,也就是对于某些最大tile较大的棋盘状态进行树搜索时,由于决策效果有限,于是将这个棋盘状态进行Tile-Downgrading。Tile-Downgrading就是找到棋盘状态中最大tile的数值到最小tile数值中第一个断掉的不连贯的tile数值,然后将大于该数值的所有tile的数值都除2,于是形成新的棋盘状态,在这一次的树搜索过程中root状态就使用这个新的棋盘状态进行。如下图:

由于256是第一个断开的tile数值,于是将所有大于这个数值的tile均除2,形成新的棋盘状态后用其进行树搜索,最后得到的决策动作用于原始的棋盘状态(进行树搜索之前的原始状态的决策)。
============================
个人感觉,这篇论文中最有原创的工作,第一个是初始化权重使用较大数值,第二个就是这个树搜索时使用Tile-Downgrading。
不过即使这篇论文做了这些工作,不过最后对最终结果影响最大的还是多阶段的设计,也就是multi-stage,也可以看做是分层强化学习,即使本篇论文只使用了2个阶段,并不像上个论文使用了16个阶段,不过也正是由于使用了这个多阶段才最终获得了最好的成绩。
----------------------------------------------
一些个人认为可能有用的改进:
使用缓存池设计:
感觉主要是研究《2048》游戏解法的人比较少,现在Reinforcment learning的很多SOTA的方法其实都是可以用在这里的,比如使用缓冲池的方法,这样就可以提高数据的利用率了,当然具体如何使用还是需要具体去调的,不过由于这个《2048》游戏现在发展的形式已经很难会有人真的去调这个了。在这里如果使用缓冲池,如何存储数据,不同的最大tile的棋盘数据是否按照一定的比例去存储都是需要考虑的问题,比如在这个游戏解法中往往tile越大的棋盘状态越少,那么我们是不是可以在训练一定episodes后尽量多存储max tile较大的那些状态数据呢。不过使用multi-stage的分层强化学习后,和这里的这个按比例存储就很像了,比如在第二个stage里面就很像缓冲池只存储2**14=16384以上的棋盘状态,不过即使这样multi-stage+缓冲池设计也会有望提高训练的效率。
使用epsilon-greedy设计增加探索性:
之所以在max tile较小的时候训练的好,较大的时候训练的差,个人观点就是max tile较小的时候棋盘中空格数量多,环境随机性大,而且每种动作选择可能都会有一个较好的搜获(可以理解为每个动作的Q值都很高,并且相差不大),这个时候不使用epsilon-greedy探索机制往往也可以获得比较好的结果;但是在max tile较大的时候,空格较少,环境的随机性较差,不同动作的Q值往往可能相差很大,这时候就需要较大探索力度,而这时候可以考虑使用epsilon-greedy探索机制。比如在《Optimistic Temporal Difference Learning for 2048》论文算法中的第二阶段加入epsilon-greedy探索机制。
一些个人的理解:
其实可以看到,尤其《2048》棋盘数据的分布不规整性(数值分布为0,1,2,3,......,16)比较适合使用n-tuple网络;棋盘的max tile的数值在较小和较大时整个游戏环境差别是很大的,主要就是空格数量的多少,因此在max tile较小时训练的n-tuple net可能不适合于max tile较大时的棋盘状态,因此使用multi-stage方式的分层强化学习会有很大提升。即使max tile较大时棋盘状态也会有空格较多的情况,但是往往只需要几步移动就会轻易的出现空格较少的情况,因为max tile很大,所以即使很好的移动也往往不会很好的消减掉棋盘上的tile。《Optimistic Temporal Difference Learning for 2048》中提出的大数值初始化n-tuple net权重的设计其实更适合max tile较大的情况,比如multi-stage中的第二阶段。
===============================================
附:
Expectimax Search和Tile-Downgrading都是在测试时候加入的,并不是在训练时候使用的。
再探 游戏 《 2048 》 —— AI方法—— 缘起、缘灭(8) —— 2021年9月SOTA的TDL算法——《Optimistic Temporal Difference Learning for 2048》——完结篇的更多相关文章
- 跟k8s工作负载Deployments的缘起缘灭
跟k8s工作负载Deployments的缘起缘灭 考点之简单介绍一下什么是Deployments吧? 考点之怎么查看 Deployment 上线状态? 考点之集群中能不能设置多个Deployments ...
- 再探JS数组原生方法—没想到你是这样的数组
最近作死又去做了一遍javascript-puzzlers上的44道变态题,这些题号称"JS语言专业八级"的水准,建议可以去试试,这里我不去解析这44道题了, ...
- 【再探backbone 02】集合-Collection
前言 昨天我们一起学习了backbone的model,我个人对backbone的熟悉程度提高了,但是也发现一个严重的问题!!! 我平时压根没有用到model这块的东西,事实上我只用到了view,所以昨 ...
- 再探jQuery
再探jQuery 前言:在使用jQuery的时候发现一些知识点记得并不牢固,因此希望通过总结知识点加深对jQuery的应用,也希望和各位博友共同分享. jQuery是一个JavaScript库,它极大 ...
- [老老实实学WCF] 第五篇 再探通信--ClientBase
老老实实学WCF 第五篇 再探通信--ClientBase 在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创 ...
- Spark Streaming揭秘 Day7 再探Job Scheduler
Spark Streaming揭秘 Day7 再探Job Scheduler 今天,我们对Job Scheduler再进一步深入一下,对一些更加细节的源码进行分析. Job Scheduler启动 在 ...
- 第四节:SignalR灵魂所在Hub模型及再探聊天室样例
一. 整体介绍 本节:开始介绍SignalR另外一种通讯模型Hub(中心模型,或者叫集线器模型),它是一种RPC模式,允许客户端和服务器端各自自定义方法并且相互调用,对开发者来说相当友好. 该节包括的 ...
- 深入出不来nodejs源码-内置模块引入再探
我发现每次细看源码都能发现我之前写的一些东西是错误的,去改掉吧,又很不协调,不改吧,看着又脑阔疼…… 所以,这一节再探,是对之前一些说法的纠正,另外再缝缝补补一些新的内容. 错误在哪呢?在之前的初探中 ...
- 再探Redux Middleware
前言 在初步了解Redux中间件演变过程之后,继续研究Redux如何将中间件结合.上次将中间件与redux硬结合在一起确实有些难看,现在就一起看看Redux如何加持中间件. 中间件执行过程 希望借助图 ...
- c++再探string之eager-copy、COW和SSO方案
在牛客网上看到一题字符串拷贝相关的题目,深入挖掘了下才发现原来C++中string的实现还是有好几种优化方法的. 原始题目是这样的: 关于代码输出正确的结果是()(Linux g++ 环境下编译运行) ...
随机推荐
- MapInfo 12.0 及 mapbasic 12.0 安装过程当中遇到的问题的汇总
目录 MapInfo 12.0 及 mapbasic 12.0 安装过程当中遇到的问题的汇总 C++ 运行时库 Unable to load the CLR (-2147467263) 1) .NET ...
- 一个基于SSM的CRUD的标准写法
CRUD即CREATE,READ,UPDATE,DELETE的首字母的合写,意思是增读改删.前人为了便于发音和理解,改为增删改查. CRUD基本上是软件开发中中相当部分功能的最小功能模块构成,虽然软件 ...
- 天翼云安装nexus3.37.1
有点操蛋,官网网络太慢了! 百度了不少网友的内容,综合如下 总体是个皮毛,但已经可以用于开发了! 一.下载和安装 https://download.sonatype.com/nexus/3/nexus ...
- ONNX Runtime入门示例:在C#中使用ResNet50v2进行图像识别
ONNX Runtime简介 ONNX Runtime 是一个跨平台的推理和训练机器学习加速器.ONNX 运行时推理可以实现更快的客户体验和更低的成本,支持来自深度学习框架(如 PyTorch 和 T ...
- 嵌入式入门必看!调试工具安装——基于 AM64x核心板
本章节内容是为评估板串口安装USB转串口驱动程序.驱动适用于CH340.CH341等USB转串口芯片. USB转串口驱动安装 适用安装环境:Windows 7 64bit.Windows 10 64b ...
- Go 使用原始套接字捕获网卡流量
Go 使用原始套接字捕获网卡流量 Go 捕获网卡流量使用最多的库为 github.com/google/gopacket,需要依赖 libpcap 导致必须开启 CGO 才能够进行编译. 为了减少对环 ...
- 深度长文解析SpringWebFlux响应式框架15个核心组件源码
Spring WebFlux 介绍 Spring WebFlux 是 Spring Framework 5.0 版本引入的一个响应式 Web 框架,它与 Spring MVC 并存,提供了一种全新的编 ...
- VulnHub_DC-1渗透流程
DC-1 DC-1 是一个专门建造的易受攻击的实验室,目的是在渗透测试领域获得经验. 它旨在为初学者提供挑战,但它的难易程度取决于您的技能和知识,以及您的学习能力. 要成功完成此挑战,您将需要 Lin ...
- redis出现错误提示MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for......
某天,redis出现了这样一个错误提示: MISCONF Redis is configured to save RDB snapshots, but is currently not able to ...
- Solo 开发者周刊 (第4期):什么样的新科技,能提高生活效率?
这里会整合 Solo 社区每周推广内容.产品模块或活动投稿,每周五发布.在这期周刊中,我们将深入探讨开源软件产品的开发旅程,分享来自一线独立开发者的经验和见解.本杂志开源,欢迎投稿. 好文推荐 AI生 ...