All Posts

教会AI认识麻将牌之实践篇

开头视频先认识下今天的主角 -【小番】App (谢谢周董的新歌《Mojito》) 说起打麻将我一直是处于比较业余并且不思进取的水平,各个地方的麻将规则不一,繁琐的规则也懒得放脑袋里记忆了,于是每次跟朋友打麻将都是现场临时约定规则,怎么简单怎么来,周边也有不少年轻的小伙伴我一样。 年前回家前跟朋友聚会又打了几圈麻将,几个人又经历了一遍边上网查算番规则边打牌,生怕少算番多亏了五毛钱的过程,于是萌生做个麻将算番APP的想法,解决我们几个懒人的打牌需求。 当时趁着那几天想法热乎,花了一周多撸了一个iOS App 取名叫【小番】就给弄上架了(第一次写swift,发现居然有双感叹号!!这样的运算符,当时我就惊呆了)。小番使用国标规则来算番, 最大番数88番,当一副牌有多种胡法时取最大番数的胡法(基本牌划分的动态规划怎么写来着?),其中各种特殊规则wikipedia里可以写出满满几屏,照着学了一遍。一开始我们就对【小番】的需求比较明确, 把在线麻将游戏的便捷性融入到线下朋友间社交打牌欢乐里 。所以【小番】刚诞生就带了一副AI的眼睛,以便更快更好的服务好大家。 刚开始的【小番】认识麻将牌的能力一般,70%左右。基本上摄像头识别完还需要手动再补上几张牌,所谓人工+智能结合。AI模型准备上我只是做完一次目标检测深度学习模型的数据采集,标注,训练,评估及移动端推理的实现流程。中间没有太多优化,本着先看看有多少人愿意为这项功能买单的想法,把App撸完就上架了😁。 App出来后碰上疫情期间大家也都家里呆着了,App好久也没实践上,中间去江西朋友家呆了一段时间打牌用了一次,发现一些易用性的问题修正了一下,在统一算番规则方面【小番】发挥了实际作用起到了公平公正的效果(让我当晚输了100块钱)。App上架后忙别的事情有段时间也没多管了。最近各地疫情反复,周末看了一下App Store排行榜,发现在没有任何推广的情况下,小番最近还爬到过娱乐版前50名内,改变了我对目前的应用市场没有推广不会有人购买的看法。于是周末花了点时间再好好优化了一把模型,把【小番】识别牌的精准度提到98%以上。 优化后的【小番】,即使是带有一定透视角度的,20多张不同牌同屏也可以一次轻松识别,做到快速认牌算番的效果。行文至此,好玩的东西就介绍完了。各位看官, 目前【小番】新版本上线,特价2折优惠活动中 ,赶紧App Store搜索下载一个收藏起来 ,以备逢年过节打牌使用吧😁(近30层深度神经网络,平均每层不到3毛钱哦)。 下面进入技术干货的部分,实践中怎么提高模型麻将牌识别的精确度和召回率?行业内的人清楚,AI模型是容易做出demo,但产品化过程中需要投入非常多精力打磨的一项技术。以前做个性化推荐和定向广告推荐,深知数据的优化对效果的作用远大于算法自身形式的优化作用。所以这次在模型效果打磨的过程中,我优先着重考虑数据方面的优化。 问题定义 麻将牌识别是一类目标检测问题(Object Detection),目标检测与只输出单一标签的图像分类问题(Image Classification)不同,目标检测问题的输出包含两个信息:图片中可能包含的多个物体的位置,以及各自的分类标签。麻将牌识别在目标检测问题里属于多个小目标(目标相对于完整图像大小)的同时检测,含34个分类(不计花牌),需要考虑不同灯光亮度,投影角度,桌面背景,麻将牌面样式变化下的识别。类似的,自动驾驶的视觉感知部分也包含了一些目标检测(红绿灯,车辆,雪糕筒等)问题。 目标检测算法 解决目标检测算法的常见模型主要分为两类,以R-CNN/Fast R-CNN/Faster R-CNN为代表的两步法:先用启发式搜索(R-CNN/Fast R-CNN)的算法或者特定的网络(Faster R-CNN)找出图片中可能的物体区域(region proposals),然后用卷积神经网络对各个物体区域做一次图像分类。Faster R-CNN可以做到比较高的精确度检测目标,但检测延迟方面比较高,不适于需要视频实时检测的场景。 另一类算法是以SSD(Single Shot MultiBox Detector)和YOLO(You Only Look Once)为代表的一步检测法,这类算法将位置检测的外包矩形和分类概率统一编码到一个卷积神经网络预测输出里。具体的,图像可以以一定的步长划分成格子,每个格子关联几个(比如6个)不同比例的Anchor Box(比如1:1, 1:2, 2:1等),每个格子的每个Anchor Box可以预测一个中心点(x, y)和长宽(l, w)的偏移量以及对应区域是某个分类的概率p,训练过程中通过梯度下降来调整模型参数最小化损失函数。目标检测的损失函数由两部分组成:分类损失函数及定位误差,为减少过拟合的情况也会加入正则化损失,3者做一个加权线性组合。 SSD及YOLO算法在延时和精确度方面平衡比较好,可以满足视频实时检测(30FPS)的需求。本质上SSD和YOLO这类目标检测算法可以理解成一个框架,在这个框架里有多个组成部分或决策算法,不同的选择组成具体的目标检测模型。比如框架中做为主干的特征抽取网络部分,ssd原始使用的vgg16,yolo使用的Darknet53,在平衡速度和精确度时,也可以选择其他的特征抽取网络,如为移动设备优化的mobilenet v1,v2,或者inception等,不同的选择可以组合出多种不同的具体模型。今年最新发布的YOLO v4模型在COCO的数据集上速度和精确度都达到了不错的提升。 麻将识别模型训练 首先是训练数据准备,尝试在网上搜了一圈没有找到现有的麻将牌目标检测的标注数据,于是开始自己的数据采集和标注。 训练数据分为两部分,一部分人工真实数据,一部分生成数据(大量)。真实数据采集使用视频录制的方式,分两种场景,单个麻将牌(34类),以及多张牌组合(14张)。摆好牌后移动手机拍摄不同角度下的成像,完了用脚本从视频里自动截取出来x张图片进行标注。标注工具使用labelimg工具,可以比较方便的框出麻将牌打上标签,准备就绪后就是一顿狂标注,标注后的图片类似这个样子,生成对应的PASCAL VOC格式的xml文件。 第一部分数据接近真实使用场景(一次检测大于14张牌,牌相对于图像的大小也接近实际情况)。但由于只有一副麻将牌,场景比较单一数据量也比较小,训练过程种容易产生过拟合的情况于是引入第二部分数据。第二部分数据是真正帮助提高模型泛化能力的生成数据,这部分数据使用了两个数据源来合成,34张麻将牌的不同样式的图片共600多张,以及4000多张纹理图片的数据集。随机合成生成的场景图片:每次挑两张麻将进行一定的随机旋转/缩放/亮度及对比度调整/投影变换后放在一张随机选的纹理图上作为背景,生成合成图片以及对应的标注数据(麻将牌的外框作为最小外包矩形参与随机变换使得标注信息需要的label&bounding box都是已知信息,可以按模版生成标注xml文件不需要人工标注)。用这个办法可以轻松生成数万个标注图片,生成的数据长下面这个样子。注意 生成数据时直接生成模型输入需要的分辨率 ,减少模型预处理的resize步骤和加载图片数据不必要的内存开销。 像SSD和YOLO等算法都带有一定的数据增强(Data Augmentation)预处理功能,比如随机的的对训练数据进行垂直/水平翻转或者随机截取区域放大等操作,一方面提升模型的通用能力,也减少对训练数据overfitting的问题。由于我在自动化生成数据方面考虑了不同情况的图像变换并且生成了大量的训练数据,对模型自带的数据增强功能并不太依赖。 数据集准备好后(80%数据做训练,20%做测试),使用tensorflow进行模型训练,基于一个预先训练好的图像特征抽取模型(比如ssd_mobilenet_v1_coco)进行迁移学习,减少从零开始训练模型需要的学习图像特征抽取(学习基本的图像理解能力,比如边缘检测,基本形状检测等基础功)的时间,也减少需要的训练图片数据量。 由于生成的模型最终是在手机上做推理,我在手机上使用Tensorflow Lite框架,训练后导出的模型需要转成tflite格式。tflite相对于tensorflow模型少了不少运算符的支持,比如ssd模型的预处理步骤需要放到模型外来做,处理时需要注意根据模型输入图片RGB信息接受的浮点数范围(有模型用[-1,1],也有模型用[0, 1])做相应的正则化,另外也需要检测推理时输入图片或者视频的RGB通道顺序和模型需求是一样的, 任何一个小地方的数据不一致都会使你的模型效果大打折扣 。 在麻将算番这个应用场景下,由于浮点类型的tflite模型在手机上的性能已经不错,没有进一步再对模型的浮点数进行int8离散化处理(quantization)。性能方面在iOS上可以进一步提升: 可以使用Tensorflow Lite的CoreML delegate来做推理 ,利用手机内置AI芯片的并行处理能力来无损的提升推理速度。 AI模型性能的提升使得深度学习在手机及IOT设备上的应用越来越多,Tensorflow Lite在嵌入式设备甚至微控制器上也提供了运行时环境,为不同应用场景在速度与精确度方面的平衡提供了更多的选择,相信后续会有越来越多便捷生活的AI应用产生。Life's getting better.