2020 年,移动设备上的机器学习将不再是什么热门的新事物。在移动应用中添加某种智能已经成为一种标准做法。幸运的是,这并不意味着苹果已经停止了创新。在这篇博文中,作者将总结一下 Core ML 的新特性以及苹果生态系统中的其他 AI 和 ML 技术。
2020 年,移动设备上的机器学习将不再是什么热门的新事物。在移动应用中添加某种智能已经成为一种标准做法。
幸运的是,这并不意味着苹果已经停止了创新。
在这篇博文中,我将总结一下 Core ML 的新特性 以及苹果生态系统中的其他 AI 和 ML 技术。
去年 Core ML 有很大的更新,但今年的改进要温和得多:几个新的层类型、对加密模型的支持,以及在 CloudKit 上托管模型更新的能力。
版本号似乎被去掉了。去年的更新名为 Core ML 3,但是现在的名称是 Core ML,没有了编号。然而,coremltools 确实升到了版本 4。
注意:内部的 mlmodel 规范版本号现在是 5,所以新的模型在 Netron 中将显示为“Core ML v5”。
新增的层类型包括:
Convolution3DLayer
、Pooling3DLayer
、GlobalPooling3DLayer
:这些类型在处理视频数据时特别有用,现在你是用 Vision 框架来做这些事情。(Core ML 仍然没有明确支持 1D 卷积,不过你可以使用常规的 2D 卷积层。)
OneHotLayer
:对输入进行独热编码。
ClampedReLULayer
:有最大值的 ReLU 激活函数(可用于生成 ReLU6)。
ArgSortLayer
:对输入张量进行排序。它将返回排好序的索引,而不是排序后的值。Core ML 中没有常规的排序层,但是你可以使用GatherLayer
来重新排序 argsort 输出的元素。
CumSumLayer
:计算输入张量的累积和。
SliceBySizeLayer
:Core ML 已经有了几种类型的切片层。这个允许你传入一个张量,包括切片起始索引;切片的大小是固定的。
这些层类型只能用于规范版本 5 或更高的版本,也就是 iOS 14 和 macOS 11.0 或更高版本。
另一个有用的改进是对以下层的 8 位量化操作:
InnerProductLayer
BatchedMatMulLayer
在以前的 Core ML 版本中,你可以量化权重,但是在加载模型时,权重会被反量化为浮点数。借助新的int8DynamicQuantize
特性,权重将始终为 8 位整数值,实际计算也使用整数进行。
INT8 的计算有可能比浮点运算快很多,但我怀疑这是只存在于 CPU 上的优势而不是 GPU,因为 GPU 真的很适合浮点运算。也许即将到来的 Neural Engine 更新将提供 INT8 操作原生支持。(苹果不久前确实 收购了Xnor.ai……)
在 CPU 上,Core ML 现在也可以使用 16 位浮点操作,而不是 32 位浮点操作(A11 Bionic 及以上)。正如视频“探索 Swift 的数值计算”中提到的,Float16
现在是一种一等 Swift 数据类型。由于 CPU 原生支持 16 位浮点数,Core ML 的速度可以达到原来的两倍多!
注意:Core ML 已经在 GPU 和 Neural Engine 上使用了Float16
,所以只有在使用 CPU 时才会有所不同。
以下是其他一些小的变化:
UpsampleLayer
现在可以有一个分数比例因子。在BILINEAR
模式下,网格点采样方式(“align corners”)有了新的选择。这应该能解决我在 这篇博文 中指出的大部分问题。
ReorganizeDataLayerParams
有一个PIXEL_SHUFFLE
模式。这是另一种向上采样的方法。以前你可以使用几次置换(permute)和重塑图层重组像素图,但现在它已是内置的。
SliceStaticLayer
和SliceDynamicLayer
现在多了一个squeezeMasks
属性,切片更有趣了。
TileLayer 接受第二个输入张量,这样就可以动态指定重复的次数。
设备上的训练似乎没有什么变化:仍然只支持全连接和卷积层。CoreML.framework 中的MLParameterKey
类现在为 RMSprop 优化器提供了一个配置选项,但它目前没有在 NeuralNetwork.proto 中列出。它可能会在以后的测试版中添加。
下面是 新增的模型类型:
VisionFeaturePrint.Object
:一个针对对象检测优化过的特征提取器。
SerializedModel
:不确定这个是干什么用的。这是一个“私有”定义,是“在没有通知或支持的情况下做出的修改”。也许,这是苹果在 mlmodel 中嵌入专有模型格式的一种方式?
这个新的 Core ML 特性允许独立于应用进行模型更新。
不必发布应用更新,现有的应用只需下载一个新版本的 mlmodel 文件。公平地说,这并不是一个新想法,一些第三方供应商已经为此开发了 SDK。自己构建这个也不是那么困难。苹果解决方案的优势是 模型托管在苹果云上。
因为你的应用中可能有多个模型,模型集合 这个新概念让你可以将多个模型捆绑在一起,应用将一次性更新所有模型。你可以在 CloudKit 控制面板上创建这些集合。
在应用中,你可以使用MLModelCollection
类来下载和管理模型更新。这个 WWDC 的视频展示了相关的代码片段。
为了准备一个用于部署的 Core ML 模型,Xcode 中现在有了一个 Create Model Archive 按钮。这会写一个 .mlarchive 文件。你可以将这个版本的模型上传到你的 CloudKit 控制面板上,然后将其放到一个模型集合中。(看起来,mlarchive 只是一个包含 mlmodelc 文件夹内容的 zip 文件。)
一个不错的特性是,你可以针对不同的用户部署不同的模型集合。例如,iPhone 上的摄像头和 iPad 上的摄像头是不同的,所以你可能想创建一个模型的两个版本,并将一个发送给 iPhone 用户的应用,另一个发送给 iPad 用户。
你可以针对设备类别(iPhone/iPad/TV/Watch)、操作系统和版本、区域代码、语言代码和应用版本创建目标规则。
似乎没有根据其他标准将用户分组的机制,例如 A/B 测试模型更新,或针对特定的设备类型,如“iPhone X 或更低版本”。但是,你仍然可以手动实现,方法是创建具有不同名称的集合,然后在运行时显式地请求MLModelCollection
按名称获取适当的集合。
部署一个新的模型版本 并非总是立即生效。应用会在某个时候检测到一个新的可用模型,自动下载它并将其放入应用的沙箱中。但是你无法控制下载的时间和方式——例如,Core ML 可能会在手机未使用的时候在后台执行下载。
正因为如此,在应用中提供一个内置模型作为后备选项是一个好主意,比如一个通用模型,可以同时适用于 iPhone 和 iPad。
尽管这是一个便捷的解决方案,而且不必自己托管模型,但请记住,应用现在使用的是 CloudKit。从我的理解来看,模型集合确实会计入存储配额,而下载模型会计入网络流量配额。
另见:
使用 Core ML 的模型部署和安全特性:
https://developer.apple.com/videos/play/wwdc2020/10152/(WWDC 视频)
创建部署一个模型集合:
https://developer.apple.com/documentation/coreml/core_ml_api/creating_and_deploying_a_model_collection
从部署获取模型集合:
https://developer.apple.com/documentation/coreml/core_ml_api/retrieving_a_model_collection_from_a_deployment
注意:如果你想在设备上实现模型的个性化,没有什么简单的方法可以将其与新的 CloudKit 更新特性结合起来,也没有直接的方法将个性化模型学到的东西迁移到新模型中,或者以某种方式组合这些模型。
到目前为止,有人可以很容易地窃取你的 Core ML 模型,并将其粘贴到他们自己的应用中。在 iOS 14/macOS 11.0 中,Core ML 可以自动加密和解密模型,这样陌生人就不会再看到你的 mlmodelc 文件夹了。在新的 CloudKit 部署中,你可以选择使用加密,也可以选择不使用。
Xcode 加密编译后的模型 mlmodelc,而不是原始的 mlmodel 文件。模型总是以加密的形式保存在用户的设备上。只有当应用实例化模型时,Core ML 才会自动解密它。该解密版本仅存在于内存中,不会作为文件存储在任何地方。
首先,你需要一个加密密钥。好消息是你不需要自己管理这个密钥!现在,在 Xcode 的 Core ML 模型查看器中有一个 Create Encryption Key 按钮。当你点击这个按钮时,Xcode 会生成一个新的加密密钥,这个密钥与你的苹果开发账户相关联。对于这个密钥,你不需要使用 CSR 和 Keychain Access。
这个过程将创建一个新的 .mlmodelkey 文件。这个密钥存储在苹果的服务器上,但你也会获得一个本地副本,用于在 Xcode 中加密模型。你不需要在应用中嵌入这个加密密钥,也不应该这样做!
要加密 Core ML 模型,可以在这个模型的编译器标识中加上--encrypt YourModel.mlmodelkey
。或者,如果你打算使用 CloudKit 部署,那么在创建模型存档时就需要提供加密密钥。
为了在应用实例化模型时解密,Core ML 需要 通过网络 从苹果的服务器获取加密密钥,因此这显然需要连接网络。Core ML 只需要在你第一次使用该模型时执行此操作。
当然,如果网络断开导致加密密钥未能下载,应用将无法实例化 Core ML 模型。为此,你应该使用新增的YourModel.load()
函数。它有一个完成处理程序,让你可以响应加载错误。例如,错误代码modelKeyFetch
可以告诉你 Core ML 无法从苹果服务器加载解密密钥。
如果你担心人们会窃取你的专有技术,那么这是个非常酷的特性,而且很容易在应用中实现。
另见:
使用 Core ML 的模型部署和安全特性:
https://developer.apple.com/videos/play/wwdc2020/10152/(WWDC 视频)
生成模型加密密钥;
https://developer.apple.com/documentation/coreml/core_ml_api/generating_a_model_encryption_key
在应用中加密模型 :
https://developer.apple.com/documentation/coreml/core_ml_api/encrypting_a_model_in_your_app
注意:根据这个 帖子 介绍,加密模型不能和设备个性化共用。说得有道理。
使用 Core ML 模型的 iOS API 没有太大变化。尽管如此,还是有一些有趣的事情需要指出。
唯一的新类是MLModelCollection
,它是用于 CloudKit 部署的。
如你所知,当你向项目中添加一个 mlmodel 文件时,Xcode 会自动生成一个 Swift 或 Objective-C 源文件,其中有一些类可以简化模型的使用。这些生成的类有以下几个变化:
默认的init()
现在已不推荐使用,因此let model = YourModel()
将导致编译器发出警告。取而代之,使用YourModel(configuration:)
或新的YourModel.load()
方法,该方法让你可以处理模型加载错误,比如加密模型无法解密的错误。
如果你的模型是作用在图像上,那么你不一定要创建CVPixelBuffer
对象,你现在可以使用CGImage
或本地 PNG 或 JPG 文件的 URL 创建一个YourModelInput
对象,并对其进行预测。但是,你无法设置cropAndScale
方法或cropRect
,因此,如果你想更多地控制图像大小的调整方式,那么这些新方法并不是很有用。
MLModel 文档 中有一个新的警告:
一次在一个线程或一个分派队列上使用一个 MLModel 实例。要实现这一点,你可以通过序列化模型的方法调用,或者为每个线程和分派队列创建单独的模型实例。
哎,这让我觉得有罪恶感。我以前以为,MLModel
内部使用一个串行队列来处理请求,但事实并非如此,或许是已经改了。无论如何,从现在开始最好听从建议。
MLMultiArray
有一个新的初始化器init(concatenating:axis:dataType:)
,它通过连接几个已有的多维数组创建一个新的多维数组。除了指定的 axis,它们必须具有相同的 shape,并且将沿着该 axis 连接到一起。它看起来像是专门为视频预测而添加的,就像 Create ML 中新增的 Action Classifier 模型一样。
注意:现在,MLMultiArrayDataType
enum 有了静态.float
和.float64
属性。我不确定这些是干什么用的,因为它已经有了.float32
和.double
。这是测试版的小问题?
Xcode 现在可以显示关于模型的更多信息了,比如类标签和添加到模型中的任何自定义元数据。它还显示了模型中关于层类型的统计信息。
它提供了一个方便的 交互式预览,让你可以在不运行应用的情况下摆弄模型。你可以把图片、视频或文本拖到这个预览窗口中,并立即查看模型的预测结果。非常好!
此外,现在可以 在操练场上使用 Core ML 模型 了。Xcode 会自动为它生成类,你可以像往常一样使用它。这是在将模型置入应用之前的另一种交互方式。
对于简单的项目来说,使用 Create ML 构建自己的模型会很有趣,但是,更常见的是使用 TensorFlow 或 PyTorch 训练。要在 Core ML 中使用这样的模型,首先需要将其转换为 mlmodel 文件格式。这就是 coremltools 的作用。
好消息:文档有了极大的改进。你应该去看看。希望他们能及时更新这个用户指南,因为之前的旧文档并不总是最新的版本。
注意:遗憾的是,Jupyter notebook 的例子消失不见了。这些例子现在是用户指南的一部分,但不再是 Jupyter notebook 的一部分。
模型转换的方式变化较大。旧有的神经网络转换器已经弃用多时,取而代之的是一种更新、更灵活的设计。
转换器现在有三种类型:
用于 TensorFlow (1.x 和 2.x)、tf.keras 和 PyTorch 的现代转换器。所有这些转换器都构建在同一个技术栈上,使用了一种称为 MIL 或模型中间语言的东西。对于这类模型,不需要再使用 tfcoreml 或 onnx-coreml。
用于 Keras 1.x、Caffe 和 ONNX 的旧转换器。这些都是有专门用途的转换器。它们将不会再进一步开发,而只会进行 Bug 修复。不建议再使用 ONNX 转换 PyTorch 模型!
用于非神经网络模型(如 scikit-learn 和 XGBoost)的转换器。
要转换一个 TensorFlow 1.x 或 2.x、PyTorch 或 tf.keras 模型,需要使用新增的 统一转换 API,如下所示:
import coremltools as ct
class_labels = [ "cat", "dog" ]
image_input = ct.ImageType(shape=(1, 224, 224, 3),
bias=[-1, -1, -1],
scale=2/255.)
model = ct.convert(
keras_model,
inputs=[ image_input ],
classifier_config=ct.ClassifierConfig(class_labels)
)
model.save("YourModel.mlmodel")
ct.convert()
函数检查模型文件以确定其格式,并自动选择适当的转换器。参数与之前略有不同:预处理参数使用ImageType
对象传入,分类器标签使用ClassifierConfig
对象,等等。这个新增的转换 API 将模型转换为称为 MIL 的 中间表示。现在有 TensorFlow 1.x 到 MIL、TensorFlow 2.x 到 MIL——包括 tf.keras——以及 PyTorch 到 MIL 的转换器。如果某个时候,一个新的深度学习框架流行起来,它也可以获得自己的 MIL 转换器。
一旦模型转换为 MIL 格式,就可以根据一般规则进行优化了,比如剥离不必要的操作或将不同的层融合在一起。最后,将其从 MIL 转换为 mlmodel 格式。
我还没有详细地了解它,但这种新方法使我相信,coremltools 4 生成的 mlmodel 文件比以前要高效得多,特别是对于 TF 2.x 图。
我喜欢 MIL 的一点是,它允许你告诉转换器如何处理 它还无法识别的层。如果你的模型有一个 Core ML 不直接支持的层,那么你可以将其拆分为更原始的 MIL 操作,例如矩阵乘法或其他运算。
然后,只要遇到该类型的层,转换器就可以使用这种所谓的“复合操作”。这比使用自定义层实现不受支持的操作要容易得多,尽管仍然可以这样做。文档中有一个 很好的例子,演示如何使用这种复合操作。
另见:
使用 Core ML 转换器在设备上获取模型:
https://developer.apple.com/videos/play/wwdc2020/10153/(WWDC 视频)
coremltools 文档:
https://coremltools.readme.io/docs
在 iOS 和 macOS SDK 中有几个高级框架可以执行机器学习相关的任务。让我们快速看看这里有什么新东西。
计算机视觉框架 Vision 增加了很多新功能。
Vision 已经有了人脸、人脸特征和人体检测模型。这个新版本增加了以下内容:
手势检测(VNDetectHumanHandPoseRequest
)
多人全身位姿检测(VNDetectHumanBodyPoseRequest
)
实际上,苹果在操作系统中包含位姿估计是很酷的。有几种用于此目的的开源模型,但它们不是真有那么好,或者很慢,而商业解决方案则很贵。现在,你可以免费获得高品质的身体位姿检测了!
不仅仅是检查静态图像,现在更注重检测视频中的东西,包括离线的和来自实时摄像头的。方便起见,你可以直接在照相机的请求处理程序中使用CMSampleBuffer
对象。
新增类VNStatefulRequest
是VNImageBasedRequest
的一个子类。对于你要找的东西,它会随着时间的推移积累证据。与典型的VNImageBasedRequest
不同,你可以在多个帧上重用同一个有状态的请求。它对视频的每 N 帧执行一次分析操作。
一旦观察到你要找的东西,它就会使用VNObservation
对象调用完成处理程序。它现在有一个timeRange
属性,可以告诉你观察在视频中的开始和停止时间。
你不直接使用VNStatefulRequest
,这是一个抽象基类,目前只有用于轨迹检测的VNDetectTrajectoriesRequest
这一个子类。它可以沿着抛物线路径检测形状,比如 被踢或被扔的球。(这似乎是目前唯一内置的视频任务。)
要对视频执行脱机分析,可以使用VNVideoProcessor
。该对象接受一个本地电影文件的 URL,并每 N 帧或秒执行一个或多个 Vision 请求。
对于分析视频,光流法 是一种重要的传统计算机视觉技术。Vision 现在有一个VNGenerateOpticalFlowRequest
类,它计算每个像素从这一帧移动到下一帧的方向(密集光流)。输出是一个VNPixelBufferObservation
类,它包含一个新图像,每个像素有两个 32 位或 16 位浮点数。
还有一个新类VNDetectContoursRequest
,用于检测图像中 物体的轮廓。这些轮廓将作为向量路径返回。VNGeometryUtils
有辅助函数对检测到的轮廓进行后处理,如将其简化为基本的几何形状。
Vision 中的最后一个新特性是内置特性提取器VisionFeaturePrint
的一个新变体。iOS 已经自带了VisionFeaturePrint.Scene
,可以用于图像分类器。现在又有了一种新的VisionFeaturePrint.Object
模型,专门针对提取用于对象检测的特征做过优化。
对 299×299 的输入图像,它会输出两个多维数组 (288、35、35) 和 (768、17、17)。这些还不是边框预测,只是“原始”特性。一个完整的对象检测器仍然需要添加逻辑来将这些特性转换为边框和类标签。当你使用迁移学习训练一个对象检测器时,Create ML 就可以做到这一点。
另见:
探索计算机视觉:
APIhttps://developer.apple.com/videos/play/wwdc2020/10673/(WWDC 视频)
利用 Vision 检测身体和手的姿势:
https://developer.apple.com/videos/play/wwdc2020/10653/(WWDC 视频)
探索 Action&Vision 应用:
https://developer.apple.com/videos/play/wwdc2020/10099/(WWDC 视频)
对于 NLP 任务,可以使用自然语言框架。它可以与在 Create ML 中训练的模型紧密协同。
今年的新特性只有几项:
NLTagger
和NLModel
现在可以识别出多个标签,并预测它们的可信度。之前,只能得到得分最高的标签。
句子嵌入。词嵌入以前就可以使用了,但NLEmbedding
现在也支持句子。
句子嵌入使用一个内置的神经网络将整个句子编码成一个 512 维的向量,以便捕获单词在句子中的上下文(这是词嵌入单独无法做到的)。
另见:
利用语言处理让 App 更智能:
https://developer.apple.com/videos/play/wwdc2020/10657/(WWDC 视频)
这些框架似乎没什么变化。
我们一直都是用苹果 API(从 iOS 11.3 开始)和 Metal Performance Shaders 训练模型。随着时间的推移,苹果添加了一些新的训练 API,今年又增加了一些——根据我的计算,我们现在有 7 个不同的 API 可以用于在 iOS 和 / 或 macOS 上训练神经网络!
目前,你可以在 iOS 和 macOS 上使用这些苹果 API 来训练机器学习模型,特别是神经网络:
使用 Core ML 进行 设备端训练。
Create ML:你可能知道这是个 App,但这也是 macOS 上的一个框架。
Metal Performance Shaders:用于在 GPU 上进行推理和训练。这实际上是两种不同的 API,如果你不熟悉 Metal,那么它们并不是特别容易使用。现在还有一个新的“Metal Performance Shaders Graph”框架,它似乎是这些旧 API 的替代品。
BNNS:Accelerate 框架的一部分。之前,BNNS 只有做推理的例程,但今年它增加了对训练的支持。
ML Compute:一个看起来让人兴奋的全新框架。
Turi Create:基本上是 Create ML 的 Python 版本,虽然它看起来仍在维护中,但最近似乎没有得到很多人的喜爱。
让我们具体看一下这些 API 都提供了什么新特性。
似乎没有什么变化。可能有更多的层类型变为可更新的了,但我没有看到任何相关文档。
以后的测试版可能还会增加一个新特性——RMSprop 优化器,但当前的测试版还没有提供。
Create ML 一开始只是一个只能在 macOS 上使用的框架。你可以在 Swift Playground 中使用这个训练模型,只需几行代码。
去年,Create ML 变成了一个有诸多限制的应用,我很高兴地看到,这个应用在今年有了很大的改进。它仍然是一个框架,所以你仍然可以在代码中使用它。实际上,该应用只是一个裹着框架的便捷的 GUI。
在 Create ML 应用的前一个版本中,一次只能训练一个模型。如果你想调整一些东西,就必须从头开始训练,这可能会花费很长时间。
有了 Xcode 12 提供的新版本,你就可以 暂停训练并稍后继续,保存模型检查点(快照),并预览模型的运行情况。你可以更好地控制训练过程。这个更新使 Create ML 应用真正变得很有用!
在 CreateML.framework 中,还有用于设置训练会话、处理模型检查点等的新 API。我估计大多数人都只使用 Create ML 应用,但是看到框架暴露这个功能还是很让人高兴。
你可以用 Create ML(框架和应用)做一些以前没做过的事:
图像及视频风格转换
视频中人类动作分类
用于对象检测的学习迁移
带动态词嵌入的词标记迁移学习
让我们仔细看一下新的 动作分类模型。它使用了 Vision 的人体姿态模型。动作分类器是一种神经网络,它接受形状为(window_size, 3,18)
的输入,其中第一个维度是以帧数(通常是 2 秒的视频)表示的窗口大小,(3,18)
是位姿关键点。
神经网络不使用递归层,而是使用一维卷积。那似乎是 STGCN 或时空图形卷积网络的变体,是一种针对时序预测而设计的模型。在应用中使用这些模型,你不需要考虑这些细节,但我总是喜欢窥探底层的逻辑。
对于对象检测模型,你可以选择基于 TinyYOLOv2 训练一个完整的网络,或者新的转移学习模式,它使用了新的特征提取器 VisionFeaturePrint.Object。这个模型的其他部分看起来仍然像 YOLO 和 SSD,但是得益于转移学习,它的训练速度应该比完全基于 YOLO 的模型更快。
另见:
使用 Create ML 构建一个动作分类器:
https://developer.apple.com/videos/play/wwdc2020/10043/(WWDC 视频)
使用 Create ML 构建图像和视频风格转换模型:
https://developer.apple.com/videos/play/wwdc2020/10642/(WWDC 视频)
使用 Swift 控制 Create ML 中的训练:
https://developer.apple.com/videos/play/wwdc2020/10156/(WWDC 视频)
Metal Performance Shaders(MPS)是一个具有便捷的 Metal 计算内核的框架,主要用于图像处理,但自 2016 年起就支持神经网络了。我已经在博客上做了大量的探讨。
如今,大多数人都使用 Core ML 而不是 MPS。当然,在 GPU 上运行模型时,Core ML 在底层仍然是使用 MPS。但是,你也可以直接使用 MPS,特别是如果你想自己进行训练的话。(不过,现在有一个新的 ML Compute 框架,你应该使用它作为替代,见下文。)
今年,MPSCNN 中没有太多新东西,只有一些小的优化。
新增类包括用于边缘检测的MPSImageCanny
和用于线段检测的MPSImageEDLines
。它们对计算机视觉任务非常有用。
其他值得注意的变化有:
MPSCNNConvolutionDataSource
有一个新的kernelWeightsDataType
属性,这样,权重的数据类型就不必与执行卷积时的数据类型相同。有趣的是,权重不能是 INT8,即使 Core ML 现在允许某些层使用。
如果从kernelWeightsDataType
返回.float32
,那么卷积和全连接层将使用 32 位浮点数而不是 16 位浮点数运行。以前,它们只能工作在 16 位上。
损失函数现在可以reduceAcrossBatch
。
如果 Metal 没有让你望而却步,那么使用 MPSCNN 也没问题,但是,现在有一个新框架,让你可以更容易地创建和运行 MPS Graph 这样的图。
注意:WWDC 视频) 还说MPSNDArray
是新的,但是这个 API 去年就已经引入。这是一个比MPSImage
更灵活的数据结构,因为模型中的张量不一定都是图像。
MPS 提供MPSNNGraph
API 已经有一段时间了,但是这些图仅限于描述神经网络。然而,并不是所有的图都需要是神经网络,这就是 Metal Performance Shaders Graph 的用途。
你可以使用这个新框架来构建 GPU 计算操作的通用图。MPS 图独立于常规的 Metal Performance Shaders 框架,但确实是以它为基础。
旧有的MPSNNGraph
API 有一个限制,就是不能将自己的自定义操作放入图中,但这个新的 Graph 框架更加灵活。虽然你似乎不会真的添加自己的 Metal 计算内核,但你必须使用现有的原语来表示所有计算。
幸运的是,MPSGraph
编译器可以将这些原语融合到一个计算内核中,使其在 GPU 上尽可能高效地运行。但是,如果你有一个操作不可能使用所提供的原语实现,或者真的很难实现,那么就没法这样做了。我真是不明白,为什么苹果在引入这样一个新的 API 时总是忽略进行完全定制的能力?!。
新的MPSGraph
是一个相当轻量级的结构,它描述了一组MPSGraphTensors
如何通过MPSGraphTensors
(包含操作结果)相互关联。你还可以定义控制依赖项,强制某些节点先于其他节点运行。把图设置好之后,运行它或将其编码到命令缓冲区中,然后等待结果。
MPSGraph
有一大堆实例方法,可以向图中添加各种数学和神经网络操作。
它还可以进行训练,包括向图中添加一个损失操作,然后对所有层按相反的顺序做“梯度”操作,就像以前的MPSNNGraph
一样。方便起见,它还提供了自动微分模式,使MPSGraph
可以自动将梯度操作放入图中,节省了大量工作。
我很高兴他们为构建这些计算图创建了一个新的、干净的 API。与旧有的图 API 相比,它显然更容易使用——你不再需要是 Metal 方面的专家。它让我想起了很多 TensorFlow 1.x 图,它所带来的额外的好处是,图经过了积极地优化,尽可能地降低了开销。不过,无法在图中插入任意计算核,这很糟糕。
另见:
使用 Metal Performance Shaders Graph 构建自定义 ML 模型:
https://developer.apple.com/videos/play/wwdc2020/10677/(WWDC 视频)
向 Shader Graph 中添加自定义函数:
https://developer.apple.com/documentation/metalperformanceshadersgraph/adding_custom_functions_to_a_shader_graph
当 Core ML 在 CPU 上运行时,它使用 BNNS,这是 Accelerate 框架的一部分。我在 以前的一篇博文 中探讨过 BNNS。现在,那些 BNNS 函数大多已被弃用,并被一套全新的函数所取代。
以前,它只支持全连接层、卷积、池和激活函数。通过这次更新,BNNS 已经支持在训练时使用 n 维数组、来自 Core ML 的几乎所有的层类型,以及这些层的旧版本——包括目前 Core ML 无法训练的层,如 LSTM。
同样值得注意的是有一个 multihead attention 层,这在诸如 BERT 这样的 Transformer 模型中非常常见。嗯,张量缩并也很有趣。
你自己可能不会使用这些 BNNS 函数,就像你不会在 GPU 上使用 MPS 训练一样。相反,现在有一个更高级的框架 ML Compute,它抽象出了正在使用的处理器。ML Compute 底层使用了 BNNS 和 MPS,但作为开发人员,你不需要考虑这些细节。
另见:
BNNS 框架文档:
https://developer.apple.com/documentation/accelerate/bnns
https://developer.apple.com/documentation/accelerate/bnns
ML Compute 是一个用于在 CPU 或 GPU(但显然不是 Neural Engine)上训练神经网络的新框架。在拥有多个 GPU 的 Mac Pro 上,它可以自动使用所有 GPU 进行训练。
苹果又提供一个训练框架让我有点惊讶,但它确实会让事情变得更简单,因为它隐藏了 BNNS 和 MPS 的底层细节——也许将来还可以用在 Neural Engine 上。
ML Compute 的有趣之处在于它也可以在 iOS 上运行,而不只是在 Mac 上。奇怪的是,在所有这些地方都没有提到 Core ML——ML Compute 似乎是一个完全独立的东西。你不能使用它来创建 ML Compute 模型。
据我所知,ML Compute 的目标主要是 促进第三方深度学习工具。你不会希望编写代码来直接使用 ML Compute——我认为,其假设(希望?)是 TensorFlow 等工具也将开始使用它来实现 Mac 上的硬件加速训练。
它提供的层似乎与 BNNS 相同。把这些层放在一个图中,然后执行这个图。(这里没有“动态图模式”。)
要创建一个图,需要实例化一个MLCGraph
对象并向其添加节点。节点是MLCLayer
的一个子类,通过MLCTensor
对象连接到其他节点,它们是其他层的输出。
有趣的是,拆分、连接、重塑(reshape)和转置操作不是单独的层类型,而是图本身的操作。
一个很酷的调试特性是summarizedDOTDescription
,它返回图的 DOT 描述,然后可以用 Graphviz 或 OmniGraffle 程序来绘制。(这也是 Keras 生成模型图的方式。)
ML Compute 区分了推理图和训练图。后者有额外的节点,比如损失层和优化器。
似乎没有方法可以创建自己的自定义层,因此,你只能使用 ML Compute 提供的层类型。
奇怪的是,WWDC 没有关于这个新框架的环节,文档也非常少。总之,我一直在关注它,因为它似乎是可以用于在苹果设备上进行训练的 API。
另见:
ML Compute 框架文档:
https://developer.apple.com/documentation/mlcompute
Core ML 有一些有用的新特性,比如自动模型更新和加密。实际上,我们并不需要太多新的层类型,因为去年添加的层基本上涵盖了所有内容。我对这个更新很满意。
coremltools 4 是一个重大更新,它提供了新的转换器架构,并提供了 TensorFlow 2 和 PyTorch 原生支持。我很高兴,我们不需要再通过 ONNX 转换 PyTorch 模型了!
Vision 新提供了一堆很酷的东西。我很高兴苹果添加视频分析功能。虽然可以在每个视频帧上单独运行 ML,但这样做会忽略时间维度。由于移动设备的速度已经足够快,所以可以实时对视频数据执行 ML。我希望,在不久的将来,我们可以看到 Vision 在计算机视觉研究中发挥更加突出的作用。
至于 训练……我不知道为什么我们需要 7 种不同的 API。我认为,在新 API 解决所有问题之前,苹果公司不会弃用旧 API。关于 ML Compute,我们还不太了解——但我写这篇文章时,它还在 beta 1 阶段,所以谁知道呢……
参考阅读:
https://machinethink.net/blog/new-in-apple-machine-learning-2020/
InfoQ 读者交流群上线啦!各位小伙伴可以扫描下方二维码,添加 InfoQ 小助手,回复关键字“进群”申请入群。大家可以和 InfoQ 读者一起畅所欲言,和编辑们零距离接触,超值的技术礼包等你领取,还有超值活动等你参加,快来加入我们吧!
点个在看少个 bug 👇