电竞AI之视觉篇:YOLO算法在电竞中的应用(Darknet + TF)

转战米国,经过一段时间的调整和适应,终于有时间整理下最近做的一个项目。从infra到云到大数据到AI,各个领域都应该保持学习,技术的道路从来都不是一帆风顺。

1. 场景介绍

MOBA玩家都比较熟悉不论是DOTA2还是LOL,游戏内会有minimap,为玩家提供位置、视野及信号等信息,帮助对局势进行判断。

假设我们在一个非直播的比赛数据页面,通过小地图的数据,一方面帮助高玩在没有流量的情况下也能合理分析比赛形势,有更多的参与度;另一方面,一个选手的运动轨迹我们补获后,做大数据的分析,对其某个英雄某一种行动路径在什么时间都可以有一个基于历史的推演,有助于战队针对性的训练。

那我们要怎么获取小地图的行动轨迹数据呢,方法A成为游戏厂商的数据下游,花钱花关系妥妥的;方法B找下官方有没有实时数据的接口,实时数据的接口里有没有位置的信息,方法C,自己想办法人工实时录(几乎不可能), 上网一搜LOL小地图识别,Farzaa大佬已经验证过基于YOLO算法的英雄地图识别的,但是也仅此一篇文章。

Farzaa的文章中还是存在几个问题:

  • 只验证了LCS的部分比赛,样本集不够多,怎么快速覆盖全英雄比赛?
  • 根据Farzaa的描述,获取实时数据的方式貌似已经过时了,怎么获取到英雄位置数据的样本?

接下来为大家介绍我的实践方式与最终的效果。

2. yolo - yolov2 - yolov3算法

在开始我们系统搭建之前,还是有必要说明下什么是yolo算法。(作者也是AI starter,需要各位读者也补充些AI的知识,比如吴恩达的CNN课程,神经网络的课程及线性代数等等)

YOLO(You only look once) 是一种快速且高识别率的目标检测和识别的算法和网络模型。YOLO的基本逻辑是将输入图片或者视频帧做多尺寸resize,然后送入CNN网络,最后处理网络预测结果得到检测的目标的confidence和bounding box(处理大致流程如下图)。

image.png

2.1 YOLOv1的CNN网络模型

YOLOv1网络

2.2 YOLOv2及v3在此基础上做的部分优化(持续补充):

  • 正则化,做反向传播时加速训练
  • 结合Faster RCNN的思路,引入RPN(Region Proposal Network)替代v1的全连接层并减少网络计算量, 大致步骤如
    • Step1: 图片通过CNN网络进行滤波,得到各通道特征
    • Step2: 选择滑动窗(一般3X3),每次滑动时会生成9个标定窗口,并计算IoU(Intersection over union)
    • Step3: 将得到的IoU进行classification和regression,最终得到检测目标的boudning box
  • 特征提取的优化,比如参考vgg16的darknet-19到v3的darknet-53

Dark-19

Dark-53

按照yolov3作者的操作说明(YOLOv3操作指南),基本可以成功得到以下的测试结果:
检测与识别效果

3. 打标系统

CNN/DL没有想象那么复杂,一个深度学习的模型,部分精力是在设计怎么提取特征,如果对数据进行预处理,以及tuning parameter及hyperparamter。我们之前有了使用darknet训练和识别的经验,现在来说明下怎么获取LOL的minimap样本的VOC数据。

LOL minimap

3.1 打标步骤

首先,我们做个简单的截屏脚本,在各直播画面截取上图的minimap,频率可以10-15s一个; 之后我们将图片批量倒入我们的打标系统进行标定,这里主要是生成每个英雄的boudning box坐标(xmin, ymin, xmax, ymax),保存后会生成VOC所需的xml文件。

打标系统

这里为什么需要打标系统,主要是打标依赖人工,以s9为例,一场比赛就可以生成1000多张的图片,所以打标系统服务化可以最大程度的多人同时标定。当然每个人获取到的图片也是随机获取显示的,尽量不重复标定。(后续我们做了基于特定场景的自动生成样本集和自动增量训练系统,下一篇可以介绍)

3.2 打标数据

标定之后的xml数据格式如

<annotation>
    <folder>imgs</folder>
    <filename>xxxxxx.jpg</filename>
    <path>/xxxx/lol-champions/imgs/xxxxxx.jpg</path>
    <source>
        <database>Unknown</database>
    </source>
    <size>
        <width>290</width>
        <height>285</height>
        <depth>3</depth>
    </size>
    <segemented>0</segemented>
    <object>
        <name>urgot</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>94</xmin>
            <ymin>22</ymin>
            <xmax>110</xmax>
            <ymax>43</ymax>
        </bndbox>
    </object>
    <object>
        <name>thresh</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>189</xmin>
            <ymin>70</ymin>
            <xmax>203</xmax>
            <ymax>89</ymax>
        </bndbox>
    </object>
</annotation>

4. TF训练

这里貌似没有什么特别要介绍的,github上已经有挺多开源的YOLOv3的TF实现,不过这里有必要提一下darknet是c的实现,会比tf的训练速度快很多,GTX 1080的显卡跑20000个epoch darknet基本一晚上就好了,tf跑的话要几天,当然这个还取决于显卡的内存,batch size的大小,图片的大小以及epoch的设置,仁者见仁,智者见智吧。

这里逻辑上说下我的思路,打标系统的图片和生成的xml文件需要符合VOC的目录结构,这样可以用一些生成训练集及测试集的脚本统一处理,再由下面的脚本处理提取出坐标和分类名。

PASCAL VOC的目录结构(自定义也可以)

.
└── VOCdevkit     
    └── VOC2012 
        ├── Annotations      #xml文件
        ├── ImageSets        
        │   ├── Main         #训练集与测试集.txt文件
        ├── JPEGImages       #原图片
def convert_voc_annotation(data_path, data_type, anno_path, use_difficult_bbox=True):

    classes = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus',
               'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',
               'motorbike', 'person', 'pottedplant', 'sheep', 'sofa',
               'train', 'tvmonitor']
    img_inds_file = os.path.join(data_path, 'ImageSets', 'Main', data_type + '.txt')
    with open(img_inds_file, 'r') as f:
        txt = f.readlines()
        image_inds = [line.strip() for line in txt]

    with open(anno_path, 'a') as f:
        for image_ind in image_inds:
            image_path = os.path.join(data_path, 'JPEGImages', image_ind + '.jpg')
            annotation = image_path
            label_path = os.path.join(data_path, 'Annotations', image_ind + '.xml')
            root = ET.parse(label_path).getroot()
            objects = root.findall('object')
            for obj in objects:
                difficult = obj.find('difficult').text.strip()
                if (not use_difficult_bbox) and(int(difficult) == 1):
                    continue
                bbox = obj.find('bndbox')
                class_ind = classes.index(obj.find('name').text.lower().strip())
                xmin = bbox.find('xmin').text.strip()
                xmax = bbox.find('xmax').text.strip()
                ymin = bbox.find('ymin').text.strip()
                ymax = bbox.find('ymax').text.strip()
                annotation += ' ' + ','.join([xmin, ymin, xmax, ymax, str(class_ind)])
            print(annotation)
            f.write(annotation + "\n")
    return len(image_inds)

5. 结论

image.png
image.png

video效果.gif

是不是很有意思,欢迎讨论。还有什么电竞中的场景可以结合deeplearning呢?

文献参考

发布了8 篇原创文章 · 获赞 0 · 访问量 298

猜你喜欢

转载自blog.csdn.net/xiaojax/article/details/105372925
tf