Examples — cppflow 2.0 documentation
Create and load model
To create a model that you can open with cppflow you just need to create a tf.Module or a tf.keras.Model and save it. Using the functional API of keras this is as easy as:
import tensorflow as tf input = tf.keras.Input(shape=(5,)) output = tf.keras.layers.Dense(5, activation=tf.nn.relu)(input) output = tf.keras.layers.Dense(1, activation=tf.nn.sigmoid)(output) model = tf.keras.Model(inputs=input, outputs=output) model.compile() # Export the model to a SavedModel model.save('model', save_format='tf')
Now a new directory named model is created, and it contains the saved model. You can open it from cppflow using the model class, to then feed it with a tensor to obtain the output.
#include <iostream> #include "cppflow/cppflow.h" int main() { auto input = cppflow::fill({10, 5}, 1.0f); cppflow::model model("../model"); auto output = model(input); std::cout << output << std::endl; return 0; }
Inference on EfficientNet
For this example we use a pretrained EfficientNet network that is available in Keras applications. Running the following code will create a model directory with the definition of the EfficientNet and its weights.
import tensorflow as tf model = tf.keras.applications.EfficientNetB0() # Export the model to a SavedModel model.save('model', save_format='tf')
Now we can open the model from cppflow and perform inference with a real image.
We can load the image using cppflow::read_file and cppflow::decode_jpeg. Then we have to convert it to float and feed it to the network.
#include <iostream> #include "cppflow/cppflow.h" int main() { auto input = cppflow::decode_jpeg(cppflow::read_file(std::string("../my_cat.jpg"))); input = cppflow::cast(input, TF_UINT8, TF_FLOAT); input = cppflow::expand_dims(input, 0); cppflow::model model("../model"); auto output = model(input); std::cout << "It's a tiger cat: " << cppflow::arg_max(output, 1) << std::endl; return 0; }
To see the prediction of the network we apply cppflow::arg_max to the ouput and it will show the number of the predicted class, which corresponds with a tiger cat.
Multi input/output model
For this example we will create a Keras model that takes two inputs and produce two outputs:
import tensorflow as tf input_1 = tf.keras.Input(shape=(5,), name='my_input_1') input_2 = tf.keras.Input(shape=(5,), name='my_input_2') x1 = tf.keras.layers.Dense(5, activation=tf.nn.relu)(input_1) x2 = tf.keras.layers.Dense(5, activation=tf.nn.relu)(input_2) output_1 = tf.keras.layers.Dense(1, activation=tf.nn.sigmoid, name='my_outputs_1')(x1) output_2 = tf.keras.layers.Dense(1, activation=tf.nn.sigmoid, name='my_outputs_2')(x2) model = tf.keras.Model(inputs=[input_1, input_2], outputs=[output_1, output_2]) model.compile() # Export the model to a SavedModel model.save('model', save_format='tf')
Now, we will inspect the model with the saved_model_cli to retrieve the name of the operations, and to know how to call the model.
$ saved_model_cli show --dir model 'serve' $ saved_model_cli show --dir model --tag_set serve SignatureDef key: "__saved_model_init_op" SignatureDef key: "serving_default" $ saved_model_cli show --dir model --tag_set serve --signature_def serving_default The given SavedModel SignatureDef contains the following input(s): inputs['my_input_1'] tensor_info: dtype: DT_FLOAT shape: (-1, 5) name: serving_default_my_input_1:0 inputs['my_input_2'] tensor_info: dtype: DT_FLOAT shape: (-1, 5) name: serving_default_my_input_2:0 The given SavedModel SignatureDef contains the following output(s): outputs['my_outputs_1'] tensor_info: dtype: DT_FLOAT shape: (-1, 1) name: StatefulPartitionedCall:0 outputs['my_outputs_2'] tensor_info: dtype: DT_FLOAT shape: (-1, 1) name: StatefulPartitionedCall:1 Method name is: tensorflow/serving/predict
From this output we can see that there are two inputs (serving_default_my_input_1:0 and serving_default_my_input_2:0) and two outputs (StatefulPartitionedCall:0 and StatefulPartitionedCall:1). You can run the model specifying multiple inputs as a vector of tuples <name of the input, input tensor and multiple outputs as a vector with the name of the outputs:
#include <iostream> #include "cppflow/cppflow.h" int main() { auto input_1 = cppflow::fill({10, 5}, 1.0f); auto input_2 = cppflow::fill({10, 5}, -1.0f); cppflow::model model("../model"); auto output = model({{"serving_default_my_input_1:0", input_1}, {"serving_default_my_input_2:0", input_2}}, {"StatefulPartitionedCall:0", "StatefulPartitionedCall:1"}); std::cout << "output_1: " << output[0] << std::endl; std::cout << "output_2: " << output[1] << std::endl; return 0; }