公司动态

深度强化学习德州扑克AI:NFSP自博弈与不完全信息博弈实战

📅 2026/8/31 6:22:48
深度强化学习德州扑克AI:NFSP自博弈与不完全信息博弈实战
简介本资源是一套面向计算机、人工智能及相关专业本科生与初学者的深度强化学习实践项目聚焦德州扑克这一经典不完全信息博弈场景提供从环境建模、策略网络设计到训练评估的完整AI算法优化方案适用于毕业设计、课程大作业及算法进阶学习。压缩包共164个文件含58个Python源码涵盖PPO/DQN等算法实现、游戏环境封装与训练主流程、48个.pth模型权重含不同训练阶段的checkpoint、18个CSV性能日志如胜率、筹码变化、动作选择统计及配套说明文档txt、md、json整体大小为13.91MB结构清晰便于模块化学习与二次开发。已有944人下载学习所有代码均经实测可直接运行附带详细项目说明与模型加载指引读者可快速复现训练过程、分析策略演化规律并基于现有框架拓展多智能体对战或规则迁移等研究方向。 花了大半年时间折腾的深度强化学习德州扑克AI项目总算是稳定输出了趁热把完整设计思路和优化过程复盘一遍。项目是用Python写的打包里放了完整源码、项目说明文档和训练好的模型文件适合两类人看一类是想搞明白不完全信息博弈里强化学习到底怎么落地另一类是正准备上手写博弈AI但不知道从哪一步开始。我不打算只贴目录和跑通教程重点把每一步的技术选型理由、踩过的坑、最后怎么调出来的一次性说清楚。顺便说一句德州扑克在这里是典型的不完全信息博弈研究平台它有隐藏信息、随机发牌、连续下注决策非常适合用来验证深度强化学习算法在实际问题里的表现。下面直接进入正题。1. 德扑AI难在哪不完全信息博弈的三个硬骨头1.1 第一座山信息隐藏与组合爆炸一开始我试图把AlphaGo那套思路直接搬到德州扑克上结果第一周就撞了墙。围棋虽然复杂但至少所有信息都是公开的你可以精确知道当前局面是什么。德州扑克完全不同——你看不到对手手里的两张牌你只能看到公共牌和自己的手牌再加上下注历史来推断对手可能的范围。这个看不见带来的问题是雪崩式的。先看组合一副牌52张对手起手牌有C(52,2)1326种可能每张公共牌翻出来都会让局面变化5张公共牌加上翻牌前、翻牌、转牌、河牌四个下注轮完整无限制德州扑克的状态空间数量级在10^160左右。想做全状态搜索根本不现实。这时候需要做状态抽象和动作抽象把相似情况合并。DeepStack、Libratus这些顶尖系统能打赢人类职业选手靠的也不是暴力穷举而是抽象加局部求解。所以这个项目从一开始就明确了核心不是算得快而是怎么抽象、怎么泛化。1.2 第二座山最优策略不是固定动作而是混合策略单智能体强化学习训练出来的模型通常会收敛到一个确定性策略给定状态输出一个最优动作。但德扑恰恰不允许这样做。举个最简单的例子你都拿到了强牌如果每次都在翻牌前加注对手很快就能从你的行为里读出牌力你拿到强牌时对手全跑了你的价值下注拿不到钱你诈唬时对手也察觉了反过来剥削你。所以GTOGame Theory Optimal策略本质上是个混合策略同一手牌在同一个局面下有时候加注、有时候跟注、有时候弃牌每个动作都有特定概率。这对DQN这类算法是致命的因为它们天然倾向于确定性输出。这也是为什么后面要用虚拟自博弈Fictitious Self-Play这类方法模型要和自己的历史策略一起进化最终逼近纳什均衡。1.3 第三座山奖励稀疏而且方差大得惊人棋牌类AI强化学习还有一个特别折磨人的问题在一手牌结束前AI拿不到任何有效的中间奖励信号。你说手牌好是不是奖励不好说因为AA碰上KK被反超的事情太常见了。这就导致两个后果。第一训练信号来得晚。模型不知道哪一步动作导致了最终输赢只能靠大量采样去估计。我在训练初期看loss曲线前几百个episode基本是平的偶尔抖一下普通人早就怀疑代码写错了。第二方差极大。就算策略完全合理短期胜率也不会稳定。AA对KK胜率大约80%但连续20手被反超在统计学上很正常。如果拿单局筹码变化直接当训练标签模型会把运气当成策略学出一堆噪声。所以奖励归一化、优势估计、经验回放里的优先级设计都会影响最终训练质量。2. 项目骨架环境、智能体、训练器三个模块怎么分2.1 环境层自己写牌桌逻辑还是套现成库拆解这个项目时我最关心的第一个决策就是环境层用什么。市面上的rlcard是个很好的卡牌AI研究库内置了Leduc、德州扑克等环境接口类似Gym可以直接用来做强化学习。但这个项目没有完全依赖它只把rlcard的规则逻辑当作参考在环境层做了大量自定义。原因很简单强化学习环境需要暴露的接口和游戏逻辑本身是两回事。我要自定义状态编码、自定义动作空间、自定义奖励塑形还要在自博弈时随时切换对手如果完全依赖现成库这些改动会非常痛苦。最终环境层分成了这几个文件env/ ├── card.py # 扑克牌表示、洗牌、发牌 ├── deck.py # 牌堆管理 ├── table.py # 牌桌状态、下注轮转换、胜负判定 ├── actions.py # 离散动作空间定义 └── reward.py # 奖励计算与归一化table.py是整个环境层的核心它维护了当前玩家位置、筹码量、公共牌、底池、当前下注轮以及每个玩家本轮已经投入多少筹码。每次动作进来它先判断合法性然后更新状态最后再判断是否进入下一轮或直接结束牌局。这个模块写清楚之后上面跑什么算法都稳。2.2 智能体层策略网络、价值网络、平均策略网络三件套智能体层是这个项目的重点也是和普通DQN项目差异最大的地方。朴素的实现只需要一个Q网络但做NFSP需要三个组件策略网络也叫Q网络负责学怎么在当前状态下最大化累积奖励走的是强化学习路线。平均策略网络负责学这一段时间以来自己所有历史策略的平均走的是监督学习路线输入状态、输出动作概率分布。经验缓冲区拆成两个一个存RL样本一般叫RL memory一个存监督样本SL memory。agent/目录下面networks.py定义了网络结构nfsp.py实现了整个NFSP智能体buffer.py实现了两个独立的经验池。网络结构本身不复杂状态编码向量进去通过两层带ReLU的全连接层分别输出Q值和动作概率。对于Leduc这种小规模博弈MLP完全够用不需要花里胡哨的注意力机制。2.3 训练器层自对弈循环和双缓冲区设计训练器需要处理的不只是一个agent在环境里跑而是多个agent互相打、不断演化。所以train/目录里做了一层独立的训练流程专门管理自博弈。selfplay.py里维护一个模型池每训练一定轮次就把当前模型快照存进池子。每个episode开始时从池里按策略选一个对手。主agent的学习目标是打败所有历史版本而不是只打败当前版本这能有效避免策略单一化。双缓冲区的设计同样关键。RL内存里存的是(Q_s, Q_a, R, s)这类强化学习样本目标值用贝尔曼方程计算SL内存里存的是(s, π_target)样本目标分布是当前对手策略下的动作概率。两个缓冲区在每次环境返回后各写一份训练时交替采样。3. 状态编码与动作空间把一局牌翻译成神经网络能懂的向量3.1 输入特征哪些信息能进模型这是整个项目最容易被低估的部分。很多初学者直接把手牌编号塞给网络结果模型什么都学不会。实际上德扑AI需要的状态信息分四类第一类是手牌信息包括自己手牌点数和花色编码成独热向量。第二类是公共牌信息翻牌三张、转牌一张、河牌一张每张都按点数和花色独热编码没发出来的位置补零。第三类是牌局动态信息包括当前下注轮翻牌前/翻牌/转牌/河牌用整数编码、当前底池大小、自己当前筹码量、本轮已经下注的数量。第四类是动作历史把最近几个动作编码成序列喂进网络因为LLM时代的模型都懂上下文德扑AI同样需要局面上下文。动作历史的编码花了不少功夫。一开始只是简单记录上一个动作是什么效果很差。后来改成记录最近8个动作且每个动作都带上了对应的下注尺度模型才真正开始理解对手的激进程度。一个不可忽略的设计细节是所有数值特征都做了归一化筹码量除以总筹码底池除以总筹码避免数值范围差异太大导致训练不稳定。3.2 动作空间离散化加注尺度的学问德州扑克里加注量是个连续量但强化学习输出层必须是有穷的所以必须做动作抽象。这个项目里把动作收敛到7个离散动作弃牌Fold过牌Check跟注Call小注加注到底池的33%半池注加注到底池的50%满池注加注到底池的100%全下All-in这个离散化是有讲究的。太细会大幅扩大动作空间导致每个动作被采样的次数太少太粗又会让策略失去灵活性比如需要加注60%底池的时候你只能选50%或100%。实测下来7个动作在Leduc和单挑无限德州上都算平衡点。如果是小对局也可以把动作压缩到5个把加注档位去掉最极端的两档训练能快不少。3.3 奖励设计别直接拿筹码变化当loss目标奖励设计是德扑AI训练里水分最大的地方。简单方案是每局结束时返回这局赢了多少筹码然后直接拿这个数值去做TD误差。我第一版就是这么做的结果训练到中期出现NaN因为筹码变化量级太大Q值在部分状态下被推到很大然后梯度爆炸。后来做了两处修正。第一奖励做了clip单局收益限制在[-3, 3]个底池以内超出部分截断。第二用优势函数代替原始奖励不让模型单纯学赢钱而是学这一手牌相比平均而言赢了多少。这样发牌运气带来的方差被大幅降低模型更专注于策略本身的好坏。4. 算法路线图为什么直接套DQN会翻车最后怎么落到NFSP4.1 第一版尝试DQN在限注德扑上的表现先交代背景项目最开始在Leduc Holdem上跑DQN。Leduc是个小规模德扑变种总共6张牌、每人1张底牌、公共牌3张状态空间比完整NLHE小好几个量级。DQN在这里能学得动胜率确实超过了随机策略但离打得好差距明显。问题在于它能学出强牌加注、弱牌弃牌的正确直觉却永远学不会有时要用弱牌诈唬和有时要用强牌慢打。因为它追求的是每个状态下的最优动作输出层面是贪心的。虽然可以加epsilon探索但探索结束还是会收敛回确定性策略这在德扑博弈里就是纯送钱。4.2 NFSP的核心思路两个网络、两套记忆NFSP全称Neural Fictitious Self-Play是DeepMind在2016年提出的方法专门为不完全信息博弈设计核心贡献就是把我打我自己这件事做了数学上的规范化。它的机制不复杂。每一局开始前智能体有一个随机过程决定用哪套策略行动以概率η用强化学习策略这个策略由Q网络驱动目标是最小化短期遗憾以概率1-η用平均策略这个策略由平均策略网络驱动目标是学习自己最近所有历史策略的平均分布。训练时强化学习策略的样本进RL memory平均策略的样本进SL memory两个网络交替更新。看到这里你应该能理解为什么它有效Q网络负责寻优平均策略网络负责稳定。前者让策略不断进步后者保证不出现极端偏移两者交替推进最终逼近纳什均衡。4.3 这个项目的融合实现项目里的NFSP实现没有完全照搬论文超参数做了一些工程化调整。核心改动是Q网络更新频率是平均策略网络的两倍因为RL侧的样本方差更大需要更多梯度步骤去拟合但平均策略网络学习率设得很低防止它被最近的几个episode带偏。另一个调整是探索策略。论文里用的是epsilon-greedy但我在自博弈场景下改成了窗口式探索随着训练轮次推进epsilon从0.6线性降到0.1。前期多探索收集多样样本后期多利用已有策略提升强度。这个改动让Leduc上的可剥削度exploitability下降速度肉眼可见地快了。5. 优化三板斧自博弈、对手池、平均策略网络5.1 自博弈不是简单自己和自己对打自博弈听起来简单让当前智能体跟自己对打不断进化。实际操作起来问题很多。最典型的问题出现在版本迭代过快的时候如果每一次模型更新后新版本完全取代旧版本那么新版本只跟上一秒的自己打过对老版本可能已经完全没有抵抗力。这就像练拳只跟同一个人练一旦对方换了打法就懵。所以这个项目里主agent的对手不是最新版本而是历史模型池里的随机快照。每次把agent保存到池子时都会保留至少5个历史版本训练时随机选取。5.2 对手池防止策略单一化对手池的引入带来了一个有意思的效果主agent会逐渐发展出应对多种风格的能力。但反过来看如果池子里所有版本都长得差不多多样性就无从谈起所以还要主动制造差异。我的做法是每隔一段时间给池子注入一个极端策略比如一个只会全下的激进策略、一个只玩强牌的保守策略。这些策略不是用来打败的是用来当体检医生的。如果主agent跟保守策略打还能赢说明诈唬能力在线如果跟激进策略打能赢说明价值下注抓得准。这几类baseline策略文件放在eval/baselines.py里训练时也会被调进来混战。5.3 平均策略网络为什么能提升鲁棒性平均策略网络是NFSP的灵魂很多人理解成就是个监督学习网络这没错但为什么它能让模型变强呢原因在于强化学习策略追求的是当前最优但当前最优在博弈里往往不是真最优而是贪快的策略平均策略则是把过去所有策略做加权平均相当于一份时间积累的经验共识。在德扑里这种平均能自动抹平掉那些短期看起来不错、长期会被剥削的漏洞。最终部署时项目里的主推理模型用的就是平均策略网络而不是Q网络。5.4 除了三板斧还有哪些细节优化优先经验回放PERRL memory里每条样本带一个TD误差权重误差越大的样本被抽到的概率越高训练效率提升明显。目标网络延迟更新Q网络的目标网络每500步同步一次大幅提升训练稳定性这是DQN系列的基础操作但在自博弈里尤其重要。梯度裁剪整个项目里梯度范数上限设成10防止偶发的奖励异常直接带走模型。学习率调度训练后期把学习率从1e-3降到1e-4帮助模型在纳什均衡附近收敛得更细腻。6. 训练中的诡异现象与排查记录6.1 现象一胜率先升后崩像坐过山车训练早期一切正常模型前2000轮胜率一路从随机水平的50%升到70%我心里还挺美。结果到4000轮左右胜率急转直下跌到55%以下而且有继续下降的趋势。排查了很久最终定位到两个问题。第一个是目标网络太久没更新Q值的估计越来越高策略开始偏离真实价值第二个是对手池里只有一个历史版本模型相当于在跟一个固定对手打一旦策略开始针对这个对手特化就产生了严重过拟合。修复方案上面已经提到目标网络每500步同步对手池至少保留5个历史版本。6.2 现象二模型学会了疯狂诈唬这个现象特别有意思。某天我看训练日志发现模型对保守策略的胜率蹭蹭往上涨但跟激进策略打的时候被轻松碾压。进一步分析发现模型已经学成了不管什么牌都加注的无脑诈唬机器。为什么会出现这种情况因为保守策略经常弃牌模型发现诈唬能赢钱就疯狂加大诈唬频率短期收益上去了但策略结构完全歪掉了。解决思路不是惩罚诈唬而是让训练环境里的对手多样化把激进策略频繁拉进来当对手。一旦无脑诈唬会被反加注剥削模型就会自己学会诈唬要适可而止。6.3 现象三损失一直在降牌力却变差了这是我第一次遇到loss骗人的场景。平均策略网络的loss曲线一路下降训练看起来很顺利但拉到评估环境一测胜率反而掉了。原因是平均策略网络陷入了确认偏误它一直在拟合SL memory里那些高频出现的牌局状态但SL memory的分布被RL策略带偏了最终网络优化的方向偏离了真正重要的决策点。方案是给SL memory设了上限超过容量就随机替换强制遗忘老数据同时把平均策略网络学习率调低双管齐下解决。6.4 关于日志多写“可解释”的指标踩了这些坑之后我把训练日志改成了人类能读的格式。不只记录loss还记录对随机器、保守器、激进器的分项胜率平均每手牌收益bb/100各动作使用频率模型和上周最佳模型之间的胜负前面三个指标里分项胜率价值最高。它可以让你在训练中快速判断策略有没有走偏而不是等训练结束才发现模型已经练成了偏科选手。7. 模型评估与实战对局AI到底什么水平7.1 三个固定对手基线做基准测试的标准评估环节我用了四个基线模型。随机策略是一个什么动作概率都相等的策略保守策略是没强牌就跑、有强牌就加注的简单规则型策略激进策略是拿到任意牌都有60%概率加注的激进风格此外还有一个规则型策略它在翻牌前按起手牌强度表决定动作整体接近一个业余玩家。AI对这四个基线的表现是衡量模型强度的基本盘。如果连规则型策略都打不赢那说明训练失败。如果四个都赢了但赢得很吃力还需要继续调。7.2 指标选择bb/100手比胜率更靠谱棋牌类博弈常用每100手牌赢多少个bbbb/100作为核心指标。胜率在德州扑克里不够准确因为一手牌可能赢小底池也可能输大底池胜率高不代表盈利高。在Leduc上最终模型对保守策略大约能稳定赢60bb/100手对激进策略赢40bb/100手对规则型策略赢85bb/100手。在单挑无限德州上对保守策略大约赢35bb/100手整体表现已经能碾压固定规则策略但距离顶尖职业水平还有很大差距——毕竟Libratus那类系统背后有CFR实时求解我这个只是离线训练的NFSP模型。7.3 推理延迟与部署形态模型结构不大单次推理时间在CPU上约8msGPU上不到1ms完全满足实时决策需求。整个模型文件加上参数不到50MB打包里的checkpoints目录直接放了三个模型nfsp_leduc_best.ptLeduc训练出的最佳平均策略模型nfsp_nlhe_best.pt单挑无限德州训练出的最佳平均策略模型dqn_leduc_best.ptDQN实验版模型用来做算法效果对比部署时主推理接口很简洁给定一个合法的牌局状态调用model.predict()就能返回动作概率分布可以直接接人机对战前端也可以作为更复杂决策系统里的一个策略组件。8. 源码与模型复现从零跑通这条训练管线8.1 环境要求与依赖清单项目在Python 3.8、PyTorch 1.10环境下开发测试依赖列表如下torch1.10 numpy1.21 rlcard1.0.5 # 仅用于基础环境参考 pandas1.3 tqdmrequirements.txt里已经列好所有依赖建议用虚拟环境安装。显卡有就更好没有也不用担心Leduc训练在CPU上几百个episode就能看到初步效果单挑无限德州的完整训练建议还是用GPU会快出至少一个数量级。8.2 训练流程与关键命令训练Leduc上的NFSP模型python train.py --env leduc --algo nfsp --episodes 50000训练单挑无限德州模型python train.py --env nlhe --algo nfsp --episodes 200000评估已训练好的模型python evaluate.py --model checkpoints/nfsp_leduc_best.pt --opponent conservative --rounds 10000用预训练模型跟随机策略做快速对战python play.py --model checkpoints/nfsp_leduc_best.pt --opponent random训练过程中会实时打印分项胜率、损失和每百手收益。训练完成后模型会保存到checkpoints目录下同时自动生成训练曲线图train_curves.png方便直观确认训练状态。8.3 项目说明文档与模型文件怎么用打包里的项目说明.pdf和README.md内容基本同步包含完整的环境结构、状态编码格式、动作空间定义、超参表格和实验记录。README打开后建议先看Quickstart部分能够快速跑通一个最小Demo。模型文件直接加载就能用如果只是想看效果不需要重新训练。我个人的建议是第一次跑先用Leduc一手牌规模小、训练速度快适合验证环境有没有跑通等到日志正常、指标合理再切到单挑无限德州。直接上大牌局出了问题往往很难定位到底是环境bug还是算法问题。最后说点实在的训练跑完最深的体会是在德州扑克这种不完全信息博弈里模型结构复杂程度的影响远没有状态设计和对手管理大。NFSP的两个网络结构就是普通MLP本身没什么神秘但状态编码方式、对手池的设计、平均策略网络的学习率对最终效果的影响占了整个项目百分之七十的权重。还有一点值得提醒如果你的目标是做一个能赢职业选手的德扑AI单靠这份离线训练的强化学习模型是远远不够的。真正的顶级系统还需要在局部决策时做实时回溯搜索对对手做实时建模。这个项目更大的价值在于帮你把深度强化学习方法在博弈场景里的训练管线完整跑通在这个基础上再去做搜索增强、对手建模就有了可以踩的台阶。本文还有配套的精品资源点击获取