From ec6aca70f70723818603d8057387458e53faf1bd Mon Sep 17 00:00:00 2001 From: Sharron LIU Date: Fri, 6 Dec 2019 17:23:41 +0800 Subject: [PATCH 1/4] Enabling OpenVINO: added configure `openvino` Yaml config files under "cfg/examples/replication" were updated. Config "openvino" added to specify the target device for an OpenVINO model to execute. So far, it is supported to execute GQCNN OpenVINO models on CPU, GPU, or MYRIAD (Movidius NCS2). This config is set to OFF by default, for GQCNN with Tensorflow backend. Signed-off-by: Sharron LIU --- cfg/examples/replication/dex-net_2.0.yaml | 2 ++ cfg/examples/replication/dex-net_2.1.yaml | 4 +++- cfg/examples/replication/dex-net_3.0.yaml | 2 ++ cfg/examples/replication/dex-net_4.0_pj.yaml | 2 ++ cfg/examples/replication/dex-net_4.0_suction.yaml | 2 ++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cfg/examples/replication/dex-net_2.0.yaml b/cfg/examples/replication/dex-net_2.0.yaml index f2ba3ac9..7852e06f 100644 --- a/cfg/examples/replication/dex-net_2.0.yaml +++ b/cfg/examples/replication/dex-net_2.0.yaml @@ -64,6 +64,8 @@ policy: metric: type: gqcnn gqcnn_model: models/GQCNN-2.0 + # openvino: OFF|CPU|GPU|MYRIAD + openvino: OFF crop_height: 96 crop_width: 96 diff --git a/cfg/examples/replication/dex-net_2.1.yaml b/cfg/examples/replication/dex-net_2.1.yaml index 93391ea9..5fbc48a2 100644 --- a/cfg/examples/replication/dex-net_2.1.yaml +++ b/cfg/examples/replication/dex-net_2.1.yaml @@ -64,7 +64,9 @@ policy: metric: type: gqcnn gqcnn_model: models/GQCNN-2.1 - + # openvino: OFF|CPU|GPU|MYRIAD + openvino: OFF + crop_height: 96 crop_width: 96 diff --git a/cfg/examples/replication/dex-net_3.0.yaml b/cfg/examples/replication/dex-net_3.0.yaml index e8b9bca2..9e551f4e 100644 --- a/cfg/examples/replication/dex-net_3.0.yaml +++ b/cfg/examples/replication/dex-net_3.0.yaml @@ -58,6 +58,8 @@ policy: metric: type: gqcnn gqcnn_model: models/GQCNN-3.0 + # openvino: OFF|CPU|GPU|MYRIAD + openvino: OFF crop_height: 96 crop_width: 96 diff --git a/cfg/examples/replication/dex-net_4.0_pj.yaml b/cfg/examples/replication/dex-net_4.0_pj.yaml index 0e970c56..f4f8a964 100644 --- a/cfg/examples/replication/dex-net_4.0_pj.yaml +++ b/cfg/examples/replication/dex-net_4.0_pj.yaml @@ -63,6 +63,8 @@ policy: metric: type: gqcnn gqcnn_model: models/GQCNN-4.0-PJ + # openvino: OFF|CPU|GPU|MYRIAD + openvino: OFF crop_height: 96 crop_width: 96 diff --git a/cfg/examples/replication/dex-net_4.0_suction.yaml b/cfg/examples/replication/dex-net_4.0_suction.yaml index 1d8d5d9c..3bebcfe0 100644 --- a/cfg/examples/replication/dex-net_4.0_suction.yaml +++ b/cfg/examples/replication/dex-net_4.0_suction.yaml @@ -57,6 +57,8 @@ policy: metric: type: gqcnn gqcnn_model: models/GQCNN-4.0-SUCTION + # openvino: OFF|CPU|GPU|MYRIAD + openvino: OFF crop_height: 96 crop_width: 96 From c683ad28a752cbc5d20e4d3035f8aaebb1e414f6 Mon Sep 17 00:00:00 2001 From: Sharron LIU Date: Fri, 6 Dec 2019 17:24:24 +0800 Subject: [PATCH 2/4] Enabling OpenVINO: added tutorials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tutorials added introducing how to - install the OpenVINO™ toolkit - freeze a GQCNN model - convert a GQCNN model - evaluate the GQCNN OpenVINO model Signed-off-by: Sharron LIU --- docs/source/images/gqcnn_openvino.png | Bin 0 -> 28350 bytes docs/source/index.rst | 6 +++ docs/source/openvino/openvino.rst | 72 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100755 docs/source/images/gqcnn_openvino.png create mode 100644 docs/source/openvino/openvino.rst diff --git a/docs/source/images/gqcnn_openvino.png b/docs/source/images/gqcnn_openvino.png new file mode 100755 index 0000000000000000000000000000000000000000..b9ef140263fb80890a555e6931f76d105ee0d238 GIT binary patch literal 28350 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 F{}-v9i)sJ> literal 0 HcmV?d00001 diff --git a/docs/source/index.rst b/docs/source/index.rst index d985dfff..310274a7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -111,6 +111,12 @@ If you use the code, datasets, or models in a publication, please cite the appro replication/replication.rst +.. toctree:: + :maxdepth: 2 + :caption: Enabling OpenVINO™ + + openvino/openvino.rst + .. toctree:: :maxdepth: 2 :caption: Benchmarks diff --git a/docs/source/openvino/openvino.rst b/docs/source/openvino/openvino.rst new file mode 100644 index 00000000..c60322a5 --- /dev/null +++ b/docs/source/openvino/openvino.rst @@ -0,0 +1,72 @@ +Enabling OpenVINO™ +~~~~~~~~~~~~~~~~~~ + +This tutorial introduces how to enable OpenVINO™ for GQCNN deployment on Intel® devices. + +Intel® Distribution of OpenVINO™ (Open Visual Inference & Neural network Optimization) toolkit, based on convolutional neural networks (CNNs), extends computer visoin workloads across Intel® hardware (including accelerators) and maximizes performance. The toolkit enables deep learning inference at the edge computation, and supports heterogeneous execution across various compution vision devices -- CPU, GPU, Intel® Movidius™ NCS2, and FPGA -- using a **common** API. + +.. image:: ../images/gqcnn_openvino.png + :width: 440 px + :height: 250 px + :align: center + +Install OpenVINO™ Toolkit +========================= +The toolkit is available from open source project `Intel® OpenVINO™ Toolkit`_. + +.. note:: GQCNN uses two layers, RandomUniform and Floor, which are not supported by the 2019_R3 release of OpenVINO™. `PR #338 `_ adds the support for these two layers. + +You may get start with `Build Inference Engine`_. The ``Introduction`` section lists supported device types. The ``Build on Linux* Systems`` section tells how to build and install the toolkit. Here're the CMake options for reference. Need adaption to your specific environment. :: + + $ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DGEMM=OPENBLAS -DBLAS_INCLUDE_DIRS=/usr/include/x86_64-linux-gnu -DBLAS_LIBRARIES=/usr/lib/x86_64-linux-gnu/openblas/libblas.so -DENABLE_MKL_DNN=ON -DENABLE_CLDNN=ON -DENABLE_PYTHON=ON -DPYTHON_EXECUTABLE=`which python3.6` -DPYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libpython3.6m.so -DPYTHON_INCLUDE_DIR=/usr/include/python3.6 .. + +Then install the ``Model Optimizer``. :: + + $ cd model_optimizer + $ sudo pip3 install -r requirements*.txt + +And setup environment for the toolkit. :: + + $ export InferenceEngine_DIR=/inference-engine/build + $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/inference-engine/bin/intel64/Release/lib + $ export PYTHONPATH=/inference-engine/bin/intel64/Release/lib/python_api/python3.6:/model-optimizer:$PYTHONPATH + +Freeze a GQCNN Model +==================== +A frozen graph file is expected by the Model Optimzer of OpenVINO™. This is done in ``gqcnn.model.tf.GQCNNTF.initialize_network()`` right after the graph is built. Seek into the code pieces below. :: + + # Freeze graph. Make it 'True' to freeze this graph + if False: + +Switch the ``if`` condition to ``True``, run an example policy with a specific GQCNN model (refer to ``scripts/policies/run_all_dex-net__examples.sh``), the frozen graph will be created into the file named ``inference_graph_frozen.pb`` + +Convert a GQCNN Model +===================== +``mo_tf.py`` is the Model Optimizer script for converting a Tensorflow model. :: + + $ sudo python3 /model-optimizer/mo_tf.py --input_model inference_graph_frozen.pb --data_type FP16 --output_dir /models/OpenVINO//FP16 + $ sudo python3 /model-optimizer/mo_tf.py --input_model inference_graph_frozen.pb --data_type FP32 --output_dir /models/OpenVINO//FP32 + +Parameters passed to the conversion script: + #. ``input_model`` the frozen tensorflow model to be converted. + #. ``output_dir`` the directory of the converted model. + #. ``data_type`` data type of the converted model. + +For more detail instructions on model conversion, refer to the `OpenVINO™ Docs`_. + +.. note:: ``gqcnn.model.openvino.GQCNNOpenVINO.load_openvino()`` expect to load an OpenVINO model from ``models/OpenVINO//FP16``, where ``model_name`` comes from the original GQCNN model, e.g. ``GQCNN-4.0-SUCTION``, ``GQ-Suction``, etc. + +Evaluate the OpenVINO™ Model with Example Policies +================================================== +Now the GQCNN model has been successfully converted into OpenVINO™ model. You may evaluate the GQCNN OpenVINO™ model with the example policy. Seek ``cfg/examples/replication/dex-net_.yaml`` for the below configure: :: + + # openvino: OFF|CPU|GPU|MYRIAD + openvino: OFF + +Toggle ``openvino`` among CPU, GPU, or MYRIAD. This configure specifies the target device type for the GQCNN inference to execute (supported device types listed in the ``introduction section`` of `Build Inference Engine`_). Then run the example policy in the same way as given in ``scripts/policies/run_all_dex-net__examples.sh``, e.g. :: + + $ python3 examples/policy.py GQCNN-4.0-SUCTION --depth_image data/examples/clutter/phoxi/dex-net_4.0/depth_0.npy --segmask data/examples/clutter/phoxi/dex-net_4.0/segmask_0.png --config_filename cfg/examples/replication/dex-net_4.0_suction.yaml --camera_intr data/calib/phoxi/phoxi.intr + +.. _Intel® OpenVINO™ Toolkit: https://github.com/opencv/dldt +.. _Build Inference Engine: https://github.com/opencv/dldt/blob/2019/inference-engine/README.md +.. _OpenVINO™ Docs: https://docs.openvinotoolkit.org/latest/_docs_MO_DG_prepare_model_convert_model_Convert_Model_From_TensorFlow.html From fa923de5ad968214882b3cf041abdecd126f2ecf Mon Sep 17 00:00:00 2001 From: Sharron LIU Date: Fri, 6 Dec 2019 17:23:05 +0800 Subject: [PATCH 3/4] Enabling OpenVINO: freeze GQCNN graph MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GQCNNTF.initialize_network() is updated to freeze the GQCNN graph, right after the initialization of the network. This code block is inactive unless explicitly switching the `if` condition into `True` when freezing is needed. A frozen graph is expected by the Model Optimizer of OpenVINO™. Signed-off-by: Sharron LIU --- gqcnn/model/tf/network_tf.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gqcnn/model/tf/network_tf.py b/gqcnn/model/tf/network_tf.py index 8b90d135..d2a86551 100644 --- a/gqcnn/model/tf/network_tf.py +++ b/gqcnn/model/tf/network_tf.py @@ -552,6 +552,19 @@ def initialize_network(self, if add_sigmoid: self.add_sigmoid_to_output() + # Freeze graph. Make it 'True' to freeze this graph + if False: + from tensorflow.python.framework import graph_io, graph_util + self.open_session() + frozen = graph_util.convert_variables_to_constants( + self._sess, self._sess.graph_def, ["softmax/Softmax"]) + graph_io.write_graph(frozen, + ".", + "inference_graph_frozen.pb", + as_text=False) + self.close_session() + self._logger.info("Wrote frozen graph") + # Create feed tensors for prediction. self._input_im_arr = np.zeros((self._batch_size, self._im_height, self._im_width, self._num_channels)) From a3eccff2ee75eaaf090e2af7c00fdf8760bebc8e Mon Sep 17 00:00:00 2001 From: Sharron LIU Date: Fri, 6 Dec 2019 17:22:19 +0800 Subject: [PATCH 4/4] Enabling OpenVINO: added GQCNNOpenVINO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GQCNNOpenVINO, derived from GQCNNTF, implements GQCNN inference with OpenVINO™ backend. GQCnnQualityFunction.__init__() is also updated, to load the GQCNNOpenVINO model. Signed-off-by: Sharron LIU --- gqcnn/grasping/grasp_quality_function.py | 7 +- gqcnn/model/__init__.py | 4 + gqcnn/model/openvino/__init__.py | 14 ++ gqcnn/model/openvino/network_openvino.py | 222 +++++++++++++++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 gqcnn/model/openvino/__init__.py create mode 100644 gqcnn/model/openvino/network_openvino.py diff --git a/gqcnn/grasping/grasp_quality_function.py b/gqcnn/grasping/grasp_quality_function.py index 5d5637e2..61ac237b 100644 --- a/gqcnn/grasping/grasp_quality_function.py +++ b/gqcnn/grasping/grasp_quality_function.py @@ -928,7 +928,12 @@ def __init__(self, config): self._crop_width = config["crop_width"] # Init GQ-CNN - self._gqcnn = get_gqcnn_model().load(self._gqcnn_model_dir) + + self._gqcnn = get_gqcnn_model().load(self._gqcnn_model_dir) \ + if ("openvino" not in self._config or + self._config["openvino"] == "OFF") \ + else get_gqcnn_model("openvino").load( + self._gqcnn_model_dir, self._config["openvino"]) # Open Tensorflow session for gqcnn. self._gqcnn.open_session() diff --git a/gqcnn/model/__init__.py b/gqcnn/model/__init__.py index d120daa1..bd21beaa 100644 --- a/gqcnn/model/__init__.py +++ b/gqcnn/model/__init__.py @@ -60,6 +60,10 @@ def get_gqcnn_model(backend="tf", verbose=True): if backend == "tf": logger.info("Initializing GQ-CNN with Tensorflow as backend...") return GQCNNTF + elif backend == "openvino": + from .openvino import GQCNNOpenVINO + logger.info("Initializing GQ-CNN with OpenVINO as backend...") + return GQCNNOpenVINO else: raise ValueError("Invalid backend: {}".format(backend)) diff --git a/gqcnn/model/openvino/__init__.py b/gqcnn/model/openvino/__init__.py new file mode 100644 index 00000000..33eeeaa8 --- /dev/null +++ b/gqcnn/model/openvino/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2019 Intel Corporation. All Rights Reserved. + +GQ-CNN inference with OpenVINO. + +Author +------ +Sharron LIU +""" + +from .network_openvino import GQCNNOpenVINO + +__all__ = ["GQCNNOpenVINO"] diff --git a/gqcnn/model/openvino/network_openvino.py b/gqcnn/model/openvino/network_openvino.py new file mode 100644 index 00000000..f746bdb8 --- /dev/null +++ b/gqcnn/model/openvino/network_openvino.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +""" +Copyright (c) 2019 Intel Corporation. All Rights Reserved. + +GQ-CNN inference with OpenVINO. + +Author +------ +Sharron LIU +""" +from collections import OrderedDict +import json +import math +import os +import time +import numpy as np + +from autolab_core import Logger +from ...utils import (InputDepthMode, GQCNNFilenames) +from ..tf import GQCNNTF +from openvino.inference_engine import IENetwork, IECore + + +class GQCNNOpenVINO(GQCNNTF): + """GQ-CNN network implemented in OpenVINO.""" + + BatchSize = 64 + + def __init__(self, gqcnn_config, verbose=True, log_file=None): + """ + Parameters + ---------- + gqcnn_config : dict + Python dictionary of model configuration parameters. + verbose : bool + Whether or not to log model output to `stdout`. + log_file : str + If provided, model output will also be logged to this file. + """ + self._sess = None + # Set up logger. + self._logger = Logger.get_logger(self.__class__.__name__, + log_file=log_file, + silence=(not verbose), + global_log_file=verbose) + self._parse_config(gqcnn_config) + + @staticmethod + def load(model_dir, device, verbose=True, log_file=None): + """Instantiate a trained GQ-CNN for fine-tuning or inference. + + Parameters + ---------- + model_dir : str + Path to trained GQ-CNN model. + device : str + Device type for inference to execute CPU|GPU|MYRIAD + verbose : bool + Whether or not to log model output to `stdout`. + log_file : str + If provided, model output will also be logged to this file. + + Returns + ------- + :obj:`GQCNNOpenVINO` + Initialized GQ-CNN. + """ + # Load GQCNN config + config_file = os.path.join(model_dir, GQCNNFilenames.SAVED_CFG) + with open(config_file) as data_file: + train_config = json.load(data_file, object_pairs_hook=OrderedDict) + # Support for legacy configs. + try: + gqcnn_config = train_config["gqcnn"] + except KeyError: + gqcnn_config = train_config["gqcnn_config"] + gqcnn_config["debug"] = 0 + gqcnn_config["seed"] = 0 + # Legacy networks had no angular support. + gqcnn_config["num_angular_bins"] = 0 + # Legacy networks only supported depth integration through pose + # stream. + gqcnn_config["input_depth_mode"] = InputDepthMode.POSE_STREAM + + # Initialize OpenVINO network + gqcnn = GQCNNOpenVINO(gqcnn_config, verbose=verbose, log_file=log_file) + if (device == "MYRIAD"): # MYRIAD batch size force to 1 + gqcnn.set_batch_size(1) + else: + gqcnn.set_batch_size(64) + + # Initialize input tensors for prediction + gqcnn._input_im_arr = np.zeros((gqcnn._batch_size, gqcnn._im_height, + gqcnn._im_width, gqcnn._num_channels)) + gqcnn._input_pose_arr = np.zeros((gqcnn._batch_size, gqcnn._pose_dim)) + + # Initialize mean tensor and standard tensor + gqcnn.init_mean_and_std(model_dir) + + # Load OpenVINO network on specified device + gqcnn.load_openvino(model_dir, device) + + return gqcnn + + def open_session(self): + pass + + def close_session(self): + pass + + def load_openvino(self, model_dir, device): + self._ie = IECore() + # load OpenVINO executable network to device + model_path = os.path.split(model_dir) + model_xml = os.path.join(model_path[0], "OpenVINO", model_path[1]) + model_xml = os.path.join(model_xml, "FP16", + "inference_graph_frozen.xml") + model_bin = os.path.splitext(model_xml)[0] + ".bin" + self._vino_net = IENetwork(model_xml, model_bin) + self._vino_net.batch_size = self._batch_size + assert len(self._vino_net.inputs.keys()) == 2, "GQCNN two input nodes" + assert len(self._vino_net.outputs) == 1, "GQCNN one output node" + vino_inputs = iter(self._vino_net.inputs) + self._input_im = next(vino_inputs) + self._input_pose = next(vino_inputs) + self._output_blob = next(iter(self._vino_net.outputs)) + self._vino_exec_net = self._ie.load_network(network=self._vino_net, + device_name=device) + + def unload_openvino(self): + del self._vino_exec_net + del self._vino_net + del self._ie + + def predict_openvino(self, image_arr, pose_arr, verbose=False): + """ Predict a set of images in batches + Parameters + ---------- + image_arr : :obj:`tensorflow Tensor` + 4D Tensor of images to be predicted + pose_arr : :obj:`tensorflow Tensor` + 4D Tensor of poses to be predicted + """ + + # Get prediction start time. + start_time = time.time() + + if verbose: + self._logger.info("Predicting...") + + # Setup for prediction. + num_batches = math.ceil(image_arr.shape[0] / self._batch_size) + num_images = image_arr.shape[0] + num_poses = pose_arr.shape[0] + + output_arr = np.zeros( + [num_images] + + list(self._vino_net.outputs[self._output_blob].shape[1:])) + if num_images != num_poses: + raise ValueError("Must provide same number of images as poses!") + + # Predict in batches. + i = 0 + batch_idx = 0 + while i < num_images: + if verbose: + self._logger.info("Predicting batch {} of {}...{}".format( + batch_idx, num_batches, num_images)) + batch_idx += 1 + dim = min(self._batch_size, num_images - i) + cur_ind = i + end_ind = cur_ind + dim + + if self._input_depth_mode == InputDepthMode.POSE_STREAM: + self._input_im_arr[:dim, ...] = ( + image_arr[cur_ind:end_ind, ...] - + self._im_mean) / self._im_std + self._input_pose_arr[:dim, :] = ( + pose_arr[cur_ind:end_ind, :] - + self._pose_mean) / self._pose_std + elif self._input_depth_mode == InputDepthMode.SUB: + self._input_im_arr[:dim, ...] = image_arr[cur_ind:end_ind, ...] + self._input_pose_arr[:dim, :] = pose_arr[cur_ind:end_ind, :] + elif self._input_depth_mode == InputDepthMode.IM_ONLY: + self._input_im_arr[:dim, ...] = ( + image_arr[cur_ind:end_ind, ...] - + self._im_mean) / self._im_std + + n, c, h, w = self._vino_net.inputs[self._input_im].shape + input_im_arr = self._input_im_arr.reshape((n, c, h, w)) + res = self._vino_exec_net.infer( + inputs={ + self._input_im: input_im_arr, + self._input_pose: self._input_pose_arr + }) + + # Allocate output tensor. + output_arr[cur_ind:end_ind, :] = res[self._output_blob][:dim, :] + i = end_ind + + # Get total prediction time. + pred_time = time.time() - start_time + if verbose: + self._logger.info("Prediction took {} seconds.".format(pred_time)) + + return output_arr + + def predict(self, image_arr, pose_arr, verbose=False): + """Predict the probability of grasp success given a depth image and + gripper pose. + + Parameters + ---------- + image_arr : :obj:`numpy ndarray` + 4D tensor of depth images. + pose_arr : :obj:`numpy ndarray` + Tensor of gripper poses. + verbose : bool + Whether or not to log progress to stdout, useful to turn off during + training. + """ + return self.predict_openvino(image_arr, pose_arr, verbose=verbose)