From: @moran3 Reviewed-by: @ouwenchang,@yelihua Signed-off-by: @yelihuatags/v1.2.0
| @@ -15,7 +15,6 @@ | |||||
| - [Example](#example) | - [Example](#example) | ||||
| - [AST-Based Conversion](#ast-based-conversion) | - [AST-Based Conversion](#ast-based-conversion) | ||||
| - [Graph-Based Conversion](#graph-based-conversion) | - [Graph-Based Conversion](#graph-based-conversion) | ||||
| - [PyTorch Model Scripts Conversion](#pytorch-model-scripts-conversion) | |||||
| - [TensorFlow Model Scripts Conversion](#tensorflow-model-scripts-conversion) | - [TensorFlow Model Scripts Conversion](#tensorflow-model-scripts-conversion) | ||||
| - [ONNX Model File Conversion](#onnx-model-file-conversion) | - [ONNX Model File Conversion](#onnx-model-file-conversion) | ||||
| - [Caution](#caution) | - [Caution](#caution) | ||||
| @@ -48,7 +47,6 @@ usage: mindconverter [-h] [--version] [--in_file IN_FILE] | |||||
| [--input_nodes INPUT_NODES [INPUT_NODES ...]] | [--input_nodes INPUT_NODES [INPUT_NODES ...]] | ||||
| [--output_nodes OUTPUT_NODES [OUTPUT_NODES ...]] | [--output_nodes OUTPUT_NODES [OUTPUT_NODES ...]] | ||||
| [--output OUTPUT] [--report REPORT] | [--output OUTPUT] [--report REPORT] | ||||
| [--project_path PROJECT_PATH] | |||||
| optional arguments: | optional arguments: | ||||
| -h, --help show this help message and exit | -h, --help show this help message and exit | ||||
| @@ -56,10 +54,10 @@ optional arguments: | |||||
| --in_file IN_FILE Specify path for script file to use AST schema to do | --in_file IN_FILE Specify path for script file to use AST schema to do | ||||
| script conversation. | script conversation. | ||||
| --model_file MODEL_FILE | --model_file MODEL_FILE | ||||
| PyTorch(.pth), Tensorflow(.pb) or ONNX(.onnx) model | |||||
| file path is expected to do script generation based on | |||||
| graph schema. When `--in_file` and `--model_file` are | |||||
| both provided, use AST schema as default. | |||||
| Tensorflow(.pb) or ONNX(.onnx) model file path is | |||||
| expected to do script generation based on graph | |||||
| schema. When `--in_file` and `--model_file` are both | |||||
| provided, use AST schema as default. | |||||
| --shape SHAPE [SHAPE ...] | --shape SHAPE [SHAPE ...] | ||||
| Optional, expected input tensor shape of | Optional, expected input tensor shape of | ||||
| `--model_file`. It is required when use graph based | `--model_file`. It is required when use graph based | ||||
| @@ -67,46 +65,31 @@ optional arguments: | |||||
| with `--input_nodes`. Usage: --shape 1,512 1,512 | with `--input_nodes`. Usage: --shape 1,512 1,512 | ||||
| --input_nodes INPUT_NODES [INPUT_NODES ...] | --input_nodes INPUT_NODES [INPUT_NODES ...] | ||||
| Optional, input node(s) name of `--model_file`. It is | Optional, input node(s) name of `--model_file`. It is | ||||
| required when use TensorFlow and ONNX model. Both | |||||
| order and number should be consistent with `--shape`. | |||||
| Usage: --input_nodes input_1:0 input_2:0 | |||||
| required when use graph based schema. Both order and | |||||
| number should be consistent with `--shape`. Usage: | |||||
| --input_nodes input_1:0 input_2:0 | |||||
| --output_nodes OUTPUT_NODES [OUTPUT_NODES ...] | --output_nodes OUTPUT_NODES [OUTPUT_NODES ...] | ||||
| Optional, output node(s) name of `--model_file`. It is | Optional, output node(s) name of `--model_file`. It is | ||||
| required when use TensorFlow and ONNX model. Usage: | |||||
| required when use graph based schema. Usage: | |||||
| --output_nodes output_1:0 output_2:0 | --output_nodes output_1:0 output_2:0 | ||||
| --output OUTPUT Optional, specify path for converted script file | --output OUTPUT Optional, specify path for converted script file | ||||
| directory. Default output directory is `output` folder | directory. Default output directory is `output` folder | ||||
| in the current working directory. | in the current working directory. | ||||
| --report REPORT Optional, specify report directory. Default is | --report REPORT Optional, specify report directory. Default is | ||||
| converted script directory. | converted script directory. | ||||
| --project_path PROJECT_PATH | |||||
| Optional, PyTorch scripts project path. If PyTorch | |||||
| project is not in PYTHONPATH, please assign | |||||
| `--project_path` when use graph based schema. Usage: | |||||
| --project_path ~/script_file/ | |||||
| ``` | ``` | ||||
| ### PyTorch Model Scripts Migration | ### PyTorch Model Scripts Migration | ||||
| #### MindConverter Provides Two Modes for PyTorch: | |||||
| #### MindConverter Provides AST for PyTorch: | |||||
| 1. **Abstract Syntax Tree (AST) based conversion**: Use the argument `--in_file` will enable the AST mode. | |||||
| 2. **Computational Graph based conversion**: Use `--model_file` and `--shape` arguments will enable the Graph mode. | |||||
| **Abstract Syntax Tree (AST) based conversion**: Use the argument `--in_file` will enable the AST mode. | |||||
| > The AST mode will be enabled, if both `--in_file` and `--model_file` are specified. | > The AST mode will be enabled, if both `--in_file` and `--model_file` are specified. | ||||
| For the Graph mode, `--shape` is mandatory. | |||||
| `--output` and `--report` is optional. MindConverter creates an `output` folder under the current working directory, and outputs generated scripts to it. | |||||
| For the AST mode, `--shape` is ignored. | |||||
| `--output` and `--report` is optional. MindConverter creates an `output` folder under the current working directory, and outputs generated scripts, converted checkpoint file, weight map file and conversion reports to it. | |||||
| Please note that your original PyTorch project is included in the module search path (PYTHONPATH). Use the python interpreter and test your module can be successfully loaded by `import` command. Use `--project_path` instead if your project is not in the PYTHONPATH to ensure MindConverter can load it. | |||||
| > Assume the project is located at `/home/user/project/model_training`, users can use this command to add the project to `PYTHONPATH` : `export PYTHONPATH=/home/user/project/model_training:$PYTHONPATH` | |||||
| > MindConverter needs the original PyTorch scripts because of the reverse serialization. | |||||
| PyTorch(.pth) conversion only supports one input and one output model, it is recommended to convert multi-input or multi-output PyTorch script using ONNX conversion after converting PyTorch script to ONNX file. | |||||
| > While computational graph based conversion is required, it is recommended to use ONNX file after converting PyTorch model scripts to ONNX file, and the tutorial is [PyTorch instruction](https://pytorch.org/docs/stable/onnx.html). | |||||
| ### TensorFlow Model Scripts Migration | ### TensorFlow Model Scripts Migration | ||||
| @@ -114,12 +97,16 @@ PyTorch(.pth) conversion only supports one input and one output model, it is rec | |||||
| > AST mode is not supported for TensorFlow, only computational graph based mode is available. | > AST mode is not supported for TensorFlow, only computational graph based mode is available. | ||||
| If both `output` and `report` are not set, mindConverter creates an `output` folder under the current working directory, and outputs generated scripts, converted checkpoint file, weight map file and conversion reports to it. | |||||
| ### ONNX Model File Migration | ### ONNX Model File Migration | ||||
| **MindConverter provides computational graph based conversion for ONNX**: Transformation will be done given `--model_file`, `--shape`, `--input_nodes` and `--output_nodes`. | **MindConverter provides computational graph based conversion for ONNX**: Transformation will be done given `--model_file`, `--shape`, `--input_nodes` and `--output_nodes`. | ||||
| > AST mode is not supported for ONNX, only computational graph based mode is available. | > AST mode is not supported for ONNX, only computational graph based mode is available. | ||||
| If both `output` and `report` are not set, mindConverter creates an `output` folder under the current working directory, and outputs generated scripts, converted checkpoint file, weight map file and conversion reports to it. | |||||
| ## Scenario | ## Scenario | ||||
| MindConverter provides two modes for different migration demands. | MindConverter provides two modes for different migration demands. | ||||
| @@ -133,7 +120,7 @@ For the second demand, the Graph mode is recommended. As the computational graph | |||||
| Some typical image classification networks have been tested for the Graph mode. Note that: | Some typical image classification networks have been tested for the Graph mode. Note that: | ||||
| > 1. The Dropout operator will be lost after conversion because the inference mode is used to load the PyTorch or TensorFlow model. Manually re-implement is necessary. | |||||
| > 1. The Dropout operator will be lost after conversion because the inference mode is used to load the ONNX or TensorFlow model. Manually re-implement is necessary. | |||||
| > 2. The Graph-based mode will be continuously developed and optimized with further updates. | > 2. The Graph-based mode will be continuously developed and optimized with further updates. | ||||
| [Supported models list (Models in below table have been tested based on PyTorch 1.5.0 and TensorFlow 1.15.0, X86 Ubuntu released version)](./docs/supported_model_list.md). | [Supported models list (Models in below table have been tested based on PyTorch 1.5.0 and TensorFlow 1.15.0, X86 Ubuntu released version)](./docs/supported_model_list.md). | ||||
| @@ -174,18 +161,23 @@ For non-transformed operators, suggestions are provided in the report. For insta | |||||
| ### Graph-Based Conversion | ### Graph-Based Conversion | ||||
| #### PyTorch Model Scripts Conversion | |||||
| #### TensorFlow Model Scripts Conversion | |||||
| To use TensorFlow model script migration, users need to export TensorFlow model to Pb format first, and obtain the model input node and output node name. For exporting pb model, please refer to [TensorFlow Pb model exporting](#tensorflow-pb-model-exporting). | |||||
| Assume the PyTorch model (.pth file) is located at `/home/user/model.pth`, with input shape (1, 3, 224, 224) and the original PyTorch script is at `/home/user/project/model_training`. Output the transformed MindSpore script, MindSpore checkpoint file and weight map file to `/home/user/output`, with the conversion report to `/home/user/output/report`. Use the following command: | |||||
| Suppose the input node name is `input_1:0`, output node name is `predictions/Softmax:0`, the input shape of model is `1,224,224,3` and the original TensorFlow model is at `/home/user/xxx/frozen_model.pb`. Output the transformed MindSpore script and MindSpore checkpoint file to `/home/user/output`, with the conversion report and weight map file to `/home/user/output/report`. Use the following command: | |||||
| ```bash | ```bash | ||||
| mindconverter --model_file /home/user/model.pth --shape 1,3,224,224 \ | |||||
| mindconverter --model_file /home/user/xxx/frozen_model.pb --shape 1,224,224,3 \ | |||||
| --input_nodes input_1:0 \ | |||||
| --output_nodes predictions/Softmax:0 \ | |||||
| --output /home/user/output \ | --output /home/user/output \ | ||||
| --report /home/user/output/report \ | |||||
| --project_path /home/user/project/model_training | |||||
| --report /home/user/output/report | |||||
| ``` | ``` | ||||
| The Graph mode has the same conversion report as the AST mode. However, the line number and column number refer to the transformed scripts since no original scripts are used in the process. | |||||
| After executed, MindSpore script, MindSpore checkpoint file, weight map file and report file can be found in corresponding directory. | |||||
| Since the graph based scheme is a generative method, the original TensorFlow script is not referenced in the conversion process. Therefore, the code line and column numbers involved in the generated conversion report refer to the generated script. | |||||
| In addition, input and output Tensor shape of unconverted operators shows explicitly (`input_shape` and `output_shape`) as comments in converted scripts to help further manual modifications. Here is an example of the `Reshape` operator (Not supported in current version): | In addition, input and output Tensor shape of unconverted operators shows explicitly (`input_shape` and `output_shape`) as comments in converted scripts to help further manual modifications. Here is an example of the `Reshape` operator (Not supported in current version): | ||||
| @@ -265,26 +257,6 @@ Here is an example of the weight map: | |||||
| Weight information in MindSpore (`converted_weight`) and that in source framework(`source_weight`) are saved in weight map separately. | Weight information in MindSpore (`converted_weight`) and that in source framework(`source_weight`) are saved in weight map separately. | ||||
| #### TensorFlow Model Scripts Conversion | |||||
| To use TensorFlow model script migration, users need to export TensorFlow model to Pb format first, and obtain the model input node and output node name. For exporting pb model, please refer to [TensorFlow Pb model exporting](#tensorflow-pb-model-exporting). | |||||
| Suppose the model is saved to `/home/user/xxx/frozen_model.pb`, corresponding input node name is `input_1:0`, output node name is `predictions/Softmax:0`, the input shape of model is `1,224,224,3`, the following command can be used to generate the script: | |||||
| ```bash | |||||
| mindconverter --model_file /home/user/xxx/frozen_model.pb --shape 1,224,224,3 \ | |||||
| --input_nodes input_1:0 \ | |||||
| --output_nodes predictions/Softmax:0 \ | |||||
| --output /home/user/output \ | |||||
| --report /home/user/output/report | |||||
| ``` | |||||
| After executed, MindSpore script, MindSpore checkpoint file, weight map file and report file can be found in corresponding directory. | |||||
| Since the graph based scheme is a generative method, the original TensorFlow script is not referenced in the conversion process. Therefore, the code line and column numbers involved in the generated conversion report refer to the generated script. | |||||
| In addition, for operators that are not converted successfully, the input and output shape of tensor of the node will be identified in the code by `input_shape` and `output_shape`. For example, please refer to the example in **PyTorch Model Scripts Conversion** section. | |||||
| #### ONNX Model File Conversion | #### ONNX Model File Conversion | ||||
| To use ONNX model file migration, user needs to obtain the model input node and output node name from ONNX model. To get input node and output node name, [Netron](https://github.com/lutzroeder/netron) is recommended. | To use ONNX model file migration, user needs to obtain the model input node and output node name from ONNX model. To get input node and output node name, [Netron](https://github.com/lutzroeder/netron) is recommended. | ||||
| @@ -303,14 +275,14 @@ After executed, MindSpore script, MindSpore checkpoint file, weight map file and | |||||
| Since the graph based scheme is a generative method, the original ONNX model is not referenced in the conversion process. Therefore, the code line and column numbers involved in the generated conversion report refer to the generated script. | Since the graph based scheme is a generative method, the original ONNX model is not referenced in the conversion process. Therefore, the code line and column numbers involved in the generated conversion report refer to the generated script. | ||||
| In addition, for operators that are not converted successfully, the input and output shape of tensor of the node will be identified in the code by `input_shape` and `output_shape`. For example, please refer to the example in **PyTorch Model Scripts Conversion** section. | |||||
| In addition, for operators that are not converted successfully, the input and output shape of tensor of the node will be identified in the code by `input_shape` and `output_shape`. For example, please refer to the example in **TensorFlow Model Scripts Conversion** section. | |||||
| ## Caution | ## Caution | ||||
| 1. PyTorch, TensorFlow are not an explicitly stated dependency libraries in MindInsight. The Graph conversion requires the consistent PyTorch or TensorFlow version as the model is trained. (For MindConverter, PyTorch 1.5.0 is supported while PyTorch 1.4.x is unsupported; PyTorch 1.6.x and PyTorch 1.7.x are untested.). | |||||
| 2. This script conversion tool relies on operators which supported by MindConverter and MindSpore. Unsupported operators may not be successfully mapped to MindSpore operators. You can manually edit, or implement the mapping based on MindConverter, and contribute to our MindInsight repository. We appreciate your support for the MindSpore community. | |||||
| 1. TensorFlow are not an explicitly stated dependency libraries in MindInsight. The Graph conversion requires the consistent TensorFlow version as the model is trained. | |||||
| 2. This script conversion tool relies on operators which supported by ONNX and MindSpore. Unsupported operators may not be successfully mapped to MindSpore operators. You can manually edit, or implement the mapping based on MindConverter, and contribute to our MindInsight repository. We appreciate your support for the MindSpore community. | |||||
| 3. MindConverter converts dynamic input shape to constant one based on `--shape` while using graph based scheme, as a result, it is required that inputs shape used to retrain or inference in MindSpore are the same as that used to convert using MindConverter. If inputs shape has changed, rerunning MindConverter with new `--shape` or fixing shape related parameters in old script manually is necessary. | 3. MindConverter converts dynamic input shape to constant one based on `--shape` while using graph based scheme, as a result, it is required that inputs shape used to retrain or inference in MindSpore are the same as that used to convert using MindConverter. If inputs shape has changed, rerunning MindConverter with new `--shape` or fixing shape related parameters in old script manually is necessary. | ||||
| 4. MindSpore script, MindSpore checkpoint file and weight map file are saved in the same file folder path. | |||||
| 4. MindSpore script and MindSpore checkpoint file are saved in the same file folder path, while report file and weight map are saved in the same one. | |||||
| ## Unsupported situation of AST mode | ## Unsupported situation of AST mode | ||||
| @@ -345,7 +317,7 @@ class ConvBNReLU(nn.Sequential): | |||||
| ## Requirements | ## Requirements | ||||
| For users using MindConverter, in addition to install the TensorFlow or PyTorch that can satisfy the model loading, inference and training requirements, users also need to pip install the following third party package (tf2onnx is not required for users that convert PyTorch model definition script to MindSpore): | |||||
| For users using MindConverter, in addition to install the TensorFlow that can satisfy the model loading, inference and training requirements, users also need to pip install the following third party package (tf2onnx is not required for users that convert ONNX model definition file to MindSpore): | |||||
| ```text | ```text | ||||
| onnx>=1.8.0 | onnx>=1.8.0 | ||||
| @@ -359,16 +331,13 @@ For some models, if the onnx or tf2onnx error message appears during the convers | |||||
| ## Frequently asked questions | ## Frequently asked questions | ||||
| Q1. `terminate called after throwing an instance of 'std::system_error', what(): Resource temporarily unavailable, Aborted (core dumped)`: | Q1. `terminate called after throwing an instance of 'std::system_error', what(): Resource temporarily unavailable, Aborted (core dumped)`: | ||||
| > Answer: This problem is caused by TensorFlow. First step of conversion process is loading TensorFlow model into memory using TensorFlow module, and TensorFlow starts to apply for needed resource. When required resource is unavailable, such as exceeding max process number of Linux system limit, etc., TensorFlow will raise an error from its C/C++ layer. For more detail, please refer to TensorFlow official repository. There are some known issue for reference only: | |||||
| > Answer: This problem is caused by TsorFlow. First step of conversion process is loading TensorFlow model into memory using TensorFlow module, and TensorFlow starts to apply for needed resource. When required resource is unavailable, such as exceeding max process number of Linux system limit, etc., TensorFlow will raise an error from its C/C++ layer. For more detail, please refer to TensorFlow official repository. There are some known issue for reference only: | |||||
| [TF ISSUE 14885](https://github.com/tensorflow/tensorflow/issues/14885), [TF ISSUE 37449](https://github.com/tensorflow/tensorflow/issues/37449) | [TF ISSUE 14885](https://github.com/tensorflow/tensorflow/issues/14885), [TF ISSUE 37449](https://github.com/tensorflow/tensorflow/issues/37449) | ||||
| Q2. Can MindConverter run on ARM platform? | Q2. Can MindConverter run on ARM platform? | ||||
| > Answer: MindConverter supports both x86 and ARM platform. Please ensure all required dependencies and environments installed in the ARM platform. | > Answer: MindConverter supports both x86 and ARM platform. Please ensure all required dependencies and environments installed in the ARM platform. | ||||
| Q3. Why did I get message of `Error detail: [NodeInputMissing] ...` when converting PyTorch model? | |||||
| > Answer: For PyTorch model, if operations in `torch.nn.functional.xxx`, `torch.xxx`, `torch.Tensor.xxx` were used, node parsing could be failed. It's better to replace those operations with `torch.nn.xxx`. | |||||
| Q4. Why does the conversion process take a lot of time (more than 10 minutes), but the model is not so large? | |||||
| Q3. Why does the conversion process take a lot of time (more than 10 minutes), but the model is not so large? | |||||
| > Answer: When converting, MindConverter needs to use protobuf to deserialize the model file. Please make sure that the protobuf installed in Python environment is implemented by C++ backend. The validation method is as follows. If the output is "python", you need to install Python protobuf implemented by C++ (download the protobuf source code, enter the "python" subdirectory in the source code, and use `python setup.py install --cpp_implementation` to install). If the output is "cpp" and the conversion process still takes a long time, please add environment variable `export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp` before conversion. | > Answer: When converting, MindConverter needs to use protobuf to deserialize the model file. Please make sure that the protobuf installed in Python environment is implemented by C++ backend. The validation method is as follows. If the output is "python", you need to install Python protobuf implemented by C++ (download the protobuf source code, enter the "python" subdirectory in the source code, and use `python setup.py install --cpp_implementation` to install). If the output is "cpp" and the conversion process still takes a long time, please add environment variable `export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp` before conversion. | ||||
| ```python | ```python | ||||
| @@ -15,7 +15,6 @@ | |||||
| - [使用示例](#使用示例) | - [使用示例](#使用示例) | ||||
| - [基于AST的脚本转换示例](#基于ast的脚本转换示例) | - [基于AST的脚本转换示例](#基于ast的脚本转换示例) | ||||
| - [基于图结构的脚本生成示例](#基于图结构的脚本生成示例) | - [基于图结构的脚本生成示例](#基于图结构的脚本生成示例) | ||||
| - [PyTorch模型脚本生成示例](#pytorch模型脚本生成示例) | |||||
| - [TensorFlow模型脚本生成示例](#tensorflow模型脚本生成示例) | - [TensorFlow模型脚本生成示例](#tensorflow模型脚本生成示例) | ||||
| - [ONNX模型文件生成示例](#onnx模型文件生成示例) | - [ONNX模型文件生成示例](#onnx模型文件生成示例) | ||||
| - [注意事项](#注意事项) | - [注意事项](#注意事项) | ||||
| @@ -48,7 +47,6 @@ usage: mindconverter [-h] [--version] [--in_file IN_FILE] | |||||
| [--input_nodes INPUT_NODES [INPUT_NODES ...]] | [--input_nodes INPUT_NODES [INPUT_NODES ...]] | ||||
| [--output_nodes OUTPUT_NODES [OUTPUT_NODES ...]] | [--output_nodes OUTPUT_NODES [OUTPUT_NODES ...]] | ||||
| [--output OUTPUT] [--report REPORT] | [--output OUTPUT] [--report REPORT] | ||||
| [--project_path PROJECT_PATH] | |||||
| optional arguments: | optional arguments: | ||||
| -h, --help show this help message and exit | -h, --help show this help message and exit | ||||
| @@ -56,10 +54,10 @@ optional arguments: | |||||
| --in_file IN_FILE Specify path for script file to use AST schema to do | --in_file IN_FILE Specify path for script file to use AST schema to do | ||||
| script conversation. | script conversation. | ||||
| --model_file MODEL_FILE | --model_file MODEL_FILE | ||||
| PyTorch(.pth), Tensorflow(.pb) or ONNX(.onnx) model | |||||
| file path is expected to do script generation based on | |||||
| graph schema. When `--in_file` and `--model_file` are | |||||
| both provided, use AST schema as default. | |||||
| Tensorflow(.pb) or ONNX(.onnx) model file path is | |||||
| expected to do script generation based on graph | |||||
| schema. When `--in_file` and `--model_file` are both | |||||
| provided, use AST schema as default. | |||||
| --shape SHAPE [SHAPE ...] | --shape SHAPE [SHAPE ...] | ||||
| Optional, expected input tensor shape of | Optional, expected input tensor shape of | ||||
| `--model_file`. It is required when use graph based | `--model_file`. It is required when use graph based | ||||
| @@ -67,45 +65,30 @@ optional arguments: | |||||
| with `--input_nodes`. Usage: --shape 1,512 1,512 | with `--input_nodes`. Usage: --shape 1,512 1,512 | ||||
| --input_nodes INPUT_NODES [INPUT_NODES ...] | --input_nodes INPUT_NODES [INPUT_NODES ...] | ||||
| Optional, input node(s) name of `--model_file`. It is | Optional, input node(s) name of `--model_file`. It is | ||||
| required when use TensorFlow and ONNX model. Both | |||||
| order and number should be consistent with `--shape`. | |||||
| Usage: --input_nodes input_1:0 input_2:0 | |||||
| required when use graph based schema. Both order and | |||||
| number should be consistent with `--shape`. Usage: | |||||
| --input_nodes input_1:0 input_2:0 | |||||
| --output_nodes OUTPUT_NODES [OUTPUT_NODES ...] | --output_nodes OUTPUT_NODES [OUTPUT_NODES ...] | ||||
| Optional, output node(s) name of `--model_file`. It is | Optional, output node(s) name of `--model_file`. It is | ||||
| required when use TensorFlow and ONNX model. Usage: | |||||
| required when use graph based schema. Usage: | |||||
| --output_nodes output_1:0 output_2:0 | --output_nodes output_1:0 output_2:0 | ||||
| --output OUTPUT Optional, specify path for converted script file | --output OUTPUT Optional, specify path for converted script file | ||||
| directory. Default output directory is `output` folder | directory. Default output directory is `output` folder | ||||
| in the current working directory. | in the current working directory. | ||||
| --report REPORT Optional, specify report directory. Default is | --report REPORT Optional, specify report directory. Default is | ||||
| converted script directory. | converted script directory. | ||||
| --project_path PROJECT_PATH | |||||
| Optional, PyTorch scripts project path. If PyTorch | |||||
| project is not in PYTHONPATH, please assign | |||||
| `--project_path` when use graph based schema. Usage: | |||||
| --project_path ~/script_file/ | |||||
| ``` | ``` | ||||
| ### PyTorch模型脚本迁移 | ### PyTorch模型脚本迁移 | ||||
| #### MindConverter提供两种PyTorch模型脚本迁移方案: | |||||
| 1. **基于抽象语法树(Abstract syntax tree, AST)的脚本转换**:指定`--in_file`的值,将使用基于AST的脚本转换方案; | |||||
| 2. **基于图结构的脚本生成**:指定`--model_file`与`--shape`将使用基于图结构的脚本生成方案。 | |||||
| #### MindConverter仅提供使用AST进行PyTorch模型脚本迁移方案: | |||||
| **基于抽象语法树(Abstract syntax tree, AST)的脚本转换**:指定`--in_file`的值,将使用基于AST的脚本转换方案; | |||||
| > 若同时指定了`--in_file`,`--model_file`将默认使用AST方案进行脚本迁移。 | > 若同时指定了`--in_file`,`--model_file`将默认使用AST方案进行脚本迁移。 | ||||
| 当使用基于图结构的脚本生成方案时,要求必须指定`--shape`的值;当使用基于AST的脚本转换方案时,`--shape`会被忽略。 | |||||
| 其中,`--output`与`--report`参数可省略。若省略,MindConverter将在当前工作目录(Working directory)下自动创建`output`目录,将生成的脚本、转换报告、权重文件、权重映射表输出至该目录。 | |||||
| 另外,当使用基于图结构的脚本生成方案时,请确保原PyTorch项目已在Python包搜索路径中,可通过CLI进入Python交互式命令行,通过import的方式判断是否已满足;若未加入,可通过`--project_path` | |||||
| 命令手动将项目路径传入,以确保MindConverter可引用到原PyTorch脚本。 | |||||
| 其中,`--output`与`--report`参数可省略。若省略,MindConverter将在当前工作目录(Working directory)下自动创建`output`目录,将生成的脚本、转换报告输出至该目录。 | |||||
| > 假设用户项目目录为`/home/user/project/model_training`,用户可通过如下命令手动将项目添加至包搜索路径中:`export PYTHONPATH=/home/user/project/model_training:$PYTHONPATH`; | |||||
| > 此处MindConverter需要引用原PyTorch脚本,是因为PyTorch模型反向序列化过程中会引用原脚本。 | |||||
| PyTorch(.pth)模型转换仅支持单输入、单输出的PyTorch模型,如需转换多输入、多输出模型,建议转换为ONNX之后,使用ONNX进行转换。 | |||||
| > 若需要基于图模式进行PyTorch模型脚本迁移,建议将PyTorch模型转换为ONNX之后,使用ONNX文件进行模型模型脚本迁移,详情见 [PyTorch使用说明](https://pytorch.org/docs/stable/onnx.html) 。 | |||||
| ### TensorFlow模型脚本迁移 | ### TensorFlow模型脚本迁移 | ||||
| @@ -113,12 +96,16 @@ PyTorch(.pth)模型转换仅支持单输入、单输出的PyTorch模型,如需 | |||||
| > AST方案不支持TensorFlow模型脚本迁移,TensorFlow脚本迁移仅支持基于图结构的方案。 | > AST方案不支持TensorFlow模型脚本迁移,TensorFlow脚本迁移仅支持基于图结构的方案。 | ||||
| 若省略`--output`与`--report`参数,MindConverter将在当前工作目录(Working directory)下自动创建`output`目录,将生成的脚本、转换报告、权重文件、权重映射表输出至该目录。 | |||||
| ### ONNX模型文件迁移 | ### ONNX模型文件迁移 | ||||
| **MindConverter提供基于图结构的脚本生成方案**:指定`--model_file`、`--shape`、`--input_nodes`、`--output_nodes`进行脚本迁移。 | **MindConverter提供基于图结构的脚本生成方案**:指定`--model_file`、`--shape`、`--input_nodes`、`--output_nodes`进行脚本迁移。 | ||||
| > AST方案不支持ONNX模型文件迁移,ONNX文件迁移仅支持基于图结构的方案。 | > AST方案不支持ONNX模型文件迁移,ONNX文件迁移仅支持基于图结构的方案。 | ||||
| 若省略`--output`与`--report`参数,MindConverter将在当前工作目录(Working directory)下自动创建`output`目录,将生成的脚本、转换报告、权重文件、权重映射表输出至该目录。 | |||||
| ## 使用场景 | ## 使用场景 | ||||
| MindConverter提供两种技术方案,以应对不同脚本迁移场景: | MindConverter提供两种技术方案,以应对不同脚本迁移场景: | ||||
| @@ -132,7 +119,7 @@ MindConverter提供两种技术方案,以应对不同脚本迁移场景: | |||||
| 目前已基于典型图像分类网络对图结构的脚本转换方案进行测试。 | 目前已基于典型图像分类网络对图结构的脚本转换方案进行测试。 | ||||
| > 1. 基于图结构的脚本生成方案,由于要加载PyTorch、TensorFlow模型,会导致转换后网络中Dropout算子丢失,需要用户手动补齐。 | |||||
| > 1. 基于图结构的脚本生成方案,由于要以推理模式加载ONNX,TensorFlow模型,会导致转换后网络中Dropout算子丢失,需要用户手动补齐。 | |||||
| > 2. 基于图结构的脚本生成方案持续优化中。 | > 2. 基于图结构的脚本生成方案持续优化中。 | ||||
| [支持的模型列表(如下模型已基于x86 Ubuntu发行版,PyTorch 1.5.0以及TensorFlow 1.15.0测试通过)](./docs/supported_model_list_cn.md)。 | [支持的模型列表(如下模型已基于x86 Ubuntu发行版,PyTorch 1.5.0以及TensorFlow 1.15.0测试通过)](./docs/supported_model_list_cn.md)。 | ||||
| @@ -175,26 +162,27 @@ line x:y: [UnConvert] 'operator' didn't convert. ... | |||||
| ### 基于图结构的脚本生成示例 | ### 基于图结构的脚本生成示例 | ||||
| #### PyTorch模型脚本生成示例 | |||||
| #### TensorFlow模型脚本生成示例 | |||||
| 使用TensorFlow模型脚本迁移,需要先将TensorFlow模型导出为pb格式,并且获取模型输入节点、输出节点名称。TensorFlow pb模型导出可参考[TensorFlow Pb模型导出](#tensorflow-pb模型导出) | |||||
| 。 | |||||
| 若用户已将PyTorch模型保存为.pth格式,假设模型绝对路径为`/home/uer/model.pth`,该模型期望的输入shape为(1, 3, 224, 224) | |||||
| ,原PyTorch脚本位于`/home/user/project/model_training`,希望将脚本、权重文件和权重映射表输出至`/home/user/output` | |||||
| ,转换报告输出至`/home/user/output/report` | |||||
| 。<br /> 则脚本生成命令为: | |||||
| 假设输入节点名称为`input_1:0`、输出节点名称为`predictions/Softmax:0`,模型输入样本尺寸为`1,224,224,3`,模型绝对路径为`/home/user/xxx/frozen_model.pb`,希望将脚本、权重文件输出至`/home/user/output`,转换报告以及权重映射表输出至`/home/user/output/report`,则脚本生成命令为: | |||||
| ```bash | ```bash | ||||
| mindconverter --model_file /home/user/model.pth --shape 1,3,224,224 \ | |||||
| mindconverter --model_file /home/user/xxx/frozen_model.pb --shape 1,224,224,3 \ | |||||
| --input_nodes input_1:0 \ | |||||
| --output_nodes predictions/Softmax:0 \ | |||||
| --output /home/user/output \ | --output /home/user/output \ | ||||
| --report /home/user/output/report \ | |||||
| --project_path /home/user/project/model_training | |||||
| --report /home/user/output/report | |||||
| ``` | ``` | ||||
| 执行该命令,MindSpore代码文件、权重文件、权重映射表和转换报告生成至相应目录。 | 执行该命令,MindSpore代码文件、权重文件、权重映射表和转换报告生成至相应目录。 | ||||
| 基于图结构的脚本生成方案产生的转换报告格式与AST方案相同。然而,由于基于图结构方案属于生成式方法,转换过程中未参考原PyTorch脚本,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | |||||
| 由于基于图结构方案属于生成式方法,转换过程中未参考原TensorFlow脚本,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | |||||
| 另外对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`, `output_shape` | 另外对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`, `output_shape` | ||||
| 标识),便于用户手动修改。以Reshape算子为例(暂不支持Reshape),将生成如下代码: | |||||
| 标识),便于用户手动修改。以Reshape算子为例,将生成如下代码: | |||||
| ```python | ```python | ||||
| class Classifier(nn.Cell): | class Classifier(nn.Cell): | ||||
| @@ -274,27 +262,6 @@ class Classifier(nn.Cell): | |||||
| 映射表中分别保存算子在MindSpore中的权重信息(`converted_weight`)和在原始框架中的权重信息(`source_weight`)。 | 映射表中分别保存算子在MindSpore中的权重信息(`converted_weight`)和在原始框架中的权重信息(`source_weight`)。 | ||||
| #### TensorFlow模型脚本生成示例 | |||||
| 使用TensorFlow模型脚本迁移,需要先将TensorFlow模型导出为pb格式,并且获取模型输入节点、输出节点名称。TensorFlow pb模型导出可参考[TensorFlow Pb模型导出](#tensorflow-pb模型导出) | |||||
| 。 | |||||
| 假设输入节点名称为`input_1:0`、输出节点名称为`predictions/Softmax:0`,模型输入样本尺寸为`1,224,224,3`,则可使用如下命令进行脚本生成: | |||||
| ```bash | |||||
| mindconverter --model_file /home/user/xxx/frozen_model.pb --shape 1,224,224,3 \ | |||||
| --input_nodes input_1:0 \ | |||||
| --output_nodes predictions/Softmax:0 \ | |||||
| --output /home/user/output \ | |||||
| --report /home/user/output/report | |||||
| ``` | |||||
| 执行该命令,MindSpore代码文件、权重文件、权重映射表和转换报告生成至相应目录。 | |||||
| 由于基于图结构方案属于生成式方法,转换过程中未参考原TensorFlow脚本,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | |||||
| 另外,对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`、`output_shape`标识),便于用户手动修改,示例见**PyTorch模型脚本生成示例**。 | |||||
| #### ONNX模型文件生成示例 | #### ONNX模型文件生成示例 | ||||
| 使用ONNX模型文件迁移,需要先从.onnx文件中获取模型输入节点、输出节点名称。获取ONNX模输入、输出节点名称,可使用 [Netron](https://github.com/lutzroeder/netron) 工具查看。 | 使用ONNX模型文件迁移,需要先从.onnx文件中获取模型输入节点、输出节点名称。获取ONNX模输入、输出节点名称,可使用 [Netron](https://github.com/lutzroeder/netron) 工具查看。 | ||||
| @@ -313,15 +280,14 @@ mindconverter --model_file /home/user/xxx/model.onnx --shape 1,3,224,224 \ | |||||
| 由于基于图结构方案属于生成式方法,转换过程中未参考ONNX文件,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | 由于基于图结构方案属于生成式方法,转换过程中未参考ONNX文件,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | ||||
| 另外,对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`、`output_shape`标识),便于用户手动修改,示例见**PyTorch模型脚本生成示例**。 | |||||
| 另外,对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`、`output_shape`标识),便于用户手动修改,示例见**TensorFlow模型脚本生成示例**。 | |||||
| ## 注意事项 | ## 注意事项 | ||||
| 1. PyTorch、TensorFlow不作为MindInsight明确声明的依赖库。若想使用基于图结构的脚本生成工具,需要用户手动安装与生成PyTorch模型版本一致的PyTorch库(MindConverter使用PyTorch | |||||
| 1.5.0进行测试,不支持PyTorch 1.4.x; PyTorch 1.6.x、PyTorch 1.7.x未进行测试。),或TensorFlow。 | |||||
| 2. 脚本转换工具本质上为算子驱动,对于MindConverter未维护的PyTorch或ONNX算子与MindSpore算子映射,将会出现相应的算子无法转换的问题,对于该类算子,用户可手动修改,或基于MindConverter实现映射关系,向MindInsight仓库贡献。 | |||||
| 1. TensorFlow不作为MindInsight明确声明的依赖库。若想使用基于图结构的脚本生成工具,需要用户手动安装TensorFlow。 | |||||
| 2. 脚本转换工具本质上为算子驱动,对于MindConverter未维护的ONNX算子与MindSpore算子映射,将会出现相应的算子无法转换的问题,对于该类算子,用户可手动修改,或基于MindConverter实现映射关系,向MindInsight仓库贡献。 | |||||
| 3. 在使用基于计算图的迁移时,MindConverter会根据`--shape`参数将模型输入的批次大小(batch size)、句子长度(sequence length)、图片尺寸(image shape)等尺寸相关参数固定下来,用户需要保证基于MindSpore重训练、推理时输入shape与转换时一致;若需要调整输入尺寸,请重新指定`--shape`进行转换或修改转换后脚本中涉及张量尺寸变更操作相应的操作数。 | 3. 在使用基于计算图的迁移时,MindConverter会根据`--shape`参数将模型输入的批次大小(batch size)、句子长度(sequence length)、图片尺寸(image shape)等尺寸相关参数固定下来,用户需要保证基于MindSpore重训练、推理时输入shape与转换时一致;若需要调整输入尺寸,请重新指定`--shape`进行转换或修改转换后脚本中涉及张量尺寸变更操作相应的操作数。 | ||||
| 4. 脚本文件、权重文件和权重映射表输出于同一个目录下。 | |||||
| 4. 脚本文件和权重文件输出于同一个目录下,转换报告和权重映射表输出于同一个目录下。 | |||||
| ## AST方案不支持场景 | ## AST方案不支持场景 | ||||
| @@ -357,8 +323,8 @@ class ConvBNReLU(nn.Sequential): | |||||
| ## 三方库依赖 | ## 三方库依赖 | ||||
| 用户在使用MindConverter时,下列三方库未在MindInsight依赖列表(requirements.txt)中声明。用户除安装可满足模型加载、训练、推理的TensorFlow或PyTorch外,还需要安装(pip | |||||
| install)如下依赖库(PyTorch模型脚本转MindSpore的用户无需安装tf2onnx): | |||||
| 用户在使用MindConverter时,下列三方库未在MindInsight依赖列表(requirements.txt)中声明。用户除安装可满足模型加载、训练、推理的TensorFlow外,还需要安装(pip | |||||
| install)如下依赖库(ONNX模型文件转MindSpore的用户无需安装tf2onnx): | |||||
| ```text | ```text | ||||
| onnx>=1.8.0 | onnx>=1.8.0 | ||||
| @@ -379,11 +345,7 @@ Q2. MindConverter是否可以在ARM平台运行? | |||||
| > 答:MindConverter同时支持X86、ARM平台,若在ARM平台运行需要用户自行安装模型所需的依赖包和运行环境。 | > 答:MindConverter同时支持X86、ARM平台,若在ARM平台运行需要用户自行安装模型所需的依赖包和运行环境。 | ||||
| Q3. PyTorch模型转换时为什么提示`Error detail: [NodeInputMissing] ...`? | |||||
| > 答:对于PyTorch模型,若网络中存在`torch.nn.functional.xxx`, `torch.xxx`, `torch.Tensor.xxx`层算子,可能存在节点解析失败的情况,需要用户手动替换为torch.nn层算子。 | |||||
| Q4. 为什么使用MindConverter进行模型转换需要很长时间(超过十分钟),而模型并不大? | |||||
| Q3. 为什么使用MindConverter进行模型转换需要很长时间(超过十分钟),而模型并不大? | |||||
| > 答:MindConverter进行转换时,需要使用Protobuf对模型文件进行反序列化,请确保Python环境中安装的Protobuf采用C++后端实现,检查方法如下,若输出为python,则需要安装采用C++实现的Python Protobuf(下载Protobuf源码并进入源码中的python子目录,使用python setup.py install --cpp_implementation进行安装);若输出为cpp,转换过程仍耗时较长,请在转换前使用添加环境变量`export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp`。 | > 答:MindConverter进行转换时,需要使用Protobuf对模型文件进行反序列化,请确保Python环境中安装的Protobuf采用C++后端实现,检查方法如下,若输出为python,则需要安装采用C++实现的Python Protobuf(下载Protobuf源码并进入源码中的python子目录,使用python setup.py install --cpp_implementation进行安装);若输出为cpp,转换过程仍耗时较长,请在转换前使用添加环境变量`export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp`。 | ||||
| @@ -124,31 +124,6 @@ class OutputDirAction(argparse.Action): | |||||
| setattr(namespace, self.dest, output) | setattr(namespace, self.dest, output) | ||||
| class ProjectPathAction(argparse.Action): | |||||
| """Project directory action class definition.""" | |||||
| def __call__(self, parser_in, namespace, values, option_string=None): | |||||
| """ | |||||
| Inherited __call__ method from argparse.Action. | |||||
| Args: | |||||
| parser_in (ArgumentParser): Passed-in argument parser. | |||||
| namespace (Namespace): Namespace object to hold arguments. | |||||
| values (object): Argument values with type depending on argument definition. | |||||
| option_string (str): Optional string for specific argument name. Default: None. | |||||
| """ | |||||
| ArgsCheck.check_repeated(namespace, self.dest, self.default, option_string, parser_in) | |||||
| outfile_dir = FileDirAction.check_path(parser_in, values, option_string) | |||||
| if not os.path.exists(outfile_dir): | |||||
| parser_in.error(f'{option_string} {outfile_dir} not exists') | |||||
| if not os.path.isdir(outfile_dir): | |||||
| parser_in.error(f'{option_string} [{outfile_dir}] should be a directory.') | |||||
| setattr(namespace, self.dest, outfile_dir) | |||||
| class InFileAction(argparse.Action): | class InFileAction(argparse.Action): | ||||
| """Input File action class definition.""" | """Input File action class definition.""" | ||||
| @@ -202,8 +177,8 @@ class ModelFileAction(argparse.Action): | |||||
| frame_type = get_framework_type(outfile_dir) | frame_type = get_framework_type(outfile_dir) | ||||
| if frame_type == FrameworkType.UNKNOWN.value: | if frame_type == FrameworkType.UNKNOWN.value: | ||||
| parser_in.error(f'{option_string} {outfile_dir} should be an valid ' | |||||
| f'TensorFlow pb or PyTorch pth model file') | |||||
| parser_in.error(f'{option_string} {outfile_dir} should be ' | |||||
| f'a valid TensorFlow(.pb) or an ONNX(.onnx) model file.') | |||||
| setattr(namespace, self.dest, outfile_dir) | setattr(namespace, self.dest, outfile_dir) | ||||
| @@ -277,7 +252,6 @@ class NodeAction(argparse.Action): | |||||
| namespace (Namespace): Namespace object to hold arguments. | namespace (Namespace): Namespace object to hold arguments. | ||||
| values (list): Argument values with type depending on argument definition. | values (list): Argument values with type depending on argument definition. | ||||
| option_string (str): Optional string for specific argument name. Default: None. | option_string (str): Optional string for specific argument name. Default: None. | ||||
| """ | """ | ||||
| ArgsCheck.check_repeated(namespace, self.dest, self.default, option_string, parser_in) | ArgsCheck.check_repeated(namespace, self.dest, self.default, option_string, parser_in) | ||||
| @@ -326,7 +300,7 @@ parser.add_argument( | |||||
| action=ModelFileAction, | action=ModelFileAction, | ||||
| required=False, | required=False, | ||||
| help=""" | help=""" | ||||
| PyTorch(.pth), Tensorflow(.pb) or ONNX(.onnx) model file path | |||||
| Tensorflow(.pb) or ONNX(.onnx) model file path | |||||
| is expected to do script generation based on graph schema. When | is expected to do script generation based on graph schema. When | ||||
| `--in_file` and `--model_file` are both provided, | `--in_file` and `--model_file` are both provided, | ||||
| use AST schema as default. | use AST schema as default. | ||||
| @@ -354,7 +328,7 @@ parser.add_argument( | |||||
| required=False, | required=False, | ||||
| nargs="+", | nargs="+", | ||||
| help=""" | help=""" | ||||
| Optional, input node(s) name of `--model_file`. It is required when use TensorFlow and ONNX model. | |||||
| Optional, input node(s) name of `--model_file`. It is required when use graph based schema. | |||||
| Both order and number should be consistent with `--shape`. Usage: --input_nodes input_1:0 input_2:0 | Both order and number should be consistent with `--shape`. Usage: --input_nodes input_1:0 input_2:0 | ||||
| """) | """) | ||||
| @@ -366,7 +340,7 @@ parser.add_argument( | |||||
| required=False, | required=False, | ||||
| nargs="+", | nargs="+", | ||||
| help=""" | help=""" | ||||
| Optional, output node(s) name of `--model_file`. It is required when use TensorFlow and ONNX model. | |||||
| Optional, output node(s) name of `--model_file`. It is required when use graph based schema. | |||||
| Usage: --output_nodes output_1:0 output_2:0 | Usage: --output_nodes output_1:0 output_2:0 | ||||
| """) | """) | ||||
| @@ -391,19 +365,6 @@ parser.add_argument( | |||||
| converted script directory. | converted script directory. | ||||
| """) | """) | ||||
| parser.add_argument( | |||||
| '--project_path', | |||||
| type=str, | |||||
| action=ProjectPathAction, | |||||
| required=False, | |||||
| default=None, | |||||
| help=""" | |||||
| Optional, PyTorch scripts project path. If PyTorch | |||||
| project is not in PYTHONPATH, please assign | |||||
| `--project_path` when use graph based schema. | |||||
| Usage: --project_path ~/script_file/ | |||||
| """) | |||||
| def cli_entry(): | def cli_entry(): | ||||
| """Entry point for mindconverter CLI.""" | """Entry point for mindconverter CLI.""" | ||||
| @@ -425,23 +386,21 @@ def cli_entry(): | |||||
| _run(args.in_file, args.model_file, | _run(args.in_file, args.model_file, | ||||
| args.shape, | args.shape, | ||||
| args.input_nodes, args.output_nodes, | args.input_nodes, args.output_nodes, | ||||
| args.output, args.report, | |||||
| args.project_path) | |||||
| args.output, args.report) | |||||
| def _run(in_files, model_file, shape, input_nodes, output_nodes, out_dir, report, project_path): | |||||
| def _run(in_files, model_file, shape, input_nodes, output_nodes, out_dir, report): | |||||
| """ | """ | ||||
| Run converter command. | Run converter command. | ||||
| Args: | Args: | ||||
| in_files (str): The file path or directory to convert. | in_files (str): The file path or directory to convert. | ||||
| model_file(str): The pytorch .pth to convert on graph based schema. | |||||
| model_file(str): The model to convert on graph based schema. | |||||
| shape(list): The input tensor shape of module_file. | shape(list): The input tensor shape of module_file. | ||||
| input_nodes(str): The input node(s) name of Tensorflow model, split by ','. | |||||
| output_nodes(str): The output node(s) name of Tensorflow model, split by ','. | |||||
| input_nodes(str): The input node(s) name of model. | |||||
| output_nodes(str): The output node(s) name of model. | |||||
| out_dir (str): The output directory to save converted file. | out_dir (str): The output directory to save converted file. | ||||
| report (str): The report file path. | report (str): The report file path. | ||||
| project_path(str): Pytorch scripts project path. | |||||
| """ | """ | ||||
| if in_files: | if in_files: | ||||
| files_config = { | files_config = { | ||||
| @@ -470,10 +429,6 @@ def _run(in_files, model_file, shape, input_nodes, output_nodes, out_dir, report | |||||
| 'outfile_dir': out_dir, | 'outfile_dir': out_dir, | ||||
| 'report_dir': report if report else out_dir | 'report_dir': report if report else out_dir | ||||
| } | } | ||||
| if project_path: | |||||
| paths = sys.path | |||||
| if project_path not in paths: | |||||
| sys.path.append(project_path) | |||||
| main_graph_base_converter(file_config) | main_graph_base_converter(file_config) | ||||
| log_console.info("MindConverter: conversion is completed.") | log_console.info("MindConverter: conversion is completed.") | ||||
| @@ -13,7 +13,7 @@ | |||||
| # limitations under the License. | # limitations under the License. | ||||
| # ============================================================================== | # ============================================================================== | ||||
| """Graph based scripts converter definition.""" | """Graph based scripts converter definition.""" | ||||
| __all__ = ["graph_based_converter_pytorch_to_ms", "graph_based_converter_tf_to_ms"] | |||||
| __all__ = ["graph_based_converter_onnx_to_ms", "graph_based_converter_tf_to_ms"] | |||||
| from mindinsight.mindconverter.graph_based_converter.framework import graph_based_converter_pytorch_to_ms | |||||
| from mindinsight.mindconverter.graph_based_converter.framework import graph_based_converter_onnx_to_ms | |||||
| from mindinsight.mindconverter.graph_based_converter.framework import graph_based_converter_tf_to_ms | from mindinsight.mindconverter.graph_based_converter.framework import graph_based_converter_tf_to_ms | ||||
| @@ -23,10 +23,9 @@ from typing import List, Tuple, Mapping | |||||
| import numpy as np | import numpy as np | ||||
| from mindinsight.mindconverter.common.exceptions import ScriptGenerationError, ReportGenerationError, \ | from mindinsight.mindconverter.common.exceptions import ScriptGenerationError, ReportGenerationError, \ | ||||
| UnknownModelError, CheckPointGenerationError, WeightMapGenerationError | |||||
| from mindinsight.mindconverter.common.log import logger as log | |||||
| from mindinsight.mindconverter.graph_based_converter.constant import SEPARATOR_IN_ONNX_OP, BINARY_HEADER_PYTORCH_BITS, \ | |||||
| FrameworkType, BINARY_HEADER_PYTORCH_FILE, TENSORFLOW_MODEL_SUFFIX, THIRD_PART_VERSION | |||||
| CheckPointGenerationError, WeightMapGenerationError | |||||
| from mindinsight.mindconverter.graph_based_converter.constant import SEPARATOR_IN_ONNX_OP, FrameworkType, \ | |||||
| TENSORFLOW_MODEL_SUFFIX, THIRD_PART_VERSION, ONNX_MODEL_SUFFIX, DTYPE_MAP | |||||
| def is_converted(operation: str): | def is_converted(operation: str): | ||||
| @@ -73,7 +72,7 @@ def check_dependency_integrity(*packages): | |||||
| def build_feed_dict(onnx_model, input_nodes: dict): | def build_feed_dict(onnx_model, input_nodes: dict): | ||||
| """Build feed dict for onnxruntime.""" | """Build feed dict for onnxruntime.""" | ||||
| dtype_mapping = getattr(import_module("tf2onnx.utils"), "ONNX_TO_NUMPY_DTYPE") | |||||
| dtype_mapping = DTYPE_MAP | |||||
| input_nodes_types = { | input_nodes_types = { | ||||
| node.name: dtype_mapping[node.type.tensor_type.elem_type] | node.name: dtype_mapping[node.type.tensor_type.elem_type] | ||||
| for node in onnx_model.graph.input | for node in onnx_model.graph.input | ||||
| @@ -170,7 +169,7 @@ def save_code_file_and_report(model_name: str, code_lines: Mapping[str, Tuple], | |||||
| except TypeError as error: | except TypeError as error: | ||||
| raise CheckPointGenerationError(str(error)) | raise CheckPointGenerationError(str(error)) | ||||
| weight_map_path = os.path.realpath(os.path.join(out_folder, f"weight_map_of_{model_name}.json")) | |||||
| weight_map_path = os.path.realpath(os.path.join(report_folder, f"weight_map_of_{model_name}.json")) | |||||
| try: | try: | ||||
| if os.path.exists(weight_map_path): | if os.path.exists(weight_map_path): | ||||
| raise WeightMapGenerationError("Weight map file with the same name already exists.") | raise WeightMapGenerationError("Weight map file with the same name already exists.") | ||||
| @@ -248,22 +247,14 @@ def convert_bytes_string_to_string(bytes_str): | |||||
| def get_framework_type(model_path): | def get_framework_type(model_path): | ||||
| """Get framework type.""" | """Get framework type.""" | ||||
| if model_path.endswith('.onnx'): | |||||
| return FrameworkType.PYTORCH.value | |||||
| try: | |||||
| with open(model_path, 'rb') as f: | |||||
| if f.read(BINARY_HEADER_PYTORCH_BITS) == BINARY_HEADER_PYTORCH_FILE: | |||||
| framework_type = FrameworkType.PYTORCH.value | |||||
| elif os.path.basename(model_path).split(".")[-1].lower() == TENSORFLOW_MODEL_SUFFIX: | |||||
| framework_type = FrameworkType.TENSORFLOW.value | |||||
| else: | |||||
| framework_type = FrameworkType.UNKNOWN.value | |||||
| except IOError: | |||||
| error_msg = "Get UNSUPPORTED model." | |||||
| error = UnknownModelError(error_msg) | |||||
| log.error(str(error)) | |||||
| raise error | |||||
| model_suffix = os.path.basename(model_path).split(".")[-1].lower() | |||||
| if model_suffix == ONNX_MODEL_SUFFIX: | |||||
| framework_type = FrameworkType.ONNX.value | |||||
| elif model_suffix == TENSORFLOW_MODEL_SUFFIX: | |||||
| framework_type = FrameworkType.TENSORFLOW.value | |||||
| else: | |||||
| framework_type = FrameworkType.UNKNOWN.value | |||||
| return framework_type | return framework_type | ||||
| @@ -47,7 +47,6 @@ ONNXRUNTIME_MIN_VER = "1.5.2" | |||||
| ONNXOPTIMIZER_MIN_VER = "0.1.2" | ONNXOPTIMIZER_MIN_VER = "0.1.2" | ||||
| ONNXOPTIMIZER_MAX_VER = "0.1.2" | ONNXOPTIMIZER_MAX_VER = "0.1.2" | ||||
| TORCH_MIN_VER = "1.5.0" | |||||
| DTYPE_MAP = { | DTYPE_MAP = { | ||||
| 1: np.float32, | 1: np.float32, | ||||
| @@ -111,8 +110,7 @@ class ExchangeMessageKeywords(Enum): | |||||
| GROUP_INPUTS = "group_inputs" | GROUP_INPUTS = "group_inputs" | ||||
| BINARY_HEADER_PYTORCH_FILE = \ | |||||
| b'\x80\x02\x8a\nl\xfc\x9cF\xf9 j\xa8P\x19.\x80\x02M\xe9\x03.\x80\x02}q\x00(X\x10\x00\x00\x00' | |||||
| ONNX_MODEL_SUFFIX = "onnx" | |||||
| TENSORFLOW_MODEL_SUFFIX = "pb" | TENSORFLOW_MODEL_SUFFIX = "pb" | ||||
| BINARY_HEADER_PYTORCH_BITS = 32 | BINARY_HEADER_PYTORCH_BITS = 32 | ||||
| @@ -128,7 +126,6 @@ MIN_SCOPE_LENGTH = 2 | |||||
| ONNX_OPSET_VERSION = 11 | ONNX_OPSET_VERSION = 11 | ||||
| MODEL_INPUT_NAME = 'input.1' | |||||
| NO_CONVERTED_OPERATORS = [ | NO_CONVERTED_OPERATORS = [ | ||||
| "onnx::Constant", | "onnx::Constant", | ||||
| @@ -136,7 +133,6 @@ NO_CONVERTED_OPERATORS = [ | |||||
| ] | ] | ||||
| THIRD_PART_VERSION = { | THIRD_PART_VERSION = { | ||||
| "torch": (TORCH_MIN_VER,), | |||||
| "onnx": (ONNX_MIN_VER,), | "onnx": (ONNX_MIN_VER,), | ||||
| "onnxruntime": (ONNXRUNTIME_MIN_VER,), | "onnxruntime": (ONNXRUNTIME_MIN_VER,), | ||||
| "onnxoptimizer": (ONNXOPTIMIZER_MIN_VER,), | "onnxoptimizer": (ONNXOPTIMIZER_MIN_VER,), | ||||
| @@ -161,7 +157,7 @@ class InputType(Enum): | |||||
| @unique | @unique | ||||
| class FrameworkType(Enum): | class FrameworkType(Enum): | ||||
| PYTORCH = 0 | |||||
| ONNX = 0 | |||||
| TENSORFLOW = 1 | TENSORFLOW = 1 | ||||
| UNKNOWN = 2 | UNKNOWN = 2 | ||||
| @@ -13,9 +13,7 @@ | |||||
| # limitations under the License. | # limitations under the License. | ||||
| # ============================================================================== | # ============================================================================== | ||||
| """Graph based scripts converter workflow.""" | """Graph based scripts converter workflow.""" | ||||
| import multiprocessing as mp | |||||
| import os | import os | ||||
| import re | |||||
| import sys | import sys | ||||
| from typing import List | from typing import List | ||||
| from importlib import import_module | from importlib import import_module | ||||
| @@ -26,7 +24,7 @@ from mindinsight.mindconverter.graph_based_converter.common.global_context impor | |||||
| from mindinsight.mindconverter.graph_based_converter.common.utils import lib_version_satisfied, onnx_satisfied, \ | from mindinsight.mindconverter.graph_based_converter.common.utils import lib_version_satisfied, onnx_satisfied, \ | ||||
| save_code_file_and_report, get_framework_type, check_dependency_integrity, get_third_part_lib_validation_error_info | save_code_file_and_report, get_framework_type, check_dependency_integrity, get_third_part_lib_validation_error_info | ||||
| from mindinsight.mindconverter.graph_based_converter.constant import FrameworkType, \ | from mindinsight.mindconverter.graph_based_converter.constant import FrameworkType, \ | ||||
| ONNX_MIN_VER, TF2ONNX_MIN_VER, ONNXRUNTIME_MIN_VER, ONNXOPTIMIZER_MIN_VER, TORCH_MIN_VER | |||||
| ONNX_MIN_VER, TF2ONNX_MIN_VER, ONNXRUNTIME_MIN_VER, ONNXOPTIMIZER_MIN_VER | |||||
| from mindinsight.mindconverter.graph_based_converter.generator import batch_add_nodes | from mindinsight.mindconverter.graph_based_converter.generator import batch_add_nodes | ||||
| from mindinsight.mindconverter.graph_based_converter.mapper import ONNXToMindSporeMapper | from mindinsight.mindconverter.graph_based_converter.mapper import ONNXToMindSporeMapper | ||||
| from mindinsight.mindconverter.common.log import logger as log, logger_console as log_console | from mindinsight.mindconverter.common.log import logger as log, logger_console as log_console | ||||
| @@ -60,17 +58,7 @@ def _print_error(err): | |||||
| log_console.error(str(err)) | log_console.error(str(err)) | ||||
| def torch_version_satisfied(output_queue): | |||||
| """Check Torch version whether is satisfied.""" | |||||
| satisfied = False | |||||
| pattern = r"\d+\.\d+\.\d+" | |||||
| torch_version = re.findall(pattern, getattr(import_module('torch'), "__version__")) | |||||
| if torch_version: | |||||
| satisfied = lib_version_satisfied(torch_version[0], TORCH_MIN_VER) | |||||
| output_queue.put(satisfied) | |||||
| def torch_installation_validation(func): | |||||
| def onnx_installation_validation(func): | |||||
| """ | """ | ||||
| Validate args of func. | Validate args of func. | ||||
| @@ -83,40 +71,16 @@ def torch_installation_validation(func): | |||||
| def _f(graph_path: str, input_nodes: dict, output_nodes: List[str], | def _f(graph_path: str, input_nodes: dict, output_nodes: List[str], | ||||
| output_folder: str, report_folder: str = None): | output_folder: str, report_folder: str = None): | ||||
| # Check whether pytorch is installed. | |||||
| error_info = None | |||||
| torch_version_validation = False | |||||
| if graph_path.endswith('.onnx'): | |||||
| if not onnx_satisfied() or not check_common_dependency_integrity(): | |||||
| error_info = f"{get_third_part_lib_validation_error_info(['onnx', 'onnxruntime', 'onnxoptimizer'])} " \ | |||||
| f"are required when using graph based scripts converter." | |||||
| else: | |||||
| if not find_spec("torch") or not onnx_satisfied() or not check_common_dependency_integrity(): | |||||
| error_info = \ | |||||
| f"{get_third_part_lib_validation_error_info(['torch', 'onnx', 'onnxruntime', 'onnxoptimizer'])} " \ | |||||
| f"are required when using graph based scripts converter, and PyTorch version must " \ | |||||
| f"be consisted with model generation runtime." | |||||
| if not error_info: | |||||
| output_queue = mp.Queue() | |||||
| process = mp.Process(target=torch_version_satisfied, args=(output_queue,)) | |||||
| process.start() | |||||
| torch_version_validation = output_queue.get() | |||||
| process.join() | |||||
| if error_info: | |||||
| # Check whether onnx is installed. | |||||
| error_info = f"{get_third_part_lib_validation_error_info(['onnx', 'onnxruntime', 'onnxoptimizer'])} " \ | |||||
| f"are required when using graph based scripts converter or ONNX conversion." | |||||
| if not onnx_satisfied() or not check_common_dependency_integrity(): | |||||
| _print_error(RuntimeIntegrityError(error_info)) | _print_error(RuntimeIntegrityError(error_info)) | ||||
| sys.exit(0) | sys.exit(0) | ||||
| if (not torch_version_validation and not graph_path.endswith('.onnx')) or not onnx_lib_version_satisfied(): | |||||
| lib_check_list = ['onnx', 'onnxruntime', 'onnxoptimizer'] | |||||
| if not graph_path.endswith('.onnx'): | |||||
| lib_check_list.insert(0, 'torch') | |||||
| error = RuntimeIntegrityError( | |||||
| f"{get_third_part_lib_validation_error_info(lib_check_list)} " | |||||
| f"are required when using graph based scripts converter." | |||||
| ) | |||||
| _print_error(error) | |||||
| if not onnx_lib_version_satisfied(): | |||||
| _print_error(RuntimeIntegrityError(error_info)) | |||||
| sys.exit(0) | sys.exit(0) | ||||
| func(graph_path=graph_path, | func(graph_path=graph_path, | ||||
| @@ -194,16 +158,16 @@ def _extract_model_name(model_path): | |||||
| return model_name | return model_name | ||||
| @torch_installation_validation | |||||
| @onnx_installation_validation | |||||
| @GraphInitError.uniform_catcher() | @GraphInitError.uniform_catcher() | ||||
| @TreeCreationError.uniform_catcher() | @TreeCreationError.uniform_catcher() | ||||
| @SourceFilesSaveError.uniform_catcher() | @SourceFilesSaveError.uniform_catcher() | ||||
| @GeneratorError.uniform_catcher() | @GeneratorError.uniform_catcher() | ||||
| def graph_based_converter_pytorch_to_ms(graph_path: str, | |||||
| input_nodes: dict, output_nodes: List[str], | |||||
| output_folder: str, report_folder: str = None): | |||||
| def graph_based_converter_onnx_to_ms(graph_path: str, | |||||
| input_nodes: dict, output_nodes: List[str], | |||||
| output_folder: str, report_folder: str = None): | |||||
| """ | """ | ||||
| PyTorch to MindSpore based on Graph. | |||||
| ONNX to MindSpore based on Graph. | |||||
| Args: | Args: | ||||
| graph_path (str): Graph file path. | graph_path (str): Graph file path. | ||||
| @@ -271,12 +235,8 @@ def main_graph_base_converter(file_config): | |||||
| if not file_config.get("shape"): | if not file_config.get("shape"): | ||||
| raise ParamMissingError("Param missing, `--shape` is required when using graph mode.") | raise ParamMissingError("Param missing, `--shape` is required when using graph mode.") | ||||
| if graph_path.endswith("pth") and not file_config.get("input_nodes", []) and \ | |||||
| file_config.get("shape") and len(file_config.get("shape", ())) == 1: | |||||
| file_config['input_nodes'] = ["input.1"] | |||||
| else: | |||||
| check_params = ['input_nodes', 'output_nodes'] | |||||
| check_params_exist(check_params, file_config) | |||||
| check_params = ['input_nodes', 'output_nodes'] | |||||
| check_params_exist(check_params, file_config) | |||||
| if len(file_config['shape']) != len(file_config.get("input_nodes", [])): | if len(file_config['shape']) != len(file_config.get("input_nodes", [])): | ||||
| raise BadParamError("`--shape` and `--input_nodes` must have the same length, " | raise BadParamError("`--shape` and `--input_nodes` must have the same length, " | ||||
| @@ -286,19 +246,13 @@ def main_graph_base_converter(file_config): | |||||
| for shape, node in zip(file_config['shape'], file_config['input_nodes']): | for shape, node in zip(file_config['shape'], file_config['input_nodes']): | ||||
| input_nodes[node] = shape | input_nodes[node] = shape | ||||
| if frame_type == FrameworkType.PYTORCH.value: | |||||
| if graph_path.endswith('.onnx'): | |||||
| graph_based_converter_pytorch_to_ms(graph_path=graph_path, | |||||
| input_nodes=input_nodes, | |||||
| output_nodes=file_config['output_nodes'], | |||||
| output_folder=file_config['outfile_dir'], | |||||
| report_folder=file_config['report_dir']) | |||||
| else: | |||||
| graph_based_converter_pytorch_to_ms(graph_path=graph_path, | |||||
| input_nodes=input_nodes, | |||||
| output_nodes=[], | |||||
| output_folder=file_config['outfile_dir'], | |||||
| report_folder=file_config['report_dir']) | |||||
| if frame_type == FrameworkType.ONNX.value: | |||||
| graph_based_converter_onnx_to_ms(graph_path=graph_path, | |||||
| input_nodes=input_nodes, | |||||
| output_nodes=file_config['output_nodes'], | |||||
| output_folder=file_config['outfile_dir'], | |||||
| report_folder=file_config['report_dir']) | |||||
| elif frame_type == FrameworkType.TENSORFLOW.value: | elif frame_type == FrameworkType.TENSORFLOW.value: | ||||
| graph_based_converter_tf_to_ms(graph_path=graph_path, | graph_based_converter_tf_to_ms(graph_path=graph_path, | ||||
| input_nodes=input_nodes, | input_nodes=input_nodes, | ||||
| @@ -21,7 +21,6 @@ from mindinsight.mindconverter.common.log import logger as log | |||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.base import Graph | from mindinsight.mindconverter.graph_based_converter.third_party_graph.base import Graph | ||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.input_node import InputNode | from mindinsight.mindconverter.graph_based_converter.third_party_graph.input_node import InputNode | ||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.onnx_graph_node import OnnxGraphNode | from mindinsight.mindconverter.graph_based_converter.third_party_graph.onnx_graph_node import OnnxGraphNode | ||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.pytorch_graph_parser import PyTorchGraphParser | |||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.tf_graph_parser import TFGraphParser | from mindinsight.mindconverter.graph_based_converter.third_party_graph.tf_graph_parser import TFGraphParser | ||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.onnx_utils import OnnxDataLoader, \ | from mindinsight.mindconverter.graph_based_converter.third_party_graph.onnx_utils import OnnxDataLoader, \ | ||||
| NodeWeight, NodeOutputShape | NodeWeight, NodeOutputShape | ||||
| @@ -206,11 +205,10 @@ class OnnxGraph(Graph): | |||||
| onnx_model = TFGraphParser.parse(graph_path, | onnx_model = TFGraphParser.parse(graph_path, | ||||
| input_nodes=input_nodes, | input_nodes=input_nodes, | ||||
| output_nodes=output_nodes) | output_nodes=output_nodes) | ||||
| elif graph_path.endswith('.onnx'): | |||||
| else: | |||||
| onnx = import_module('onnx') | onnx = import_module('onnx') | ||||
| onnx_model = onnx.load(graph_path) | onnx_model = onnx.load(graph_path) | ||||
| else: | |||||
| onnx_model = PyTorchGraphParser.parse(graph_path, **kwargs) | |||||
| onnx_inputs = [onnx_input.name for onnx_input in onnx_model.graph.input] | onnx_inputs = [onnx_input.name for onnx_input in onnx_model.graph.input] | ||||
| invalid_input_node_name = list() | invalid_input_node_name = list() | ||||
| @@ -27,7 +27,7 @@ from mindinsight.mindconverter.graph_based_converter.common.global_context impor | |||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.optimizer import OnnxSimplify | from mindinsight.mindconverter.graph_based_converter.third_party_graph.optimizer import OnnxSimplify | ||||
| from mindinsight.mindconverter.graph_based_converter.constant import ONNX_TYPE_INT, ONNX_TYPE_INTS, ONNX_TYPE_STRING, \ | from mindinsight.mindconverter.graph_based_converter.constant import ONNX_TYPE_INT, ONNX_TYPE_INTS, ONNX_TYPE_STRING, \ | ||||
| ONNX_TYPE_FLOATS, ONNX_TYPE_FLOAT, SCALAR_WITHOUT_SHAPE, DYNAMIC_SHAPE, UNKNOWN_DIM_VAL, DTYPE_MAP | |||||
| ONNX_TYPE_FLOATS, ONNX_TYPE_FLOAT, SCALAR_WITHOUT_SHAPE, DYNAMIC_SHAPE, UNKNOWN_DIM_VAL | |||||
| from mindinsight.mindconverter.common.exceptions import GraphInitError | from mindinsight.mindconverter.common.exceptions import GraphInitError | ||||
| @@ -382,11 +382,6 @@ class OnnxDataLoader: | |||||
| def _get_outputs_using_onnxruntime(self, output_nodes_name): | def _get_outputs_using_onnxruntime(self, output_nodes_name): | ||||
| """Get outputs using onnxruntime.""" | """Get outputs using onnxruntime.""" | ||||
| onnx_inputs = self.inferred_model.graph.input | |||||
| dtype_dict = dict() | |||||
| for onnx_input in onnx_inputs: | |||||
| dtype_dict[onnx_input.name] = DTYPE_MAP[onnx_input.type.tensor_type.elem_type] | |||||
| feed_dict = build_feed_dict(self.inferred_model, self.input_nodes) | feed_dict = build_feed_dict(self.inferred_model, self.input_nodes) | ||||
| outputs_infer = fetch_output_from_onnx_model(self.model, feed_dict, output_nodes_name) | outputs_infer = fetch_output_from_onnx_model(self.model, feed_dict, output_nodes_name) | ||||
| @@ -1,136 +0,0 @@ | |||||
| # Copyright 2020-2021 Huawei Technologies Co., Ltd.All Rights Reserved. | |||||
| # | |||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| # you may not use this file except in compliance with the License. | |||||
| # You may obtain a copy of the License at | |||||
| # | |||||
| # http://www.apache.org/licenses/LICENSE-2.0 | |||||
| # | |||||
| # Unless required by applicable law or agreed to in writing, software | |||||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| # See the License for the specific language governing permissions and | |||||
| # limitations under the License. | |||||
| # ============================================================================== | |||||
| """Third party graph parser.""" | |||||
| import multiprocessing as mp | |||||
| import os | |||||
| from importlib import import_module | |||||
| from mindinsight.mindconverter.common.log import logger as log | |||||
| from mindinsight.mindconverter.graph_based_converter.third_party_graph.base import GraphParser | |||||
| from mindinsight.mindconverter.common.exceptions import ModelLoadingError | |||||
| class PyTorchGraphParser(GraphParser): | |||||
| """Define pytorch graph parser.""" | |||||
| @classmethod | |||||
| @ModelLoadingError.check_except( | |||||
| "Error occurs when loading model with given params, please check `--shape`, " | |||||
| "`--input_nodes`, `--output_nodes`, `--model_file` or runtime environment integrity." | |||||
| ) | |||||
| def parse(cls, model_path: str, **kwargs): | |||||
| """ | |||||
| Parser pytorch graph. | |||||
| Args: | |||||
| model_path (str): Model file path. | |||||
| Returns: | |||||
| object, torch model. | |||||
| """ | |||||
| if not os.path.exists(model_path): | |||||
| error = FileNotFoundError("`model_path` must be assigned with " | |||||
| "an existed file path.") | |||||
| log.error(str(error)) | |||||
| raise error | |||||
| try: | |||||
| onnx_model_sim = cls._convert_pytorch_graph_to_onnx( | |||||
| model_path, kwargs['input_nodes'], opset_version=11) | |||||
| return onnx_model_sim | |||||
| except ModuleNotFoundError: | |||||
| error_msg = "Cannot find model scripts in system path, " \ | |||||
| "set `--project_path` to the path of model scripts folder correctly." | |||||
| error = ModuleNotFoundError(error_msg) | |||||
| raise error | |||||
| @staticmethod | |||||
| def _convert_pytorch_graph_to_onnx(model_path, input_nodes, opset_version=None): | |||||
| """ | |||||
| Convert Pytorch model to ONNX model. | |||||
| Args: | |||||
| model_path (str): Path to the Pytorch model. | |||||
| input_nodes (dict): Input nodes to generate onnx model. | |||||
| opset_version (int): Op set version of onnx. | |||||
| """ | |||||
| output_queue = mp.Queue() | |||||
| process = mp.Process(target=PyTorchGraphParser._pytorch_graph_to_proto, | |||||
| args=(output_queue, model_path, input_nodes, opset_version)) | |||||
| process.start() | |||||
| proto = output_queue.get() | |||||
| process.join() | |||||
| onnx = import_module('onnx') | |||||
| onnx_model = onnx.load_model_from_string(proto) | |||||
| return onnx_model | |||||
| @staticmethod | |||||
| def _pytorch_graph_to_proto(output_queue, model_path, input_nodes, opset_version): | |||||
| """ | |||||
| Convert pytorch graph to pytorch proto. | |||||
| Args: | |||||
| output_queue (Queue): Output queue from multi-processing. | |||||
| model_path (str): Path to the Pytorch model. | |||||
| input_nodes (dict): Input nodes to generate onnx model. | |||||
| opset_version (int): Op set version of onnx. | |||||
| """ | |||||
| try: | |||||
| torch = import_module('torch') | |||||
| has_cuda = torch.cuda.is_available() | |||||
| dump_inputs = dict() | |||||
| if has_cuda: | |||||
| model = torch.load(f=model_path).cuda() | |||||
| for node_name, node_shape in input_nodes.items(): | |||||
| dump_inputs[node_name] = torch.randn(*node_shape, device='cuda') | |||||
| else: | |||||
| model = torch.load(f=model_path, map_location="cpu") | |||||
| for node_name, node_shape in input_nodes.items(): | |||||
| dump_inputs[node_name] = torch.randn(*node_shape, device='cpu') | |||||
| if isinstance(model, torch.nn.DataParallel): | |||||
| raise ValueError('torch.nn.DataParallel is not supported by ONNX exporter.') | |||||
| torch_onnx = import_module('torch.onnx') | |||||
| operator_export_types = getattr(torch_onnx, 'OperatorExportTypes') | |||||
| utils = import_module('torch.onnx.utils') | |||||
| model_to_graph = getattr(utils, '_model_to_graph') | |||||
| symbolic_helper = import_module('torch.onnx.symbolic_helper') | |||||
| default_onnx_opset_version = getattr(symbolic_helper, '_default_onnx_opset_version') | |||||
| set_opset_version = getattr(symbolic_helper, '_set_opset_version') | |||||
| set_operator_export_type = getattr(symbolic_helper, '_set_operator_export_type') | |||||
| if not opset_version: | |||||
| opset_version = default_onnx_opset_version | |||||
| operator_export_type = operator_export_types.ONNX | |||||
| set_opset_version(opset_version) | |||||
| set_operator_export_type(operator_export_type) | |||||
| graph, params_dict, _ = model_to_graph(model, args=tuple(dump_inputs.values()), | |||||
| input_names=list(dump_inputs.keys()), _retain_param_name=True) | |||||
| export_onnx = getattr(graph, '_export_onnx') | |||||
| proto, _ = export_onnx( | |||||
| params_dict, opset_version, dict(), False, | |||||
| operator_export_type, True, False, dict(), | |||||
| True, False) | |||||
| output_queue.put(proto) | |||||
| except ModelLoadingError.raise_from() as e: | |||||
| output_queue.put(e) | |||||