Можно ли заменить лямбда-слои keras родными слоями CoreML для преобразования модели? - PullRequest
0 голосов
/ 04 апреля 2020

Впервые я столкнулся с необходимостью конвертировать модель mu keras в coreml. Это можно сделать с помощью пакета coremltools,

import coremltools
import keras

model = Model(...) # keras

coreml_model = coremltools.converters.keras.convert(model,
    input_names="input_image_NHWC",
    output_names="output_image_NHWC",
    image_scale=1.0,
    model_precision='float32',
    use_float_arraytype=True,
    custom_conversion_functions={ "Lambda": convert_lambda },
    input_name_shape_dict={'input_image_NHWC': [None, 384, 384, 3]}
    )

. Однако у меня есть два лямбда-слоя, первый из которых - глубина-космос (пикселшафф), а другой - масштабатор:

def tf_upsampler(x):
    return tf.nn.depth_to_space(x, 4)

def mulfunc(x, beta=0.2):
    return beta*x

...

x = Lambda(tf_upsampler)(x)
...
x = Lambda(mulfunc)(x)

Единственный совет, который я нашел, был, насколько я понимаю, использовать пользовательский слой с необходимостью позже реализовать мой слой в коде Swift. Примерно так: MyPixelShuffle и MyScaleLayer должны быть реализованы как классы в проекте XCode (?):

def convert_lambda(layer):
    # Only convert this Lambda layer if it is for our swish function.
    if layer.function == tf_upsampler:
        params = NeuralNetwork_pb2.CustomLayerParams()

        # The name of the Swift or Obj-C class that implements this layer.
        params.className = "MyPixelShuffle"

        # The desciption is shown in Xcode's mlmodel viewer.
        params.description = "pixelshuffle"

        params.parameters["blockSize"].intValue = 4



        return params
    elif layer.function == mulfunc:
        # https://stackoverflow.com/questions/47987777/custom-layer-with-two-parameters-function-on-core-ml
        params = NeuralNetwork_pb2.CustomLayerParams()

        # The name of the Swift or Obj-C class that implements this layer.
        params.className = "MyScaleLayer"
        params.description = "scaling input"

        # HERE!! This is important.
        params.parameters["scale"].doubleValue = 0.2


        # The desciption is shown in Xcode's mlmodel viewer.
        params.description = "multiplication by constant"

        return params

Однако я обнаружил, что в CoreML есть слои, которые мне нужны, их можно найти как ScaleLayer и ReorganizeDataLayer

Как я могу использовать эти собственные слои для замены лямбд в модели керас? Можно ли отредактировать coreML protobuf для сети? Или, если для них есть классы Swift / OBj- C, как они называются?

Можно ли это сделать, удалив / добавив слои с помощью coremltools.models.neural_network.NeuralNetworkBuilder?

ОБНОВЛЕНИЕ:

Я обнаружил, что keras converter на самом деле вызывает построитель нейронной сети добавить разные слои. Строитель имеет слой builder.add_reorganize_data Мне нужно . Теперь вопрос в том, как заменить пользовательские слои в модели. Я могу загрузить его в слои builder и isnpect:

coreml_model_path = 'mymodel.mlmodel'

spec = coremltools.models.utils.load_spec(coreml_model_path)
builder = coremltools.models.neural_network.NeuralNetworkBuilder(spec=spec)
builder.inspect_layers(last=10)
[Id: 417], Name: lambda_10 (Type: custom)
          Updatable: False
          Input blobs: ['up1_output']
          Output blobs: ['lambda_10_output']

Ответы [ 2 ]

1 голос
/ 05 апреля 2020

Гораздо проще сделать что-то вроде этого:

def convert_lambda(layer):
    if layer.function == tf_upsampler:
        params = NeuralNetwork_pb2.ReorganizeDataLayerParams()

        params.fillInTheOtherPropertiesHere = someValue

        return params
    ...etc..

Другими словами, вам не нужно возвращать пользовательский слой, если какой-либо существующий тип слоя уже делает то, что вы хотите.

0 голосов
/ 04 апреля 2020

Хорошо, кажется, я нашел способ go. Я создал виртуальную среду с отдельной копией coremltools и отредактировал метод _convert() в _keras2_converter.py, добавив следующий код:

 for iter, layer in enumerate(graph.layer_list):
        keras_layer = graph.keras_layer_map[layer]
        print("%d : %s, %s" % (iter, layer, keras_layer))
        if isinstance(keras_layer, _keras.layers.wrappers.TimeDistributed):
            keras_layer = keras_layer.layer
        converter_func = _get_layer_converter_fn(keras_layer, add_custom_layers)
        input_names, output_names = graph.get_layer_blobs(layer)
        # this may be none if we're using custom layers
        if converter_func:
            converter_func(builder, layer, input_names, output_names,
                           keras_layer, respect_trainable)
        else:

            if _is_activation_layer(keras_layer):
                import six
                if six.PY2:
                    layer_name = keras_layer.activation.func_name
                else:
                    layer_name = keras_layer.activation.__name__
            else:
                layer_name = type(keras_layer).__name__
            if layer_name in custom_conversion_functions:
                custom_spec = custom_conversion_functions[layer_name](keras_layer)
            else:
                custom_spec = None

            if layer.find('tf_up') != -1:
                print('TF_UPSCALE found')
                builder.add_reorganize_data(layer, input_names[0], output_names[0], mode='DEPTH_TO_SPACE', block_size=4)
            elif layer.find('mulfunc') != -1:
                print('SCALE found')
                builder.add_scale(layer, W=0.2, b=0, has_bias=False, input_name=input_names[0], output_name=output_names[0])
            else:
                builder.add_custom(layer, input_names, output_names, custom_spec)

Триггер - имя слоя. В керасе я использую model.load_weights(by_name=True) и следующую маркировку моих лямбд:

x = Lambda(mulfunc, name=scope+'mulfunc')(x)

x = Lambda(tf_upsampler,name='tf_up')(x)

Теперь у модели по крайней мере есть нужные мне слои:

[Id: 417], Name: tf_up (Type: reorganizeData)
          Updatable: False
          Input blobs: ['up1_output']
          Output blobs: ['tf_up_output']

Теперь пришло время проверить мои VBox MacOS, опубликует то, что у меня есть.

ОБНОВЛЕНИЕ:

Я не вижу ошибок, связанных с моими замененными лямбда-слоями, но есть еще один, который не позволяет мне прогнозировать:

Layer 'concatenate_1' type 320 has 1 inputs but expects at least 2

Я думаю, это связано с тем, что Keras получает один вход для сцепленного слоя, который представляет собой список входов. Посмотрим, как это исправить

ОБНОВЛЕНИЕ 2:

Попытка исправить это с помощью функции построителя add_concat_nd(self, name, input_names, output_name, axis) для моих слоев сцепления: во время вывода получена ошибка, что данный тип слоя поддерживается (?! ):

if converter_func:
            if layer.find('concatenate') != -1:
                print('CONCATENATE FOUND')
                builder.add_concat_nd(layer, input_names, output_names[0], axis=3)
            else:
                converter_func(builder, layer, input_names, output_names,
                           keras_layer, respect_trainable)

Unsupported layer type (CoreML.Specification.NeuralNetworkLayer)

ОБНОВЛЕНИЕ 4:

Найдены исправления для этого и изменен способ инициализации компоновщика:

builder = _NeuralNetworkBuilder(input_features, output_features, mode = mode, use_float_arraytype=use_float_arraytype, disable_rank5_shape_mapping=True)

Теперь сообщение об ошибке исчезло, но у меня проблемы с версией XCode: модель имеет версию 4, а мой Xcode поддерживает 3. Будет пытаться обновить мою ВМ.

«Руководство по выживанию CoreML» в этом случае предлагает pdf:

pip install -U git+https://github.com/apple/coremltools.git Например, если при загрузке модели с помощью coremltools выдается ошибка, такая как показано ниже, попробуйте установить последнюю версию coremltools непосредственно из репозиторий GitHub.

Error compiling model: "Error reading protobuf spec. validator error: The .mlmodel supplied is of version 3, intended for a newer version of Xcode. This version of Xcode supports model version 2 or earlier.

ОБНОВЛЕНИЕ:

Обновлено с git. Ошибка:

Нет модуля с именем 'coremltools.libcoreml python'

Похоже, что последний git не работает: (

Черт, кажется, я нужны macos 10.15 и xcode 11

ОБНОВЛЕНИЕ 5:

Все еще борется с ошибкой в ​​10.15. Обнаружено, что

  1. coremltools каким-то образом дедуплицирует входные данные для уровня Concatenate, так что если в вашем коде keras есть что-то вроде Concatenate()([x,x]), у вас будет слой сцепления с 1 входом в coreml и ошибкой. Чтобы исправить это, я попытаюсь изменить код, приведенный выше:
if layer.find('concatenate') != -1:
                print('CONCATENATE FOUND', len(input_names))
                if len(input_names) == 1:
                    input_names = [input_names[0],input_names[0]]
                builder.add_concat_nd(layer, input_names, output_names[0], axis=3)
[Я сталкивался с этой ошибкой: input layer 'conv_in' of type 'Convolution' has input rank 3 but expects rank at least 4] ( coremltools: как правильно использовать NeuralNetworkMultiArrayShapeRange? , которая, кажется, вызвана тем, что coreml делает ввод 3-мерным CHW, в то время как оно должно быть 4 NWH C (?). В настоящее время играет со следующими

Получение неправильной формы капли из внутренних элементов модели

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...