案例解析:用Tensorflow和Pytorch计算骰子值

点击上方关注,All in AI中国

在之前的文章中,我讨论了如何在Tensorflow中使用大约400个带注释的图像作为训练数据来构建12类骰子检测器。该模型的目标是检测6、8、10或12面骰子的骰子每一面的存在,然后确定显示的那一面的值是什么。完成后,我可以在屏幕上获得骰子的总值。

这个模型在任务上表现得相当不错,但问题是要么不识别骰子的那一面,要么错误分类了它确实检测到的面部值。所以在上一篇文章的最后,我提到我要解决这个问题的另一种方法是建立一个专门用于检测骰子面的第一阶段对象检测器,然后是第二阶段CNN,它可以使用第一阶段的输出模型来确定数字。虽然这增加了训练和实施管道的复杂性,但我觉得它可以提高整体性能。

我对潜在性能改进的推理基于是使用一种尺寸适合所有通用模型的优势,而不是将问题分解为更小的部分并构建专门针对特定任务的模型。在这种情况下,第一阶段物体检测器可以学习单独地与每种类型的骰子来识别骰子面部。这意味着它可以从不同的角度更多地看到骰子,因为根据方向识别8面和10面骰子上的面部就会产生类似的问题。然后在第二阶段CNN我可以应用大量的旋转和翻转来增加数据,这比我之前构建的通用对象检测模型中的数据要高得多,它可以帮助在任何方向都更好地识别骰子值。

案例解析:用Tensorflow和Pytorch计算骰子值

新的对象检测模型具有单个类“dice_top”,这些框被传递到后端ResNet模型进行分类。

案例解析:用Tensorflow和Pytorch计算骰子值

来自之前博客的同一视频的GIF重新运行。两阶段管道也获得20的值

你可以在这里找到github上的代码(https://github.com/sugi-chan/2-stage-dice-pipeline)。这些脚本需要用作Tensorflow对象检测库的一部分,并且我在各个点都修改了检测脚本以进行数据准备。那些有我用来做图像和视频最终标签的那些,就如帖子所示。

训练新物体探测器

对于这个项目,我使用了我之前博客中使用的相同的数据集。对于物体检测模型,我仅使用200个图像,其中我调整了所有边界框以具有标签“dice_top”而不是数字1-12。我通过使用Labelimg快速浏览xmls并手动调整标签来完成此操作。最初我试图在xmls生成的csv中自动调整标签,但在训练时遇到了奇怪的模型行为,所以我又回到了手动方法。

对于单一类,模型只需要运行一个小时左右才能将其停在一个良好的阈值,而不是前一个模型所需的6个小时左右。

案例解析:用Tensorflow和Pytorch计算骰子值

在这个阶段,我能够评估新物体检测模型与其前身相比如何进行预处理。快速出现的是它检测到错过第一个模型的检测骰子。左下方的图像是第一个模型的输出,而右侧的图像是新的单个类检测器处理的相同图像。左侧的6可以在新的中检测到,但在第一个模型中省略。

案例解析:用Tensorflow和Pytorch计算骰子值

案例解析:用Tensorflow和Pytorch计算骰子值

类似的故事可以在以下图像中看到。第一个模型在d8顶部附近未检测到,但第二个模型可以检测到。

案例解析:用Tensorflow和Pytorch计算骰子值

案例解析:用Tensorflow和Pytorch计算骰子值

两个模型都显示出出现10面骰子的劣势。在这种情况下,两个模型在检测角落中的蓝色10面骰子时表现不佳,第一个模型更是直接失败了。

案例解析:用Tensorflow和Pytorch计算骰子值

案例解析:用Tensorflow和Pytorch计算骰子值

因此,虽然第一个模型在未能识别蓝色10面骰子的顶部方面做得更好,但我认为值得注意的是,新的单类检测器的数据量只有一半,但它可以比前一代更好的检测骰子。

现在新的骰子探测器就位,我可以继续开始建立第二阶段的价值分类模型。

为后端模型准备数据

更有趣的数据准备工作是获取后端CNN的数据。我知道我需要训练一个模型来识别骰子面上的数字,无论它们的方向如何,所以我认为CNN对于随机垂直和水平翻转以及随机旋转形式的大量数据增加将是有帮助的。我决定使用Pytorch后端模型,因为它提供了一个很好的简单管道来训练和部署它的模型(我也只运行了太多的Keras模型和类似的品种)。使用Pytorch的其他原因主要是我喜欢使用Pytorch,并且在我的其他博客中为这类问题提供了良好的代码库。

因此,为了将骰子顶部图像放入我想要的文件夹结构中,我有几个选项。

  1. 手工裁剪每个类的几百张图像:这个过程显然是缓慢的。对我来说也有太多的手工工作需要去做。
  2. 使用新的骰子顶部模型裁剪出所有骰子顶部,然后将它们分类到文件夹中。这也需要大量的工作来排序。在这一点上,我猜你们都知道我下一步要做什么。
  3. 使用我以前训练过的12类物体探测器根据预测的类别将图像分类到12个文件夹中,然后查看数据集以查找错误分类。

我选择了3,因为它需要我做的机械工作较少,而且我发现其他问题在过去的一个聪明的解决方案。

为此,我所要做的就是修改我用于将边界框输出到图像上的脚本以提供框坐标,以便我可以切割图像阵列以裁剪出图像中的骰子顶部。然后使用边界框中的预测类别,我将图像分类到不同的文件夹中。这让我可以在几分钟内生成2500张左右的裁剪图像,而不是话费太多时间手工裁剪2500张图像。

接下来是清洁。下面是6s文件夹的屏幕截图,其中有很多9s的也隐藏在其中。对于这些骰子,9s和6s通常在底部附近标有线或点,对于没有标记的骰子,这意味着它是6。

案例解析:用Tensorflow和Pytorch计算骰子值

另一个有趣的类别是看1s vs 7s

案例解析:用Tensorflow和Pytorch计算骰子值

这也让我对我的12类对象检测模型所产生的分类错误类型有了一个很好的感觉。根据角度,您会看到5s被标记为2s或6s,11s或12s被标记为10s,2s或1s,因为模型对这些数字的接触相对较少。

一旦数据集被分类到12个文件夹中,我就准备向它扔几个CNN以查看哪些有效。

训练Pytorch CNN

有了数据集并按类分解到文件夹中,使用标准数据集和数据加载器函数来利用pytorch CNN管道非常简单。这里要注意的一件事是我制作了120张图像的验证集(每个类10张)。我做了一个平衡的验证集,即使对值1-6存在很大的不平衡,因为那些显示在所有骰子上,而值11和12仅出现在d12s上,因此它们表现最少。

我发现使用经过预先训练的ResNet模型工作得相当好,我只是允许在几个时期内对权重进行微调。在大约15个时期之后,我使用ResNet 50获得了95%的验证设置准确度,并发现将其增加到ResNet 101可将其提高到97%的准确度。我最终坚持使用ResNet 101,因为目前模型足迹不是我的一个大问题。

把它们放在一起

现在我已经完成了两件事,我只需将它们组合成一个管道。第一阶段模型将检测骰子面并将这些骰子面部馈送到第二模型以进行分类。然后根据后端模型的分类,我能够在屏幕上添加骰子值并显示它们。

这是相对简单的,需要添加初始化Pytorch模型并添加其他函数来预处理图像,将结果映射到其标签,并将结果相加。唯一令人烦恼的部分是我没有一台当前可用的多GPU机器,并且当其中一些基于Tensorflow的时候使用多个模型会让人讨厌。

对于其他项目,我在同一个GPU上使用了多个基于Pytorch的大型模型,就像我的Fate Grand Order机器人一样。但是我发现Tensorflow倾向于将所有可用的GPU资源分配给自己。因此,为了运行多个模型,我基本上处理了在任何给定点上哪一个处于活动状态,但对于这个用例,我只是将Pytorch CNN分配给我的CPU运行。这会降低评估速度,但暂时是一个可以快速解决的办法。接下来的技术购买可能会建立一个GPU集群,使这样的事情不那么烦人。

案例解析:用Tensorflow和Pytorch计算骰子值

它得到正确的数值20

比较两种方法

在考虑这两种方法时,我认为主要的权衡是速度和准确性之间的权衡。

我可以在单个GPU上运行的单一模型的第一种方法评估速度比我的GPU + CPU方法快得多。但是,在两个GPU上运行第二个管道可能会最大限度地减少这种差异。

现在为了让事情变得有趣些,你可以看看两者之间的表现。为此,我从训练集和验证集中提取了25个示例,我想你可以称之为测试集,并在它们上运行两个模型。为此,我只是看看他们是否得到了正确的总骰子值,如果他们没有,我就注意到他们所犯的错误类型。

第一个模型得到了25个中的8个完全正确(32%),而两个阶段的管道中有25个完全正确(64%)。因此,我在做这个博客时的一般假设似乎是正确的,即使两者仍有改进的余地。

第一个模型所犯的错误通常不是检测某些骰子,也不是如上所述错误分类以及某些数字。常见的是6s vs 9s,1s vs 7s,8s vs 3s vs 6s。下面的图像有3个未检测到的骰子和3个被归类为6的骰子。

案例解析:用Tensorflow和Pytorch计算骰子值

来自第一个单一物体检测模型的图像

两阶段管道通过识别丢失的3个骰子并且正确地将所有骰子面部分类为41来优于第一模型。

案例解析:用Tensorflow和Pytorch计算骰子值

然而,第二个模型仍然存在问题,在25个案例中,我看到了它没有正确检测10面骰子面部的问题(两个模型都做得不好),然后将6s错误分类为9s。要解决此问题,我必须检查以确保我在数据集的6和9文件夹中没有任何问题,如果这不能解决问题,那么使用更多9s和6s的图像来扩充数据。在这两种机制之间,我认为这有助于解决很多问题。对于错误识别10面骰子上的面部,我认为为该特定骰子添加更多数据将是有用的。

案例解析:用Tensorflow和Pytorch计算骰子值

真值应该是41,但是有3左右的差值。我认为这个情况是归结为没有标记的6面骰子。事实上,在6和9文件夹

最后的想法

所以在这篇文章中,我展示了如何构建我之前在第一个骰子计数帖中提到的两阶段管道。此外,我很高兴地表示,两阶段管道确实胜过我的第一个模型,并且在检测骰子面部然后对面部进行分类方面做得更好。对我而言,这是另一个建立良好专业模型的实例,执行通用方法。

神经网络的相似之处在于它们通常需要数千个示例才能汇聚形成一个好的解决方案并学会识别特定的类。因此,简化他们必须优化的事物,可以让他们更好地针对特定问题进行优化并了解其细节。从技术意义上讲,对于我的12级探测器,它可能只能看到几十个特定的骰子,并且必须学会根据这几个例子进行本地化和分类。然而单级检测器看到它的单级2500次。然后第二级模型可能只看到每个类几十到几百次,但它显著增加了数据,大大增加了它的有效训练数据量的处理。这使得它在分类时比基本对象检测模型能获得更多的经验。

这种增加的专业化带来了更多的复杂性,即训练更多模型并在管道中实施它们。在这种情况下,两级流水线需要2个GPU才能平稳运行,而不是单个型号只需1个GPU。从技术上讲,我可以尝试不使用Tensorflow ... 因为我在使用Pytorch之前一次运行3-4个大型ResNets。 这种速度与准确度权衡的价值都取决于特定任务所需的准确度与速度之间的关系。

案例解析:用Tensorflow和Pytorch计算骰子值

你可以在这里找到github上的代码(https://github.com/sugi-chan/2-stage-dice-pipeline)

案例解析:用Tensorflow和Pytorch计算骰子值

相关推荐