Files
openvino/docs/notebooks/118-optimize-preprocessing-with-output.rst
Sebastian Golebiewski 6761a29af5 [DOCS] Updating Interactive Tutorials (#18504)
* update-notebooks

* Update docs/nbdoc/nbdoc.py

Co-authored-by: bstankix <bartoszx.stankiewicz@intel.com>

* Update docs/nbdoc/nbdoc.py

Co-authored-by: bstankix <bartoszx.stankiewicz@intel.com>

---------

Co-authored-by: bstankix <bartoszx.stankiewicz@intel.com>
2023-07-14 10:57:06 +02:00

628 lines
22 KiB
ReStructuredText
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Optimize Preprocessing
======================
When input data does not fit the model input tensor perfectly,
additional operations/steps are needed to transform the data to the
format expected by the model. This tutorial demonstrates how it could be
performed with Preprocessing API. Preprocessing API is an easy-to-use
instrument, that enables integration of preprocessing steps into an
execution graph and performing it on a selected device, which can
improve device utilization. For more information about Preprocessing
API, see this
`overview <https://docs.openvino.ai/2023.0/openvino_docs_OV_UG_Preprocessing_Overview.html#>`__
and
`details <https://docs.openvino.ai/2023.0/openvino_docs_OV_UG_Preprocessing_Details.html>`__
This tutorial include following steps:
- Downloading the model.
- Setup preprocessing with ModelOptimizer, loading the model and inference with original image.
- Setup preprocessing with Preprocessing API, loading the model and inference with original image.
- Fitting image to the model input type and inference with prepared image.
- Comparing results on one picture.
- Comparing performance.
Settings
--------
Imports
-------
.. code:: ipython3
import cv2
import time
import numpy as np
import tensorflow as tf
from pathlib import Path
from openvino.tools import mo
import matplotlib.pyplot as plt
from openvino.runtime import Core, serialize
.. parsed-literal::
2023-07-11 22:53:33.947684: I tensorflow/core/util/port.cc:110] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.
2023-07-11 22:53:33.981920: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.
To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.
2023-07-11 22:53:34.528705: W tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:38] TF-TRT Warning: Could not find TensorRT
Setup image and device
~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython3
image_path = "../data/image/coco.jpg"
.. code:: ipython3
import ipywidgets as widgets
core = Core()
device = widgets.Dropdown(
options=core.available_devices + ["AUTO"],
value='AUTO',
description='Device:',
disabled=False,
)
device
.. parsed-literal::
Dropdown(description='Device:', index=1, options=('CPU', 'AUTO'), value='AUTO')
Downloading the model
~~~~~~~~~~~~~~~~~~~~~
This tutorial uses the
`InceptionResNetV2 <https://www.tensorflow.org/api_docs/python/tf/keras/applications/inception_resnet_v2>`__.
The InceptionResNetV2 model is the second of the
`Inception <https://github.com/tensorflow/tpu/tree/master/models/experimental/inception>`__
family of models designed to perform image classification. Like other
Inception models, InceptionResNetV2 has been pre-trained on the
`ImageNet <https://image-net.org/>`__ data set. For more details about
this family of models, see the `research
paper <https://arxiv.org/abs/1602.07261>`__.
Load the model by using `tf.keras.applications
api <https://www.tensorflow.org/api_docs/python/tf/keras/applications/inception_resnet_v2>`__
and save it to the disk.
.. code:: ipython3
model_name = "InceptionResNetV2"
model_dir = Path("model")
model_dir.mkdir(exist_ok=True)
model_path = model_dir / model_name
model = tf.keras.applications.InceptionV3()
model.save(model_path)
.. parsed-literal::
2023-07-11 22:53:35.902963: W tensorflow/core/common_runtime/gpu/gpu_device.cc:1956] Cannot dlopen some GPU libraries. Please make sure the missing libraries mentioned above are installed properly if you would like to use GPU. Follow the guide at https://www.tensorflow.org/install/gpu for how to download and setup the required libraries for your platform.
Skipping registering GPU devices...
.. parsed-literal::
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
.. parsed-literal::
WARNING:absl:Found untraced functions such as _jit_compiled_convolution_op, _jit_compiled_convolution_op, _jit_compiled_convolution_op, _jit_compiled_convolution_op, _jit_compiled_convolution_op while saving (showing 5 of 94). These functions will not be directly callable after loading.
.. parsed-literal::
INFO:tensorflow:Assets written to: model/InceptionResNetV2/assets
.. parsed-literal::
INFO:tensorflow:Assets written to: model/InceptionResNetV2/assets
Create core
~~~~~~~~~~~
.. code:: ipython3
core = Core()
Check the original parameters of image
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython3
image = cv2.imread(image_path)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB));
print(f"The original shape of the image is {image.shape}")
print(f"The original data type of the image is {image.dtype}")
.. parsed-literal::
The original shape of the image is (577, 800, 3)
The original data type of the image is uint8
.. image:: 118-optimize-preprocessing-with-output_files/118-optimize-preprocessing-with-output_12_1.png
Convert model to OpenVINO IR and setup preprocessing steps with Model Optimizer
-------------------------------------------------------------------------------
Use Model Optimizer to convert a TensorFlow model to OpenVINO IR.
``mo.convert_model`` python function will be used for converting model
using `OpenVINO Model
Optimizer <https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_Python_API.html>`__.
The function returns instance of OpenVINO Model class, which is ready to
use in Python interface but can also be serialized to OpenVINO IR format
for future execution using ``openvino.runtime.serialize``. The models
will be saved to the ``./model/ir_model/`` directory.
In this step, some conversions can be setup, which will enable reduction
of work on processing the input data before propagating it through the
network. These conversions will be inserted as additional input
pre-processing sub-graphs into the converted model.
Setup the following conversions:
- mean normalization with ``mean_values`` parameter
- scale with ``scale_values``
- color conversion, the color format of example image will be ``BGR``, but the model required ``RGB`` format, so add ``reverse_input_channels=True`` to process the image into the desired format
Also converting of layout could be specified with ``layout`` option.
More information and parameters described in the `Embedding
Preprocessing Computation
article <https://docs.openvino.ai/2023.0/openvino_docs_MO_DG_Additional_Optimization_Use_Cases.html#embedding-preprocessing-computation>`__.
.. code:: ipython3
ir_path_mo_preprocess = model_dir / "ir_model" / f"{model_name}_mo_preproc.xml"
ov_model_mo_preprocess = None
if ir_path_mo_preprocess.exists():
ov_model_mo_preprocess = core.read_model(model=ir_path_mo_preprocess)
print(f"Model in OpenVINO format already exists: {ir_path_mo_preprocess}")
else:
ov_model_mo_preprocess = mo.convert_model(saved_model_dir=model_path,
model_name=model_path.name,
mean_values=[127.5,127.5,127.5],
scale_values=[127.5,127.5,127.5],
reverse_input_channels=True,
input_shape=[1,299,299,3])
serialize(ov_model_mo_preprocess, str(ir_path_mo_preprocess))
Prepare image
~~~~~~~~~~~~~
.. code:: ipython3
def prepare_image_mo_preprocess(image_path, model):
img = cv2.imread(filename=image_path)
input_layer_ir = next(iter(model.inputs))
# N, H, W, C = batch size, height, width, number of channels
N, H, W, C = input_layer_ir.shape
# Resize image to the input size expected by the model.
img = cv2.resize(img, (H, W))
# Fit image data type to expected by the model value
img = np.float32(img)
# Reshape to match the input shape expected by the model.
input_tensor = np.expand_dims(img, axis=0)
return input_tensor
mo_pp_input_tensor = prepare_image_mo_preprocess(image_path, ov_model_mo_preprocess)
print(f"The shape of the image is {mo_pp_input_tensor.shape}")
print(f"The data type of the image is {mo_pp_input_tensor.dtype}")
.. parsed-literal::
The shape of the image is (1, 299, 299, 3)
The data type of the image is float32
Compile model and perform inerence
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython3
compiled_model_mo_pp = core.compile_model(model=ov_model_mo_preprocess, device_name=device.value)
output_layer = compiled_model_mo_pp.output(0)
result = compiled_model_mo_pp(mo_pp_input_tensor)[output_layer]
Setup preprocessing steps with Preprocessing API and perform inference
----------------------------------------------------------------------
Intuitively, preprocessing API consists of the following parts:
- Tensor - declares user data format, like shape, layout, precision, color format from actual users data.
- Steps - describes sequence of preprocessing steps which need to be applied to user data.
- Model - specifies model data format. Usually, precision and shape are already known for model, only additional information, like layout can be specified.
Graph modifications of a model shall be performed after the model is
read from a drive and before it is loaded on the actual device.
Pre-processing support following operations (please, see more details
`here <https://docs.openvino.ai/2023.0/classov_1_1preprocess_1_1PreProcessSteps.html#doxid-classov-1-1preprocess-1-1-pre-process-steps-1aeacaf406d72a238e31a359798ebdb3b7>`__)
- Mean/Scale Normalization - Converting Precision - Converting layout
(transposing) - Resizing Image - Color Conversion - Custom Operations
Convert model to OpenVINO IR with Model Optimizer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The options for preprocessing are not required.
.. code:: ipython3
ir_path = model_dir / "ir_model" / f"{model_name}.xml"
ppp_model = None
if ir_path.exists():
ppp_model = core.read_model(model=ir_path)
print(f"Model in OpenVINO format already exists: {ir_path}")
else:
ppp_model = mo.convert_model(saved_model_dir=model_path,
input_shape=[1,299,299,3])
serialize(ppp_model, str(ir_path))
Create PrePostProcessor Object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The
`PrePostProcessor() <https://docs.openvino.ai/2023.0/classov_1_1preprocess_1_1PrePostProcessor.html#doxid-classov-1-1preprocess-1-1-pre-post-processor>`__
class enables specifying the preprocessing and postprocessing steps for
a model.
.. code:: ipython3
from openvino.preprocess import PrePostProcessor
ppp = PrePostProcessor(ppp_model)
Declare Users Data Format
~~~~~~~~~~~~~~~~~~~~~~~~~~
To address particular input of a model/preprocessor, use the
``PrePostProcessor.input(input_name)`` method. If the model has only one
input, then simple ``PrePostProcessor.input()`` will get a reference to
pre-processing builder for this input (a tensor, the steps, a model). In
general, when a model has multiple inputs/outputs, each one can be
addressed by a tensor name or by its index. By default, information
about users input tensor will be initialized to same data
(type/shape/etc) as models input parameter. User application can
override particular parameters according to applications data. Refer to
the following
`page <https://docs.openvino.ai/2023.0/classov_1_1preprocess_1_1InputTensorInfo.html#doxid-classov-1-1preprocess-1-1-input-tensor-info-1a98fb73ff9178c8c71d809ddf8927faf5>`__
for more information about parameters for overriding.
Below is all the specified input information:
- Precision is ``U8``(unsigned 8-bit integer).
- Size is non-fixed, setup of one determined shape size can be done with ``.set_shape([1, 577, 800, 3])``.
- Layout is ``“NHWC”``. It means, for example: height=577, width=800, channels=3.
The height and width are necessary for resizing, and channels are needed
for mean/scale normalization.
.. code:: ipython3
from openvino.runtime import Type, Layout
# setup formant of data
ppp.input().tensor().set_element_type(Type.u8)\
.set_spatial_dynamic_shape()\
.set_layout(Layout('NHWC'))
.. parsed-literal::
<openvino._pyopenvino.preprocess.InputTensorInfo at 0x7efb4037f5b0>
Declaring Model Layout
~~~~~~~~~~~~~~~~~~~~~~
Model input already has information about precision and shape.
Preprocessing API is not intended to modify this. The only thing that
may be specified is input data
`layout <https://docs.openvino.ai/2023.0/openvino_docs_OV_UG_Layout_Overview.html#doxid-openvino-docs-o-v-u-g-layout-overview>`__.
.. code:: ipython3
input_layer_ir = next(iter(ppp_model.inputs))
print(f"The input shape of the model is {input_layer_ir.shape}")
ppp.input().model().set_layout(Layout('NHWC'))
.. parsed-literal::
The input shape of the model is [1,299,299,3]
.. parsed-literal::
<openvino._pyopenvino.preprocess.InputModelInfo at 0x7efc3610ac70>
Preprocessing Steps
~~~~~~~~~~~~~~~~~~~
Now, the sequence of preprocessing steps can be defined. For more
information about preprocessing steps, see
`here <https://docs.openvino.ai/2023.0/api/ie_python_api/_autosummary/openvino.preprocess.PreProcessSteps.html>`__.
Perform the following:
- Convert ``U8`` to ``FP32`` precision.
- Resize to height/width of a model. Be aware that if a model accepts dynamic size, for example, ``{?, 3, ?, ?}`` resize will not know how to resize the picture. Therefore, in this case, target height/ width should be specified. For more details, see also the `PreProcessSteps.resize() <https://docs.openvino.ai/2023.0/classov_1_1preprocess_1_1PreProcessSteps.html#doxid-classov-1-1preprocess-1-1-pre-process-steps-1a40dab78be1222fee505ed6a13400efe6>`__.
- Subtract mean from each channel.
- Divide each pixel data to appropriate scale value.
There is no need to specify conversion layout. If layouts are different,
then such conversion will be added explicitly.
.. code:: ipython3
from openvino.preprocess import ResizeAlgorithm
ppp.input().preprocess().convert_element_type(Type.f32) \
.resize(ResizeAlgorithm.RESIZE_LINEAR)\
.mean([127.5,127.5,127.5])\
.scale([127.5,127.5,127.5])
.. parsed-literal::
<openvino._pyopenvino.preprocess.PreProcessSteps at 0x7efd2c4155b0>
Integrating Steps into a Model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once the preprocessing steps have been finished, the model can be
finally built. It is possible to display PrePostProcessor configuration
for debugging purposes.
.. code:: ipython3
print(f'Dump preprocessor: {ppp}')
model_with_preprocess = ppp.build()
.. parsed-literal::
Dump preprocessor: Input "input_1":
User's input tensor: [1,?,?,3], [N,H,W,C], u8
Model's expected tensor: [1,299,299,3], [N,H,W,C], f32
Pre-processing steps (4):
convert type (f32): ([1,?,?,3], [N,H,W,C], u8) -> ([1,?,?,3], [N,H,W,C], f32)
resize to model width/height: ([1,?,?,3], [N,H,W,C], f32) -> ([1,299,299,3], [N,H,W,C], f32)
mean (127.5,127.5,127.5): ([1,299,299,3], [N,H,W,C], f32) -> ([1,299,299,3], [N,H,W,C], f32)
scale (127.5,127.5,127.5): ([1,299,299,3], [N,H,W,C], f32) -> ([1,299,299,3], [N,H,W,C], f32)
Load model and perform inference
--------------------------------
.. code:: ipython3
def prepare_image_api_preprocess(image_path, model=None):
image = cv2.imread(image_path)
input_tensor = np.expand_dims(image, 0)
return input_tensor
compiled_model_with_preprocess_api = core.compile_model(model=ppp_model, device_name=device.value)
ppp_output_layer = compiled_model_with_preprocess_api.output(0)
ppp_input_tensor = prepare_image_api_preprocess(image_path)
results = compiled_model_with_preprocess_api(ppp_input_tensor)[ppp_output_layer][0]
Fit image manually and perform inference
----------------------------------------
Load the model
~~~~~~~~~~~~~~
.. code:: ipython3
model = core.read_model(model=ir_path)
compiled_model = core.compile_model(model=model, device_name=device.value)
Load image and fit it to model input
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython3
def manual_image_preprocessing(path_to_image, compiled_model):
input_layer_ir = next(iter(compiled_model.inputs))
# N, H, W, C = batch size, height, width, number of channels
N, H, W, C = input_layer_ir.shape
# load image, image will be resized to model input size and converted to RGB
img = tf.keras.preprocessing.image.load_img(image_path, target_size=(H, W), color_mode='rgb')
x = tf.keras.preprocessing.image.img_to_array(img)
x = np.expand_dims(x, axis=0)
# will scale input pixels between -1 and 1
input_tensor = tf.keras.applications.inception_resnet_v2.preprocess_input(x)
return input_tensor
input_tensor = manual_image_preprocessing(image_path, compiled_model)
print(f"The shape of the image is {input_tensor.shape}")
print(f"The data type of the image is {input_tensor.dtype}")
.. parsed-literal::
The shape of the image is (1, 299, 299, 3)
The data type of the image is float32
Perform inference
~~~~~~~~~~~~~~~~~
.. code:: ipython3
output_layer = compiled_model.output(0)
result = compiled_model(input_tensor)[output_layer]
Compare results
---------------
Compare results on one image
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: ipython3
def check_results(input_tensor, compiled_model, imagenet_classes):
output_layer = compiled_model.output(0)
results = compiled_model(input_tensor)[output_layer][0]
top_indices = np.argsort(results)[-5:][::-1]
top_softmax = results[top_indices]
for index, softmax_probability in zip(top_indices, top_softmax):
print(f"{imagenet_classes[index]}, {softmax_probability:.5f}")
return top_indices, top_softmax
# Convert the inference result to a class name.
imagenet_classes = open("../data/datasets/imagenet/imagenet_2012.txt").read().splitlines()
imagenet_classes = ['background'] + imagenet_classes
# get result for inference with preprocessing api
print("Result of inference for preprocessing with ModelOptimizer:")
res = check_results(mo_pp_input_tensor, compiled_model_mo_pp, imagenet_classes)
print("\n")
# get result for inference with preprocessing api
print("Result of inference with Preprocessing API:")
res = check_results(ppp_input_tensor, compiled_model_with_preprocess_api, imagenet_classes)
print("\n")
# get result for inference with the manual preparing of the image
print("Result of inference with manual image setup:")
res = check_results(input_tensor, compiled_model, imagenet_classes)
.. parsed-literal::
Result of inference for preprocessing with ModelOptimizer:
n02099601 golden retriever, 0.56439
n02098413 Lhasa, Lhasa apso, 0.35731
n02108915 French bulldog, 0.00730
n02111129 Leonberg, 0.00687
n04404412 television, television system, 0.00317
Result of inference with Preprocessing API:
n02099601 golden retriever, 0.80560
n02098413 Lhasa, Lhasa apso, 0.10039
n02108915 French bulldog, 0.01915
n02111129 Leonberg, 0.00825
n02097047 miniature schnauzer, 0.00294
Result of inference with manual image setup:
n02098413 Lhasa, Lhasa apso, 0.76848
n02099601 golden retriever, 0.19304
n02111129 Leonberg, 0.00725
n02097047 miniature schnauzer, 0.00290
n02100877 Irish setter, red setter, 0.00116
Compare performance
~~~~~~~~~~~~~~~~~~~
.. code:: ipython3
def check_performance(compiled_model, preprocessing_function=None):
num_images = 1000
start = time.perf_counter()
for _ in range(num_images):
input_tensor = preprocessing_function(image_path, compiled_model)
compiled_model(input_tensor)
end = time.perf_counter()
time_ir = end - start
return time_ir, num_images
time_ir, num_images = check_performance(compiled_model_mo_pp, prepare_image_mo_preprocess)
print(
f"IR model in OpenVINO Runtime/CPU with preprocessing API: {time_ir/num_images:.4f} "
f"seconds per image, FPS: {num_images/time_ir:.2f}"
)
time_ir, num_images = check_performance(compiled_model, manual_image_preprocessing)
print(
f"IR model in OpenVINO Runtime/CPU with preprocessing API: {time_ir/num_images:.4f} "
f"seconds per image, FPS: {num_images/time_ir:.2f}"
)
time_ir, num_images = check_performance(compiled_model_with_preprocess_api, prepare_image_api_preprocess)
print(
f"IR model in OpenVINO Runtime/CPU with preprocessing API: {time_ir/num_images:.4f} "
f"seconds per image, FPS: {num_images/time_ir:.2f}"
)
.. parsed-literal::
IR model in OpenVINO Runtime/CPU with preprocessing API: 0.0200 seconds per image, FPS: 49.90
IR model in OpenVINO Runtime/CPU with preprocessing API: 0.0153 seconds per image, FPS: 65.52
IR model in OpenVINO Runtime/CPU with preprocessing API: 0.0187 seconds per image, FPS: 53.59