用Create ML进行物体检测:训练和演示应用

358 阅读12分钟

作者。 Maxim Skorynin, Evil Martians的iOS工程师和Travis Turner, Evil Martians的技术编辑

你已经收集了图像,对它们进行了对象检测的注释,并导出了你的数据集。在惊讶地注视着Xcode时,你会想到...现在怎么办?答案是:我们将把数据导入Create ML,配置训练参数,执行训练本身,考虑我们的结果,并将模型集成到一个演示应用程序中。在这一过程中,我们将提供一些关于一些算法和框架的见解,这些算法和框架使这个未来主义的巫术成为现实。请将目光锁定在屏幕上,并开始向下滚动!

这是一个系列的第二部分,介绍使用苹果原生ML工具的基本原理。这里的目标是建立一个应用程序,能够检测和突出视频中车牌的外观。(例如,一个显示停放的汽车前面的视频应该在车牌周围画一个方框)。在上一期中,我们从头开始收集图像,并准备了一个数据集与Create ML一起工作。如果你想看看我们是怎么做的,或者想知道你的数据应该遵守哪些参数--请查看我们的物体检测指南的第一部分!

向Create ML导入数据

既然我们已经准备好了我们的三个文件夹,让我们直接跳入并开始使用Create ML。有两种方法来打开这个应用程序。我们可以在Spotlight搜索中输入 "Create ML",或者打开Xcode并从下拉菜单中选择Xcode→Open Developer Tool→Create ML

We select Xcode from the macOS menu, then the Open Developer Tool option, then Create ML itself

这就是...Create ML。你现在正站在伟大的门槛上。

一个文件浏览器窗口将打开,你需要选择新文件,然后,一旦应用程序被打开,我们将创建一个新项目。选择 "对象检测"模板,让我们输入项目的名称(以及你想在这个领域输入的任何其他信息),然后选择项目在你的机器上的位置,继续前进。在下一个屏幕上,我们将把我们的数据添加到适当的领域。要把预训练的图像添加到项目中,把相应的文件夹拖到所需的字段中。

The Create ML menu allows us to add three dataset types and to set some custom parameters before training the model

🤔 添加数据的三个地方......我们有三个文件夹的数据。

你的数据部分现在看起来应该是这样的。

We're adding our Training, Validation, and Testing datasets

如果你使用的是自己的数据集,预计数字会有所不同。

训练参数

默认情况下,Create ML提示我们使用YOLO算法来训练我们的模型。YOLO是训练物体检测模型的最有效的算法之一。在Create ML中,这被称为全网络。

迭代批量大小参数与梯度下降的概念密切相关--这是一种用于神经网络权重迭代优化的算法。

Some of the training parameters we can account for include Iterations, Batch Size, and Grid Size

一次性加载所有的数据可能是低效的,而且经常会因此而出现问题。为了帮助防止这种情况,数据集被分为较小的数据包(这被称为批次),然后由算法在迭代的基础上进行处理。在每次迭代中,收集一定大小的批次(由批次大小参数指定)。然后,基于这批图像,算法进行预测(它试图在每批图像中找到物体),并将结果保存起来,以便进一步进行神经网络修正。在每个迭代结束时,模型会进行 "校准"(即更新权重)。因此,迭代参数描述了迭代的总次数。因此,迭代梯度下降法有助于模型以最佳方式适应数据。

但是,更多的迭代次数并不总是对模型的效率产生积极的影响,反而会导致过拟合。过度拟合的模型将无法在任何不包括在训练集中的图像中识别出所需的数据(在我们的例子中,车牌)。反之亦然,如果你的迭代次数太少,你就会出现欠拟合,模型出错的几率就会很高。

批量大小与迭代次数同样重要。如果批量大小=1,模型的权重将被调整得过于频繁。而在事情的另一面,如果Batch Size=数据集大小,就会发生一次。

The Core ML flow from data training to app implementation

我们应该选择哪些参数?

那么,你应该选择哪些参数值呢?

让我们从迭代开始。根据我自己的实验来确定,事实证明,1000-1500次迭代对我们的目的来说是足够的。

在它的一份报告中,苹果公司声称,批量大小越大越好。不过,下拉列表还是提供了几个选项。16、32、64、128和自动。当选择自动时,系统会根据当前计算机的性能来选择批量大小。(在分别尝试了所有的选项后,我们设法发现,对于装有M1专业芯片的MacBook Pro来说,自动=32)。由于我们的数据集规模较小,用不同的批处理量值训练出来的模型的准确性和效率差别不大,所以为了最大限度地提高模型性能,减少训练时间,并简化我们的任务,我们会把这个值设置为自动

This graph shows that as Batch Size grows, so does training time

接着是最后一个参数,网格大小是原始图像的片段网格,大小为S x S;算法在处理过程中自动划定这个网格。这是YOLO算法与其他此类算法的主要区别。网格的大小是根据图像内物体的大小来决定的。

An octopush plush in a 3 by 3 grid

再解释一下,要找到一个占整个图像50-60%的物体,13x13的网格很可能是矫枉过正。在我们的设定中,所有的副本都是方形的。因此,我们将把数值设置为13x13

Several objects in a 13 by 13 grid

当你准备好了,点击训练,让Create ML做它的事情。

分析我们的结果

训练结束后,你还可以运行额外数量的迭代(通过点击Train More)。理论上,这将提高准确率,但它不会有明显的区别。为了更有效地提高我们的准确性,我们应该改变初始数据集。这可以通过复制左侧面板中的当前数据集,改变或扩展数据集,然后从头开始训练来实现。

在训练过程中,Create ML建立了一个显示损失函数值的图表,你可以在训练标签中查看。该值越低,模型的错误就越少。下图显示的是使用全网络(YOLO)算法的结果。你可以看到准确性逐渐增加,直到训练完成。我认为,当损失函数的值等于0.64时,说明结果相当不错

The results of the full network algoritm for training, the graph line slopes downward and eventually turns into a nearly flat line

通过选择评估标签,你可以看到训练、验证和测试阶段的指标。例如,如果我们的模型除了汽车牌照之外还包括其他的对象类别,这个表格将显示每个类别的准确度值。这对于了解哪个类在实例数量上占优势是很方便的。

I/U (intersection-over-union)值是一个从0-100的百分比,表示注释的盒子(这包括在注释的JSON文件中)和模型预测的盒子之间的重叠。因此,I/U 10%意味着它们只是勉强重叠,95%的I/U意味着它们是非常接近的匹配。

The graph showing the results of the Transfer Learning algorithm is smoother and it also suggests a faster speed and smaller model

百分比代表每个模型类别的平均预测准确性,它们被计算在两个阈值I/U 50%Varied I/U。网上没有很多资源解释这些阈值的含义(即使是苹果公司的),所以我们来描述一下。**I/U 50%**是表示有多少预测的准确性高于50%的值。Varied I/U是数据集中特定类别的预测准确率的平均值。

预览标签中,你可以直观地测试学习阶段的结果。只需将图像拖到左边的列表中,Create ML就会将预测结果显示为表示车牌位置的蓝色方框。

The preview tab allows us to test our results, the license plate in this image has successfully been identified

输出部分,你可以查看模型信息、元数据并导出你的模型。

The Core ML flow from data training to app implementation

转移学习

在进入应用集成之前,让我们花一分钟时间来谈谈转移学习,然后我们将根据所选择的方法和硬件来比较结果。

迁移学习需要在已经训练好的模型的基础上建立一个新的模型。为了让事情更清楚,试着想象一下,现有的模型正在将其知识 "转移 "到新的模型上。事实上,苹果公司提供了这样一个模型:它已经在大量的图像上进行了训练,现在正被用作Create ML的基础。这种方法可以更快地得到结果,而且最后模型的最终文件也更小。通过实验。为了用转移学习算法获得更多的准确性,我们需要准备更多的初始数据进行训练。

在下面的图片中,你可以看到由转移学习算法训练出来的模型。它在大约250次迭代时达到了其损失函数的最小值。这是因为对于这种方法来说,原始集的规模很小。但速度显然更快,而且最终模型的大小也更小。

The graph showing the results of the Transfer Learning algorithm is smoother and it also suggests a faster speed and smaller model

比较训练结果

让我们根据设备和选择的方法来比较训练性能和结果(使用在Roboflow上创建的模型)。

Comparing the YOLO and Transfer Learning approaches on a MacBook Pro 2015 mid model and a M1 MacBook Pro. In both cases, the training time and model sizes are lower on the M1 Macbook Pro and the Transfer Learning approach is faster and has a smaller model in both cases

关于视觉框架

为了将我们的模型集成到一个项目中,我们将使用Vision框架。它在Xcode中适用于iOS 11.0+,并允许将模型作为一个类实例使用。

Vision适用于分析视频流或图像帧的模型,例如。对象检测、分类、文本识别、动作分类、手部姿势分类。

将我们的模型整合到一个iOS项目中

首先,你应该创建一个新的iOS项目,将之前导入的模型添加到根目录中。

The directory of our newly created project which has been named CarLicense

由于我们将使用相机,不要忘记在Info.plist中包含所需的属性(隐私 - 相机使用说明)。

This menu reflects the fact that Privacy - Camera Usage Description has been included in info.plist

让我们走过设置一个模型和处理一个视频流的主要方法。

setupRequest方法中,配置了一个VNCoreMLRequest 类的对象,接受两个参数作为输入:一个模型实例和一个closure。结果将被传递给闭包。非常方便的是,在将模型文件添加到项目中后,我们可以将其作为CarLicensePlateExperimentFullNetwork 类使用,而不需要额外的努力。

private func setupRequest() {
    let configuration = MLModelConfiguration()

    guard let model = try? CarLicensePlateExperimentFullNetwork(configuration: configuration).model, let visionModel = try? VNCoreMLModel(for: model) else {
        return
    }

    request = VNCoreMLRequest(model: visionModel, completionHandler: visionRequestDidComplete)
    request?.imageCropAndScaleOption = .centerCrop
}

现在这里要注意的是最重要的一点:我们需要为摄像机的输出流覆盖Sample Buffer Delegate。具体来说,captureOutput方法将视频流转换为所需的格式,并将其传递给VNImageRequestHandler 类对象,该对象将启动先前创建的请求。

// MARK: - Video Delegate

extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {

    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer), let request = request else {
            return
        }

        let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer)
        try? handler.perform([request])
    }

}

请注意下面的代码--不要忘记将self 设置为output 对象的一个委托。

private func setupCaptureSession() {
    ...
    let queue = DispatchQueue(label: "videoQueue", qos: .userInteractive)

    let output = AVCaptureVideoDataOutput()
    output.setSampleBufferDelegate(self, queue: queue)
    ...
}

结果将作为VNRequest 类的一个实例传递给visionRequestDidComplete 方法。然后我们将使用VNRequest 对象获得VNRecognizedObjectObservation 数组。VNRecognizedObjectObservation 是一个包含预测结果和一些参数的对象。

  • labels: [VNClassificationObservation]:一个对象分类器
  • boundingBox: CGRect: 对象的大小和位置
  • confidence: [Float]预测准确度:从0到1

使用boundingBox 参数的值,我们可以在屏幕上绘制框架。你可以在演示项目库中看到一个渲染样本。

private func visionRequestDidComplete(request: VNRequest, error: Error?) {
    if let prediction = (request.results as? [VNRecognizedObjectObservation])?.first {
        DispatchQueue.main.async {
            self.drawingBoxesView?.drawBox(with: [prediction])
        }
    }
}

结果准确性

让我们比较一下我们使用全网络算法与转移学习的结果的准确性。

In this image corresponding to transfer learning accuracy, there is a man next to a car, a blue box has been drawn almost around the license plate on the front of the car. There is extra space on the top and bottom, so the blue box doesn't perfectly overlap the plate. The box also does not include the farthest edges of the box. In the image corresponding to full network, we see the same scene, except, in this case, a blue box has been drawn almost perfectly around the license plate on the front of the car

预览标签结果

这里是在iOS应用内的操作。

Alt

探测到的结论

那么,让我们总结一下我们在整个系列中所做的事情。尽管数据集相对较小,但我们还是取得了相当不错的结果(另外,我想指出的是,配备新一代M1芯片的新MacBook Pro的性能,可以将模型学习时间提高到6倍)。

Core ML和Create ML是非常强大的工具,有很多可能性。我很高兴苹果正在推进机器学习,并不断改进其工具。然而,目前,详细的文档并没有真正提供,只有苹果公司的一些小文章。因此,当你想深入了解这个话题或更好地理解Create ML里面可配置参数的细微差别时,你必须在互联网上收集信息,简直是一点一点地收集。这真的会减慢学习的进程。希望这篇文章能让你明白一点。

我试图在这里分享我认为在我刚开始使用ML时缺少的东西。我希望我能够告诉你一些新的东西,你现在对标记数据集和从头开始创建一个物体检测模型的过程有了更多的信心和知识。一旦你知道你在做什么,这个过程就很简单了这很好,因为这意味着实验和演示是相当容易开始的。


顺便说一下,如果你有一个问题,(无论是否与ML有关)Evil Martians已经准备好帮助你了!我们将检测它,分析它。我们将检测它,分析它,并将它消灭掉(或者,如果你错过了什么,我们会它消灭掉!)请给我们留言

第一部分| 第二部分