
德州扑克策略揭秘:小绿皮书到数学决策全知道
2026年2月9日
围棋国手与网红剑指浪体德州扑克专业赛冠军
2026年2月9日选自willtipton
机器之心编译
参与:Jane W、蒋思源
近来,强化学习即 RL 的成功像 AlphaGo 那样获得了众人的极高关注,然而其基本的思路是颇为简单的。接下来,我们于一对一无限注德州扑克游戏里开展强化学习。为能够尽可能清晰地呈现,我们会从无到有去开发一个解决方案,而并非需要预设的机器学习框架像 Tensorflow 那般。那就让我们借助 Python3 Jupyter notebook 开启吧!
问题设置
规则提醒:该游戏是一个 2 人无限注的德扑游戏,其中:
游戏开启,有两名参赛者,他们都持有 S 筹码,且拥有随机分配的 2 张底牌。
大盲注玩家所下的注为一个盲注的量,小盲注玩家下的注是半个盲注的量,其中大盲注玩家下的注为一个盲注量的数值是一点零,小盲注玩家下的注是半个盲注量的数值是零点五。
3. 玩小盲注的玩家,能够选择全押,也就是全部筹码押注或者弃牌,即放弃继续参与这轮投注。
4. 要是小盲注玩家进行全押操作,那么对于大盲注玩家而言,能够进而选择跟注或者选择弃牌。
规则,我们能够通过想象使其呈现为如同下图所展示那般的决策树模样。游戏,它起始于 E 这个点,在这个时段内里,SB 可选择去做出全押这一行为来,或者去选择弃牌。要是他采取弃牌的行动,那么咱们就会转移至状态 A 处,此时此刻游戏宣告结束。要是他选择全押,那咱们便会转移至状态 D,在此状态下 BB 一定要在跟注以及弃牌这两者之间做出相应决断。要是有一个玩家选择弃牌,那么另一个玩家就会获取到盲注,要是两个玩家都选择全押,那就会发放 5 张公共牌,而且金额会依照扑克的正常规则来进行分配。

存在着这样一个游戏,它有着声名远扬的解决方案,其网址为(http://www.dandbpoker.com/preflop-charts),除此之外,还有别的办法,像是虚拟对局,其链接是(https://www.youtube.com/watch?v=MVMfDswjJE0),以及直接优化,相关网址为(http://willtipton.com/coding/poker/2016/03/06/shove-fold-with-tensorflow.html)。这里,我们将使用强化学习估算解决方案。
存在一种不重复的两张手牌的组合数量,所以呀,能对所有的牌进行排序,并且从0开始一直编到1325号。只要前后编号是相同的,那具体的排列顺序就没什么重要的啦。下面这个函数暗中定义了这样一种排序情况,还搞出来了从牌的编号,到相关决策信息的一种映射关系:牌的排序情况(牌面的顺序或者说rank)以及同花的性质(牌面的花色也就是suitedness)。
注意一下,输出元组内部的首个元素,也就是代码里的 r2,始终会排序在前面,要是存在这种情况的话。比如说,手牌编号 57 准确无误地就是 62,此时我们有:
玩家处于全押这种状况时,他们平均所获取的底池,也就是所谓的「期望利益」,是依据游戏规则来进行确定的。文件pf_eqs.dat(其网址为http://willtipton.com/static/pf_eqs.dat),包含着一个numpy矩阵pfeqs(其参考文档网址为http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.savetxt.html),这里面的pfeqs所指代的恰好是当对手持有手牌j的时候,持有手牌i所具有的期望利益。
的确,偶尔两人开始时持有的手牌存在一张是相同的情形,处于这种境遇下,它们的期望无法同时进行计算,此时获取他们的期望利益也是不妥当的。文件pf_confl.dat(http://willtipton.com/static/pf_confl.dat)存有另一个1326×1326矩阵,在此矩阵里每个元素是0或者1。A 0表明两位玩家开始时持有的手牌不相同,a 1表明开始时持有的手牌相同。
举个例子,因手牌当中,56的数值是62,57的数值为62这一情况发生,58的数值同样是62,所以我们便拥有了如下结果:

为什么结果不正好是 0.5 呢?
强化学习
紧接着步入 RL 教程,RL 问题存在着三项关键重要部分 状态(state) 动作(action) 奖励(reward) 它们组合在一起呈现出如下这般。
1. 我们处于某「状态」(即我们观察到的世界的状态)。
2. 我们使用这个信息来采取某「动作」。
3. 我们会得到某种「奖励」。
4. 重复以上过程。
上述过程需一遍又一遍地重复:先观察状态,接着采取行动,之后获得奖励,随后观察新的状态,再进行另一个行动,进而获另一个奖励等。RL问题仅是找出怎样选择行动的方案,借以取得尽可能多的奖励。结果表明,这是个极为普遍的框架。我们能够运用这种方式思量诸多问题,解决这些问题存在诸多不同的方法。通常来讲,解决方法关联随机游走,于各异状态挑选各类行为,将哪些组合能获取何种奖励记住,而后试着借助这些信息于未来做出更优选择。
德扑游戏中,RL是怎样被用于其中的呢?在任意一个决策点上,玩家清楚自身持有的2张底牌以及所处的位置,这便是状态。玩家能够采取行动,具体为:要么选择弃牌,要么进行GII。(对于SB而言,GII意味着全押(shove),而对于BB来说,GII意味着跟注)。随后即可获取奖励,此奖励是玩家所赢得的钱数,在最后的手牌里会运用玩家的总筹码大小。举例来说,若初始筹码大小为S = 10,SB全押BB弃牌,那么玩家所获得的奖励分别是11和9。
我们要借助效仿手牌的组合形式,借此寻觅到游戏具备的策略,我们会同步去处置两个玩家产生的随机手牌情况,使之能够做出有关怎样开展游玩的决策,接着去观察他们每次结束之际到底能获得多少钱财,我们会运用这些所获取的信息对Q函数Q(S,A)予以学习(也就是估计),Q函数的参数乃是状态S以及动作A,其输出数值指的是于该状态之时采取该动作之际所能获取的最终赏赐值,一旦我们得到了Q(抑或是其采用的某种估计数值),那么策略的选取便十分简单明了了,那便是我们能够对每个有策略进行评估,从而分辨出哪一个更为优良。
既然如此,在我们所在之处从事的工作是对Q进行估量,我们会运用Q^(其发音为「Q hat」去指代该项估量),于初始化之际,我们会随机地去猜测一些Q^,接着,我们要模拟一些手牌,有两名玩家依据Q^来做出决策,伴随每次手牌过后,我们会对估计值Q^予以调整,从而去反映玩家于特定状态时采取特定动作之后所获取的实际数值,最终,我们理应能得到一个颇为优良的Q^估计,而这便是确定玩家相应策略所需的全部内容。
这里要留意一点,我们得保证在全部状态下施行所有动作,每一个状态与动作的组合最少都要尝试一回,这样才能够很好地估算出最终每一个有可能的值。所以,我们会让玩家在一小段时长ε内随机去实施行动,动用他们如今所估计的最优策略。首先,我们应当积极去探究选择方面存在的可能性,频繁地进行随机选择。随着时间的流逝,我们会更多地借助我们所获取的知识。也就是说,ε会随着时间的推进而缩减。有诸多方式能够达成这点,比如:

被称作「动作价值函数(action – value function)」的 Q,因给出从任何状态采取任何特定动作的值的缘故,它在多数 RL 方法里有着重要的作用。那么 Q^ 是怎样进行表示的?又是如何去评估的?它是不是在每次手牌之后就更新了?
特征:Q^ 的输入
首先,Q^的输入是状态和动作,把这个信息传递给Q函数,作为位置,比如SB为1,BB为0,还有手牌编码,从0到1325,以及动作,比如GII为1,弃牌为0。不过,我们将会看到,如果我们做更多的工作,会得到更好的结果。在这里,我们用7个数字的向量描述状态和动作:

向量φ由函数phi返回,它会成为Q函数的输入,此向量被称作特征向量,其各元素均是特征,φ发音为「fee」。我们将会看到,我们所挑选的特征,能在结果质量方面引发极大差异。在挑选特征(此过程称为「特征工程」)时,我们借助了与该问题相关领域的知识。它同一门艺术那样具有科学性。在此处,我们会把判断哪些属于相关信息(在这种情形下)的知识,按照以下几种方式进行编码。让我们瞧瞧。
为了便于操作,首个元素一直是1,去考量后续的四个元素,这些是玩家手牌的代表,我们从手牌编码转变成了rank1、rank2以及isSuited,这三个变量在技术层面给出和手牌编码一样的信息(不考虑特定组合),然而该模型会更出色地利用此种格式的信息。除了原始排序外,我们纳入了(|rank1 – rank2|)^0.25,我们恰好晓得connectedness是德扑的关键属性,就如同其名称所显示的那样。此外,要是所有特征在量纲方面保持一致,那么该模型的学习成效将会更佳。在此处,所有的特征大体上处于0与1之间,这是我们借助把rank除以numRanks之后所达成的。
若是最后,not isGII这种情况出现,也就是动作属于弃牌时,实际上我们会把这些数字设定成为0。我们清楚,玩家弃牌之际,特定的持有手牌对于结果不存在任何作用影响,小概率的卡牌移除效果予以忽略,所以这种情形下我们会将无关的信息给删除掉。
当下思考最后那两个元素,头一个直接对玩家的位置进行编码,然而第二个同时依赖于isSB以及isGII,为何会出现这种情况呢,过后我们会展示这个“交叉项”的必要性。
关于 Q^ 的线性模型
我们会去学习一个用于进行估计的、属于线性函数的Q^函数,这表明我们实际上要去学习一个参数向量,这个向量一般称作θ,它的长度是7,并且与特征向量的长度一样。随后,我们会为了特定的φ去估计Q^。
于此,下标 i 用以指代向量的特定元素,且将参数列表书写成 (φ;θ) 的形式,其意味着 Q^ 的值是依赖于 φ 和 θ 的,可是我们能够把它视作是 φ 的函数,而 θ 是固定不变的值。代码十分简单:

尽管该函数被广泛运用,然而此算法并无突出之处以至于能让其成为解决这个问题的最优选项。这仅是其中一种方式:把某些学习参量跟某些特征进行整合从而获取输出,并且完全由我们来界定一个θ向量,让它生成我们期望的输出。可是,正确挑选θ将会帮我们有效估算在持有特定手牌条件时进行特定行动的价值。
模拟扑克游戏
接下来,我们要着手「玩」手牌了。这一行为,会在后续的几个部分里开展,然而当下,我们得先去构建三个关键的概念。这三个概念,与RL问题的三个关键组成部分存在关联,分别是:状态、动作以及奖励。首要的是状态,每一次手牌活动当中,咱们会以随机发牌的形式,去初始化每位玩家的状态。
第二点,要采取动作,每个玩家会运用现今的模型,该模型由theta给出,且玩家知晓自己的手牌以及身份,身份情况为作为大盲小盲位,以此来挑选动作。在下面的函数当中,我们会估算GII以及弃牌折叠的值,也就是qGII和qFOLD的值。接着会挑选当下的最优项 ,这个最优项是1减去ε的值,不然就随机去挑选动作。最后返回所采取的动作,还有与之对应的价值估算以及特征向量,这两项我们在后续是会用到的。

第三点,一旦我们清楚每个玩家当下所拥有的手牌以及其做出的动作,我们便去模拟剩余的手牌以此来获取玩家的奖励。要是有任何一个玩家弃牌,我们能够马上返回正确的奖励值。不然的话,我们参照玩家的状态以及奖励期望(equity),在恰当的时间段随机挑选出一个赢家。

若玩家处于全押情形,我们借小技巧避开了模拟。不同于运用5张公共牌切实模拟游戏、评估玩家的手牌进而查看谁获胜,我们当下依据预先算出的概率随机挑出一个赢家。这在数学方面是等同的(琐碎的证明予以忽略),这仅是一种更便利且计算效率更高的办法。
极关键的是,我们所经历的学习进程未曾运用这些公平性或关乎游戏规则的讯息。就如同我们即刻便会目睹的这般,哪怕完全是模拟情形,该学习进程也并无差异,甚至智能体还会与外部如同黑盒般的扑克游戏系统展开交互进而有可能遵循各异的规则!那么,这个学习进程到底是怎样推行的呢?
学习:更新 Q^
我们在一次手牌结束之后,要去更新theta,对于每一个玩家,我们知晓其状态以及所采取的动作,我们还有动作对应的估计价值,以及从游戏当中获取的实际奖励,从某种意义来讲,实际获取的奖励是「正确解」,要是动作的估计价值与之不一样,那么我们的模型存在错误,我们需要更新theta,从而让Q^(φ;θ)更靠近正确的答案。
设 φ’ 是一个玩家所处的特定状态,R 是她所取得的实际奖赏。设 L 等于 (R – Q^(φ;θ))的平方,L 被称作损失函数。L 越小,R 就越接近 Q^(φ;θ),要是 L 为 0,那么 Q^ 恰好等于 R。也就是说,我们要微调整 θ,让 L 变得更小。(留意,存在许多可能的损失函数,致使随着 Q^ 愈发接近 R,L 越来越小。这里的损失函数仅是一个常见的选取)。
所以,「更新Q」的意思是,通过改变θ,从而让L变得更小。能够做到这一点的方法不止一种,其中一种简单的方法是随机梯度下降(stochastic gradient descent)。简单来讲,它更新θ的规则是:
我们要去挑选「超参数」α,它被称作学习率,能对每次更新的幅度予以控制。要是α过小,学习的速度就会十分缓慢,然而假使它过大,那么学习的进程有可能无法实现收敛。把L代入到这个更新规则当中,再开展几行微积分计算,我们就得到。

最后的那一行给出了用于更新参数的准则,我们会依据这个准则去编写代码。要留意,这里面的 θ 和 φ 都是向量,且向量的长度是 7。在这里的更新参数的准则,分别对每个元素都适用。
整合
最后,该整合所有内容了。重复以下步骤:
1. 随机发给每个玩家手牌。
2. 令玩家各自选择一个动作。
3. 得到结果。
4. 使用观测到的(状态,动作,结果)元组更新模型。
有着这样一种蒙特卡罗算法,下面的函数mc将其实现了,该函数会返回用以回报学习模型的参数theta。

特别注意,上节推导出的参数更新规则在代码中得到了实现。
结果
解释模型
本例中,固定 S=10。
咱们获取到了数字,然而这些数字具备意义么?事实上,存在着好几种办法能够协助咱们去判断,并且借助这些办法获取到一些针对模型的解释。
首先,我们思索某些特定的情形。当SB选择弃牌(FOLD)时,其该项的估计数值是多少呢?因在这类情形下,φ具备相对简单的表现形式,所以较容易得出结果。实际上,除开第1个(固定设定为1)以及第6个(对应于isSB)之外,其余所有的元素均变为0:phi =。
1,0,0,0,0,1,0
所以,我们的线性模型的Q^,仅仅是相当于给theta的第1个元素与第6个元素相加汇总起来的量:
现今,我们已然知晓,依据游戏的规则,SB 所做出的弃牌这一行为的价值为 9.5。故而,极其厉害,模型同真实状貌极为贴近!这属于一种很棒的逻辑判定,且借由例子阐述了怎样去估量我们模型有可能出现的误差值的大小。
再有另外一种情形,BB选择弃牌,仅仅phi的第1个元素并非为零,我们察觉到一个估计数值。
虽然没办法明确究竟正确的答案会是怎样的,只是晓得它必定理应处于9呈现出如果SB总是GII的状况和10.5呈现出如果SB总是弃牌的状况之间。实际上,这个数字相较于10.5而言更靠近9了,这跟SB更趋向于GII而并非相一致了。
思考每个 θ 输入,存在一个更一般的方法,每个元素 θ_i 会致使 Q^ 增加,这是由于对应的特征 φ_i 会增加 1,比如当有合适的手牌同时执行 GII 策略时,θ 的第 5 个元素会增加 1,所以有适合手牌的估计奖励值是 0.22571655,这是一个小的正向奖励,看上去是合理的。
θ的第2个元素,它对应玩家排名较高的手牌,此元素是6.16764962。这对应的特征是这样的:若isGII成立,那就是rank2除以numRanks;若isGII不成立,那结果就是0。如此一来,意思就是玩家排名较高手牌时的GII策略有关的情况。这里是rank2除以numRanks,所以呢,特征每增加1,大概就约等于2和ace之间的差。以一个多余的6BB加上1个ace来取得胜利,而不是用2来取得胜利,这样看来似乎是合理的。(但是,为什么你会觉得有第二张更高的手牌显然是负的?)
查看跟第6个特征对应的θ的元素,若isSB为1则取该元素,若isSB不为1则取0,要是所有其他特征都一样,那么在SB里的附加值明显是 -0.15230302。我们大概能把这理解成位置方面的劣势,也就是因得先采取行动而有的小惩罚。
然而,其他所有方面并非必然相同。要是 SB 施行 GII 策略,那么最后那个特征同样不为零。所以,SB 执行弃牌动作时的附加值是 -0.15230302。在执行 GII 之际,我们对最后那个特征的贡献予以归纳,发觉奖励是 -0.15230302 + 0.14547532 = -0.0068277。明显地,假如 SB 采用更为激进的策略,此刻位置方面的劣势便会减少!
在这里,我们看到,于本问题范畴之中,挑选有意义的特征方能助力我们有效地阐释结果。有意思的是,存在着一条名为SAGE的玩德扑游戏的老规则。此规则在锦标赛现场易于被记住。其原则是为你的手牌构建「能力指数」,该指数依照顺子、同花以及对子来构建规则,随后运用它去判定是否GII。它们的特征组合跟我们的特征组合相比较会怎样呢?它们的结果又是如何呢?
最终,为何我们选取isSB以及isGII去判定最后一个特征,而非仅仅是isGII呢,对此展开思考。(BB,FOLD)的估计数值仅仅是θ的第1个元素,因而这个第1个元素应当可以随意变动,以便获取正确的(BB,FOLD)值,那么,第6个元素是在SB里的额外贡献,它得能够随意变动来获取正确的(SB,FOLD)。
当我们从弃牌转至 GII 时,元素 2 变成非零状态,元素 3 变成非零状态,元素 4 变成非零状态,元素 5 变成非零状态,且会依据玩家情况调整为特定的值,然而这些决策对于 SB 和 BB 都是适用的。此模型得为 SB 全押给出某些与 BB 全押不同的决策。
要是我们的最终特征是这样的:要是 isGII 成立那么就是 1,不然就是 0。这并非由玩家决定,所以 SB 和 BB 的估计值之间唯一的差别会在 isSB 项上。这个数值得把执行弃牌时 SB 和 BB 之间的差异考虑进去,还要考虑执行 GII 时 SB 和 BB 之间的差异。模型得在这两个差异里选一个数值,最终有可能会导致一些不太好的折中情况。相反地,我们所需要的是:要是 isGII 和 isSB 都成立那就为 1,否则为 0。如此一来,该模型能够辨别出SB GII跟BB GII的增量数值。
需留意,此模型依旧没办法捕捉诸多细微的详情。举例来讲,鉴于模型完全以内置的函数形式存在,我们所见到的,在两个特定手牌组合下,像是 A2 和 K2 这样的情况下,针对于 SB 和 BB 而言,GII 的估计值的差异是全然一样的。不论 θ 的数值怎样,我们的模型都不太可能预测到。
这样的模型,偏差值(bias)很高,它不灵活,且有强大的内置「观点」来定结果模样,这便是特征工程重要的原因,若未尝试给算法提供精心设计的特征,那它或许没能力表征很好的解决方案。
能够给模型添入更多的特征,像是别的交叉项,借此得到偏差较小的模型,然而这也许会带来弊端。这会迅速匮乏可解释性,还可能会碰到更多的技术问题,像是过拟合。(当然,在多数运用里这并非首要问题,准确性比可解释性更为重要,并且有办法应对过拟合)。
可视化策略
就为了寻觅到完整无缺的策略,我们会去评估那个模型,其目的在于弄清楚,于每个玩家的1326种手牌组合情形之下,究竟是GII更好,还是弃牌更好呢:

看上去,针对于 SB,大概有 55%的手牌抉择是全押,然而针对于 BB,大概 49%的时候会选择跟注。
最终,我们能够生成些许 SVG,用以在 Jupyter 环境里绘制 GII 范围。


我们该如何去进行选择呀?在此处存在着诸多我们所期望的具有性质的特征呢:大的手牌状况良好、存在对子是很不错的、同花的情形要优于非同花、SB的打法相较于BB而言更为宽松等。然而,处于边界线状态的手牌其打法在有时候会跟真正的平衡策略当中的打法有所不一样呢。
结语
有一篇介绍性文章叙述应用RL技术,它给我们提供了一些策略,这些方略是合理的,可用于进行德扑游戏。该学习过程并不依赖任何结构,也不依赖游戏规则。它是纯粹地让智能体自己展开游戏德信竞技,去观察结果,再依据此来作出更好的决定。另一方面,要学习一个好的模型,重要特征工程需要一些领域专业知识。
最后的时候,来陈述些背景情况。好多适宜的议题都能够阐释成RL问题,而且存在诸多不一样的办法去处理它们。这儿的解决办法或许具备以下特性:没有模型的办法、以价值为依据的办法、蒙特卡罗方法、处在策略方面的情况、没有进行折现操作的办法,并运用线性函数进行近似估计的器具。


