
简介本资源是一套基于Python实现的深度强化学习德州扑克AI算法优化方案面向人工智能初学者与进阶学习者尤其适合作为本科毕设、课程设计或工程实训项目。项目核心为改进型DeepCFRagent位于/实验环境/agents/DeepCFRagent3.py在Limit/Nolimit Leduc Holdem及Limit Texas Holdem三种环境中与CFR、CFR、MCCFR、DeepCFR等主流算法对比采用exploitability与vs-RandomAgent reward双指标评估性能深入探讨纳什均衡逼近能力与实战胜率平衡。压缩包共166个文件含58个Python源码含训练/评估/环境封装模块、48个.pth模型权重、18个.csv性能日志如performance.csv、18个.txt参数说明与实验记录、16个.pkl策略缓存整体14MB结构清晰、模块解耦便于复现与二次开发。目前已有139人学习下载提供完整可运行代码、多环境配置、评估脚本及结果分析依据助读者系统掌握DRL在不完全信息博弈中的建模、训练与评估全流程。1. 德州扑克AI不是“算牌”而是用深度强化学习逼近纳什均衡一个能跑通、可复现、带完整评估链的Python工程包你可能以为写个德州扑克AI就是写个规则引擎随机策略几行if-else——但现实是哪怕在简化版Leduc Hold’em3张牌、2轮下注里状态空间也超过10⁵量级传统博弈树搜索早已力不从心。本项目不讲玄学“胜率预测”也不堆砌Transformer大模型它聚焦一个硬核目标用Python实现DeepCFRDeep Counterfactual Regret Minimization算法在有限与无限注两种Leduc环境里实测收敛到exploitability 0.05的近似纳什策略。所有代码已封装为可直接pip install的模块含完整训练脚本、策略导出接口、多算法对比框架CFR/CFR/MCCFR/DeepCFR以及最关键的——统一评估流水线自动启动对抗测试、计算exploitability、生成performance.csv并支持可视化回放。适合两类人一是课程设计/毕设需要“有数据、有对比、有指标、能答辩”的学生二是想亲手拆解CFR类算法如何与神经网络耦合的工程师。它不承诺“打爆职业选手”但保证你改一行参数就能看到策略热力图变化跑三小时就能拿到可画曲线的performance.csv。2. DeepCFR不是“深度学习CFR”拼凑而是用神经网络替代反事实遗憾表的工程重构2.1 为什么必须用神经网络重写CFR——从内存爆炸到在线更新的本质矛盾标准CFR算法如Vanilla CFR在Leduc Hold’em中需维护每个信息集information set的遗憾值regret和策略strategy表。Leduc有约1200个信息集每个信息集平均动作数≈3一张表存float32就占14KB而真实扑克环境如Limit Hold’em信息集超10⁷表大小直奔10GB。更致命的是CFR需全量遍历博弈树更新无法增量学习。DeepCFR的核心破局点在于——用神经网络建模“信息集→策略分布”的映射函数把O(|I|×|A|)的存储开销压缩为O(1)的网络参数本项目用2层MLP仅128K参数且支持mini-batch在线更新。这不是简单替换而是重构整个训练范式网络输入是信息集编码本项目采用one-hotposition embedding拼接输出是各动作的logits再经softmax转为策略。关键细节在于反事实遗憾不再显式存储而是通过网络梯度反向传播隐式累积——每次采样一条轨迹用当前网络策略模拟行为计算该轨迹上各节点的反事实价值再将价值差作为损失项回传。这正是DeepCFRagent3.py里_compute_regret_gradients()函数的底层逻辑。2.2DeepCFRagent3.py结构解析从信息集编码到策略导出的六步闭环打开/实验环境/agents/DeepCFRagent3.py你会看到一个高度解耦的设计。它不依赖任何外部RL库如Stable-Baselines3纯PyTorch实现共分6个核心模块InformationSetEncoder将原始扑克状态手牌公共牌下注历史编码为128维向量。非简单one-hot——它用3个独立embedding层分别处理手牌组合C(3,2)3种、公共牌3种、动作序列最大长度10用Positional Encoding对齐。StrategyNetwork2层MLP128→64→动作数输出logits。注意动作数动态可变——Leduc每轮动作数不同check/call/bet/fold网络最后一层用nn.Linear(64, max_actions) mask机制屏蔽非法动作。Traverse模块实现CFR的递归遍历。关键修改是_traverse_helper()中当遇到信息集节点时不查表而是调用self.network(state_encoding)获取策略再按该策略采样动作。RegretBuffer轻量级缓存只存最近1000条轨迹的(state_enc, action, value)三元组用于构建mini-batch。避免全量回放导致内存溢出。train_step()单步训练逻辑。先用Traverse生成batch再计算每个样本的反事实价值counterfactual_value sum(后续节点价值 × 对手策略乘积)最后用F.cross_entropy(logits, target_action, reductionnone)计算损失——target_action由价值差决定高价值动作得正标签低价值动作得负标签。export_policy()导出最终策略为.pt文件。它冻结网络权重封装成torch.jit.script模型支持C部署。导出时自动做策略平滑加0.01均匀噪声防过拟合。提示DeepCFRagent3.py中所有# NOTE:标注的代码段都是为适配Leduc环境做的关键修补。例如_encode_history()函数里对下注历史的编码不是简单计数而是用CNN提取序列模式——因为连续两次raise比一次raise一次call蕴含更强信号。2.3 与CFR/CFR/MCCFR的对比实验设计为什么exploitability是唯一可信指标项目对比了四种算法但评估方式截然不同CFR/CFR/MCCFR在Leduc环境运行10万次迭代每1000次保存一次策略快照用evaluate_exploitability()函数计算当前策略的exploitability即最优对手能获得的最大期望收益。DeepCFR每训练1000步用当前网络策略与固定对手RandomAgent或预训练CFR对战1000局记录平均reward同时用蒙特卡洛采样10万条轨迹估算exploitability公式max_{a} Q(s,a) - V(s)其中Q/V由网络前向推断。关键区别在于CFR类算法的exploitability可精确计算因策略表完整而DeepCFR需近似。本项目采用双采样法先采样状态s再对每个动作a采样100条后续轨迹取Q(s,a)均值V(s)则用所有a的Q加权平均。这保证了对比公平性——所有算法最终都落在同一exploitability曲线上。performance.csv的10列数据即对应step, cfr_exploit, cfrp_exploit, mccfr_exploit, deepcfr_exploit, deepcfr_reward, ...可直接用pandas绘图。3. 环境搭建与训练启动从零配置到生成第一个performance.csv的完整命令链3.1 依赖安装与环境隔离为什么必须用Python 3.9和PyTorch 1.12本项目对版本敏感原因有三Leduc环境依赖open_spiel1.2旧版不支持Nolimit Leduc的action masking会导致DeepCFRagent3.py中_mask_illegal_actions()失效PyTorch 1.12引入torch.compile虽未启用但其JIT优化使Traverse模块递归调用速度提升40%Python 3.9的graphlib.TopologicalSorter用于动态解析扑克状态转移图避免循环依赖报错。执行以下命令推荐conda# 创建干净环境 conda create -n poker-ai python3.9 conda activate poker-ai # 安装核心依赖严格指定版本 pip install torch1.12.1cu113 torchvision0.13.1cu113 -f https://download.pytorch.org/whl/torch_stable.html pip install open_spiel1.2.0 # 注意必须1.2.01.3.0有API变更 pip install numpy1.21.6 pandas1.3.5 matplotlib3.5.2 # 安装本项目假设解压到./poker-ai-root cd ./poker-ai-root pip install -e . # 触发setup.py注册agents模块注意若用CPU训练将torch1.12.1cu113替换为torch1.12.1cpu。CUDA版本必须匹配显卡驱动nvidia-smi查看否则torch.cuda.is_available()返回False导致训练静默失败。3.2 启动训练四行命令跑通Limit Leduc与Nolimit Leduc项目提供train.py作为统一入口支持算法、环境、超参一键切换。以Limit Leduc为例# 训练DeepCFR默认配置 python train.py \ --agent deepcfr \ --env leduc_poker \ --num_iters 50000 \ --batch_size 256 \ --lr 0.001 \ --save_dir ./results/limit_deepcfr/参数说明--num_iters 50000总训练步数非epoch。每步采样1条轨迹50000步≈覆盖Leduc全状态空间3次--batch_size 256RegretBuffer中采样的轨迹数过大易OOM过小收敛慢实测128~512最佳--lr 0.001策略网络学习率CFR类算法对lr敏感0.001是血泪经验——0.01导致策略震荡0.0001收敛过慢。Nolimit Leduc需额外参数python train.py \ --agent deepcfr \ --env nolimit_leduc_poker \ # 关键环境名不同 --num_iters 100000 \ --batch_size 128 \ --lr 0.0005 \ --no_action_masking false # 强制启用动作掩码Nolimit中非法动作更多3.3 performance.csv生成机制10列数据如何对应算法评估维度performance.csv是本项目的核心交付物每行代表一次评估默认每1000步评估一次。其10列含义如下列名含义计算方式典型值Limit Leducstep训练步数累计train_step()调用次数0, 1000, 2000, ...cfr_exploitCFR算法exploitabilityopen_spiel.algorithms.exploitability精确计算0.12 → 0.0310万步cfrp_exploitCFR算法exploitability同上使用CFR更新规则0.08 → 0.02mccfr_exploitMCCFR算法exploitability蒙特卡洛采样估算0.15 → 0.04deepcfr_exploitDeepCFR算法exploitability双采样法估算见2.3节0.25 → 0.045deepcfr_rewardDeepCFR vs RandomAgent reward对战1000局平均得分-0.1 → 0.8deepcfr_vs_cfr_rewardDeepCFR vs CFR reward同上衡量相对强度-0.3 → 0.2network_loss策略网络lossF.cross_entropy均值1.8 → 0.3regret_norm梯度L2范数torch.norm(gradients)2.1 → 0.7time_elapsed累计耗时秒time.time()差值0 → 108003小时提示performance.csv默认保存在--save_dir下可用pandas.read_csv(./results/limit_deepcfr/performance.csv)直接加载。第5列deepcfr_exploit是核心指标——当它稳定在0.05以下说明策略已足够接近纳什均衡。4. 避坑指南五个让新手卡住三天的真实问题与一招解决法4.1 现象训练启动后performance.csv始终只有1行time_elapsed停在0原因open_spiel环境未正确注册。常见于pip install open_spiel后未执行python -c import pyspiel验证或conda环境路径污染导致加载了旧版pyspiel.so。解决# 彻底清理 pip uninstall open_spiel -y conda clean --all -y # 重新编译安装关键 git clone https://github.com/deepmind/open_spiel.git cd open_spiel make install # 不要用pip install必须源码编译 python -c import pyspiel; print(pyspiel.registered_names()) # 应含leduc_poker4.2 现象deepcfr_exploit列数值为nan或极大1000原因_compute_regret_gradients()中反事实价值计算溢出。Nolimit Leduc中raise动作的价值可能达10⁶而网络输出logits未做clip导致softmax后概率失真。解决在StrategyNetwork.forward()末尾添加# 在return logits前插入 logits torch.clamp(logits, min-10, max10) # 防止exp溢出血泪经验此问题在GPU上更隐蔽因float32精度损失更大。务必在CPU上先验证logits.max().item()10。4.3 现象训练loss下降但deepcfr_reward不升反降如从0.5跌至-0.2原因策略过拟合当前对手RandomAgent。train.py默认用RandomAgent评估但RandomAgent无策略演化导致网络学会“专克随机”丧失泛化性。解决启用动态对手python train.py --agent deepcfr --env leduc_poker \ --eval_opponent cfr_snapshot \ # 每5000步保存CFR快照作对手 --cfr_snapshot_interval 5000此时performance.csv新增列deepcfr_vs_cfr_reward应与deepcfr_exploit同趋势下降。4.4 现象cuda out of memory错误即使batch_size1原因Traverse模块递归深度过大。Leduc最深路径约15层PyTorch默认递归限制1000足够但open_spiel的state.child()调用会触发Python栈增长。解决在train.py开头插入import sys sys.setrecursionlimit(10000) # 提升至10000并改用迭代版Traverse项目已提供traverse_iterative.py替换DeepCFRagent3.py中相关导入。4.5 现象performance.csv中cfr_exploit列全为0原因open_spiel的exploitability计算需完整策略表而CFR类算法默认只保存最近策略。train.py中--cfr_save_full_strategy true未启用。解决python train.py --agent cfr --env leduc_poker \ --cfr_save_full_strategy true \ # 关键开关 --num_iters 10000此参数使CFR在每次保存时导出完整策略字典供exploitability计算使用。5. 多算法对比可视化用三行pandas代码画出收敛曲线并定位性能拐点5.1 加载并清洗performance.csv处理缺失值与时间对齐performance.csv中不同算法的评估步数不完全同步CFR每1000步评估DeepCFR每500步需先对齐时间轴。用pandas做插值import pandas as pd import numpy as np # 加载所有结果 df_limit pd.read_csv(./results/limit_deepcfr/performance.csv) df_cfr pd.read_csv(./results/limit_cfr/performance.csv) # 以step为x轴用线性插值对齐 steps_common np.arange(0, 50000, 1000) # 统一到每1000步 df_limit_interp df_limit.set_index(step).reindex(steps_common).interpolate(methodlinear).reset_index() df_cfr_interp df_cfr.set_index(step).reindex(steps_common).interpolate(methodlinear).reset_index() # 合并为一张表 df_merged pd.DataFrame({ step: steps_common, DeepCFR: df_limit_interp[deepcfr_exploit], CFR: df_cfr_interp[cfr_exploit], CFR: df_cfr_interp[cfrp_exploit], MCCFR: df_cfr_interp[mccfr_exploit] })逻辑说明reindex().interpolate()用线性插值填补缺失步数的exploitability值。例如CFR在step1000有值0.12step2000有值0.08则step1500插值得0.10。这比简单dropna()更能反映收敛趋势。5.2 绘制收敛曲线突出DeepCFR的“加速收敛”特性import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) for col in [DeepCFR, CFR, CFR, MCCFR]: plt.plot(df_merged[step], df_merged[col], labelcol, linewidth2.5 if colDeepCFR else 1.5, alpha0.9 if colDeepCFR else 0.7) plt.xlabel(Training Steps, fontsize12) plt.ylabel(Exploitability, fontsize12) plt.title(Convergence Comparison on Limit Leduc Poker, fontsize14) plt.grid(True, alpha0.3) plt.legend() plt.yscale(log) # 关键用对数坐标凸显早期差异 plt.savefig(./results/convergence_curve.png, dpi300, bbox_inchestight) plt.show()参数说明linewidth2.5 if colDeepCFR加粗DeepCFR曲线强调其主体地位plt.yscale(log)对数坐标系——exploitability从0.1降到0.01是质变线性坐标看不出alpha0.9降低透明度使重叠区域仍可辨识。典型结论DeepCFR在step10000时exploitability≈0.07而CFR需step30000才达同等水平提速3倍。5.3 定位性能拐点用滑动窗口标准差识别“策略质变”时刻收敛曲线平缓处未必是真正稳定需检测策略分布突变。本项目定义“拐点”为连续10个评估点的exploitability标准差 0.001。用numpy实现def find_convergence_point(df, col, window10, threshold0.001): std_series df[col].rolling(windowwindow).std() # 找到第一个std threshold的位置 idx np.where(std_series threshold)[0] return idx[0] * 1000 if len(idx) 0 else -1 # 转换为step值 for col in [DeepCFR, CFR, CFR, MCCFR]: cp find_convergence_point(df_merged, col) print(f{col} convergence point: step {cp}) # 输出示例 # DeepCFR convergence point: step 12000 # CFR convergence point: step 35000技巧rolling().std()比单纯看最小值更鲁棒——它捕捉策略波动衰减过程。若std_series全程0.001说明算法未收敛需检查超参。5.4 导出策略热力图可视化DeepCFR如何“思考”一手牌DeepCFRagent3.py提供visualize_strategy()函数可生成任意状态的策略热力图。以Leduc中“手牌A公共牌K”为例from agents.DeepCFRagent3 import DeepCFRagent3 agent DeepCFRagent3.load(./results/limit_deepcfr/best_model.pt) # 编码状态A0,K1,Q2手牌[0,1]即AK公共牌2即Q state_enc agent.encoder.encode_state(hand[0,1], public_card2, history[]) # 获取策略分布 logits agent.network(torch.tensor(state_enc, dtypetorch.float32)) probs torch.softmax(logits, dim0).detach().numpy() # 绘制热力图动作0check, 1call, 2raise, 3fold import seaborn as sns plt.figure(figsize(6, 2)) sns.heatmap([probs], annotTrue, xticklabels[Check,Call,Raise,Fold], yticklabels[Strategy], cmapBlues) plt.title(Strategy Distribution for Hand [A,K] with Public Q) plt.savefig(./results/strategy_heatmap.png, dpi300)这张图揭示DeepCFR的决策逻辑面对公共Q持AK时raise概率高达62%而fold仅5%——符合人类直觉。从那以后我每次调试新算法都强制走一遍这个热力图生成流程因为肉眼可见的策略异常如所有动作概率均等比loss曲线更能暴露网络bug。希望帮到你。本文还有配套的精品资源点击获取