apple/coremltools

Keras converter. Lambda layer with multiple inputs.

shamangary opened this issue · 4 comments

Hi. I encounter an issue when lambda layer receives multiple inputs.

# This is a custom activation function.
def swish(x,s1):
    return K.sigmoid(x)*x

def merge(x):
    return x[0]+x[1]

# Create a silly model that has our custom activation function as a new layer.
def create_model():
    a = 3
    inp = Input(shape=(256, 256, 3))
    x = Conv2D(6, (3, 3), padding="same")(inp)
    #x = Activation(swish)(x)                   # doesn't work! :-(
    x = Lambda(swish, arguments={'s1':a})(x)
    x = GlobalAveragePooling2D()(x)
    x0 = Dense(10, activation="softmax")(x)
    x1 = Dense(10, activation="softmax")(x0)
    x = Lambda(merge)([x0,x1])
    return Model(inp, x)

The conversion works fine when lambda layer only has one input. However, x = Lambda(merge)([x0,x1]) causes the following problem.

Traceback (most recent call last):
  File "test.py", line 98, in <module>
    custom_conversion_functions={ "Lambda": convert_lambda })
  File "/home/shamangary/anaconda3/envs/new/lib/python3.5/site-packages/coremltools/converters/keras/_keras_converter.py", line 745, in convert
    custom_conversion_functions=custom_conversion_functions)
  File "/home/shamangary/anaconda3/envs/new/lib/python3.5/site-packages/coremltools/converters/keras/_keras_converter.py", line 543, in convertToSpec
    custom_objects=custom_objects)
  File "/home/shamangary/anaconda3/envs/new/lib/python3.5/site-packages/coremltools/converters/keras/_keras2_converter.py", line 191, in _convert
    graph.build()
  File "/home/shamangary/anaconda3/envs/new/lib/python3.5/site-packages/coremltools/converters/keras/_topology2.py", line 643, in build
    successors = self.edge_map[layer]
KeyError: 'lambda_2'

I was trying much more complex merging function (with multiple different dim features) other than just addition.
Do you know how to resolve this? Thank you!

Can you try changing this function locally to the following and see if the error persists?

def _get_first_shared_layer(self):
        for idx, layer in enumerate(self.layer_list):
            keras_layer = self.keras_layer_map[layer]
            if not _is_merge_layer(self.keras_layer_map[layer]) and \
                len(self.get_predecessors(layer)) > 1 and \
                len(keras_layer._inbound_nodes) > 1:
                return idx
        return -1

Hello. The conversion works :). Despite I still have to write a custom swift file for my layer for the ios app, I guess the conversion is working now. Many thanks.

@aseemw Has the above fix been merged in the code?

Yes.