CoreML 执行提供程序

Core ML 是 Apple 推出的机器学习框架。它旨在以最有效的方式无缝利用强大的硬件技术,包括 CPU、GPU 和神经网络引擎,以最大限度地提高性能,同时最大限度地减少内存和功耗。

目录

要求

CoreML 执行提供程序 (EP) 需要 iOS 13 或更高版本的 iOS 设备,或者 macOS 10.15 或更高版本的 Mac 电脑。

建议使用配备 Apple 神经网络引擎 (ANE) 的 Apple 设备以获得最佳性能。

安装

带有 CoreML EP 的 ONNX Runtime 预构建二进制文件已发布到 CocoaPods。

请参阅此处获取安装说明。

构建

有关 iOS 设备的构建说明,请参阅构建用于 iOS

用法

ONNX Runtime API 的详细信息请参阅此处

CoreML EP 可以通过 C、C++、Objective-C、C# 和 Java API 使用。

创建推理会话时必须显式注册 CoreML EP。例如

Ort::Env env = Ort::Env{ORT_LOGGING_LEVEL_ERROR, "Default"};
Ort::SessionOptions so;
std::unordered_map<std::string, std::string> provider_options;
provider_options["ModelFormat"] = std::to_string("MLProgram");
so.AppendExecutionProvider("CoreML", provider_options);
Ort::Session session(env, model_path, so);

已弃用 API OrtSessionOptionsAppendExecutionProvider_CoreML 在 ONNX Runtime 1.20.0 中。请使用 OrtSessionOptionsAppendExecutionProvider

Ort::Env env = Ort::Env{ORT_LOGGING_LEVEL_ERROR, "Default"};
Ort::SessionOptions so;
uint32_t coreml_flags = 0;
Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML(so, coreml_flags));
Ort::Session session(env, model_path, so);

配置选项 (新 API)

CoreML EP 有多个运行时选项可用。

要使用 CoreML EP 运行时选项,请创建一个表示选项的无符号整数,并通过使用按位 OR 运算符设置每个独立选项。

可以通过将字符串传递给 `AppendExecutionProvider` 方法来设置 ProviderOptions。

Ort::Env env = Ort::Env{ORT_LOGGING_LEVEL_ERROR, "Default"};
Ort::SessionOptions so;
std::string model_path = "/a/b/c/model.onnx";
std::unordered_map<std::string, std::string> provider_options;
provider_options["ModelFormat"] = "MLProgram";
provider_options["MLComputeUnits"] = "ALL";
provider_options["RequireStaticInputShapes"] = "0";
provider_options["EnableOnSubgraphs"] = "0";
so.AppendExecutionProvider("CoreML", provider_options);
Ort::Session session(env, model_path, so);

使用 CoreML EP 运行时选项的 Python 推理示例代码

import onnxruntime as ort
model_path = "model.onnx"
providers = [
    ('CoreMLExecutionProvider', {
        "ModelFormat": "MLProgram", "MLComputeUnits": "ALL", 
        "RequireStaticInputShapes": "0", "EnableOnSubgraphs": "0"
    }),
]

session = ort.InferenceSession(model_path, providers=providers)
outputs = ort_sess.run(None, input_feed)

可用选项 (新 API)

`ModelFormat` 可以是以下值之一:(默认为 `NeuralNetwork`)

  • `MLProgram`:创建一个 MLProgram 格式模型。需要 Core ML 5 或更高版本(iOS 15+ 或 macOS 12+)。
  • `NeuralNetwork`:创建一个 NeuralNetwork 格式模型。需要 Core ML 3 或更高版本(iOS 13+ 或 macOS 10.15+)。

`MLComputeUnits` 可以是以下值之一:(默认为 `ALL`)

  • `CPUOnly`:将 CoreML 限制为仅在 CPU 上运行。
  • `CPUAndNeuralEngine`:对配备兼容 Apple 神经网络引擎 (ANE) 的 Apple 设备启用 CoreML EP。
  • `CPUAndGPU`:对配备兼容 GPU 的 Apple 设备启用 CoreML EP。
  • `ALL`:对所有兼容的 Apple 设备启用 CoreML EP。

`RequireStaticInputShapes` 可以是以下值之一:(默认为 `0`)

仅允许 CoreML EP 接收具有静态形状输入的节点。默认情况下,CoreML EP 也允许具有动态形状的输入,但动态形状的输入可能会对性能产生负面影响。

  • `0`:允许 CoreML EP 接收具有动态形状输入的节点。
  • `1`:仅允许 CoreML EP 接收具有静态形状输入的节点。

`EnableOnSubgraphs` 可以是以下值之一:(默认为 `0`)

使 CoreML EP 能够在控制流算子(即 LoopScanIf 算子)的主体中的子图上运行。

  • `0`:禁用 CoreML EP 在控制流算子主体中的子图上运行。
  • `1`:启用 CoreML EP 在控制流算子主体中的子图上运行。

`SpecializationStrategy`:此功能自 macOS>=10.15 或 iOS>=18.0 起可用。此过程会影响模型加载时间和预测延迟。使用此选项可为模型定制专用化策略。有关详细信息,请访问 Apple 文档。可以是以下值之一:(默认为 `Default`)

  • 默认:
  • 快速预测:

`ProfileComputePlan`:对 Core ML MLComputePlan 进行性能分析。这将记录每个算子被分派到的硬件以及估计的执行时间。此选项主要供开发者使用,但如果性能不如预期,它能提供有用的诊断信息。可以是以下值之一:(默认为 `0`)

  • `0`:禁用性能分析。
  • `1`:启用性能分析。

`AllowLowPrecisionAccumulationOnGPU`:请参阅 Apple 文档。可以是以下值之一:(默认为 `0`)

  • `0`:使用 float32 数据类型累积数据。
  • `1`:使用低精度数据(float16)累积数据。

`ModelCacheDirectory`:Core ML 模型缓存存储目录的路径。CoreML EP 会将捕获的子图编译成 CoreML 格式的图并保存到磁盘。对于给定的模型,如果未启用缓存,CoreML EP 每次都会编译并保存到磁盘,这对于复杂模型可能会花费大量时间(甚至几分钟)。通过提供缓存路径,可以重复使用 CoreML 格式的模型。(默认为禁用缓存)。

  • `""`:禁用缓存。(默认为空字符串)
  • `"/path/to/cache"`:启用缓存。(缓存目录路径,如果不存在将创建)

模型的缓存信息存储在缓存目录中一个模型哈希下。哈希的计算方式有三种,按优先级排序。

  1. 从模型的 metadata_props 中读取。这为用户提供了直接控制哈希的方式,是推荐的使用方法。缓存键应满足以下条件:(1) 值必须只包含字母数字字符。(2) 值长度 < 64。EP 将重新哈希缓存键以满足这些条件。
  2. 创建推理会话时使用的模型 URL 的哈希值。
  3. 如果推理会话是使用内存字节创建的(即没有模型路径),则计算图输入和节点输出的哈希值。

至关重要的是,如果模型发生变化,则哈希值必须变化,或者您必须清除之前的缓存信息。例如,如果使用模型 URL 进行哈希(上述选项 2),则必须从不同的路径加载更新后的模型以更改哈希值。

ONNX Runtime 没有机制跟踪模型变化,也不会删除缓存条目。

以下是如何在模型元数据中填充模型哈希的示例

import onnx
import hashlib

# You can use any other hash algorithms to ensure the model and its hash-value is a one-one mapping. 
def hash_file(file_path, algorithm='sha256', chunk_size=8192):
    hash_func = hashlib.new(algorithm)
    with open(file_path, 'rb') as file:
        while chunk := file.read(chunk_size):
            hash_func.update(chunk)
    return hash_func.hexdigest()

CACHE_KEY_NAME = "CACHE_KEY"
model_path = "/a/b/c/model.onnx"
m = onnx.load(model_path)

cache_key = m.metadata_props.add()
cache_key.key = CACHE_KEY_NAME
cache_key.value = str(hash_file(model_path))

onnx.save_model(m, model_path)

配置选项 (旧 API)

uint32_t coreml_flags = 0;
coreml_flags |= COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE;

可用选项 (已弃用 API)

COREML_FLAG_USE_CPU_ONLY

将 CoreML 限制为仅在 CPU 上运行。

这会降低性能,但提供无精度损失的参考输出值,这对验证很有用。
仅供开发者使用。

COREML_FLAG_ENABLE_ON_SUBGRAPH

使 CoreML EP 能够在控制流算子(即 LoopScanIf 算子)的主体中的子图上运行。

COREML_FLAG_ONLY_ENABLE_DEVICE_WITH_ANE

默认情况下,CoreML EP 对所有兼容的 Apple 设备启用。

设置此选项将仅对配备兼容 Apple 神经网络引擎 (ANE) 的 Apple 设备启用 CoreML EP。请注意,启用此选项并不能保证整个模型仅使用 ANE 执行。

有关详细信息,请参阅 哪些设备有 ANE?

COREML_FLAG_ONLY_ALLOW_STATIC_INPUT_SHAPES

仅允许 CoreML EP 接收具有静态形状输入的节点。默认情况下,CoreML EP 也允许具有动态形状的输入,但动态形状的输入可能会对性能产生负面影响。

COREML_FLAG_CREATE_MLPROGRAM

创建一个 MLProgram 格式模型。需要 Core ML 5 或更高版本(iOS 15+ 或 macOS 12+)。默认创建 NeuralNetwork 模型,因为它需要 Core ML 3 或更高版本(iOS 13+ 或 macOS 10.15+)。

支持的算子

NeuralNetwork

CoreML 执行提供程序在创建 NeuralNetwork 模型(默认)时支持的算子

算子 备注
ai.onnx:Add  
ai.onnx:ArgMax  
ai.onnx:AveragePool 仅支持 2D 池化。
ai.onnx:BatchNormalization  
ai.onnx:Cast  
ai.onnx:Clip  
ai.onnx:Concat  
ai.onnx:Conv 仅支持 1D/2D 卷积。
权重和偏置应为常量。
ai.onnx:DepthToSpace 仅支持 DCR 模式的 DepthToSpace。
ai.onnx:Div  
ai.onnx:Flatten  
ai.onnx:Gather 不支持输入 `indices` 为标量值。
ai.onnx:Gemm 输入 B 应为常量。
ai.onnx:GlobalAveragePool 仅支持 2D 池化。
ai.onnx:GlobalMaxPool 仅支持 2D 池化。
ai.onnx:LeakyRelu  
ai.onnx:LRN  
ai.onnx:MatMul 输入 B 应为常量。
ai.onnx:MaxPool 仅支持 2D 池化。
ai.onnx:Mul  
ai.onnx:Pad 仅支持常量模式和最后两个维度的填充。
输入 pads 和 constant_value 应为常量。
如果提供,axes 应为常量。
ai.onnx:Pow 仅支持两个输入都是 fp32 的情况。
ai.onnx:PRelu 输入 slope 应为常量。
输入 slope 的形状应为 [C, 1, 1] 或只有 1 个元素。
ai.onnx:Reciprocal  
ai.onnx.ReduceSum  
ai.onnx:Relu  
ai.onnx:Reshape  
ai.onnx:Resize 4D 输入。
`coordinate_transformation_mode` == `asymmetric`。
`mode` == `linear` 或 `nearest`。
`nearest_mode` == `floor`。
`exclude_outside` == false
`scales` 或 `sizes` 必须为常量。
ai.onnx:Shape 不支持属性 `start` 为非默认值。
不支持属性 `end`。
ai.onnx:Sigmoid  
ai.onnx:Slice 输入 `starts`、`ends`、`axes` 和 `steps` 应为常量。不支持空切片。
ai.onnx:Softmax  
ai.onnx:Split 如果提供,`splits` 必须为常量。
ai.onnx:Squeeze  
ai.onnx:Sqrt  
ai.onnx:Sub  
ai.onnx:Tanh  
ai.onnx:Transpose  

MLProgram

当创建 MLProgram 模型(设置 COREML_FLAG_CREATE_MLPROGRAM 标志)时,CoreML 执行提供程序支持的算子

算子 备注
ai.onnx:Add  
ai.onnx:Argmax  
ai.onnx:AveragePool 目前仅支持 2D 池化。如果需要,可以添加对 3D 和 5D 的支持。
ai.onnx:Cast  
ai.onnx:Clip  
ai.onnx:Concat  
ai.onnx:Conv 仅支持 1D/2D 卷积。
如果提供偏置,则必须为常量。
ai.onnx:ConvTranspose 权重和偏置必须为常量。
不支持 padding_type 为 SAME_UPPER/SAME_LOWER。
kernel_shape 必须使用默认值。
不支持 output_shape。
output_padding 必须使用默认值。
ai.onnx:DepthToSpace 如果‘mode’是‘CRD’,输入必须具有固定形状。
ai.onnx:Div  
ai.onnx:Erf  
ai.onnx:Gemm 输入 B 必须为常量。
ai.onnx:Gelu  
ai.onnx:GlobalAveragePool 目前仅支持 2D 池化。如果需要,可以添加对 3D 和 5D 的支持。
ai.onnx:GlobalMaxPool 目前仅支持 2D 池化。如果需要,可以添加对 3D 和 5D 的支持。
ai.onnx:GridSample 4D 输入。
‘mode’ 为 ‘linear’ 或 ‘zeros’。
(mode==linear && padding_mode==reflection && align_corners==0) 不受支持。
ai.onnx:GroupNormalization  
ai.onnx:InstanceNormalization  
ai.onnx:LayerNormalization  
ai.onnx:LeakyRelu  
ai.onnx:MatMul 目前仅实现了对 transA == 0、alpha == 1.0 和 beta == 1.0 的支持。
ai.onnx:MaxPool 目前仅支持 2D 池化。如果需要,可以添加对 3D 和 5D 的支持。
ai.onnx:Max  
ai.onnx:Mul  
ai.onnx:Pow 仅支持两个输入都是 fp32 的情况。
ai.onnx:PRelu  
ai.onnx:Reciprocal 这需要一个 `epislon`(默认为 1e-4),而 onnx 没有提供
ai.onnx:ReduceSum  
ai.onnx:ReduceMean  
ai.onnx:ReduceMax  
ai.onnx:Relu  
ai.onnx:Reshape  
ai.onnx:Resize 请参阅 resize_op_builder.cc 实现。有效的组合太多,无法一一描述。
ai.onnx:Round  
ai.onnx:Shape  
ai.onnx:Slice starts/ends/axes/steps 必须是常量初始化器。
ai.onnx:Split 如果提供,`splits` 必须为常量。
ai.onnx:Sub  
ai.onnx:Sigmoid  
ai.onnx:Softmax  
ai.onnx:Sqrt  
ai.onnx:Squeeze  
ai.onnx:Tanh  
ai.onnx:Transpose  
ai.onnx:Unsqueeze