Пользовательский слой с двумя параметрами работает в Core ML

Благодаря этой замечательной статье (http://machinethink.net/blog/coreml-custom-layers/), я понял, как написать преобразование с помощью coremltools и Lambda с пользовательским слоем Keras. Но, я не могу понять по ситуации, функция с двумя параметрами.

#python
def scaling(x, scale):
    return x * scale

Слой Keras здесь.

#python
up = conv2d_bn(mixed,
                   K.int_shape(x)[channel_axis],
                   1,
                   activation=None,
                   use_bias=True,
                   name=name_fmt('Conv2d_1x1'))
x = Lambda(scaling, # HERE !!
           output_shape=K.int_shape(up)[1:],
           arguments={'scale': scale})(up)
x = add([x, up])

В этой ситуации, как я могу написать func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) в custom MLCustomLayer class на Swift? Я понимаю только в ситуации с функцией одного параметра, например,

#swift 
func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws {
  for i in 0..<inputs.count {
    let input = inputs[i]
    let output = outputs[i]

    for j in 0..<input.count {
      let x = input[j].floatValue
      let y = x / (1 + exp(-x))
      output[j] = NSNumber(value: y)
    }
  }  
}

Как насчет функции с двумя параметрами, например x * scale?

Полный код здесь.

Спасибо.


person osmszk    schedule 27.12.2017    source источник


Ответы (2)


Похоже, scale - это гиперпараметр, а не параметр, который можно изучить, верно?

В этом случае вам нужно добавить scale в словарь параметров для настраиваемого слоя. Затем в вашем классе Swift scale также будет внутри словаря параметров, который передается в вашу init(parameters) функцию. Сохраните его внутри свойства, а затем в evaluate(inputs, outputs) снова прочтите из этого свойства.

В моем сообщении в блоге показано, как это сделать. ;-)

person Matthijs Hollemans    schedule 27.12.2017
comment
Ой! Большое спасибо. Я так хорош, что вы прокомментировали. - person osmszk; 27.12.2017
comment
Да вы правы. scale - гиперпараметр. Благодаря вашему плодотворному сообщению в блоге и вашему коду я смог решить эту проблему! - person osmszk; 27.12.2017

Так я решил эту проблему благодаря блогу Холланса. При преобразовании func, в данном случае в convert_lambda, я должен был добавить параметр scale для настраиваемого слоя.

код python (преобразование Core ML)

def convert_lambda(layer):
    if layer.function == scaling:
        params = NeuralNetwork_pb2.CustomLayerParams()

        params.className = "scaling"
        params.description = "scaling input"

        # HERE!! This is important.
        params.parameters["scale"].doubleValue = layer.arguments['scale']

        return params
    else:
        return None

coreml_model = coremltools.converters.keras.convert(
    model,
    input_names="image",
    image_input_names="image",
    output_names="output",
    add_custom_layers=True,
    custom_conversion_functions={ "Lambda": convert_lambda })

быстрый код (пользовательский слой)

//custom MLCustomLayer `scaling` class
let scale: Float

required init(parameters: [String : Any]) throws {
    if let scale = parameters["scale"] as? Float {
        self.scale = scale
    } else {
        self.scale = 1.0
    }
    print(#function, parameters, self.scale)
    super.init()
}

func evaluate(inputs: [MLMultiArray], outputs: [MLMultiArray]) throws {

    for i in 0..<inputs.count {
        let input = inputs[i]
        let output = outputs[i]

        for j in 0..<input.count {
            let x = input[j].floatValue
            let y = x * self.scale
            output[j] = NSNumber(value: y)
        }
        //faster
        /*
        let count = input.count
        let inputPointer = UnsafeMutablePointer<Float>(OpaquePointer(input.dataPointer))
        let outputPointer = UnsafeMutablePointer<Float>(OpaquePointer(output.dataPointer))
        var scale = self.scale
        vDSP_vsmul(inputPointer, 1, &scale, outputPointer, 1, vDSP_Length(count))
        */
    }
}

Спасибо.

person osmszk    schedule 27.12.2017