运用现代 AI 工业基础设施玩小游戏

0x0. 楔子

最近有个活动小游戏,界面长这样:
Game UI
Game UI

玩法很简单,遇到「好材料」按左方向键获取,「坏材料」按右方向键丢弃,判定正确就一次记分,判错则会有一个错误小动画停顿一段时间作为惩罚。这是一个典型的计算机视觉(CV)擅长的任务,正好一直久闻 Yolo 大名但没什么机会实践,这次便想从头开始试试能否用 Yolo 实现一个自动帮我玩这个小游戏的玩具。


0x1. 框架设计

不过在开始之前得先解决一些基础问题。 The very first thing is, 这是个网游。 它有着严苛的反作弊限制。 我一开始想直接在这台游戏机上用 conda 安装 yolo 来跑检测器,但惊讶地发现连 python 之类的程序也在反作弊检测范围内。 当我仅仅想跑一下官方的 demo 用例,我的游戏客户端都会直接断开连接并退出。

所以我需要

  1. 在另一台电脑上运行 CV 检测器;
  2. 用一种非常 Native 的方式输入键盘和鼠标消息,以避免反作弊识别为非法操作;

0x11. 双机屏幕识别方案

我脑中的第一个念头是在第二台电脑上用远程桌面连进去玩。但这个方案我以前试过,动不动就会产生秒级的延迟,对于这个实时判定类的游戏来说太慢了。后来我借鉴了 双机推流直播 的方案,用 OBSTeleport 插件把游玩电脑的画面镜像到工作机上,再借助 OBSVirtual Camera Feature,把视频流转换为虚拟摄像头设备,这样就可以在 yolo 中直接作为 source 打开了.效果如图:
Streaming

0x12. 程控输入方案

由于反作弊系统都会严防死守各种模拟输入 API 机制,我这次没有尝试软件方案,而是直接掏出了家中常备的 ESP32 板子,把它模拟为 HID 设备,再用任意信道(蓝牙、Wi-Fi)给它发送要输入的键盘鼠标指令即可。至于怎么实现,只需要简单问问 Claude:

ESP32+Arduino

不过正巧这块板子我之前装过 CircuitPython 在玩其它东西,所以:
ESP32+CircuitPython

Claude 的编程能力现在已经非常强,这种简单的任务几乎可以保留 80% 的代码,去掉的 20% 里还有 10% 左右是不需要的注释、日志等逻辑。以前程序员键盘只有 ctrl-c ctrl-v 两个键的 Meme 现在可以改为只有 按键说话 ctrl-c ctrl-vdel 了。
Keyboard, Imagined by Gemini


0x2. Detector

搞定了画面输出和键鼠输入的问题后,现在可以在工作机上使用 yolo detect predict source=1 打开游戏机的画面推流,完成检测后发起 HTTP 请求 curl http://192.168.x.x/send-control/ 通知模拟成 HID 设备的 ESP32 执行按键操作了。那么最关键的检测器部分,在我翻阅完官网的 tutorio 后,发现整个流程已经相当简便且标准化、工业化。 所以我决定直接 follow 官网提到的所有步骤和平台。

0x21. annotate(打标)

我们的游戏任务是识别一系列在画面中上下排列的对象,需要识别出物体并找到它在画面中的位置。这是一个 detect 类型的任务。为了训练模型,首先需要制作一套标注了对象名称以及位置大小的数据集。目前已经有很多专为此设计的平台产品,甚至有了专门进行该种工作的工种(人工智能训练师)。 官方严选平台是 RoboFlow, 这个平台包揽了计算机视觉项目的各种 SOP,包括各类型任务的数据集拆分、打标、版本管理、模型训练、上线部署等,完全可以只使用一段录制好的视频,只用这一个平台,开发部署一个新的计算机视觉产品。 并且可以团队化协作,把打标任务分配给团队一起完成。

0x211. 数据集生成

创建好 Project 后,第一个功能就是 Upload Data。 注意到上传数据支持文件夹和视频,于是我直接录了一小段手工游玩这个小游戏的视频上去。上传成功后会弹出一个自动采样对话框,按需切分出若干张图片以便进行后续步骤。
Upload Data
Sampling

0x212. 标注

生成数据集后,工作流进入 annotate 打标步骤。 作为一个工业级平台, RoboFlow 对于各项任务步骤的管理考虑也非常完善。真实产品的数据集可能非常大,要多次逐步完成,所以 annotate 也有多个批次(job)可供管理。 打标完成后,还可能有 review 环节,还可能 split 到训练、验证、测试等不同分类的数据集中,这些都可以很方便清晰地管理。 至于打标本身,用法就跟所有人常识能想象的过程一样,给你一张图片,在上面把物体框出来并标记它的名字(class,分类)。 我这里把需要的材料标记为「好木头」,要丢弃的标记为「坏木头」,并且选了几张有一点干扰的图片(鼠标遮挡、特效遮挡)来作为训练数据。

annotate Job
annotating


0x213. 数据集增强

完成标注并Review后,就可以把它加入数据集。在我过去的记忆里,模型要使用数据集训练前,要先进行归一化和增强处理,比如先统一大小,然后随机添加一点噪点和图像变化,来增强模型 robustness. 然而在这个平台上,数据集有个 version 概念,可以用相同的数据集创建不同的版本,每个版本可以选择不同的归一化和增强方式。这就可以用同样的原料快速调整炼丹配方,比如切分、画面比例、颜色曝光变化等等,尽可能减少最繁复的打标过程。

Versions

可以看到我先后尝试了几个不同版本,并且把仅进行了36张图片标注的数据扩展成了279张图片的数据集。 由于我们这次的任务仅有原始图片对象的平移缩放,所以我感觉用一个小数据集和简单的变形也足够应对了。


0x22. Training

数据集制作完成后接下来自然是训练了。 在 RoboFlow 平台上提供了一站式的模型训练-部署功能,但这是需要付费的。由于我刚开账号,有一点 credit 点数,所以姑且也尝试了一下自带的模型训练。训练过程可以实时可视化地观察模型表现曲线,完成后可以直接在平台上部署为 Web App,但想要把模型下载下来自行使用则需要付费账号,所以我没有进一步探索。
Training on RoboFlow

0x221. Training on Google Colab

如果想要自行部署模型,我们就必须自己训练。平台支持数据集打包下载,或者干脆直接提供了 API, 以便你在各种在线算力平台上直接用代码下载数据集。我这里选择后者,平台极其周到地提供了现成的训练用 Google Colab Jupyter 文件,只需把里面的下载代码换成自己的即可。

Download Dataset
Show download code

「令人发指」的是,Google Colab 现在 GPU 机器也有免费额度,所以我这种简单任务的模型训练完全就是白嫖。

Custom Training

可以看到我使用了很小的 30个 epoch 就达到了还不错的效果,虽然模型稳定性可能还欠佳,但玩这个小游戏肯定是足够了。就算出现误识别之类的情况,手动修复起来也很简单——我在游戏里换个地方或者干脆在 OBS 里把画面遮挡一下就好了。训练时间也非常短,这 30 个 epoch 好像只用了十几分钟。

跑一下测试数据,可以看到模型已经能识别我们新标注的「好木头」和「坏木头」了。
Prediction Test


0x3. Let it play

剩下的事就很简单了,把模型下载下来,然后再让 Claude 打工给我们写一个 Python 脚本,用给 ESP32 发指令的方式不断重复游玩这个小游戏,之后挂机等着分数慢慢涨就好了😎,我最后用这个模型爬到了排行榜第六名,获得了666660分(当然手工调整了一下):

运行中
排行榜

0x4. 后记 & 不足之处

说实话,现代 AI 工业程度的发达程度超出了我的想象,我原本以为至少打标和处理数据集会是一项有点折腾/费时的工作,可能要编程调试这那的细枝末节与 edge case,但其实打标训练反而是整个项目中最简单最完善的一环,你不需要知道任何技术细节和原理,所有操作都非常直观且普适,任何人都可以参与到 AI 产业中的某一环中,不再是仅有专家才能驾驭的领域。

反而是基础框架中的一些特性我还不太满意,最大的问题是 屏幕镜像obs-teleport 这个插件使用 TCP 协议和每帧 jpeg 压缩的方式来发送数据,这对带宽和网络质量是不小的考验。我尝试使用线缆来互联两台电脑,带宽的问题可以改善,但 TCP 丢包重传导致的延迟(最长达1秒!)却还是不可避免。有机会可能会再进一步研究一下这个插件,给它加上丢帧无延迟的udp传输方式🤔。这样这个模型就不仅能完成分数累积任务,识别速率也有希望超过人类。