Core ML与iOS设备上的深度学习模型

在几年前,深度学习模型的开发需要数百万美金的投资和一个专业的研究团队,并且需要花费十年的时间。然而,随着技术的发展,现在即使是拥有半台像样的GPU的普通人也能轻松获得这些模型。但是,深度神经网络也有其缺点,它们往往体积庞大且运行缓慢,因此并不总是能在移动设备上良好运行。幸运的是,Core ML提供了一个解决方案:它允许创建在iOS设备上运行良好的精简模型。

在本系列文章中,将向展示如何以两种方式使用Core ML。首先,将学习如何将预训练的图像分类器模型转换为Core ML格式,并在iOS应用程序中使用它。然后,将训练自己的机器学习(ML)模型,并使用它来制作一个类似于HBO的《硅谷》中看到的“Not Hotdog”应用程序。

设置示例应用程序

为了专注于手头的主要任务——展示转换后的ResNet模型的使用,将“借用”苹果开发者网站上提供的示例图像分类应用程序。当在Xcode中打开下载的应用程序项目时,会显示一个简短且“直截了当”的描述:

看看这个描述,它可能会回答许多问题。要在iOS设备上运行示例应用程序,需要按照设置团队和唯一捆绑标识符的常规步骤进行。建议在真实设备上运行应用程序,以便能够使用设备的相机。

示例应用程序有三个主要方法(在ImageClassificationViewController中)处理ML处理。

设置模型

模型设置和分配给懒加载的classificationRequest变量:

lazy var classificationRequest: VNCoreMLRequest = { do { let model = try VNCoreMLModel(for: MobileNet().model) let request = VNCoreMLRequest(model: model, completionHandler: { [weak self] request, error in self?.processClassifications(for: request, error: error) }) request.imageCropAndScaleOption = .centerCrop return request } catch { fatalError("Failed to load Vision ML model: \(error)") } }()

上述代码片段中最重要的一行是模型分配(let model = (…))。在许多情况下,这将是切换到不同模型时唯一需要更新的一行。

请注意类名中的VN前缀。这意味着这些类是Vision框架的一部分。这个框架提供了一个高级API,用于处理计算机视觉任务,如面部和身体检测、矩形检测、身体和手势检测、文本检测等。除了这些使用Apple创建的模型的高级API外,Vision框架还公开了一个API,当使用自定义Core ML模型进行ML图像分析时非常方便。

在示例应用程序中,一行代码处理所有必需的任务:

request.imageCropAndScaleOption = .centerCrop

每次模型分类完成时,都会调用processClassifications方法以相应地更新UI。

在应用程序中处理分类请求

接下来的方法,updateClassifications,由其他应用程序组件调用以启动图像分类

func updateClassifications(for image: UIImage) { classificationLabel.text = "Classifying..." let orientation = CGImagePropertyOrientation(image.imageOrientation) guard let ciImage = CIImage(image: image) else { fatalError("Unable to create \(CIImage.self) from \(image).") } DispatchQueue.global(qos: .userInitiated).async { let handler = VNImageRequestHandler(ciImage: ciImage, orientation: orientation) do { try handler.perform([self.classificationRequest]) } catch { print("Failed to perform classification.\n\(error.localizedDescription)") } } }

此方法接受一个参数image,并在内部以线程安全的方式调用之前配置的classificationRequest。

显示分类结果

最后一个“主要”方法负责使用分类结果更新UI:

func processClassifications(for request: VNRequest, error: Error?) { DispatchQueue.main.async { guard let results = request.results else { self.classificationLabel.text = "Unable to classify image.\n\(error!.localizedDescription)" return } let classifications = results as! [VNClassificationObservation] if classifications.isEmpty { self.classificationLabel.text = "Nothing recognized." } else { let topClassifications = classifications.prefix(2) let descriptions = topClassifications.map { classification in return String(format: "(%.2f) %@", classification.confidence, classification.identifier) } self.classificationLabel.text = "Classification:\n" + descriptions.joined(separator: "\n") } } }

此方法显示模型置信度最高的前两个预测标签(let topClassifications = classifications.prefix(2))。

剩余的方法处理相机和捕获的图片。它们与ML无关。

检查MobileNet模型

如果在资源管理器中点击MobileNet.mlmodel文件,可以检查模型细节:

除了输入和输出定义外,还展示了相当多的元数据:作者、详细描述和许可证。

将模型添加到应用程序

现在是时候将转换的ResNet模型添加到项目中了。最简单的方法是将其从Finder拖放到Xcode的资源管理器中。请记住,这只是将模型链接到应用程序;模型并没有物理复制到项目文件夹中。如果想将新模型与应用程序的其余部分一起保留,需要在链接之前手动将其复制到那里。

完成此步骤后,可以查看ResNet模型的描述:

在案例中,仅指定了名称、类型、大小、输入和输出。如果考虑分发模型,应该考虑用有意义的信息填充这些字段。这可以使用coremltools Python库完成。

使用转换后的ResNet模型运行应用程序

要使用拖放到Xcode项目中的转换模型,需要在ImageClassificationViewController.swift文件中更改一行代码:

由于在转换过程中选择了“13”作为最低iOS版本,因此需要相应地更改目标平台设置:

在进行上述更改后,可以立即使用ResNet模型进行预测:

MobileNet和ResNet模型之间的一个明显区别是:MobileNet返回带有置信度概率的标签(感谢softmax层),而ResNet返回“原始”的、未缩放的神经网络输出。如果需要,这可以通过向ResNet模型添加自定义层或在应用程序内计算返回结果的softmax来修复。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485