Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enabling OpenVINO #93

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cfg/examples/replication/dex-net_2.0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion cfg/examples/replication/dex-net_2.1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions cfg/examples/replication/dex-net_3.0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions cfg/examples/replication/dex-net_4.0_pj.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions cfg/examples/replication/dex-net_4.0_suction.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file added docs/source/images/gqcnn_openvino.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 72 additions & 0 deletions docs/source/openvino/openvino.rst
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/opencv/dldt/pull/338>`_ adds the support for these two layers.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't have these issues here: https://github.com/BerkeleyAutomation/gqcnn/blob/ncs/gqcnn/model/tf/ncs_fc_network_tf.py#L242, so I'm curious as to what the difference is. One is that I use self._graph.as_graph_def() instead of self._sess.graph_def, however I don't think that's it. Another is that the method in which the protobufs are written is a bit different. Finally I guess you are using OpenVINO whereas I was using the NCSDK, but I would think that they would support similar operations, right?

I also think it's a bit weird that the frozen graph would require RandomUniform because that should be strictly for training. If I recall correctly, only operations that the output directly depends on (i.e. things for inference) should be kept by: https://github.com/BerkeleyAutomation/gqcnn/blob/ncs/gqcnn/model/tf/ncs_fc_network_tf.py#L245. I don't exactly remember when/where Floor is used.

This might be worth looking into until your PR for the RandomUniform and Floor operations is approved. Even if you could just copy my code and try it out, that would be super helpful. Maybe something changed and it will still break.

Copy link
Author

@sharronliu sharronliu Dec 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little bit surprised hearing from you that RandomUniform or Floor is not used...

The OpenVINO Model Optimizer tool detects the exist of these two layers. Besides, the tensorboard visualization tool also illustrates these two layers, for pre-trained GQCNN 2.0, 2.1, 3.0, 4.0-pj, 4.0-suc downloaded by download_models.sh. I'm attaching the screen snapshot from tensorboard client, which might help you to recall something... :)

The usage of RandomUniform or Floor should not be introduced by OpenVINO or NCSDK. Both tools are inference stacks. While these two layers are observed in the original GQCNN graphs.

I also noticed these two layers were not introduced in the pre-trained models in Aug. 2018, when I started some works on GQCNN acceleration. My initial guessing is they were added later.

I won't think it related to self._graph.as_graph_def() or self._sess.graph_def, these two are equal if check == while freezing.

As you mentioned RandomUniform should be strictly for training. Would you suggest a right place to freeze the model? I'm thinking whether network_tf.py is the proper place, since it loads the GQCNNTF model for training. If the inference model is loaded in somewhere else, I can invoke the freezing there.


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=<path to dldt>/inference-engine/build
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<path to dldt>/inference-engine/bin/intel64/Release/lib
$ export PYTHONPATH=<path to dldt>/inference-engine/bin/intel64/Release/lib/python_api/python3.6:<path to dldt>/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_<model_name>_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 <path to dldt>/model-optimizer/mo_tf.py --input_model inference_graph_frozen.pb --data_type FP16 --output_dir <path to gqcnn>/models/OpenVINO/<model_name>/FP16
$ sudo python3 <path to dldt>/model-optimizer/mo_tf.py --input_model inference_graph_frozen.pb --data_type FP32 --output_dir <path to gqcnn>/models/OpenVINO/<model_name>/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/<model_name>/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_<model_name>.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_<model_name>_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
7 changes: 6 additions & 1 deletion gqcnn/grasping/grasp_quality_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions gqcnn/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
14 changes: 14 additions & 0 deletions gqcnn/model/openvino/__init__.py
Original file line number Diff line number Diff line change
@@ -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"]
222 changes: 222 additions & 0 deletions gqcnn/model/openvino/network_openvino.py
Original file line number Diff line number Diff line change
@@ -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)
Loading