coremltools:如何正确使用 NeuralNetworkMultiArrayShapeRange?

Posted

技术标签:

【中文标题】coremltools:如何正确使用 NeuralNetworkMultiArrayShapeRange?【英文标题】:coremltools: how to properly use NeuralNetworkMultiArrayShapeRange? 【发布时间】:2020-04-26 23:10:43 【问题描述】:

我有一个 PyTorch 网络,我想将它部署到 ios 设备。简而言之,我未能为 CoreML 中的输入张量形状增加灵活性。

该网络是一个卷积网络,它将 RGB 图像(存储为张量)作为输入并返回相同大小的 RGB 图像。使用 PyTorch,我可以输入我想要的任何尺寸的图像,例如 300x300 图像的尺寸为 (1, 3, 300, 300) 的张量。

为了将 PyTorch 模型转换为 CoreML 模型,我首先使用 torch.onnx.export 将其转换为 ONNX 模型。此函数需要传递一个虚拟输入,以便它可以执行图形。所以我确实使用了:

input = torch.rand(1, 3, 300, 300)

我的猜测是 ONNX 模型只接受大小为(1、3、300、300)的图像/张量。现在,我可以使用onnx_coreml.convert 函数将 ONNX 模型转换为 CoreML 模型。通过使用 Python 打印 CoreML 模型的规范描述,我得到如下信息:

input 
  name: "my_image"
  type 
    multiArrayType 
      shape: 1
      shape: 3
      shape: 300
      shape: 300
      dataType: FLOAT32
    
  

output 
  name: "my_output"
  type 
    multiArrayType 
      shape: 1
      shape: 3
      shape: 300
      shape: 300
      dataType: FLOAT32
    
  

metadata 
  userDefined 
    key: "coremltoolsVersion"
    value: "3.1"
  

模型的输入必须是大小为 (1, 3, 300, 300) 的 multiArrayType。通过将此模型复制到 XCode,我可以在检查模型时看到 my_name 列在“输入”部分下,并且预计是 MultiArray (Float32 1 x 3 x 300 x 300)。到目前为止,一切都是一致的。

我的问题是增加输入形状的灵活性。我尝试使用 coremltools 没有运气。这是我的问题。这是我的代码:

import coremltools
from coremltools.models.neural_network import flexible_shape_utils

spec = coremltools.utils.load_spec('my_model.mlmodel')

shape_range = flexible_shape_utils.NeuralNetworkMultiArrayShapeRange()
shape_range.add_channel_range((3,3))
shape_range.add_height_range((64, 5000))
shape_range.add_width_range((64, 5000))
flexible_shape_utils.update_multiarray_shape_range(spec, feature_name='my_image', shape_range=shape_range)
coremltools.models.utils.save_spec(spec, 'my_flexible_model.mlmodel')

我使用 Python 得到以下规范描述:

input 
  name: "my_image"
  type 
    multiArrayType 
      shape: 1
      shape: 3
      shape: 300
      shape: 300
      dataType: FLOAT32
      shapeRange 
        sizeRanges 
          lowerBound: 3
          upperBound: 3
        
        sizeRanges 
          lowerBound: 64
          upperBound: 5000
        
        sizeRanges 
          lowerBound: 64
          upperBound: 5000
        
      
    
  

只有指定的 3 个范围,这是有道理的,因为我只定义了通道、高度和宽度的范围,而不是批量大小。在 XCode 中,检查灵活的 CoreML 模型时出现以下错误:

There was a problem decoding this CoreML document
validator error: Description of multiarray feature 'my_image' has a default 4-d shape but a 3-d shape range

当我在 macOS X Mojave 上时,我很确定它正在开发另一个项目,但现在我什么都不确定。

我正在使用:

macOS X Catalina conda 4.7.12 python 3.7.5 pytorch 1.3.1 onnx 1.6.0 onnx-coreml 1.1 coremltools 3.1

感谢您的帮助

【问题讨论】:

【参考方案1】:

最简单的方法是删除shape:1。像这样的:

del spec.description.input[0].shape[0]

现在默认形状也应该有 3 个维度。

但是,我建议将输入类型从多数组更改为实际图像。因为无论如何您都将使用它来处理图像。这将使您可以将图像作为CVPixelBufferCGImage 对象而不是MLMultiArray 传递。

【讨论】:

使用del spec.description.input[0].type.multiArrayType.shape[0] 我能够按照您的建议摆脱第一个维度,然后我能够增加灵活性。但是,我无法使用 coremltools 调用预测函数(无法加载 CoreML.framework。无法进行预测。)并且 XCode 在模型描述窗口中显示错误(验证器错误:“卷积”类型的层“88”有输入排名 3,但预计排名至少为 4。)。问题可能来自非常规的极端层。我会尝试按照您的建议切换到图片。 可以在没有灵活输入的情况下将模型加载到 Xcode 中吗?奇怪的是,仅仅因为你增加了输入灵活性,你就会在卷积层上得到一个错误。 对不起,我的回答不清楚。 1) 我可以在 XCode 中加载初始 CoreML 模型,但我无法为其添加灵活性(因为输入是 4D 张量)。 2) 通过使用del (...) 删除第一个维度,我可以为模型增加灵活性,但是我无法在 XCode 中加载具有或不具有灵活性的模型 (validator error: Layer '88' of type 'Convolution' has input rank 3 but expects rank at least 4.) 很难从远处调试它,但是当您将shape: 1 留在其中并添加带有lowerBound: 1upperBound: -1 的第4 个sizeRanges 事物时,它是否有效?我认为您不能为此使用 NeuralNetworkMultiArrayShapeRange,因此您必须直接使用 protobuf 对象修改shapeRange。不确定 Core ML 是否会喜欢有 4 个维度而不是 3 个维度,但值得一试。 我认为手动添加sizeRanges 可以解决我的问题。我已经尝试过我的模型的一个子部分,它看起来很有希望。我现在必须在主模型上验证这一点。一旦我有新信息,将在这里发表评论。

以上是关于coremltools:如何正确使用 NeuralNetworkMultiArrayShapeRange?的主要内容,如果未能解决你的问题,请参考以下文章

导入 coremltools 时出错 - 无法导入名称 ParametricSoftPlus

构建一个 sklearn 文本分类器并使用 coremltools 进行转换

尝试使用 coremltools 4.1 将模型转换为 coreml 不工作

使用 iOS 11 mlmodel 进行图像分类 - 使用 coremltools 和经过训练的 .caffemodel 转换问题

Distilling the Knowledge in a Neural Network

Distilling the Knowledge in a Neural Network