Gkazanta/update doc master (#3413)

* Updated transformations docs

* Added RTTI to examples

* Fixed grammar mistakes

* Mention Sinks in NG docs
This commit is contained in:
Gleb Kazantaev
2020-11-30 15:13:01 +03:00
committed by GitHub
parent e9e99931b3
commit 3862477ce8
8 changed files with 102 additions and 37 deletions

View File

@@ -14,10 +14,11 @@ Transformation library is independent from Inference Engine target library named
and is located in the `inference-engine/src/transformations` directory.
Transformations root directory contains two folders:
* `ngraph_ops` - Contains legacy opset operations needed for nGraph to CNNNetwork conversion.
> **NOTE**: This operation is prohibited inside new plugins until they are not moved to a separate directory with allowed operations.
* `ngraph_ops` - Contains internal opset operations that are common for plugins.
* `transformations` - Includes all transformations, utils, runtime info attributes, and pass managers.
> **NOTE**: Do not use transformation that belongs to `ngraph::pass::ConvertOpSet1ToLegacy` transformations until they are not moved to a separate directory with allowed transformations.
All internal operations and transformations located inside the [Transformation Library](group__ie__transformation__api.html) can be used inside plugins.
All legacy operations and transformations were moved to a legacy library and are not recommended to be used.
### Transformation Flow Layers
Transformation flow in the transformation library has several layers:
@@ -32,15 +33,15 @@ But if some transformation parts can potentially be reused in other transformati
To decide where to store your transformation code, please follow these rules:
1. If it is a plugin-specific transformation and cannot be reused by other plugins, keep source code inside plugin.
2. If this transformation relates to the OpSetXToOpSetY conversion or it is common optimization, keep sources inside the transformation library.
2. If this transformation relates to opset operation conversion or optimization, keep sources inside the transformation library.
After you decide where to store your transformation code, you can start developing your own nGraph transformation.
## ngraph::Function and graph representation <a name="ngraph_function"></a>
An nGraph function is a simple thing: it stores shared pointers to `ngraph::op::Result` and `ngraph::op::Parameter` operations that are inputs and outputs of the graph.
All other operations hold each other via shared pointers: child operation holds its parent (hard link). If the operation has no consumers and it is not a Result operation
(shared pointer counter is zero), it is destructed and is not accessible anymore. Each operation in `ngraph::Function` has a `std::shared_ptr<ngraph::Node>` type.
nGraph function is a very simple thing: it stores shared pointers to `ngraph::op::Parameter`, `ngraph::op::Result` and `ngraph::op::Sink` operations that are inputs, outputs and sinks of the graph.
Sinks of the graph have no consumers and not included into results vector. All other operations hold each other via shared pointers: child operation holds its parent (hard link). If operation has no consumers and it's not Result or Sink operation
(shared pointer counter is zero) then it will be destructed and won't be accessible anymore. Each operation in `ngraph::Function` has a `std::shared_ptr<ngraph::Node>` type.
For examples of how to build an nGraph function, see the [Build nGraph Function](./build_function.md) page.
@@ -50,7 +51,7 @@ nGraph has three main transformation types:
* `ngraph::pass::FunctionPass` - straightforward way to work with `ngraph::Function` directly
* `ngraph::pass::MatcherPass` - pattern-based transformation approach
* `ngraph::pass::GraphRewrite` - container for matcher passes
* `ngraph::pass::GraphRewrite` - container for matcher passes needed for efficient execution
![transformations_structure]
@@ -87,14 +88,15 @@ To use `ngraph::pass::MatcherPass`, you need to complete these steps:
So let's go through each of these steps.
### Create a pattern
Pattern is a single root `ngraph::Function`. But the only difference is that you do not need to create a function object, you just need to create and connect nGraph or special pattern operations. Then you need to take the last created operation and put it as a root of the pattern. This root node will be used as a root node in pattern matching.
Pattern is a single root `ngraph::Function`. But the only difference is that you do not need to create a function object, you just need to create and connect opset or special pattern operations.
Then you need to take the last created operation and put it as a root of the pattern. This root node will be used as a root node in pattern matching.
> **NOTE**: Any nodes in a pattern that have no consumers and are not registered as root will not be used in pattern matching.
@snippet example_ngraph_utils.cpp pattern:simple_example
The `Parameter` operation in the example above has type and shape specified. These attributes are needed only to create Parameter operation class and will not be used in pattern matching.
For instructions on how to match a pattern where `ShapeOf` takes any operation as an input, follow the [pattern matching](#pattern_matching) section.
For more pattern examples, refer to the [pattern matching](#pattern_matching) section.
### Implement callback
Callback is an action applied to every pattern entrance. In general, callback is the lambda function that takes Matcher object with detected subgraph.
@@ -153,6 +155,8 @@ And then creates map from registered MatcherPasses. That helps to avoid addition
![graph_rewrite_efficient_search]
> **NOTE**: GraphRewrite execution algorithm cannot be set manually and depends only on root nodes registered inside MatcherPasses.
## Pattern Matching <a name="pattern_matching"></a>
Sometimes patterns cannot be expressed via regular nGraph operations or it is too complicated.
@@ -255,7 +259,7 @@ When developing a transformation, you need to follow these transformation rules:
###1. Operation Set (OpSet)
Use the latest version of OpSet in your transformation. An exception is ConvertOpSetXToOpSetY transformations, where you must use operations from OpSetX and OpSetY.
Use the latest version of OpSet in your transformation. An exception is op_conversion transformations, where different opsets can be used.
@snippet example_ngraph_utils.cpp ngraph:include
@@ -399,33 +403,22 @@ NGRAPH_ENABLE_VISUALIZE_TRACING=1 - enables visualization after each transforma
## Disabling/Enabling specific transformations for plugin X <a name="disabling_transformation"></a>
This topic is mostly related to conversion to legacy opset and plugins that are based on CNNNetwork. But this mechanism still can be applied for other cases.
Let's suppose that plugin X enabled the `opset3::StridedSlice` operation support and you want to disable the `ngraph::pass::ConvertStridedSliceToCrop` transformation for plugin X.
To do this, you need to create a callback on plugin side and pass it to transformation. And also you need to update particular transformation to use this callback.
In transformation library, we provide plugins transformations like CommonOptimizations, which contains predefined sequence of transformations.
We also provide a tool that helps to disable or partially disable particular transformations in a transformation pipeline.
For example, if a plugin uses the CommonOptimization transformation and needs to disable the ConvertGELU transformation, then inside the plugin we have to take the PassConfig instance
from pass::Manger and call disable method.
```cpp
// Update callback to be able to use m_transformation_callback if this transformation based on GraphRewrite.
ngraph::graph_rewrite_callback callback = [this](pattern::Matcher &m) {
...
}
@snippet example_ngraph_utils.cpp ngraph:disable_gelu
// Use transformation_callback not to execute transformation if callback returns true for given node
if (m_transformation_callback(node)) {
return false;
}
In some cases, we need to disable transformation for some condition:
// Implement transformation callback and pass it directly to transformation or pass::Manager
const auto transformations_callback = [](const std::shared_ptr<const ::ngraph::Node> &node) -> bool {
return std::dynamic_pointer_cast<const ::ngraph::opset3::StridedSlice>(node) != nullptr;
};
@snippet example_ngraph_utils.cpp ngraph:disable_callback
// Register transformation and pass callback to pass::Manager
ngraph::pass::Manager manager;
manager.register_pass<ngraph::pass::ConvertStridedSliceToCrop>();
// pass::Manager will set callback to all reistered transformations automatically
manager.set_callback(transformations_callback);
manager.run_passes(f);
```
In some cases, pass::Manager pipelines inside transformations may have transformations disabled by default but enabled inside plugins.
@snippet example_ngraph_utils.cpp ngraph:disabled_by_default
PassConfig instance taken from pass::Manager is shared across all registered transformations including nested transformations. So it does not matter where we work with this object (before passes registration or after).
## Transformations testing <a name="transformations_testing"></a>

View File

@@ -4,8 +4,8 @@ The nGraph represents neural networks in uniform format. User can create differe
## nGraph Function and Graph Representation <a name="ngraph_function"></a>
nGraph function is a very simple thing: it stores shared pointers to `ngraph::op::Result` and `ngraph::op::Parameter` operations that are inputs and outputs of the graph.
All other operations hold each other via shared pointers: child operation holds its parent (hard link). If operation has no consumers and it's not Result operation
nGraph function is a very simple thing: it stores shared pointers to `ngraph::op::Parameter`, `ngraph::op::Result` and `ngraph::op::Sink` operations that are inputs, outputs and sinks of the graph.
Sinks of the graph have no consumers and not included into results vector. All other operations hold each other via shared pointers: child operation holds its parent (hard link). If operation has no consumers and it's not Result or Sink operation
(shared pointer counter is zero) then it will be destructed and won't be accessible anymore. Each operation in `ngraph::Function` has a `std::shared_ptr<ngraph::Node>` type.
For details on how to build an nGraph Function, see the [Build nGraph Function](./build_function.md) page.

View File

@@ -54,4 +54,4 @@ if(NGRAPH_ONNX_IMPORT_ENABLE)
target_link_libraries(${TARGET_NAME} PRIVATE onnx_importer)
endif()
target_link_libraries(${TARGET_NAME} PRIVATE inference_engine_plugin_api ngraph)
target_link_libraries(${TARGET_NAME} PRIVATE inference_engine_plugin_api ngraph inference_engine_transformations)

View File

@@ -5,6 +5,11 @@
#include <memory>
#include <ngraph/pattern/op/wrap_type.hpp>
#include <transformations/common_optimizations/common_optimizations.hpp>
#include <transformations/op_conversions/convert_gelu.hpp>
#include <transformations/op_conversions/convert_space_to_depth.hpp>
#include <transformations/op_conversions/convert_depth_to_space.hpp>
#include <transformations/op_conversions/convert_pad_to_group_conv.hpp>
// ! [ngraph:include]
#include <ngraph/ngraph.hpp>
@@ -249,3 +254,61 @@ void visualization_example(std::shared_ptr<ngraph::Function> f) {
manager.run_passes(f);
}
// ! [ngraph:visualize]
void pass_manager_example1(std::shared_ptr<ngraph::Function> f) {
// ! [ngraph:disable_gelu]
ngraph::pass::Manager manager;
manager.register_pass<ngraph::pass::CommonOptimizations>();
auto pass_config = manager.get_pass_config();
pass_config->disable<ngraph::pass::ConvertGELU>();
manager.run_passes(f);
// ! [ngraph:disable_gelu]
}
void pass_manager_example2(std::shared_ptr<ngraph::Function> f) {
ngraph::pass::Manager manager;
std::function<bool(const std::shared_ptr<const Node>)> transformation_callback;
// ! [ngraph:disable_callback]
// Set callback to particular transformation with specific condition
auto pass_config = manager.get_pass_config();
pass_config->set_callback<ngraph::pass::ConvertSpaceToDepth,
ngraph::pass::ConvertDepthToSpace>(
[](const std::shared_ptr<const Node> &node) -> bool {
return node->input_value(0).get_shape().size() <= 5lu &&
node->input_value(0).get_shape().size() == node->get_output_shape(0).size();
});
// Update transformation to call callback
ngraph::matcher_pass_callback callback = [=](pattern::Matcher &m) {
auto node = m.get_match_root();
if (transformation_callback(node)) {
return false;
}
// transformation code
return false;
};
// ! [ngraph:disable_callback]
}
void pass_manager_example3(std::shared_ptr<ngraph::Function> f) {
std::function<bool(const std::shared_ptr<const Node>)> transformation_callback;
// ! [ngraph:disabled_by_default]
// Example of disabled by default transformation
{
ngraph::pass::Manager manager;
manager.register_pass<ngraph::pass::ConvertPadToGroupConvolution, false>();
manager.run_passes(f);
}
// Enable disabled by default transformation inside plugin
{
ngraph::pass::Manager manager;
manager.register_pass<ngraph::pass::CommonOptimizations>();
auto pass_config = manager.get_pass_config();
pass_config->enable<ngraph::pass::ConvertPadToGroupConvolution>();
manager.run_passes(f);
}
// ! [ngraph:disabled_by_default]
}

View File

@@ -8,6 +8,8 @@ using namespace ngraph;
// ! [function_pass:template_transformation_cpp]
// template_function_transformation.cpp
NGRAPH_RTTI_DEFINITION(ngraph::pass::MyFunctionTransformation, "MyFunctionTransformation", 0);
bool pass::MyFunctionTransformation::run_on_function(std::shared_ptr<ngraph::Function> f) {
// Example transformation code
NodeVector nodes;

View File

@@ -18,6 +18,7 @@ class MyFunctionTransformation;
// template_function_transformation.hpp
class ngraph::pass::MyFunctionTransformation: public ngraph::pass::FunctionPass {
public:
NGRAPH_RTTI_DECLARATION;
bool run_on_function(std::shared_ptr<ngraph::Function> f) override;
};
// ! [function_pass:template_transformation_hpp]

View File

@@ -14,6 +14,8 @@ using namespace ngraph;
// ! [graph_rewrite:template_transformation_cpp]
// template_pattern_transformation.cpp
NGRAPH_RTTI_DEFINITION(ngraph::pass::DecomposeDivideMatcher, "DecomposeDivideMatcher", 0);
ngraph::pass::DecomposeDivideMatcher::DecomposeDivideMatcher() {
// Pattern example
auto input0 = pattern::any_input();
@@ -54,6 +56,8 @@ ngraph::pass::DecomposeDivideMatcher::DecomposeDivideMatcher() {
// ! [graph_rewrite:template_transformation_cpp]
// ! [matcher_pass:relu_fusion]
NGRAPH_RTTI_DEFINITION(ngraph::pass::ReluReluFusionMatcher, "ReluReluFusionMatcher", 0);
ngraph::pass::ReluReluFusionMatcher::ReluReluFusionMatcher() {
auto m_relu1 = ngraph::pattern::wrap_type<ngraph::opset3::Relu>(pattern::consumers_count(1));
auto m_relu2 = ngraph::pattern::wrap_type<ngraph::opset3::Relu>({m_relu1});

View File

@@ -23,11 +23,13 @@ class ReluReluFusionMatcher;
*/
class ngraph::pass::DecomposeDivideMatcher: public ngraph::pass::MatcherPass {
public:
NGRAPH_RTTI_DECLARATION;
DecomposeDivideMatcher();
};
// ! [graph_rewrite:template_transformation_hpp]
class ngraph::pass::ReluReluFusionMatcher: public ngraph::pass::MatcherPass {
public:
NGRAPH_RTTI_DECLARATION;
ReluReluFusionMatcher();
};