From f754a86d6a2ae6c6630ce485984450e05ea1032b Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 21 Mar 2018 03:44:03 -0700 Subject: [PATCH 1/6] Init onnx convertor design doc --- doc/fluid/design/onnx/onnx_convertor.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 doc/fluid/design/onnx/onnx_convertor.md diff --git a/doc/fluid/design/onnx/onnx_convertor.md b/doc/fluid/design/onnx/onnx_convertor.md new file mode 100644 index 0000000000..f3529d7966 --- /dev/null +++ b/doc/fluid/design/onnx/onnx_convertor.md @@ -0,0 +1,15 @@ +### Backgroud + +(@kuke) + +### How it works +(@kuke) + +### Project structure +(@pkuyym) + +### Usage +(@pkuyym + +### Supported models +(@kuke) From be9977d7a33bcb1ec40524ea3f673adff22c76a4 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 21 Mar 2018 21:36:21 +0800 Subject: [PATCH 2/6] Add content for Poject Structure and Usage section. --- .../design/onnx/images/project_structure.png | Bin 0 -> 29725 bytes doc/fluid/design/onnx/onnx_convertor.md | 26 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 doc/fluid/design/onnx/images/project_structure.png diff --git a/doc/fluid/design/onnx/images/project_structure.png b/doc/fluid/design/onnx/images/project_structure.png new file mode 100644 index 0000000000000000000000000000000000000000..ab1c2ff23cfff586516876684348bb15bd2084fc GIT binary patch literal 29725 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 literal 0 HcmV?d00001 diff --git a/doc/fluid/design/onnx/onnx_convertor.md b/doc/fluid/design/onnx/onnx_convertor.md index f3529d7966..c030366e78 100644 --- a/doc/fluid/design/onnx/onnx_convertor.md +++ b/doc/fluid/design/onnx/onnx_convertor.md @@ -6,10 +6,32 @@ (@kuke) ### Project structure -(@pkuyym) + +

+ +

+ +The project contains four important modules: + +* **fluid**: Contain wrappers for fluid related APIs. Fluid has provided some low-level APIs to parse or generate the inference model. However, directly using these low-level APIs makes the code tediously long. This module wraps low-level APIs to provide simplied interfaces. + +* **onnx**: ONNX uses proto file to save computation flow and model weights. This module is responsible for parsing and generating ONNX binary model. + +* **onnx_fluid**: Concepts in fluid like program, block etc. haven't direct corresponding concepts in ONNX. Even that both contains operator concept, for many operators adaption is also necessary. This module is the most important module responsible for acutal converting. Adaption for different level concepts should be provided like fluid program/block to ONNX graph, fluid operators to ONNX operators etc. + +* **convert.py**: Simple top user interface. ### Usage -(@pkuyym +The converter is very easy to use. Bi-directional conversion between fluid inference model and ONNX binary model is supported. Model validation is also provided to verify the correctness of converted model. + +* fluid inference model to ONNX binary model + +`python convert.py --direct fluid2ONNX --whether_do_validation True` + +* ONNX binary model to fluid inference model + +`python convert.py --direct ONNX2fluid --whether_do_validation True` + ### Supported models (@kuke) From f565e5da9d3703cad8cb4e18d7c7269f422ab298 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Tue, 27 Mar 2018 22:22:44 -0700 Subject: [PATCH 3/6] Add background and other sections --- doc/fluid/design/onnx/onnx_convertor.md | 50 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/doc/fluid/design/onnx/onnx_convertor.md b/doc/fluid/design/onnx/onnx_convertor.md index c030366e78..2734ec504e 100644 --- a/doc/fluid/design/onnx/onnx_convertor.md +++ b/doc/fluid/design/onnx/onnx_convertor.md @@ -1,9 +1,26 @@ ### Backgroud -(@kuke) +[ONNX (Open Neural Network Exchange)](https://github.com/onnx/onnx) bridges different deep learning frameworks by providing an open source format for models. The models trained in other frameworks can be converted into the ONNX format to execute inference by utilizing the built-in operators in ONNX. With the converse conversion, different frameworks can share any models supported by ONNX in pinciple. Now most mainstream frameworks have joined the ONNX community, e.g. Caffe2, TensorFlow, and MXNet etc. And there is a trendency that more and more vendors begin to support ONNX or even choose ONNX as the only machine learning engine in their devices. + +Therefore, it is necessary to enable the conversion between PaddlePaddle and ONNX. This design doc aims to implement the convertor, mainly for the ONNX conversion of models in Fluid and possibly including some important models in V2 format in the future. A complete convertor should be bidirectional, but considering the importance, the conversion from Fluid to ONNX will be implemented preferentially. + ### How it works -(@kuke) + +As the first step, Fluid must cover [all the listed operators](https://github.com/onnx/onnx/blob/master/docs/Operators.md) in ONNX. The complement is being carried out and only a few minor operators need to be newly added or enhanced, which would not postpone the convertor and the test of common models. + +About the convertor, several things need to be considered: + +- OP-level conversion + - How to map the inputs, attributes, weights, and outputs each operator. +- Data type mapping +- Network representation adapation + - The model in Fluid is represented by nested `Block`, how to parse and reconstruct it in ONNX graph format, and vice versa; + +- Model validation + - To assure the correctness of conversion. A simple way may be to generate some dummy data as the input and compare the inference results. +- Long term support + - As ONNX keeps evolving, a mechanism to make sure long term support is needed. ### Project structure @@ -11,27 +28,32 @@

-The project contains four important modules: +The project contains four important parts: -* **fluid**: Contain wrappers for fluid related APIs. Fluid has provided some low-level APIs to parse or generate the inference model. However, directly using these low-level APIs makes the code tediously long. This module wraps low-level APIs to provide simplied interfaces. +* **fluid**: The directory that contains wrappers for fluid related APIs. Fluid has provided some low-level APIs to parse or generate the inference model. However, directly using these low-level APIs makes the code tediously long. This module wraps low-level APIs to provide simplied interfaces. -* **onnx**: ONNX uses proto file to save computation flow and model weights. This module is responsible for parsing and generating ONNX binary model. +* **onnx**: ONNX uses protobuf to save computation flow and model weights. This directory consists of scripts responsible for parsing and generating an ONNX binary model. -* **onnx_fluid**: Concepts in fluid like program, block etc. haven't direct corresponding concepts in ONNX. Even that both contains operator concept, for many operators adaption is also necessary. This module is the most important module responsible for acutal converting. Adaption for different level concepts should be provided like fluid program/block to ONNX graph, fluid operators to ONNX operators etc. +* **onnx_fluid**: Concepts in fluid like ```program```, ```block``` etc. don't have direct corresponding concepts in ONNX. Even though both contain the operator concept, the adaption is also necessary for many operators. This directory consists of the most important modules responsible for acutal converting. Adaption for different level concepts should be provided like fluid ```program/block``` to ONNX graph, fluid operators to ONNX operators etc. -* **convert.py**: Simple top user interface. +* **convert.py**: The interface exposed to users. ### Usage -The converter is very easy to use. Bi-directional conversion between fluid inference model and ONNX binary model is supported. Model validation is also provided to verify the correctness of converted model. +The converter is designed to very easy-to-use. Bidirectional conversion between Fluid inference model and ONNX binary model is supported. Model validation is also provided to verify the correctness of converted model. -* fluid inference model to ONNX binary model +* Fluid inference model to ONNX binary model -`python convert.py --direct fluid2ONNX --whether_do_validation True` +``` +python convert.py --input --output --to_validate True +``` -* ONNX binary model to fluid inference model - -`python convert.py --direct ONNX2fluid --whether_do_validation True` +The conversion and model validation will be completed consecutively, finally output a readable model structure description. And for the converse conversion, users only need to exchange the input and output. ### Supported models -(@kuke) + +Potential risks may come from the conversion of sequence-related models, including the LodTensor, ```if/else``` and ```while``` operator. +So a good choice is to focus on some important feedforward models first, then implement some simple recurrent models. + +- Feedforward models: common models selected in PaddleBook, e.g. VGG, ResNet and some other models proposed by application teams. +- Recurrent models: language model, stacked LSTMs etc. From 5550bd106c46658e3d4c8751e5b15eb49d20bcb4 Mon Sep 17 00:00:00 2001 From: Varun Arora Date: Mon, 2 Apr 2018 14:10:14 -0700 Subject: [PATCH 4/6] Adding some challenges / mitigations to ONNX conversion --- doc/fluid/design/onnx/onnx_convertor.md | 87 ++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/doc/fluid/design/onnx/onnx_convertor.md b/doc/fluid/design/onnx/onnx_convertor.md index 2734ec504e..b46b7786a1 100644 --- a/doc/fluid/design/onnx/onnx_convertor.md +++ b/doc/fluid/design/onnx/onnx_convertor.md @@ -1,20 +1,22 @@ -### Backgroud +### Background -[ONNX (Open Neural Network Exchange)](https://github.com/onnx/onnx) bridges different deep learning frameworks by providing an open source format for models. The models trained in other frameworks can be converted into the ONNX format to execute inference by utilizing the built-in operators in ONNX. With the converse conversion, different frameworks can share any models supported by ONNX in pinciple. Now most mainstream frameworks have joined the ONNX community, e.g. Caffe2, TensorFlow, and MXNet etc. And there is a trendency that more and more vendors begin to support ONNX or even choose ONNX as the only machine learning engine in their devices. +[ONNX (Open Neural Network Exchange)](https://github.com/onnx/onnx) bridges different deep learning frameworks by providing an open source graph format for models. The models trained in other frameworks can be converted into the ONNX format to execute inference by utilizing the built-in operators in ONNX. With the inverse conversion, different frameworks can share any models supported by ONNX in principle. Now most mainstream frameworks have joined the ONNX community, e.g. Caffe2, TensorFlow, and MXNet etc. And there is a tendency that more and more vendors begin to support ONNX or even choose ONNX as the only machine learning engine in their devices. -Therefore, it is necessary to enable the conversion between PaddlePaddle and ONNX. This design doc aims to implement the convertor, mainly for the ONNX conversion of models in Fluid and possibly including some important models in V2 format in the future. A complete convertor should be bidirectional, but considering the importance, the conversion from Fluid to ONNX will be implemented preferentially. +Therefore, it is necessary to enable the conversion between PaddlePaddle and ONNX. This design doc aims to implement the convertor, mainly for the ONNX conversion of models in Fluid and possibly including some important models in V2 format in the future. A complete convertor should be bidirectional, but considering the importance, the conversion from Fluid to ONNX will be implemented preferentially. + +One thing that makes it doable in Fluid's case is the use of a static IR - the `ProgramDesc` - as opposed to a dynamic graph, as created in the cases of frameworks like PyTorch. ### How it works As the first step, Fluid must cover [all the listed operators](https://github.com/onnx/onnx/blob/master/docs/Operators.md) in ONNX. The complement is being carried out and only a few minor operators need to be newly added or enhanced, which would not postpone the convertor and the test of common models. -About the convertor, several things need to be considered: +About the convertor, several things need to be considered: -- OP-level conversion +- OP-level conversion - How to map the inputs, attributes, weights, and outputs each operator. - Data type mapping -- Network representation adapation +- Network representation adapation - The model in Fluid is represented by nested `Block`, how to parse and reconstruct it in ONNX graph format, and vice versa; - Model validation @@ -28,7 +30,7 @@ About the convertor, several things need to be considered:

-The project contains four important parts: +The project contains four important parts: * **fluid**: The directory that contains wrappers for fluid related APIs. Fluid has provided some low-level APIs to parse or generate the inference model. However, directly using these low-level APIs makes the code tediously long. This module wraps low-level APIs to provide simplied interfaces. @@ -36,7 +38,7 @@ The project contains four important parts: * **onnx_fluid**: Concepts in fluid like ```program```, ```block``` etc. don't have direct corresponding concepts in ONNX. Even though both contain the operator concept, the adaption is also necessary for many operators. This directory consists of the most important modules responsible for acutal converting. Adaption for different level concepts should be provided like fluid ```program/block``` to ONNX graph, fluid operators to ONNX operators etc. -* **convert.py**: The interface exposed to users. +* **convert.py**: The interface exposed to users. ### Usage The converter is designed to very easy-to-use. Bidirectional conversion between Fluid inference model and ONNX binary model is supported. Model validation is also provided to verify the correctness of converted model. @@ -47,13 +49,78 @@ The converter is designed to very easy-to-use. Bidirectional conversion between python convert.py --input --output --to_validate True ``` -The conversion and model validation will be completed consecutively, finally output a readable model structure description. And for the converse conversion, users only need to exchange the input and output. +The conversion and model validation will be completed consecutively, finally output a readable model structure description. And for the converse conversion, users only need to exchange the input and output. + + +### Challenges and mitigation + +#### Cycles + +Cycles are unsupported in ONNX. In Paddle, the `while` op is the most prominent example of a cycle. + +*Resolution*: We won't support models with `while`s which can't be substituted until ONNX adds support for such ops. + +#### Sequences + +Sequence processing operators like `sequence_expand`, `sequence_reshape`, `sequence_concat`, and `sequence_pool` are not supported by ONNX as well, because they do not support non-padded datatypes like LoDTensors. + +*Resolution*: Since the runtimes using our ONNX exported graphs won't be using LoDTensors in the first place, such sequence operators should be mapped to ONNX ops that will do the necessary transposing ops with the knowledge of the padding and shape of the Tensors. + +#### Ops that can't easily be mapped + +There are ops that just aren't possible to map today: + +**Control flow operators** + +Paddle supports control flow ops like `If/Else` and `Switch` (if we ignore the CSP operations like `select` for now). ONNX has `If` support in the experimental phase. + +*Resolution*: Map Paddle's `If/Else` to ONNX's `If`, but ignore other control flow operators until ONNX brings support for them. + + +**Non-existent in Fluid** + +There are several ONNX operators that are not available in Fluid today, e.g. `InstanceNormalization`, `RandomUniform`, `Unsqueeze`, etc. + +*Resolution*: For the initial phase, we can choose to not support ops that our models don't care for and are subsequently not available in Fluid. However, for ops that we think might be necessary for Fluid users also, we must implement them on our side and support the ONNX conversion to them. This list is TBD. + + +**Concurrency** + +ONNX does not have any considerations for concurrency right now. + +*Resolution*: There are two ways to approach this: + +a. We choose to not support concurrent models. +b. We only support `go_op`s (basically threads) shallowly. This could mean that we enqueue `go_op` ops prior to gradient calculations OR even prior to the entire graph, and that's it - since `go_op`s do not have support for backprop anyways. One of the core target use cases of `go_op`: batch reading - can be handled through this approach. + + +**Overloaded in Fluid** + +There are ops in ONNX whose job can't be accomplished by a single corresponding Paddle operator (e.g. ), but a collection of operators. + +*Resolution*: Chain multiple Paddle operators. + + +#### Lack of LoDTensors + +As stated above, ONNX only supports simple Tensor data. + +(...) + +TBD + + +#### Reconstruction from deprecated ONNX ops + +For higher-level Fluid ops, such as a few offered by the `nn` layer that do not have direct corresponding mappings but can be converted to ONNX by chaining a series of ops without cycles, it would be useful to map them back to the higher-level Fluid ops once converted back from the deprecated ONNX graphs. + +*Resolution*: Graphs that have the deprecation from Paddle -> ONNX. When converting back from ONNX, if we encounter the identical graphs by doing a forward search, we can replace the subgraphs with the matching ONNX op. ### Supported models Potential risks may come from the conversion of sequence-related models, including the LodTensor, ```if/else``` and ```while``` operator. So a good choice is to focus on some important feedforward models first, then implement some simple recurrent models. - + - Feedforward models: common models selected in PaddleBook, e.g. VGG, ResNet and some other models proposed by application teams. - Recurrent models: language model, stacked LSTMs etc. From 56b8784c24acfb648c77610f5a1ea4f7218ee2ef Mon Sep 17 00:00:00 2001 From: Varun Arora Date: Fri, 20 Apr 2018 18:59:22 -0700 Subject: [PATCH 5/6] Update design doc based on early implementation --- doc/fluid/design/onnx/onnx_convertor.md | 71 ++++++++++++------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/doc/fluid/design/onnx/onnx_convertor.md b/doc/fluid/design/onnx/onnx_convertor.md index b46b7786a1..275d79753d 100644 --- a/doc/fluid/design/onnx/onnx_convertor.md +++ b/doc/fluid/design/onnx/onnx_convertor.md @@ -1,30 +1,31 @@ -### Background +# Background -[ONNX (Open Neural Network Exchange)](https://github.com/onnx/onnx) bridges different deep learning frameworks by providing an open source graph format for models. The models trained in other frameworks can be converted into the ONNX format to execute inference by utilizing the built-in operators in ONNX. With the inverse conversion, different frameworks can share any models supported by ONNX in principle. Now most mainstream frameworks have joined the ONNX community, e.g. Caffe2, TensorFlow, and MXNet etc. And there is a tendency that more and more vendors begin to support ONNX or even choose ONNX as the only machine learning engine in their devices. +[ONNX (Open Neural Network Exchange)](https://github.com/onnx/onnx) bridges different deep learning frameworks by providing an open source graph format for models. The models trained in other frameworks can be converted into the ONNX format to execute inference by utilizing the built-in operators in ONNX - this is called a **frontend**. With the inverse conversion (called a **backend**), different frameworks can share any models supported by ONNX in principle. Now most mainstream frameworks have joined the ONNX community, e.g. Caffe2, PyTorch, and MXNet etc. And there is a momentum driving more and more vendors to begin supporting ONNX or even choose ONNX as the only machine learning runtime in their devices. -Therefore, it is necessary to enable the conversion between PaddlePaddle and ONNX. This design doc aims to implement the convertor, mainly for the ONNX conversion of models in Fluid and possibly including some important models in V2 format in the future. A complete convertor should be bidirectional, but considering the importance, the conversion from Fluid to ONNX will be implemented preferentially. +Therefore, it is necessary to enable the conversion between PaddlePaddle and ONNX. This design doc is aimed at implementing a convertor, mainly for converting between **Fluid** models and ONNX (it is very likely that we may support older v2 models in the future). A complete convertor should be bidirectional - with a frontend AND a backend, but considering the importance, the we will start with the frontend i.e. Fluid models to ONNX models. One thing that makes it doable in Fluid's case is the use of a static IR - the `ProgramDesc` - as opposed to a dynamic graph, as created in the cases of frameworks like PyTorch. -### How it works +# How it works -As the first step, Fluid must cover [all the listed operators](https://github.com/onnx/onnx/blob/master/docs/Operators.md) in ONNX. The complement is being carried out and only a few minor operators need to be newly added or enhanced, which would not postpone the convertor and the test of common models. +ONNX has a [working list of operators](https://github.com/onnx/onnx/blob/master/docs/Operators.md) which is versioned. -About the convertor, several things need to be considered: +When prioritizing implementation of a frontend over a backend, choice of coverage of Fluid -> ONNX operators comes down to choices of models to be supported (see section `Supported models`). Eventually, this will allow us to reach a really-wide coverage of all operators. -- OP-level conversion - - How to map the inputs, attributes, weights, and outputs each operator. -- Data type mapping -- Network representation adapation - - The model in Fluid is represented by nested `Block`, how to parse and reconstruct it in ONNX graph format, and vice versa; +Here are a few major considerations when it comes to converting models: -- Model validation - - To assure the correctness of conversion. A simple way may be to generate some dummy data as the input and compare the inference results. -- Long term support - - As ONNX keeps evolving, a mechanism to make sure long term support is needed. +- **Op-level conversion**: How to map the inputs, attributes, and outputs of each Paddle operator to those of the ONNX operator. In several cases, these require transformations. For each direction (frontend vs. backend), a different conversion mapping is needed. +- **Parameters (weights) initialization**: Setting initial parameters on different nodes. +- **Tensor data type mapping** (Note: Some ONNX data types are not supported in Fluid) +- **Network representation adaption**: Fluid `ProgramDesc` include nested blocks. Since ONNX is free of nesting, the `ProgramDesc` ops need to be traversed to only include ops from the global scope in the root block. The variables used as inputs and outputs should also be in this scope. +- **Model validation**: There are two kinds of validations that are necessary: + 1. We need to ensure that the inference outputs of the ops in run inside a model are the same as those when running the ONNX converted ops through an alternative ONNX backend. + 2. Checking to see if the generated nodes on the graph are validated by the internal ONNX checkers. +- **Versioning**: ONNX versions its op listing over versions. In fact, it has versioning on 3 different levels: ops, graphs, and ONNX models. This requires that we are conscious about versioning the convertor and updating tests and op convertor logic for each release. It also implies that we release pre-trained ONNX models upon each version release. -### Project structure + +# Project structure

@@ -32,41 +33,42 @@ About the convertor, several things need to be considered: The project contains four important parts: -* **fluid**: The directory that contains wrappers for fluid related APIs. Fluid has provided some low-level APIs to parse or generate the inference model. However, directly using these low-level APIs makes the code tediously long. This module wraps low-level APIs to provide simplied interfaces. +* **fluid**: The directory that contains wrappers for fluid related APIs. Fluid has provided some low-level APIs to parse or generate the inference model. However, directly using these low-level APIs makes the code tediously long. This module wraps low-level APIs to provide simplified interfaces. + +* **onnx**: This is a Python package provided by ONNX containing helpers for creating nodes, graphs, and eventually binary protobuf models with initializer parameters. -* **onnx**: ONNX uses protobuf to save computation flow and model weights. This directory consists of scripts responsible for parsing and generating an ONNX binary model. +* **onnx_fluid**: Contains two-way mapping (Fluid -> ONNX ops and ONNX -> Fluid ops). Called from `convert.py`, the program uses this mapping along with modifier functions to construct ONNX nodes with the help of ONNX's `make_node` helper. It also contains mapping between datatypes and tensor deprecation / amplification logic. -* **onnx_fluid**: Concepts in fluid like ```program```, ```block``` etc. don't have direct corresponding concepts in ONNX. Even though both contain the operator concept, the adaption is also necessary for many operators. This directory consists of the most important modules responsible for acutal converting. Adaption for different level concepts should be provided like fluid ```program/block``` to ONNX graph, fluid operators to ONNX operators etc. +* **convert.py**: The interface exposed to users. This will traverse the global program blocks/variables and construct the write-able model. -* **convert.py**: The interface exposed to users. -### Usage -The converter is designed to very easy-to-use. Bidirectional conversion between Fluid inference model and ONNX binary model is supported. Model validation is also provided to verify the correctness of converted model. +# Usage +The converter should be designed to very easy-to-use. Bidirectional conversion between a Fluid inference model and an ONNX binary model will be supported. Model validation will also provided to verify the correctness of converted model. * Fluid inference model to ONNX binary model ``` -python convert.py --input --output --to_validate True +python convert.py --fluid_model --onnx_model validate True ``` The conversion and model validation will be completed consecutively, finally output a readable model structure description. And for the converse conversion, users only need to exchange the input and output. -### Challenges and mitigation +# Challenges and mitigation -#### Cycles +## Cycles Cycles are unsupported in ONNX. In Paddle, the `while` op is the most prominent example of a cycle. *Resolution*: We won't support models with `while`s which can't be substituted until ONNX adds support for such ops. -#### Sequences +## Sequences Sequence processing operators like `sequence_expand`, `sequence_reshape`, `sequence_concat`, and `sequence_pool` are not supported by ONNX as well, because they do not support non-padded datatypes like LoDTensors. *Resolution*: Since the runtimes using our ONNX exported graphs won't be using LoDTensors in the first place, such sequence operators should be mapped to ONNX ops that will do the necessary transposing ops with the knowledge of the padding and shape of the Tensors. -#### Ops that can't easily be mapped +## Ops that can't easily be mapped There are ops that just aren't possible to map today: @@ -101,26 +103,23 @@ There are ops in ONNX whose job can't be accomplished by a single corresponding *Resolution*: Chain multiple Paddle operators. -#### Lack of LoDTensors - -As stated above, ONNX only supports simple Tensor data. +## Lack of LoDTensors -(...) +As stated above, ONNX only supports simple Tensor values. -TBD +*Resolution*: Deprecate to plain old numpy-able tensors. -#### Reconstruction from deprecated ONNX ops +## Reconstruction from deprecated ONNX ops For higher-level Fluid ops, such as a few offered by the `nn` layer that do not have direct corresponding mappings but can be converted to ONNX by chaining a series of ops without cycles, it would be useful to map them back to the higher-level Fluid ops once converted back from the deprecated ONNX graphs. *Resolution*: Graphs that have the deprecation from Paddle -> ONNX. When converting back from ONNX, if we encounter the identical graphs by doing a forward search, we can replace the subgraphs with the matching ONNX op. -### Supported models +# Supported models -Potential risks may come from the conversion of sequence-related models, including the LodTensor, ```if/else``` and ```while``` operator. -So a good choice is to focus on some important feedforward models first, then implement some simple recurrent models. +As mentioned above, potential risks may come from the conversion of sequence-related models, including the LodTensor, ```if/else``` and ```while``` operator. So a good choice is to focus on some important feedforward models first, then implement some simple recurrent models. - Feedforward models: common models selected in PaddleBook, e.g. VGG, ResNet and some other models proposed by application teams. - Recurrent models: language model, stacked LSTMs etc. From 47668644812f5826af1324b8961512420183e163 Mon Sep 17 00:00:00 2001 From: Varun Arora Date: Mon, 23 Apr 2018 13:43:43 -0700 Subject: [PATCH 6/6] Minor fixes based on @kuke's feedback --- doc/fluid/design/onnx/onnx_convertor.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/fluid/design/onnx/onnx_convertor.md b/doc/fluid/design/onnx/onnx_convertor.md index 275d79753d..bc1665d7c3 100644 --- a/doc/fluid/design/onnx/onnx_convertor.md +++ b/doc/fluid/design/onnx/onnx_convertor.md @@ -4,8 +4,6 @@ Therefore, it is necessary to enable the conversion between PaddlePaddle and ONNX. This design doc is aimed at implementing a convertor, mainly for converting between **Fluid** models and ONNX (it is very likely that we may support older v2 models in the future). A complete convertor should be bidirectional - with a frontend AND a backend, but considering the importance, the we will start with the frontend i.e. Fluid models to ONNX models. -One thing that makes it doable in Fluid's case is the use of a static IR - the `ProgramDesc` - as opposed to a dynamic graph, as created in the cases of frameworks like PyTorch. - # How it works @@ -24,6 +22,8 @@ Here are a few major considerations when it comes to converting models: 2. Checking to see if the generated nodes on the graph are validated by the internal ONNX checkers. - **Versioning**: ONNX versions its op listing over versions. In fact, it has versioning on 3 different levels: ops, graphs, and ONNX models. This requires that we are conscious about versioning the convertor and updating tests and op convertor logic for each release. It also implies that we release pre-trained ONNX models upon each version release. +One thing that makes this conversion more feasible in Fluid's case is the use of a static IR - the `ProgramDesc` - as opposed to a dynamic graph, as created in the cases of frameworks like PyTorch. + # Project structure @@ -45,11 +45,17 @@ The project contains four important parts: # Usage The converter should be designed to very easy-to-use. Bidirectional conversion between a Fluid inference model and an ONNX binary model will be supported. Model validation will also provided to verify the correctness of converted model. -* Fluid inference model to ONNX binary model +* Convert Fluid inference model to ONNX binary model + + ``` + python convert.py --fluid_model --onnx_model validate True + ``` + +* Validate the converted model -``` -python convert.py --fluid_model --onnx_model validate True -``` + ``` + python validate.py --fluid_model --onnx_model + ``` The conversion and model validation will be completed consecutively, finally output a readable model structure description. And for the converse conversion, users only need to exchange the input and output.