尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLOv8与强化学习的麻将AI实战:从视觉识别到智能决策

基于YOLOv8与强化学习的麻将AI实战:从视觉识别到智能决策 1. 项目概述当AI坐上麻将桌“AI打麻将”这个事儿听起来像是科幻电影里的桥段但今天它已经是一个可以动手实现的、充满挑战和趣味的实战项目。这不仅仅是让电脑学会“碰杠吃胡”的规则那么简单它背后融合了计算机视觉、强化学习、博弈论等多个前沿领域的技术。想象一下你坐在家里打开电脑就能和三个不知疲倦、算力超群的AI牌友来上几圈或者开发一个能帮你复盘、分析牌局的智能助手是不是挺带劲的这个项目的核心目标是构建一个能够“看懂”牌局并“做出决策”的AI智能体。它需要完成两个关键任务一是“感知”即通过摄像头或图片实时识别出麻将牌桌上的所有牌面包括手牌、河牌、碰杠牌二是“决策”即基于识别出的牌面信息结合麻将的复杂规则如番种、听牌概率、对手行为推测计算出当前最优的出牌、吃、碰、杠或胡牌策略。对于开发者、麻将爱好者或者对AI应用感兴趣的朋友来说这个项目都是一个绝佳的练手机会。它不像围棋、象棋那样有完全信息麻将是不完全信息博弈充满了概率、欺骗和心理战这让AI的决策逻辑更加复杂和有趣。通过这个项目你不仅能深入理解目标检测、图像分类等CV技术如何落地还能一窥强化学习在非完美信息博弈中的魅力。接下来我就结合自己折腾这个项目的经验从头到尾拆解一遍把关键的技术选型、实操步骤和踩过的坑都摊开来聊聊。2. 核心思路与技术选型让AI先“看见”再“思考”要让AI打麻将我们得把它拆解成两个相对独立但又紧密协作的模块视觉感知模块和决策推理模块。这个架构思路很清晰先解决“是什么”的问题再解决“怎么办”的问题。2.1 视觉感知模块如何让AI“看清”每一张牌这是整个项目的地基。麻将牌种类固定通常为34种花色×4张特殊牌但实际场景复杂牌可能以不同角度摆放、部分被遮挡、光照条件多变、背景杂乱。因此我们需要一个鲁棒性强的目标检测模型。为什么选择YOLO系列在相关热搜词里SSD和YOLO都被频繁提及。两者都是单阶段目标检测算法的佼佼者。经过实测和社区反馈我最终选择了YOLOv8原因如下精度与速度的平衡YOLOv8在保持YOLO系列一贯高速的同时精度又有显著提升。对于需要实时或准实时响应的麻将场景比如从视频流中识别速度至关重要。生态完善易于部署Ultralytics公司维护的YOLOv8开源库文档清晰API友好。从训练到导出为ONNX、TensorRT等格式进行部署一条龙服务非常顺畅。相比之下虽然SSD也很经典但其生态和社区活跃度在当前阶段略逊于YOLO。对小目标友好麻将牌在整张图片中占比可能较小尤其是远处的牌YOLOv8在模型结构上针对小目标检测进行了一些优化表现更稳定。关于“三维目标检测”和“不规则目标”热搜词里提到了“三维目标检测”和“注意力机制检测不规则目标”。在标准麻将AI场景中我们通常假设牌是平铺在二维平面上的因此2D目标检测已经足够。除非你的场景是机器人手臂抓取立体堆叠的牌否则不需要引入复杂的3D检测。至于牌的形状是规则的矩形所以“不规则目标检测”的复杂机制在这里不是必需品标准的边界框回归就能很好工作。注意模型选择不是绝对的。如果你对轻量化有极致要求可以考虑YOLOv5s或Nano版本如果追求极致精度且算力充足可以试试YOLOv9或DETR系列模型。但对于大多数入门和中级项目YOLOv8是一个“开箱即用”且效果不错的起点。2.2 决策推理模块如何让AI“思考”出下一张牌当AI“看到”了所有牌难题才真正开始。麻将的决策属于不完全信息、多人、零和、随机摸牌博弈。主流思路有以下几种基于规则的专家系统这是最直观的方法。编写大量“IF-THEN”规则例如“如果手牌差一张听牌则优先打出生张或字牌”。这种方法实现简单但麻将局面浩如烟海规则难以穷尽且无法处理概率和长期收益问题AI会显得很“笨”。基于搜索的博弈树类似于象棋AI模拟未来几步所有可能的出牌、摸牌序列选择胜率最高的路径。但麻将分支因子巨大每轮可能打出的牌有几十种且信息不完全搜索深度非常有限计算量爆炸。强化学习Reinforcement Learning, RL这是目前最主流且效果最好的方法。让AI作为智能体Agent与环境麻将桌互动通过胡牌、放铳等结果获得的奖励Reward来学习策略。特别是深度强化学习DRL如Deep Q-Network (DQN)、Proximal Policy Optimization (PPO)结合神经网络来近似复杂的价值函数或策略函数能够处理高维状态空间。监督学习收集人类高手的对局数据让AI学习在给定局面下人类高手会做出什么决策。这可以作为一个很好的预训练模型为强化学习提供初始策略。我们的混合策略在实战中纯强化学习从零开始训练效率极低。一个有效的策略是**“模仿学习强化学习微调”**。先用大量人类牌谱数据做监督学习让AI学会“像人一样打牌”得到一个基础策略网络。然后让这个网络在自我对局Self-play的环境中通过强化学习比如PPO进行微调和提升探索出超越人类经验的策略。AlphaGo的成功也部分得益于这种思路。关于“AI Agent”决策模块本质上就是一个麻将AI Agent。它接收视觉模块传来的结构化牌局信息状态State输出一个动作Action如“打出三万”并随着对局进行不断更新其内部策略。3. 实战构建从零搭建一个麻将AI视觉系统理论聊完我们进入实战环节。首先攻克视觉部分这是所有后续工作的基础。我会以YOLOv8为例详细走一遍流程。3.1 数据准备与标注万事开头难没有高质量的数据再好的模型也是空中楼阁。你需要准备一个包含各种麻将牌、在各种场景下的图片数据集。数据收集来源1自行拍摄。这是最可靠的方式。使用手机或摄像头从不同角度、不同光照白天、夜晚、台灯下、不同背景麻将桌布、木质桌面、杂乱背景拍摄麻将牌。重点要覆盖单张牌特写、手牌13张叠放、河牌散落、碰杠牌两组并排放置。来源2网络爬取与游戏截图。可以从麻将教学网站、游戏视频中截图。但需注意版权且这类图片风格可能比较单一。数据量建议至少准备2000-3000张图片。每张图片中可能包含多张牌。确保34种花色牌如一万到九万、条、筒、东南西北中发白每种都有充足的样本尤其是“白板”这类特征较少的牌。数据标注工具推荐使用LabelImg或更高效的CVAT、Roboflow进行标注。标注格式YOLO格式要求每个标注对象保存为一个txt文件内容为class_id x_center y_center width height。坐标和宽高都是相对于图片尺寸的归一化值0-1之间。类别定义你需要建立一个classes.txt文件按顺序列出所有牌的种类。例如0 yi_wan 1 er_wan ... 8 jiu_wan 9 yi_tiao ... 33 bai_ban关键技巧对于“一萬”和“一筒”尽管文字不同但图案差异巨大必须分为不同类别。对于“东风”和“西风”等字牌模型是可以学会区分的。标注注意事项边界框Bounding Box要紧贴牌的四边。即使牌有轻微旋转或遮挡框也要沿着牌的边缘。对于紧密叠放的手牌确保框与框之间不要有太多重叠尽量分开。3.2 模型训练与调优教会AI认牌数据准备好后我们就可以开始训练模型了。环境搭建# 使用Ultralytics官方推荐方式安装 pip install ultralytics训练脚本 创建一个train.py脚本或直接使用命令行。核心是准备好一个数据集配置文件mahjong.yaml。# mahjong.yaml path: /path/to/your/mahjong_dataset # 数据集根目录 train: images/train # 训练集图片路径 val: images/val # 验证集图片路径 # 类别数量和名称 nc: 34 # 你的麻将牌总类别数 names: [yi_wan, er_wan, ..., bai_ban] # 与classes.txt对应然后开始训练yolo taskdetect modetrain modelyolov8n.pt datamahjong.yaml epochs100 imgsz640 batch16modelyolov8n.pt: 这里我用了纳米nano模型它体积小、速度快适合快速迭代和部署。如果你的数据量足够大且追求更高精度可以换成yolov8s.pt或yolov8m.pt。epochs100: 迭代轮数根据损失曲线loss curve提前停止或增加。imgsz640: 输入图片缩放尺寸。麻将牌细节需要较高分辨率640是一个不错的起点可以尝试768。batch16: 批大小取决于你的GPU显存。训练过程中的监控与调优损失曲线关注train/box_loss,train/cls_loss和val/box_loss,val/cls_loss。理想情况是训练损失和验证损失都平稳下降且两者差距不大。如果验证损失很早就开始上升说明模型过拟合了。评估指标主要看mAP50-95(mean Average Precision)。这是目标检测的核心指标。mAP50IoU阈值0.5达到0.95以上mAP50-95达到0.7以上对于麻将牌检测就算很不错了。常见问题与调优过拟合现象是训练集精度很高验证集精度很低。解决方案增加数据增强在mahjong.yaml中配置augmentTrue或使用更激进的数据增强参数加入随机裁剪、色彩抖动、模糊、马赛克等使用更小的模型如yolov8n添加权重衰减weight_decay。欠拟合现象是训练集和验证集精度都很低。解决方案增加模型复杂度换用yolov8m或l增加训练轮数检查数据标注质量。某些类别识别差例如“白板”和“发财”容易混淆。解决方案针对性补充这些类别的训练数据检查标注是否准确可以尝试在损失函数中为这些类别增加权重。实操心得在训练初期不要一上来就训100轮。先用小数据集比如200张图训10轮快速验证你的数据管道和基础配置是否正确。看到损失在下降再上全量数据。这能节省大量时间。3.3 模型部署与实时推理让AI“在线”看牌训练好的模型如runs/detect/train/weights/best.pt需要部署到实际应用环境中。部署方式选择本地Python脚本最简单的方式。使用Ultralytics的API进行推理。from ultralytics import YOLO import cv2 model YOLO(path/to/best.pt) # 单张图片推理 results model(your_image.jpg) # 可视化结果 plotted results[0].plot() cv2.imshow(Detection, plotted) cv2.waitKey(0) # 访问检测结果 for result in results: boxes result.boxes.xyxy # 边界框坐标 confs result.boxes.conf # 置信度 cls_ids result.boxes.cls # 类别ID # 可以进一步处理如转换为牌面文字Web服务API使用FastAPI或Flask将模型封装成HTTP API方便其他程序如决策模块调用。from fastapi import FastAPI, File, UploadFile app FastAPI() model YOLO(best.pt) app.post(/detect/) async def detect_mahjong(file: UploadFile File(...)): image_bytes await file.read() # 将bytes转换为OpenCV格式... results model(image) # 将results解析为JSON返回... return {tiles: detected_tiles_list}边缘设备部署如果想做成嵌入式产品需要将模型转换为更高效的格式。导出为ONNXyolo export modelbest.pt formatonnx。ONNX格式通用性强可以被多种推理引擎如ONNX Runtime调用。导出为TensorRTyolo export modelbest.pt formatengine。这是NVIDIA GPU上的终极加速方案能获得极低的延迟但对环境配置要求较高。实时视频流处理 对于实时打牌场景需要处理摄像头视频流。cap cv2.VideoCapture(0) # 0代表默认摄像头 while True: ret, frame cap.read() if not ret: break # 为了速度可以降低推理频率比如每5帧处理一次 results model(frame, verboseFalse) # verboseFalse关闭控制台日志 plotted_frame results[0].plot() cv2.imshow(Mahjong Detection, plotted_frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()性能优化技巧调整推理尺寸model.predict(sourceframe, imgsz480)。减小imgsz能大幅提升速度但可能会降低小目标的检测精度需要权衡。使用半精度model.predict(sourceframe, halfTrue)。在支持FP16的GPU上可以提速且几乎不损失精度。批处理如果同时处理多个摄像头画面使用批处理batch参数效率更高。4. 决策系统设计初探从规则到智能视觉部分让AI获得了“眼睛”决策部分则是“大脑”。这里我分享一个从简单到复杂的实现路径你可以循序渐进。4.1 第一步实现一个基于规则的“入门级”AI在尝试复杂的强化学习之前先实现一个规则引擎有助于你彻底理解麻将的状态表示和逻辑流程。状态表示 首先需要定义如何用数据结构表示一个牌局。class MahjongGameState: def __init__(self): self.hand_tiles [] # 手牌列表如 [1m, 1m, 2m, 3m, 5z, 5z...] self.discard_pile [] # 河牌弃牌堆 self.melds [] # 副露吃、碰、杠的牌组 self.current_draw None # 刚摸到的牌 self.wind EAST # 当前场风 self.player_wind EAST # 自家门风 self.remaining_tiles 70 # 剩余牌墙数简化 self.is_riichi False # 是否立直你需要将视觉模块识别出的文字如“yi_wan”映射到麻将通用记法如“1m”代表一万“5z”代表白板。核心规则引擎 编写一系列函数来判断牌型、计算听牌、评估打牌风险。def can_chi(self, tile): 判断是否能吃上家打出的牌 # 逻辑上家打出的牌是否能与手牌中的两张牌组成顺子 pass def can_pon(self, tile): 判断是否能碰任何一家打出的牌 # 逻辑手牌中是否有至少两张与打出牌相同的牌 pass def find_tenpai_tiles(self): 计算当前手牌听哪些牌 # 这是麻将AI的核心算法之一。常用方法是“向听数”搜索。 # 暴力法从所有牌中依次尝试添加一张到手牌判断是否和牌胡牌。 # 更高效的方法是使用“表向听”算法或基于“牌效率”的启发式搜索。 pass def evaluate_tile_to_discard(self): 评估打哪张牌最好基于规则 candidates self.hand_tiles.copy() if self.current_draw: candidates.append(self.current_draw) best_tile None best_score -float(inf) for tile in candidates: # 模拟打出这张牌 new_hand [t for t in candidates if t ! tile] # 计算打这张牌后的“牌效率”分数 # 分数可以基于向听数减少量、安全度是否生张、番种潜力等 score calculate_tile_efficiency(new_hand) if score best_score: best_score score best_tile tile return best_tile牌效率计算 这是规则AI的灵魂。一个简单的牌效率模型可以只考虑向听数Shanten。向听数是指当前牌型距离和牌听牌还差几张有效进牌。向听数越少越好。网上有开源的和牌计算库如mahjong这个Python包可以直接调用其向听数计算函数。踩坑记录自己实现一个完整的、高效的向听数计算函数非常复杂涉及到大量排列组合。强烈建议在初期使用成熟的轮子把精力放在决策逻辑的构建上。等你对麻将AI的核心有更深理解后再考虑优化底层算法。4.2 第二步引入基于统计与概率的“进阶”AI纯规则AI不懂概率和风险。进阶一步我们可以让AI具备一些“概率思维”。危险度评估 记录所有玩家打出的牌河牌根据“现物”绝对安全的牌和“筋牌理论”例如如果4万和7万都已被弃则1万相对安全来评估打某张牌是否会放铳点炮。class SafetyPredictor: def __init__(self): self.discard_history [] # 所有玩家的弃牌序列 def get_danger_score(self, tile): 评估打出某张牌的危险系数0-1越高越危险 score 0.0 # 规则1如果是“现物”对手刚打过的牌危险度为0 if tile in self.get_recent_safe_tiles(): return 0.0 # 规则2根据“筋牌”理论调整危险度 score self.calculate_suji_danger(tile) # 规则3如果是中张牌3-7且其相邻牌很少被弃危险度增加 score self.calculate_middle_tile_danger(tile) return min(score, 1.0)期望点数计算 在决定是否立直、是否追立、是否攻防转换时AI需要估算不同决策的期望收益。自摸期望根据当前听牌的种类和剩余牌数估算自摸的概率乘以胡牌后的得分。放铳损失估算打某张危险牌导致放铳的概率乘以放铳后损失的分数。决策选择“期望收益 自摸期望 - 放铳损失”最大的行动。这个阶段的AI已经具备了一定的竞技水平能够根据场况在进攻和防守之间做出权衡。4.3 第三步探索强化学习与深度学习的“终极”形态要让AI达到甚至超越人类高手水平必须引入学习能力。这里简要描述一下深度强化学习DRL的框架。状态表示State Representation 将麻将游戏状态编码成神经网络可以理解的输入。这是一个关键设计点。通常是一个多维向量或图像。向量化表示将34种牌分别用4个通道表示手牌中数量、自家副露中数量、河牌中数量、全局可见数量用于估算剩余牌。再加上场风、门风、剩余牌数、是否立直等标量信息。最终形成一个(34*4 N)维的向量。图像化表示类似AlphaGo将牌面信息、玩家信息等渲染成一个多通道的“图像”供卷积神经网络处理。动作空间Action Space AI可以执行的动作包括打出34种牌中的一张、吃、碰、杠明杠、暗杠、立直、和牌自摸、荣和、放弃过。这是一个离散动作空间。奖励函数Reward Function 这是强化学习的指挥棒。设计的好坏直接决定AI学成什么样。稀疏奖励只在游戏结束时给予奖励胡牌分放铳-分。这种奖励非常稀疏AI很难学习。稠密奖励设计中间奖励引导AI。例如每减少一向听数给予小正奖励。成功立直给予正奖励。打出高危险牌给予小负奖励。摸到有效进张给予小正奖励。最终得分作为终局奖励。网络结构与训练 使用一个深度神经网络如MLP或CNN作为策略网络Policy Network输入状态输出各个动作的概率分布。使用PPO或A3C等策略梯度算法进行训练。训练需要在模拟环境中进行大量的自我对局Self-play。开源项目参考 完全从零实现一个DRL麻将AI工程量巨大。幸运的是有一些优秀的开源项目可以借鉴和学习例如mjai或Suphx的部分公开思路。研究这些项目的架构和代码是快速入门的最佳途径。重要提示深度强化学习训练需要巨大的计算资源和时间并且对算法调参要求极高。对于个人开发者我建议先专注于完成前两步规则概率构建一个可运行的、具有一定强度的AI。第三步可以作为长期研究和学习的目标。5. 系统集成与实战调试当视觉模块和决策模块都开发得差不多了就需要将它们集成起来形成一个完整的、可以运行的AI麻将玩家。5.1 模块联调打通“感知-决策”闭环集成系统的核心是一个主循环它不断从摄像头获取图像调用视觉模块识别更新游戏状态然后调用决策模块获取动作最后执行动作在模拟器中或输出建议。# 伪代码示例 vision_model load_yolo_model() decision_ai RuleBasedAI() # 或 ProbabilisticAI() game_state MahjongGameState() cap cv2.VideoCapture(0) while game_is_on: # 1. 感知 frame get_frame_from_camera(cap) detection_results vision_model.predict(frame) parsed_tiles parse_detections_to_tile_names(detection_results) # 2. 状态更新 update_game_state(game_state, parsed_tiles) # 此函数需要根据牌的位置手牌区、河牌区来区分 # 3. 决策 if is_my_turn(game_state): action decision_ai.decide(game_state) # 4. 执行/输出 if action.type DISCARD: print(f([AI] 打牌: {action.tile}) # 在模拟环境中执行打牌操作 simulate_discard(action.tile) elif action.type PON: print(f([AI] 碰) # ... 处理其他动作 # 短暂延时模拟人类思考时间也避免CPU跑满 time.sleep(0.5)关键挑战与调试状态同步视觉识别可能会有误差或延迟导致AI内部维护的游戏状态与真实牌局不同步。必须设计状态校验和纠错机制。例如每隔几轮强制用视觉识别结果完全重置AI的内部状态或者当AI的动作在现实中无法执行时比如试图打出一张不存在的牌触发状态重新同步。动作映射决策AI输出的是“打出一万”这样的逻辑指令。你需要一个执行器在真实的在线麻将游戏中这可能意味着模拟鼠标点击“一万”这张牌。这涉及到UI自动化如使用pyautogui但请注意游戏反作弊机制。在模拟环境中则直接修改游戏状态数据结构即可。实时性要求从识别到决策必须在几秒内完成否则超时会被判负。优化视觉模型的推理速度如前文所述和决策算法的计算效率至关重要。对于复杂的DRL模型可能需要进行模型剪枝、量化才能在CPU上实时运行。5.2 性能评估与提升你的AI到底有多强如何衡量你的麻将AI水平不能只靠感觉。构建测试集标准牌谱测试收集大量人类对局的终盘局面例如来自天凤、雀魂等平台的牌谱。将这些局面输入给你的AI记录它的选择并与人类高手或牌谱中的实际选择进行对比。计算“决策一致率”。残局测试专门设计一些经典的攻防残局、立直判断残局看AI能否做出最优解。进行模拟对局自我对局让多个相同版本的AI相互对战成千上万局统计它们的平均顺位和得分。虽然不能完全代表对阵人类的水平但可以观察其稳定性。与规则AI对战让你的AI与一个纯随机出牌的AI、一个基于简单规则的AI对战看胜率提升。关键指标平均顺位越低越好1为最高。和牌率成功胡牌的局数比例。放铳率点炮的局数比例。立直率与立直和了率衡量进攻性。打点平均每次胡牌获得的分数。通过分析这些指标你可以定位AI的弱点。例如如果放铳率很高说明危险评估模块有问题如果和牌率很低说明牌效率计算或听牌判断有问题。5.3 常见问题与故障排查在开发和集成过程中你肯定会遇到各种各样的问题。这里列一些我踩过的坑和解决办法。视觉模块问题问题识别准确率在真实场景下骤降。排查检查训练数据与真实环境差异是否过大。真实环境的光照、牌具颜色、摄像头角度是否在训练集中有充分体现解决进行领域自适应。在真实环境下拍几百张新图只对模型进行少量轮次的微调Fine-tuning而不是从头训练。问题某些牌如“白板”和“發财”总是混淆。排查查看混淆矩阵Confusion Matrix。如果这两个类别混淆严重说明它们在特征空间里太接近。解决针对性补充这两个类别的、带有挑战性的训练数据如侧放、反光。也可以在数据增强时专门增加针对颜色和纹理的扰动。决策模块问题问题AI总是很快打出生张疯狂放铳。排查检查危险度评估函数。是否没有正确利用“现物”信息筋牌逻辑是否正确实现解决引入“场况”评估。在比赛早期可以进攻性强一些在比赛后期或自己点数领先时应大幅提高安全牌的权重。实现一个简单的“攻防开关”。问题AI在复杂听牌如多面听、染手时选择很差。排查牌效率计算函数可能只考虑了向听数减少没有考虑听牌后的和了形好坏比如是听边张还是双碰是听1种牌4张还是听3种牌8张。解决在评估打牌选择时不仅要看打完后向听数还要模拟摸进一张有效牌后的有效进张数和听牌种类数选择“未来期望”最高的打法。集成系统问题问题系统运行一段时间后AI状态与真实牌局完全对不上。排查这是典型的状态漂移。可能是某次识别错误如漏检一张牌导致后续所有状态都错了。解决实现定期硬同步。例如在每一局开始、每次自家摸牌后都强制用视觉识别结果完全覆盖AI内部的手牌状态。对于河牌和副露可以每轮都进行全量更新。问题决策速度太慢导致超时。排查使用性能分析工具如Python的cProfile找到瓶颈。是视觉推理慢还是决策搜索慢解决视觉部分采用更小的模型或降低推理分辨率。决策部分对于规则AI检查是否有低效的循环对于搜索算法限制搜索深度对于神经网络进行模型优化。开发一个能打的麻将AI是一个系统工程涉及计算机视觉、传统博弈算法和现代强化学习。从简单的规则引擎做起逐步增加概率模型最后挑战深度学习这个路径既能不断获得正反馈又能层层深入地理解其中的技术精髓。最重要的是这个过程本身就像打麻将一样充满了未知、挑战和乐趣。当你看到自己编写的AI第一次做出一个精妙的弃和选择或者勇敢地立直并自摸时那种成就感是无与伦比的。希望这篇长文能为你提供一张清晰的“地图”祝你开发顺利早日和你的AI牌友战个痛快
返回列表