Keras Lambda/custom layer support
ST Edge AI Core Technology 2.2.0
r1.0
Introduction
The goal of this article is to explain how you can import a Keras model containing Lambda or Custom layers. Depending on the nature of your model you will have to follow one way or another.
How to manage lambda layers?
Let’s say you have a model containing Keras Lambda layers (tensorflow.keras.layers.Lambda). Depending on the nature of your lambda layer the code generation using ST Edge AI Core CLI could succeed or failed.
The simplest case a Lambda layer incorporating a python’s lambda function which performs a simple math operation like a tensor sum or a tensor square.
lambda x: x**2)) model.add(Lambda(
In this case it is likely that the Lambda layer will be interpreted without any problems.
Let’s try a little bit more complex use case, even if still quite simple. Let’s say you want to perform a TensorFlow math operation on a Tensor in a Lambda. ST Edge AI Core supports a set of standard operators (see “Custom operators” “KERAS - Custom operators” section) that can be used to interpret your Lambda layer. The following example is using a lambda function instead of the lambda keyword to tell Keras what to do.
def lambda_abs(x): import tensorflow return tensorflow.math.abs(x) ... model.add(Lambda(lambda_abs))
In this example the tensorflow operation ‘abs’ on a tensor object is used through a Lambda layer. Because ST Edge AI Core will not know the tensorflow package when reading the Lambda layer, you need to import the tensorflow package into the lambda function you are using.
In case of more complex use-cases, it is possible that the tool raise a
NOT IMPLEMENTED
error. If you are in this situation we suggest you either to split your operation into simpler operations or to use a Keras Custom Layer which enables more complex use-cases.NOT IMPLEMENTED: Unsupported operator type: Mean
How to manage custom layers?
ST Edge AI Core has a partial support of Keras Custom Layer and can help the user by (partially) managing the code generation. The Keras Custom Layer support supposes that the model has custom layers inherited from tensorflow.keras.layers.Layer. By default if the model has such custom layers the tool will raise an error about them:
error: Unknown layer: MyCustomLayer
Let’s see how this issue can be solved and how ST Edge AI Core can work with this type of models.
Simple Custom Layers (mapped on tf operators)
Let say that the Custom Layer performs a TensorFlow operation like in the following example:
class MyCustomCos(K.layers.Layer):
def __init__(self, **kwargs):
super(MyCustomCos, self).__init__(**kwargs)
def call(self, inputs, **_):
return tf.math.cos(inputs)
def get_config(self):
= super(MyCustomCos, self).get_config()
base_config return dict(list(base_config.items()))
… and in the model there is something like :
model.add(MyCustomCos())
``
### Perform code generation
with this kind of model the information about
To perform a code generation and the TensorFlow operator must be provided to the tool.
the mapping between the custom Layer is done through command line option: `--custom [JSON_FILE]`.
This
file that will be used to get the layer's information.
The tool requires a JSON config In this use case you can specify the mapped operation using the "*op*" key in a JSON object
with your custom Layer's name as follow:
named
```json
{"MyCustomCos": {
"op": "tf.math.cos"
} }
Perform model validation on desktop
To start the validation process of the model, the same JSON file used for code generation can be reused. Because the operator is mapped on a Python function, the model can be run directly and generated using ST Edge AI Core known operators list.
Perform model validation on target
To perform a validation on target, the implementation has to be generated and flashed on the target, then the validation process can be executed using the custom JSON file already used for the code generation and the validation on desktop processes.
Complex custom layers management (custom user code)
Let’s now dig into more complex use case where the custom Layer in the model is not a simple math or TensorFlow operation. Let’s say custom Layer is defined like the following example:
class MyCustomLayer(K.layers.Layer):
def __init__(self, factor, **kwargs):
super(MyCustomLayer, self).__init__(**kwargs)
self.my_param = factor
self.my_param2 = tf.Variable(initial_value=[0.5], trainable=True, shape=(1,), name=self.name)
def call(self, inputs, **_):
return inputs * self.my_param * self.my_param2
def get_config(self):
= super(MyCustomLayer, self).get_config()
base_config = {"factor": self.my_param}
config return dict(list(base_config.items()) + list(config.items()))
In this example the Layer is composed of a factor as input layer’s parameter and a TensorFlow Variable that will be trained during model training. The layer operation is still pretty simple in this case but it can be as complex as the user wants.
We can also notice that the parameter ‘factor’ is described in the get_config(self) method, so that the parameter value is exported and visible in tools like Netron.
The final model could looks to something like that:
4))
model.add(Dense(3))
model.add(MyCustomLayer(3))
model.add(Dense(5))
model.add(MyCustomLayer(1)) model.add(Dense(
Perform code generation
This is the most complex usecase supported today in ST Edge AI Core tool. To perform a code generation in this case you will need to create a JSON config file like the Simple Custom Layer usecase. But because the layer can be anything you can define, you will need to specify a python file containing your custom Layer Python implementation (the tensorflow.keras.layers.Layer inherited class) and the C implementation of the custom layer in the JSON file.
Things here can be done in two phases:
- In a first phase you indicate only the python layer definition to ST Edge AI Core so that the tool can run the model internally and generate network files for known layers. The tool will also generate an empty template file associated with your custom layer that you need to fill with your own C implementation.
- In a second phase when your C file is ready, you re-start ST Edge AI Core with the Python and C file specified in the JSON. The code should be generated correctly in the standard output folder.
For the above example we need to create first a JSON file with the following content to indicate the python file containing the implementation of MyCustomLayer. The ‘python’ key can be specified as absolute path or relative to the JSON file as follow:
{
"MyCustomLayer": {
"python": "MyCustomLayerImplementation.py"
}
}
The first key “MyCustomLayer” is the name of the custom Layer used (name of the Python class) and MyCustomLayerImplementation.py is the name of the python file containing MyCustomLayer implementation defined above.
We can perform the first phase by calling the ST Edge AI Core tool as follow:
$ stm32ai generate ~/Desktop/my_model_with_custom.h5 --custom ~/Desktop/my_model_config_file.json
The result should indicates the generated files
Generated files (6)
----------------------------------------------------------------------------
stm32ai_output\network_custom_layers.c
stm32ai_output\network_config.h
stm32ai_output\network.h
stm32ai_output\network.c
stm32ai_output\network_data.h
stm32ai_output\network_data.c
We can see that we have a network_custom_layers.c
generated file. This is our empty template file and it should like
this :
void custom_init_MyCustomLayer(ai_layer* layer)
{
* l = ai_layer_custom_get(layer);
ai_layer_custom* t_in0 = ai_layer_get_tensor_in(l, 0);
ai_tensor* t_out0 = ai_layer_get_tensor_out(l, 0);
ai_tensor/*USER CODE BEGINS HERE*/
/*USER CODE ENDS HERE*/
(layer);
ai_layer_custom_release}
/* Layer Forward Function #0 */
void custom_forward_MyCustomLayer(ai_layer* layer)
{
* l = ai_layer_custom_get(layer);
ai_layer_custom* t_in0 = ai_layer_get_tensor_in(l, 0);
ai_tensor* t_out0 = ai_layer_get_tensor_out(l, 0);
ai_tensor/*USER CODE BEGINS HERE*/
/*USER CODE ENDS HERE*/
(layer);
ai_layer_custom_release}
The template generated is filled with two functions, one to perform initialization before the network is run and the second to define the forward function to use for inferences. These functions will be called with an ai_layer parameter which allows you to retrieve information about input/output tensors and information.
Let’s now implement the C functions. The current implementation of the custom layer does not allow you to get the weights and parameter directly from the generated data array by ST Edge AI Core. This is your responsibility to set the weights and parameters in your custom layer implementation. For this example we have used Netron to get the parameters and trained weights of the custom layer.
The next section of code show you how MyCustomLayer is implemented in C:
void custom_forward_MyCustomLayer(ai_layer* layer)
{
* l = ai_layer_custom_get(layer);
ai_layer_custom* t_in0 = ai_layer_get_tensor_in(l, 0);
ai_tensor* t_out0 = ai_layer_get_tensor_out(l, 0);
ai_tensor
/*USER CODE BEGINS HERE*/
if(l->id == AI_LAYER_CUSTOM_MY_CUSTOM_LAYER_ID) // The first instance of MyCustomLayer
{
const int factor = 3; // Parameter used for the first instance of MyCustomLayer in Python
const ai_float param2 = 0.5009999871253967; // The trained weights of the first instance of MyCustomLayer
*d_in = ai_tensor_get_data(t_in0).float32; // Data of the input tensor
ai_float *d_out = ai_tensor_get_data(t_out0).float32; // Data of the output tensor
ai_float
for(int i = 0; i < ai_tensor_get_data_size(t_in0); i++){
[i] = d_in[i] * factor * param2; // Performing the layer's operation for each data
d_out}
}
if(l->id == AI_LAYER_CUSTOM_MY_CUSTOM_LAYER_1_ID) // The second instance of MyCustomLayer
{
const int factor = 5; // Parameter used for the second instance of MyCustomLayer in Python
const ai_float param2 = 0.5009999871253967; // The trained weights of the second instance of MyCustomLayer
*d_in = ai_tensor_get_data(t_in0).float32; // Data of the input tensor
ai_float *d_out = ai_tensor_get_data(t_out0).float32; // Data of the output tensor
ai_float
for(int i = 0; i < ai_tensor_get_data_size(t_in0); i++){
[i] = d_in[i] * factor * param2; // Performing the layer's operation for each data
d_out}
}
/*USER CODE ENDS HERE*/
(layer);
ai_layer_custom_release}
In the implementation of MyCustomLayer (which is simple enough), no specific computation before running was necessary, so the init function will stay empty. We can see here multiple interesting points:
We are using layer IDs to get which layer is being computed. Because we have two instances of our custom layer with two different parameters (and weights), we need to identify which layer is being infered. This is done by fetching the layer ID “l->id” and doing different computation in the different cases. Your layers IDs are available by C defines in the template file. You can also get a better view of the different layers IDs by running the analyze command on your model with ST Edge AI Core.
The layer parameters that were set in the python model need to be set manually:
3)) ==> const int factor = 3; model.add(MyCustomLayer( [...]5)) ==> const int factor = 5; model.add(MyCustomLayer(
The layer trained weights need to be set manually:
const ai_float param2 = 0.5009999871253967; [...] const ai_float param2 = 0.5009999871253967;
Note: Here my weights param2 have the same values but in a more concrete usecase and through backpropagation these values would have been different.
Pointers to tensor in/out data are obtained via ai_tensor_get_data(…) and allow you to iterate though your different values. Once you have those pointers you can get the input values, do some computation and modify the output values, which represent the inference of your layer.
Once your C implementation is complete, you are 90% done. You can then complete the JSON config file by adding your C file to it:
{
"MyCustomLayer": {
"python": "MyCustomLayerImplementation.py",
"c": "network_custom_layers.c"
}
}
You are now able to perform code generation of your model through ST Edge AI Core, all your model files will be generated in the standard output folder.
Tip
technically this is not mandatory to specify your c file in the JSON file and perform the generation again because you already have the C files, but this is a good way to do so as it will be necessary for validation.
Perform model validation on desktop
To perform validation on a model containing custom layers, you will need to follow the steps of the previous chapter Perform code generation which describes 95% of the process.
Prerequisites :
- Python file containing the implementation of your custom
layer.
- C file containing your custom layer implementation
- Your JSON file with python and c fields pointing to correct files
To perform a validation on desktop, once your prerequisites are OK, you can use ST Edge AI Core by specifying your model and your JSON file as follow:
$ stm32ai validate ~/Desktop/my_model_with_custom.h5 --custom ~/Desktop/my_model_config_file.json
There is not big changes compared to a standard model validation.
Perform model validation on target
To perform validation on target on a model containing custom layers through ST Edge AI Core, you need to follow the steps of the previous chapter Perform code generation which describes 95% of the process.
Prerequisites :
- Python file containing the implementation of your custom layer.
- C file containing your custom layer implementation
- Your JSON file with python and c fields pointing to correct files
To perform a validation on target, once your prerequisites are OK, you can use ST Edge AI Core by specifying your model, the mode set to ‘stm32’ and your JSON file as follow:
$ stm32ai validate ~/Desktop/my_model_with_custom.h5 --custom ~/Desktop/my_model_config_file.json --mode stm32
There is not big changes compared to a standard model on target validation.