Support for lambda layers in Keras or output image scale & bias

Many image processing models shift and scale the final layers of a network to go from sigmoid or tanh output [0,1] or [-1,1] back into 8-bit RGB values [0-255]. This is usually done with a simple scale/shift layer with non-learned parameters (125 and 255.0/2 are constants):


rgb = x * 125 + 255.0/2


This does not seem possible with the current coreML tools.

Approaches I have tried:

1) The current version of the coremltools (v0.4) does not support lambda layers, which is the straightforward way to do this tranform into RGB space as the final keras layer.

2) Parameters to the coremltools converter such as image_scale and rgb_bias are for the inputs. I am trying to go from unit values back into pixel space for the outputs of the model.

3) Applying the transform after receiving the results of the CoreML model. This is less than desirable since it breaks the abstraction and drags the developer back down into applying a per pixel transform that should be part of the model instead of just using the image output.

Are there better approaches to handling pixel based model outputs? This kinda of output mapping is common in image-based neural networks and it would be nice to have a clean mechanism for handling.

Replies

I had the same issue, but then I found that a bias layer is actually defined in the CoreML model spec, just not provided by the NeuralNetworkBuilder. So I wrote my own helper and it works perfectly:


def _add_bias(self, name, bias, shape, input_name, output_name):
  """
  Add bias layer to the model.
  Parameters
  ----------
  name: str
  The name of this layer.
  bias: numpy.array
  The biases to apply to the inputs.
  The size must be equal to the product of the ``shape`` dimensions.
  shape: tuple
  The shape of the bias. Must be one of the following:
  ``[1]``, ``[C]``, ``[1, H, W]`` or ``[C, H, W]``.
  input_name: str
  The input blob name of this layer.
  output_name: str
  The output blob name of this layer.
  """
  nn_spec = self.nn_spec
  # Add a new inner-product layer
  spec_layer = nn_spec.layers.add()
  spec_layer.name = name
  spec_layer.input.append(input_name)
  spec_layer.output.append(output_name)
  spec_layer_params = spec_layer.bias
  spec_layer_params.bias.floatValue.extend(map(float, bias.flatten()))
  spec_layer_params.shape.extend(shape)
_NeuralNetworkBuilder.add_bias = _add_bias


Even better for your case, there is a scale layer in the spec that also supports biases:


/*
* A layer that performs elmentwise multiplication by a scale factor
* and optionally adds a bias;
* both the scale and bias are broadcasted to match the input shape.
* ::
*   y = ScaleLayer(x)
* Requires 1 input and produces 1 output.
*
* Input
*     A blob with shape ``[C, H, W]``.
* Output
*     A blob with the same shape as the input.
*/
message ScaleLayerParams


So if you modify my code above to use

spec_layer_params = spec_layer.scale

instead and add the missing scale parameters you should be good.

The problem with lambda layers is that they can contain *anything*, even things that Core ML does not support.


In any case, Core ML does support scale layers, so if you change the lambda layer into a scaling layer it should work (although I'm not sure Keras has a scaling layer). You can also do the scaling with a "linear" activation (but not sure how you'd add the 255.0/2 then).


You may have to write your own converter to turn the lambda layer into a ScaleLayer.

How to integrate the bias layer to the "coremltools.converters.keras.convert" workflow?