coremltools: как правильно использовать NeuralNetworkMultiArrayShapeRange?

У меня есть сеть PyTorch, и я хочу развернуть ее на устройствах iOS. Короче говоря, мне не удалось добавить гибкости к форме входного тензора в CoreML.

Сеть - это свертка, которая принимает изображение RGB (сохраненное как тензор) в качестве входных данных и возвращает изображение RGB того же размера. Используя PyTorch, я могу вводить изображения любого размера, который захочу, например тензор размера (1, 3, 300, 300) для изображения 300x300.

Чтобы преобразовать модель PyTorch в модель CoreML, я сначала конвертирую ее в модель ONNX, используя torch.onnx.export. Эта функция требует передать фиктивный ввод, чтобы он мог выполнить график. Итак, я использовал:

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

Я предполагаю, что модель ONNX принимает только изображения / тензоры размера (1, 3, 300, 300). Теперь я могу использовать функцию onnx_coreml.convert для преобразования модели ONNX в модель CoreML. Распечатав описание спецификации модели CoreML с помощью Python, я получаю что-то вроде:

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"
  }
}

Входные данные модели должны иметь размер multiArrayType (1, 3, 300, 300). Копируя эту модель в 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
  • оннкс 1.6.0
  • onnx-coreml 1.1
  • coremltools 3.1

Спасибо за помощь


person Vincent Garcia    schedule 09.01.2020    source источник


Ответы (1)


Самый простой способ - это удалить shape:1. Что-то вроде этого:

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

Теперь форма по умолчанию также должна иметь 3 измерения.

Однако я бы посоветовал изменить тип ввода с множественного массива на фактическое изображение. Поскольку вы в любом случае собираетесь использовать его с изображениями. Это позволит вам передать изображение как объект CVPixelBuffer или CGImage вместо MLMultiArray.

person Matthijs Hollemans    schedule 09.01.2020
comment
Используя del spec.description.input[0].type.multiArrayType.shape[0], я смог избавиться от первого измерения, как вы предлагали, а затем я смог добавить гибкости. Однако мне не удалось вызвать функцию прогнозирования с помощью coremltools (невозможно загрузить CoreML.framework. Невозможно сделать прогнозы.), А XCode отобразил ошибку в окне описания модели (ошибка валидатора: слой 88 типа "Свертка" имеет введите ранг 3, но ожидает ранга не ниже 4.). Проблема, вероятно, связана с нетрадиционными экстремальными слоями. Попробую переключиться на изображения, как вы предлагали. - person Vincent Garcia; 10.01.2020
comment
Можете ли вы загрузить модель в Xcode без гибких входов? Странно, что вы получите ошибку на сверточном слое только потому, что добавили гибкость ввода. - person Matthijs Hollemans; 10.01.2020
comment
К сожалению, мой ответ был непонятен. 1) Я могу загрузить исходную модель CoreML в XCode, но не могу добавить к ней гибкости (так как входные данные являются 4-мерным тензором). 2) Удалив первое измерение с помощью del (...), я могу добавить гибкости модели, но тогда я не могу загрузить модель с гибкостью или без нее в XCode (validator error: Layer '88' of type 'Convolution' has input rank 3 but expects rank at least 4.) - person Vincent Garcia; 13.01.2020
comment
Трудно отладить это на расстоянии, но работает ли это, когда вы оставляете shape: 1 там и добавляете 4-ю sizeRanges вещь с lowerBound: 1 и upperBound: -1? Я думаю, вы не можете использовать NeuralNetworkMultiArrayShapeRange для этого, поэтому вам придется изменить shapeRange напрямую, используя объекты protobuf. Не уверен, понравится ли Core ML иметь 4 измерения вместо 3, но попробовать стоит. - person Matthijs Hollemans; 14.01.2020
comment
Думаю, добавление sizeRanges вручную может решить мою проблему. Я пробовал часть своей модели, и она выглядит многообещающей. Теперь мне нужно проверить это на основной модели. Прокомментирую здесь, как только появится новая информация. - person Vincent Garcia; 14.01.2020
comment
Думаю, я понимаю проблему. Моя модель PyTorch состоит из трех частей: понижающая дискретизация - ›convnet -› повышающая дискретизация (см. stackoverflow.com/questions/59177052). Если я удалю настраиваемые части понижающей / повышающей дискретизации, я могу вручную добавить гибкость, используя sizeRanges к входному тензору 4D. С coremltools CoreML выводит форму вывода из формы ввода, и вывод работает нормально. Когда я возвращаю части понижающей / повышающей дискретизации, выходная форма, вычисленная CoreML, не изменяется, чтобы отражать входную форму. Я предполагаю, что виноваты мои пользовательские функции dowsampling / upsampling. - person Vincent Garcia; 15.01.2020