| @@ -10,12 +10,14 @@ | |||
| - [Usage](#usage) | |||
| - [PyTorch Model Scripts Migration](#pytorch-model-scripts-migration) | |||
| - [TensorFlow Model Scripts Migration](#tensorflow-model-scripts-migration) | |||
| - [ONNX Model File Migration](#onnx-model-file-migration) | |||
| - [Scenario](#scenario) | |||
| - [Example](#example) | |||
| - [AST-Based Conversion](#ast-based-conversion) | |||
| - [Graph-Based Conversion](#graph-based-conversion) | |||
| - [PyTorch Model Scripts Conversion](#pytorch-model-scripts-conversion) | |||
| - [TensorFlow Model Scripts Conversion](#tensorflow-model-scripts-conversion) | |||
| - [ONNX Model File Conversion](#onnx-model-file-conversion) | |||
| - [Caution](#caution) | |||
| - [Unsupported situation of AST mode](#unsupported-situation-of-ast-mode) | |||
| - [Situation1](#situation1) | |||
| @@ -30,7 +32,7 @@ | |||
| ## Overview | |||
| MindConverter is a migration tool to transform the model scripts from PyTorch or TensorFlow to MindSpore. Users can migrate their PyTorch or TensorFlow models to MindSpore rapidly with minor changes according to the conversion report. | |||
| MindConverter is a migration tool to transform the model scripts and weights from PyTorch, TensorFlow or ONNX to MindSpore. Users can migrate their PyTorch, TensorFlow or ONNX models to MindSpore rapidly with minor changes according to the conversion report. | |||
| ## Installation | |||
| @@ -53,10 +55,10 @@ optional arguments: | |||
| --in_file IN_FILE Specify path for script file to use AST schema to do | |||
| script conversation. | |||
| --model_file MODEL_FILE | |||
| PyTorch .pth or TensorFlow .pb model file path to use | |||
| graph based schema to do script generation. When | |||
| `--in_file` and `--model_file` are both provided, use | |||
| AST schema as default. | |||
| 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. | |||
| --shape SHAPE Optional, expected input tensor shape of | |||
| `--model_file`. It is required when use graph based | |||
| schema. Usage: --shape 1,3,244,244 | |||
| @@ -93,7 +95,7 @@ For the Graph mode, `--shape` is mandatory. | |||
| 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 and conversion reports to it. | |||
| `--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. | |||
| @@ -106,6 +108,12 @@ Please note that your original PyTorch project is included in the module search | |||
| > AST mode is not supported for TensorFlow, only computational graph based mode is available. | |||
| ### 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`. | |||
| > AST mode is not supported for ONNX, only computational graph based mode is available. | |||
| ## Scenario | |||
| MindConverter provides two modes for different migration demands. | |||
| @@ -125,38 +133,38 @@ Some typical image classification networks have been tested for the Graph mode. | |||
| Supported models list (Models in below table have been tested based on PyTorch 1.4.0(TorchVision 0.5.0) and TensorFlow 1.15.0, X86 Ubuntu released version): | |||
| | Supported Model | PyTorch Script | TensorFlow Script | Comment | | |||
| | :----: | :----: | :----: | :----: | | |||
| | ResNet18 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | | |||
| | ResNet34 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | | |||
| | ResNet50 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | | |||
| | ResNet50V2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | | |||
| | ResNet101 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | | |||
| | ResNet101V2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | | |||
| | ResNet152 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | | |||
| | ResNet152V2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | | |||
| | Wide ResNet50 2 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | | |||
| | Wide ResNet101 2 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | | |||
| | VGG11/11BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | | |||
| | VGG13/13BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | | |||
| | VGG16 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg16.py) | | | |||
| | VGG16BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | | |||
| | VGG19 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg19.py) | | | |||
| | VGG19BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | | |||
| | AlexNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/alexnet.py) | / | | | |||
| | GoogLeNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/googlenet.py) | / | | | |||
| | Xception | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/xception.py) | | | |||
| | InceptionV3 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/inception.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_v3.py) | | | |||
| | InceptionResNetV2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_resnet_v2.py) | | | |||
| | MobileNetV1 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet.py) | | | |||
| | MobileNetV2 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mobilenet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet_v2.py) | | | |||
| | MNASNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mnasnet.py) | / | | | |||
| | SqueezeNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/squeezenet.py) | / | | | |||
| | DenseNet121/169/201 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/densenet.py) | | | |||
| | DenseNet161 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | / | | | |||
| | NASNetMobile/Large | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/nasnet.py) | | | |||
| | EfficientNetB0~B7 | [Link](https://github.com/lukemelas/EfficientNet-PyTorch) | [TF1.5Link](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) [TF2.3Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/efficientnet.py) | | | |||
| | Unet | [Link](https://github.com/milesial/Pytorch-UNet) | [Link](https://github.com/zhixuhao/unet) | Due to Operator `mindspore.ops.ResizeBilinear` is not implemented on GPU device for now, operator `mindspore.ops.ResizeBilinear` should be replaced by operator `mindspore.ops.ResizeNearestNeighbor`, while running in GPU device | | |||
| | Supported Model | PyTorch Script | TensorFlow Script | Comment | PyTorch Weights Converted | TensorFlow Weights Converted | | |||
| | :----: | :----: | :----: | :----: | :----: | :----: | | |||
| | ResNet18 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | TESTED | / | | |||
| | ResNet34 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | TESTED | / | | |||
| | ResNet50 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | TESTED | TESTED | | |||
| | ResNet50V2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | / | TESTED | | |||
| | ResNet101 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | TESTED | TESTED | | |||
| | ResNet101V2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | / | TESTED | | |||
| | ResNet152 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | TESTED | TESTED | | |||
| | ResNet152V2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | / | TESTED | | |||
| | Wide ResNet50 2 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | TESTED | / | | |||
| | Wide ResNet101 2 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | / | | TESTED | / | | |||
| | VGG11/11BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | TESTED | / | | |||
| | VGG13/13BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | TESTED | / | | |||
| | VGG16 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg16.py) | | TESTED | TESTED | | |||
| | VGG16BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | TESTED | / | | |||
| | VGG19 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg19.py) | | TESTED | TESTED | | |||
| | VGG19BN | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | / | | TESTED | / | | |||
| | AlexNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/alexnet.py) | / | | TESTED | / | | |||
| | GoogLeNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/googlenet.py) | / | | TESTED | / | | |||
| | Xception | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/xception.py) | | / | TESTED | | |||
| | InceptionV3 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/inception.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_v3.py) | | TESTED | TESTED | | |||
| | InceptionResNetV2 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_resnet_v2.py) | | / | TESTED | | |||
| | MobileNetV1 | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet.py) | | / | TESTED | | |||
| | MobileNetV2 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mobilenet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet_v2.py) | | TESTED | TESTED | | |||
| | MNASNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mnasnet.py) | / | | TESTED | / | | |||
| | SqueezeNet | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/squeezenet.py) | / | | TESTED | / | | |||
| | DenseNet121/169/201 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/densenet.py) | | TESTED | TESTED | | |||
| | DenseNet161 | [Link](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | / | | TESTED | / | | |||
| | NASNetMobile/Large | / | [Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/nasnet.py) | | / | TESTED | | |||
| | EfficientNetB0~B7 | [Link](https://github.com/lukemelas/EfficientNet-PyTorch) | [TF1.5Link](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) [TF2.3Link](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/efficientnet.py) | | TESTED | UNTESTED(TF1.5) TESTED(TF2.3) | | |||
| | Unet | [Link](https://github.com/milesial/Pytorch-UNet) | [Link](https://github.com/zhixuhao/unet) | Due to Operator `mindspore.ops.ResizeBilinear` is not implemented on GPU device for now, operator `mindspore.ops.ResizeBilinear` should be replaced by operator `mindspore.ops.ResizeNearestNeighbor`, while running in GPU device | TESTED | TESTED | | |||
| ## Example | |||
| @@ -192,11 +200,44 @@ Here is an example of the conversion report: | |||
| For non-transformed operators, suggestions are provided in the report. For instance, MindConverter suggests that replace `torch.nn.AdaptiveAvgPool2d` with `mindspore.ops.operations.ReduceMean`. | |||
| Here is an example of the weight map: | |||
| ```json | |||
| { | |||
| "resnet50": [ | |||
| { | |||
| "converted_weight": { | |||
| "name": "conv2d_0.weight", | |||
| "shape": [ | |||
| 64, | |||
| 3, | |||
| 7, | |||
| 7 | |||
| ], | |||
| "data_type": "Float32" | |||
| }, | |||
| "source_weight": { | |||
| "name": "conv1.weight", | |||
| "shape": [ | |||
| 64, | |||
| 3, | |||
| 7, | |||
| 7 | |||
| ], | |||
| "data_type": "float32" | |||
| } | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| Weight information in MindSpore (`converted_weight`) and that in source framework(`source_weight`) are saved in weight map separately. | |||
| ### Graph-Based Conversion | |||
| #### PyTorch Model Scripts Conversion | |||
| 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 to `/home/user/output`, with the conversion report to `/home/user/output/report`. Use the following command: | |||
| 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: | |||
| ```bash | |||
| mindconverter --model_file /home/user/model.pth --shape 1,3,224,224 \ | |||
| @@ -266,17 +307,38 @@ mindconverter --model_file /home/user/xxx/frozen_model.pb --shape 1,224,224,3 \ | |||
| --report /home/user/output/report | |||
| ``` | |||
| After executed MindSpore script, and report file can be found in corresponding directory. | |||
| 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 | |||
| 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. | |||
| Suppose the model is saved to `/home/user/xxx/model.onnx`, corresponding input node name is `input_1:0`, output node name is `predictions/Softmax:0`, the input shape of model is `1,3,224,224`, the following command can be used to generate the script: | |||
| ```bash | |||
| mindconverter --model_file /home/user/xxx/model.onnx --shape 1,3,224,224 \ | |||
| --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 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. | |||
| ## 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. (MindConverter recommends PyTorch 1.4.0) | |||
| 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. | |||
| 3. MindConverter can only guarantee that the converted model scripts require a minor revision or no revision when the inputs' shape fed to the generated model script are equal to the value of `--shape` (The batch size dimension is not limited). | |||
| 4. MindSpore script, MindSpore checkpoint file and weight map file are saved in the same file folder path. | |||
| ## Unsupported situation of AST mode | |||
| @@ -317,6 +379,7 @@ For users using MindConverter, in addition to install the TensorFlow or PyTorch | |||
| onnx>=1.8.0 | |||
| tf2onnx>=1.7.1 | |||
| onnxruntime>=1.5.2 | |||
| onnxoptimizer==0.1.2 | |||
| ``` | |||
| ## Frequently asked questions | |||
| @@ -426,6 +489,8 @@ def convert_to_froze_graph(keras_model: tf.python.keras.models.Model, model_name | |||
| | NodeInputTypeNotSupportError | Fail to recognize the input type of converted operator | 3000001 | Wrong input type set in mapper | | |||
| | ScriptGenerationError | Fail to generate converted script | 3000002 | No left space on hard disk; Converted code is not legal; A file with the same name already exists in `--output` | | |||
| | ReportGenerationError | Fail to generate converted script | 3000003 | No left space on hard disk; No available operator to be converted;A file with the same name already exists in `--report` | | |||
| | CheckPointGenerationError | Fail to generate converted weight file | 3000004 | No left space on hard dist; A file with the same name already exists in `--output` | | |||
| | WeightMapGenerationError | Fail to generate weight map file | 3000005 | No left space on hard dist; A file with the same name already exists in `--output` | | |||
| | GeneratorError | Fail to generate code | 4000000 | Exception caused by 4000001~4000004 | | |||
| | NodeLoadingError | Fail to load node information | 4000001 | Essential parameters are missing after conversion of a node | | |||
| | NodeArgsTranslationError | Fail to translate the node's argument | 4000002 | Converted nodes have incorrect and conflicted information | | |||
| @@ -10,12 +10,14 @@ | |||
| - [用法](#用法) | |||
| - [PyTorch模型脚本迁移](#pytorch模型脚本迁移) | |||
| - [TensorFlow模型脚本迁移](#tensorflow模型脚本迁移) | |||
| - [ONNX模型文件迁移](#onnx模型文件迁移) | |||
| - [使用场景](#使用场景) | |||
| - [使用示例](#使用示例) | |||
| - [基于AST的脚本转换示例](#基于ast的脚本转换示例) | |||
| - [基于图结构的脚本生成示例](#基于图结构的脚本生成示例) | |||
| - [PyTorch模型脚本生成示例](#pytorch模型脚本生成示例) | |||
| - [TensorFlow模型脚本生成示例](#tensorflow模型脚本生成示例) | |||
| - [ONNX模型文件生成示例](#onnx模型文件生成示例) | |||
| - [注意事项](#注意事项) | |||
| - [AST方案不支持场景](#ast方案不支持场景) | |||
| - [场景1](#场景1) | |||
| @@ -30,7 +32,7 @@ | |||
| ## 概述 | |||
| MindConverter是一款用于将PyTorch、TensorFlow脚本转换到MindSpore脚本的工具。结合转换报告的信息,用户只需对转换后的脚本进行微小的改动,即可快速将PyTorch、TensorFlow框架的模型脚本迁移到MindSpore。 | |||
| MindConverter是一款用于将PyTorch、TensorFlow脚本或者ONNX文件转换到MindSpore脚本的工具。结合转换报告的信息,用户只需对转换后的脚本进行微小的改动,即可快速将PyTorch、TensorFlow框架的模型脚本或者ONNX文件迁移到MindSpore。 | |||
| ## 安装 | |||
| @@ -53,10 +55,10 @@ optional arguments: | |||
| --in_file IN_FILE Specify path for script file to use AST schema to do | |||
| script conversation. | |||
| --model_file MODEL_FILE | |||
| PyTorch .pth or TensorFlow .pb model file path to use | |||
| graph based schema to do script generation. When | |||
| `--in_file` and `--model_file` are both provided, use | |||
| AST schema as default. | |||
| 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. | |||
| --shape SHAPE Optional, expected input tensor shape of | |||
| `--model_file`. It is required when use graph based | |||
| schema. Usage: --shape 1,3,244,244 | |||
| @@ -91,7 +93,7 @@ optional arguments: | |||
| 当使用基于图结构的脚本生成方案时,要求必须指定`--shape`的值;当使用基于AST的脚本转换方案时,`--shape`会被忽略。 | |||
| 其中,`--output`与`--report`参数可省略。若省略,MindConverter将在当前工作目录(Working directory)下自动创建`output`目录,将生成的脚本、转换报告输出至该目录。 | |||
| 其中,`--output`与`--report`参数可省略。若省略,MindConverter将在当前工作目录(Working directory)下自动创建`output`目录,将生成的脚本、转换报告、权重文件、权重映射表输出至该目录。 | |||
| 另外,当使用基于图结构的脚本生成方案时,请确保原PyTorch项目已在Python包搜索路径中,可通过CLI进入Python交互式命令行,通过import的方式判断是否已满足;若未加入,可通过`--project_path` | |||
| 命令手动将项目路径传入,以确保MindConverter可引用到原PyTorch脚本。 | |||
| @@ -105,6 +107,12 @@ optional arguments: | |||
| > AST方案不支持TensorFlow模型脚本迁移,TensorFlow脚本迁移仅支持基于图结构的方案。 | |||
| ### ONNX模型文件迁移 | |||
| **MindConverter提供基于图结构的脚本生成方案**:指定`--model_file`、`--shape`、`--input_nodes`、`--output_nodes`进行脚本迁移。 | |||
| > AST方案不支持ONNX模型文件迁移,ONNX文件迁移仅支持基于图结构的方案。 | |||
| ## 使用场景 | |||
| MindConverter提供两种技术方案,以应对不同脚本迁移场景: | |||
| @@ -124,38 +132,38 @@ MindConverter提供两种技术方案,以应对不同脚本迁移场景: | |||
| 支持的模型列表(如下模型已基于x86 Ubuntu发行版,PyTorch 1.4.0(TorchVision 0.5)以及TensorFlow 1.15.0测试通过): | |||
| | 模型 | PyTorch脚本 | TensorFlow脚本 | 备注 | | |||
| | :----: | :----: | :----: | :----: | | |||
| | ResNet18 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | | |||
| | ResNet34 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | | |||
| | ResNet50 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | | |||
| | ResNet50V2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | | |||
| | ResNet101 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | | |||
| | ResNet101V2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | | |||
| | ResNet152 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | | |||
| | ResNet152V2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | | |||
| | Wide ResNet50 2 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | | |||
| | Wide ResNet101 2 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | | |||
| | VGG11/11BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | | |||
| | VGG13/13BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | | |||
| | VGG16 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg16.py) | | | |||
| | VGG16BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | | |||
| | VGG19 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg19.py) | | | |||
| | VGG19BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | | |||
| | AlexNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/alexnet.py) | 暂未测试 | | | |||
| | GoogLeNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/googlenet.py) | 暂未测试 | | | |||
| | Xception | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/xception.py) | | | |||
| | InceptionV3 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/inception.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_v3.py) | | | |||
| | InceptionResNetV2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_resnet_v2.py) | | | |||
| | MobileNetV1 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet.py) | | | |||
| | MobileNetV2 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mobilenet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet_v2.py) | | | |||
| | MNASNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mnasnet.py) | 暂未测试 | | | |||
| | SqueezeNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/squeezenet.py) | 暂未测试 | | | |||
| | DenseNet121/169/201 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/densenet.py) | | | |||
| | DenseNet161 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | 暂未测试 | | | |||
| | NASNetMobile/Large | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/nasnet.py) | | | |||
| | EfficientNetB0~B7 | [脚本链接](https://github.com/lukemelas/EfficientNet-PyTorch) | [TF1.5脚本链接](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) [TF2.3脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/efficientnet.py) | | | |||
| | Unet | [脚本链接](https://github.com/milesial/Pytorch-UNet) | [脚本链接](https://github.com/zhixuhao/unet) | 由于算子`mindspore.ops.ResizeBilinear`在GPU上暂未实现,所以当运行在GPU设备上时,算子`mindspore.ops.ResizeBilinear`需要被替换为算子`mindspore.ops.ResizeNearestNeighbor` | | |||
| | 模型 | PyTorch脚本 | TensorFlow脚本 | 备注 | PyTorch权重迁移 | TensorFlow权重迁移 | | |||
| | :----: | :----: | :----: | :----: | :----: | :----: | | |||
| | ResNet18 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | 已测试 | / | | |||
| | ResNet34 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | 已测试 | / | | |||
| | ResNet50 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | 已测试 | 已测试 | | |||
| | ResNet50V2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | / | 已测试 | | |||
| | ResNet101 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | 已测试 | 已测试 | | |||
| | ResNet101V2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | / | 已测试 | | |||
| | ResNet152 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet.py) | | 已测试 | 已测试 | | |||
| | ResNet152V2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/resnet_v2.py) | | / | 已测试 | | |||
| | Wide ResNet50 2 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | 已测试 | / | | |||
| | Wide ResNet101 2 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/resnet.py) | 暂未测试 | | 已测试 | / | | |||
| | VGG11/11BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | 已测试 | / | | |||
| | VGG13/13BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | 已测试 | / | | |||
| | VGG16 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg16.py) | | 已测试 | 已测试 | | |||
| | VGG16BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | 已测试 | / | | |||
| | VGG19 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/vgg19.py) | | 已测试 | 已测试 | | |||
| | VGG19BN | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/vgg.py) | 暂未测试 | | 已测试 | / | | |||
| | AlexNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/alexnet.py) | 暂未测试 | | 已测试 | / | | |||
| | GoogLeNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/googlenet.py) | 暂未测试 | | 已测试 | / | | |||
| | Xception | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/xception.py) | | / | 已测试 | | |||
| | InceptionV3 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/inception.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_v3.py) | | 已测试 | 已测试 | | |||
| | InceptionResNetV2 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/inception_resnet_v2.py) | | / | 已测试 | | |||
| | MobileNetV1 | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet.py) | | / | 已测试 | | |||
| | MobileNetV2 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mobilenet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/mobilenet_v2.py) | | 已测试 | 已测试 | | |||
| | MNASNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/mnasnet.py) | 暂未测试 | | 已测试 | / | | |||
| | SqueezeNet | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/squeezenet.py) | 暂未测试 | | 已测试 | / | | |||
| | DenseNet121/169/201 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/densenet.py) | | 已测试 | 已测试 | | |||
| | DenseNet161 | [脚本链接](https://github.com/pytorch/vision/blob/v0.5.0/torchvision/models/densenet.py) | 暂未测试 | | 已测试 | / | | |||
| | NASNetMobile/Large | 暂未测试 | [脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/nasnet.py) | | / | 已测试 | | |||
| | EfficientNetB0~B7 | [脚本链接](https://github.com/lukemelas/EfficientNet-PyTorch) | [TF1.5脚本链接](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) [TF2.3脚本链接](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/keras/applications/efficientnet.py) | | 已测试 | 未测试(TF1.5) 已测试(TF2.3)| | |||
| | Unet | [脚本链接](https://github.com/milesial/Pytorch-UNet) | [脚本链接](https://github.com/zhixuhao/unet) | 由于算子`mindspore.ops.ResizeBilinear`在GPU上暂未实现,所以当运行在GPU设备上时,算子`mindspore.ops.ResizeBilinear`需要被替换为算子`mindspore.ops.ResizeNearestNeighbor` | 已测试 | 已测试 | | |||
| ## 使用示例 | |||
| @@ -193,12 +201,46 @@ line x:y: [UnConvert] 'operator' didn't convert. ... | |||
| 对于部分未成功转换的算子,报告中会提供修改建议,如`line 157:23`,MindConverter建议将`torch.nn.AdaptiveAvgPool2d` | |||
| 替换为`mindspore.ops.operations.ReduceMean`。 | |||
| 权重映射表示例如下所示: | |||
| ```json | |||
| { | |||
| "resnet50": [ | |||
| { | |||
| "converted_weight": { | |||
| "name": "conv2d_0.weight", | |||
| "shape": [ | |||
| 64, | |||
| 3, | |||
| 7, | |||
| 7 | |||
| ], | |||
| "data_type": "Float32" | |||
| }, | |||
| "source_weight": { | |||
| "name": "conv1.weight", | |||
| "shape": [ | |||
| 64, | |||
| 3, | |||
| 7, | |||
| 7 | |||
| ], | |||
| "data_type": "float32" | |||
| } | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| 映射表中分别保存算子在MindSpore中的权重信息(`converted_weight`)和在原始框架中的权重信息(`source_weight`)。 | |||
| ### 基于图结构的脚本生成示例 | |||
| #### PyTorch模型脚本生成示例 | |||
| 若用户已将PyTorch模型保存为.pth格式,假设模型绝对路径为`/home/user/model.pth`,该模型期望的输入shape为(1, 3, 224, 224) | |||
| ,原PyTorch脚本位于`/home/user/project/model_training`,希望将脚本输出至`/home/user/output`,转换报告输出至`/home/user/output/report`,则脚本生成命令为: | |||
| 若用户已将PyTorch模型保存为.pth格式,假设模型绝对路径为`/home/uer/model.pth`,该模型期望的输入shape为(1, 3, 224, 224) | |||
| ,原PyTorch脚本位于`/home/user/project/model_training`,希望将脚本、权重文件和权重映射表输出至`/home/user/output`,转换报告输出至`/home/user/output/report`, | |||
| 。则脚本生成命令为: | |||
| ```bash | |||
| mindconverter --model_file /home/user/model.pth --shape 1,3,224,224 \ | |||
| @@ -207,7 +249,7 @@ mindconverter --model_file /home/user/model.pth --shape 1,3,224,224 \ | |||
| --project_path /home/user/project/model_training | |||
| ``` | |||
| 执行该命令,MindSpore代码文件、转换报告生成至相应目录。 | |||
| 执行该命令,MindSpore代码文件、权重文件、权重映射表和转换报告生成至相应目录。 | |||
| 基于图结构的脚本生成方案产生的转换报告格式与AST方案相同。然而,由于基于图结构方案属于生成式方法,转换过程中未参考原PyTorch脚本,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | |||
| @@ -274,17 +316,38 @@ mindconverter --model_file /home/user/xxx/frozen_model.pb --shape 1,224,224,3 \ | |||
| --report /home/user/output/report | |||
| ``` | |||
| 执行该命令,MindSpore代码文件、转换报告生成至相应目录。 | |||
| 执行该命令,MindSpore代码文件、权重文件、权重映射表和转换报告生成至相应目录。 | |||
| 由于基于图结构方案属于生成式方法,转换过程中未参考原TensorFlow脚本,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | |||
| 另外,对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`、`output_shape`标识),便于用户手动修改,示例见**PyTorch模型脚本生成示例**。 | |||
| #### ONNX模型文件生成示例 | |||
| 使用ONNX模型文件迁移,需要先从.onnx文件中获取模型输入节点、输出节点名称。获取ONNX模输入、输出节点名称,可使用 [Netron](https://github.com/lutzroeder/netron) 工具查看。 | |||
| 假设输入节点名称为`input_1:0`、输出节点名称为`predictions/Softmax:0`,模型输入样本尺寸为`1,3,224,224`,则可使用如下命令进行脚本生成: | |||
| ```bash | |||
| mindconverter --model_file /home/user/xxx/model.onnx --shape 1,3,224,224 \ | |||
| --input_nodes input_1:0 \ | |||
| --output_nodes predictions/Softmax:0 \ | |||
| --output /home/user/output \ | |||
| --report /home/user/output/report | |||
| ``` | |||
| 执行该命令,MindSpore代码文件、权重文件、权重映射表和转换报告生成至相应目录。 | |||
| 由于基于图结构方案属于生成式方法,转换过程中未参考ONNX文件,因此生成的转换报告中涉及的代码行、列号均指生成后脚本。 | |||
| 另外,对于未成功转换的算子,在代码中会相应的标识该节点输入、输出Tensor的shape(以`input_shape`、`output_shape`标识),便于用户手动修改,示例见**PyTorch模型脚本生成示例**。 | |||
| ## 注意事项 | |||
| 1. PyTorch、TensorFlow不作为MindInsight明确声明的依赖库。若想使用基于图结构的脚本生成工具,需要用户手动安装与生成PyTorch模型版本一致的PyTorch库(MindConverter推荐使用PyTorch 1.4.0进行脚本生成),或TensorFlow; | |||
| 2. 脚本转换工具本质上为算子驱动,对于MindConverter未维护的PyTorch或ONNX算子与MindSpore算子映射,将会出现相应的算子无法转换的问题,对于该类算子,用户可手动修改,或基于MindConverter实现映射关系,向MindInsight仓库贡献。 | |||
| 3. MindConverter仅保证转换后模型脚本在输入数据尺寸与`--shape`一致的情况下,可达到无需人工修改或少量修改(`--shape`中batch size维度不受限)。 | |||
| 4. 脚本文件、权重文件和权重映射表输出于同一个目录下。 | |||
| ## AST方案不支持场景 | |||
| @@ -327,6 +390,7 @@ install)如下依赖库(PyTorch模型脚本转MindSpore的用户无需安装 | |||
| onnx>=1.8.0 | |||
| tf2onnx>=1.7.1 | |||
| onnxruntime>=1.5.2 | |||
| onnxoptimizer==0.1.2 | |||
| ``` | |||
| ## 常见问题 | |||
| @@ -440,6 +504,8 @@ def convert_to_froze_graph(keras_model: tf.python.keras.models.Model, model_name | |||
| | NodeInputTypeNotSupportError | 网络节点输入类型未知 | 3000001 | 映射关系中设置节点输入类型错误。 | | |||
| | ScriptGenerationError | 转换脚本生成失败 | 3000002 | 空间不足;生成的脚本不符合PEP-8规范;`--output`目录下已有同名文件存在 | | |||
| | ReportGenerationError | 转换报告生成失败 | 3000003 | 空间不足;脚本中没有需要转换的算子;`--report`目录下已有同名文件存在。 | | |||
| | CheckPointGenerationError | 转换权重生成失败 | 3000004 | 空间不足;`--output`目录下已有同名文件存在 | | |||
| | WeightMapGenerationError | 权重映射表生成失败 | 3000005 | 空间不足;`--output`目录下已有同名文件存在 | | |||
| | GeneratorError | 代码生成失败 | 4000000 |由4000001至4000004引发的代码生成模块错误 | | |||
| | NodeLoadingError | 节点读取失败 | 4000001 |转换后的节点缺少必要参数 | | |||
| | NodeArgsTranslationError | 节点参数转换失败 | 4000002 |转换后的节点参数信息不正确 | | |||
| @@ -24,7 +24,7 @@ from mindinsight.mindconverter.common.exceptions import ScriptGenerationError, R | |||
| 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 | |||
| FrameworkType, BINARY_HEADER_PYTORCH_FILE, TENSORFLOW_MODEL_SUFFIX, THIRD_PART_VERSION | |||
| from mindspore.train.serialization import save_checkpoint | |||
| @@ -263,3 +263,29 @@ def replace_string_in_list(str_list: list, original_str: str, target_str: str): | |||
| list, the original list with replaced string. | |||
| """ | |||
| return [s.replace(original_str, target_str) for s in str_list] | |||
| def get_third_part_lib_validation_error_info(lib_list): | |||
| """Get error info when not satisfying third part lib validation.""" | |||
| error_info = None | |||
| link_str = ', ' | |||
| for idx, lib in enumerate(lib_list): | |||
| if idx == len(lib_list) - 1: | |||
| link_str = ' and ' | |||
| lib_version_required = THIRD_PART_VERSION[lib] | |||
| if len(lib_version_required) == 2: | |||
| lib_version_required_min = lib_version_required[0] | |||
| lib_version_required_max = lib_version_required[1] | |||
| if lib_version_required_min == lib_version_required_max: | |||
| info = f"{lib}(=={lib_version_required_min})" | |||
| else: | |||
| info = f"{lib}(>={lib_version_required_min} and <{lib_version_required_max})" | |||
| else: | |||
| info = f"{lib}(>={lib_version_required[0]})" | |||
| if not error_info: | |||
| error_info = info | |||
| else: | |||
| error_info = link_str.join((error_info, info)) | |||
| return error_info | |||
| @@ -43,6 +43,7 @@ ONNX_MIN_VER = "1.8.0" | |||
| TF2ONNX_MIN_VER = "1.7.1" | |||
| ONNXRUNTIME_MIN_VER = "1.5.2" | |||
| ONNXOPTIMIZER_MIN_VER = "0.1.2" | |||
| ONNXOPTIMIZER_MAX_VER = "0.1.2" | |||
| @unique | |||
| @@ -105,6 +106,13 @@ NO_CONVERTED_OPERATORS = [ | |||
| "Constant" | |||
| ] | |||
| THIRD_PART_VERSION = { | |||
| "onnx": (ONNX_MIN_VER,), | |||
| "onnxruntime": (ONNXRUNTIME_MIN_VER,), | |||
| "onnxoptimizer": (ONNXOPTIMIZER_MIN_VER, ONNXOPTIMIZER_MAX_VER), | |||
| "tf2onnx": (TF2ONNX_MIN_VER,) | |||
| } | |||
| @unique | |||
| class NodeType(Enum): | |||
| @@ -22,9 +22,9 @@ from importlib.util import find_spec | |||
| import mindinsight | |||
| from mindinsight.mindconverter.graph_based_converter.common.global_context import GlobalContext | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import lib_version_satisfied, onnx_satisfied, \ | |||
| save_code_file_and_report, get_framework_type | |||
| save_code_file_and_report, get_framework_type, get_third_part_lib_validation_error_info | |||
| from mindinsight.mindconverter.graph_based_converter.constant import FrameworkType, \ | |||
| ONNX_MIN_VER, TF2ONNX_MIN_VER, ONNXRUNTIME_MIN_VER, ONNXOPTIMIZER_MIN_VER | |||
| ONNX_MIN_VER, TF2ONNX_MIN_VER, ONNXRUNTIME_MIN_VER, ONNXOPTIMIZER_MIN_VER, ONNXOPTIMIZER_MAX_VER | |||
| from mindinsight.mindconverter.graph_based_converter.generator import batch_add_nodes | |||
| from mindinsight.mindconverter.graph_based_converter.mapper import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.common.log import logger as log, logger_console as log_console | |||
| @@ -60,7 +60,7 @@ def onnx_lib_version_satisfied(): | |||
| optimizer = import_module("onnxoptimizer.version") | |||
| if not lib_version_satisfied(getattr(onnx, "__version__"), ONNX_MIN_VER) \ | |||
| or not lib_version_satisfied(getattr(ort, "__version__"), ONNXRUNTIME_MIN_VER) \ | |||
| or not lib_version_satisfied(getattr(optimizer, "version"), ONNXOPTIMIZER_MIN_VER): | |||
| or not lib_version_satisfied(getattr(optimizer, "version"), ONNXOPTIMIZER_MIN_VER, ONNXOPTIMIZER_MAX_VER): | |||
| return False | |||
| return True | |||
| @@ -83,16 +83,13 @@ def torch_installation_validation(func): | |||
| error_info = None | |||
| if graph_path.endswith('.onnx'): | |||
| if not onnx_satisfied(): | |||
| error_info = f"onnx(>={ONNX_MIN_VER}, onnxruntime(>={ONNXRUNTIME_MIN_VER}) and " \ | |||
| f"onnxoptimizer(>={ONNXOPTIMIZER_MIN_VER}) " \ | |||
| 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(): | |||
| error_info = f"PyTorch, onnx(>={ONNX_MIN_VER}), " \ | |||
| f"onnxruntime(>={ONNXRUNTIME_MIN_VER}) and " \ | |||
| f"onnxoptimizer(>={ONNXOPTIMIZER_MIN_VER}) " \ | |||
| f"are required when using graph based " \ | |||
| f"scripts converter, and PyTorch version must " \ | |||
| error_info = f"PyTorch, " \ | |||
| f"{get_third_part_lib_validation_error_info(['onnx', 'onnxruntime', 'onnxoptimizer'])} " \ | |||
| f"are required when using graph based scripts converter, and PyTorch version must " \ | |||
| f"be consisted with model generation runtime." | |||
| if error_info: | |||
| error = RuntimeIntegrityError(error_info) | |||
| @@ -104,10 +101,8 @@ def torch_installation_validation(func): | |||
| if not onnx_lib_version_satisfied(): | |||
| error = RuntimeIntegrityError( | |||
| f"onnx(>={ONNX_MIN_VER}), " | |||
| f"onnxruntime(>={ONNXRUNTIME_MIN_VER}) and " | |||
| f"onnxoptimizer(>={ONNXOPTIMIZER_MIN_VER}) are required when using graph " | |||
| f"based scripts converter for Pytorch conversion." | |||
| f"{get_third_part_lib_validation_error_info(['onnx', 'onnxruntime', 'onnxoptimizer'])} " | |||
| f"are required when using graph based scripts converter." | |||
| ) | |||
| log.error(error) | |||
| log_console.error("\n") | |||
| @@ -149,10 +144,9 @@ def tf_installation_validation(func): | |||
| # Check whether tensorflow is installed. | |||
| if not _check_tf_installation() or not onnx_satisfied(): | |||
| error = RuntimeIntegrityError( | |||
| f"TensorFlow, tf2onnx(>={TF2ONNX_MIN_VER}), onnx(>={ONNX_MIN_VER}), " | |||
| f"onnxruntime(>={ONNXRUNTIME_MIN_VER}) and onnxoptimizer(>={ONNXOPTIMIZER_MIN_VER}) " | |||
| f"are required when using graph " | |||
| f"based scripts converter for TensorFlow conversion." | |||
| f"TensorFlow, " | |||
| f"{get_third_part_lib_validation_error_info(['tf2onnx', 'onnx', 'onnxruntime', 'onnxoptimizer'])} " | |||
| f"are required when using graph based scripts converter for TensorFlow conversion." | |||
| ) | |||
| log.error(error) | |||
| log_console.error("\n") | |||
| @@ -165,10 +159,9 @@ def tf_installation_validation(func): | |||
| if not lib_version_satisfied(getattr(tf2onnx, "__version__"), TF2ONNX_MIN_VER) \ | |||
| or not onnx_lib_version_satisfied(): | |||
| error = RuntimeIntegrityError( | |||
| f"TensorFlow, tf2onnx(>={TF2ONNX_MIN_VER}), onnx(>={ONNX_MIN_VER}), " | |||
| f"onnxruntime(>={ONNXRUNTIME_MIN_VER}) and onnxoptimizer(>={ONNXOPTIMIZER_MIN_VER}) " | |||
| f"are required when using graph " | |||
| f"based scripts converter for TensorFlow conversion." | |||
| f"TensorFlow, " | |||
| f"{get_third_part_lib_validation_error_info(['tf2onnx', 'onnx', 'onnxruntime', 'onnxoptimizer'])} " | |||
| f"are required when using graph based scripts converter for TensorFlow conversion." | |||
| ) | |||
| log.error(error) | |||
| log_console.error("\n") | |||
| @@ -16,8 +16,8 @@ | |||
| import copy | |||
| from collections import OrderedDict | |||
| from mindspore import Tensor | |||
| from yapf.yapflib.yapf_api import FormatCode | |||
| from mindspore import Tensor | |||
| from mindinsight.mindconverter.common.exceptions import GeneratorError | |||
| from mindinsight.mindconverter.graph_based_converter.generator.scope_utils import Scope | |||
| @@ -181,9 +181,9 @@ class ONNXToMindSporeMapper(Mapper, abc.ABC): | |||
| return template, exchange_msg, outputs_list, outputs_mapping | |||
| @staticmethod | |||
| def _find_val_by_index(loc_index, weights_list): | |||
| def _find_val_by_index(loc_index, weights_list, default_value=None): | |||
| """Find value by location index of weights_list.""" | |||
| result = None | |||
| result = default_value | |||
| if loc_index < 0: | |||
| return weights_list[loc_index].value | |||
| @@ -14,7 +14,6 @@ | |||
| # ============================================================================== | |||
| """Mapper module.""" | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class FlattenMapper(ONNXToMindSporeMapper): | |||
| @@ -31,7 +30,3 @@ class FlattenMapper(ONNXToMindSporeMapper): | |||
| @staticmethod | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| return Setting() | |||
| @@ -14,7 +14,6 @@ | |||
| # ============================================================================== | |||
| """Mapper module.""" | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class GlobalPoolMapper(ONNXToMindSporeMapper): | |||
| @@ -46,7 +45,3 @@ class GlobalPoolMapper(ONNXToMindSporeMapper): | |||
| @staticmethod | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| return Setting() | |||
| @@ -54,8 +54,7 @@ class PoolMapper(ONNXToMindSporeMapper): | |||
| kernel_shape = params['kernel_shape'] | |||
| strides = params['strides'] | |||
| dilations = params.get('dilations', (1, 1)) | |||
| # For mindspore, | |||
| # output_shape[i] = ceil((input_shape[i] - ((kernel_shape[i] - 1) * dilations[i] + 1) + 1) / strides[i]) | |||
| ms_opt_shape = np.true_divide(np.subtract(np.array(input_shape[-len(kernel_shape):], dtype=np.float32), | |||
| ((np.array(kernel_shape, dtype=np.float32) - 1) * | |||
| np.array(dilations, dtype=np.float32) + 1)) + 1, | |||
| @@ -124,8 +123,6 @@ class PoolMapper(ONNXToMindSporeMapper): | |||
| if np.any(np.array(ms_opt_shape) > np.array(onnx_opt_shape)): | |||
| raise ValueError(f"ms_opt_shape[{ms_opt_shape}] should be no larger than onnx_opt_shape[{onnx_opt_shape}].") | |||
| # shape_diff[i] = (onnx_opt_shape[i] - 1)*strides[i] - | |||
| # (onnx_ipt_shape[i] - ((kernel_shape[i] - 1)*dilations[i] + 1)) | |||
| shape_diff = np.subtract((np.array(onnx_opt_shape) - 1)*np.array(strides), | |||
| np.subtract(np.array(onnx_ipt_shape), | |||
| (np.array(kernel_shape) - 1)*np.array(dilations) + 1)).tolist() | |||
| @@ -25,8 +25,8 @@ class ReLUMapper(ONNXToMindSporeMapper): | |||
| name = "nn.ReLU" | |||
| else: | |||
| weights = kwargs['weights'] | |||
| min_clip = weights[0].value if weights[0] else 0 | |||
| max_clip = weights[1].value if weights[1] else 0 | |||
| min_clip = ReLUMapper._find_val_by_index(0, weights, 0) | |||
| max_clip = ReLUMapper._find_val_by_index(1, weights, 0) | |||
| if max_clip == 6 and min_clip == 0: | |||
| name = "nn.ReLU6" | |||
| elif max_clip == min_clip == 0: | |||
| @@ -14,7 +14,6 @@ | |||
| # ============================================================================== | |||
| """Mapper module.""" | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class SigmoidMapper(ONNXToMindSporeMapper): | |||
| @@ -31,7 +30,3 @@ class SigmoidMapper(ONNXToMindSporeMapper): | |||
| @staticmethod | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| return Setting() | |||
| @@ -14,7 +14,6 @@ | |||
| # ============================================================================== | |||
| """Mapper module.""" | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class SoftmaxMapper(ONNXToMindSporeMapper): | |||
| @@ -35,7 +34,3 @@ class SoftmaxMapper(ONNXToMindSporeMapper): | |||
| @staticmethod | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| return Setting() | |||
| @@ -13,11 +13,9 @@ | |||
| # limitations under the License. | |||
| # ============================================================================== | |||
| """Mapper module.""" | |||
| from mindinsight.mindconverter.graph_based_converter.constant import InputType | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import reset_init_or_construct | |||
| from mindinsight.mindconverter.graph_based_converter.constant import ExchangeMessageKeywords, TemplateKeywords | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class ConcatMapper(ONNXToMindSporeMapper): | |||
| @@ -36,11 +34,6 @@ class ConcatMapper(ONNXToMindSporeMapper): | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| input_type = InputType.LIST.value | |||
| return Setting(op_ipt_type=input_type) | |||
| @staticmethod | |||
| def _generate_snippet_template(**kwargs): | |||
| template, exchange_msg, outputs_list, outputs_mapping = ONNXToMindSporeMapper._generate_snippet_template( | |||
| @@ -16,7 +16,6 @@ | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import reset_init_or_construct | |||
| from mindinsight.mindconverter.graph_based_converter.constant import ExchangeMessageKeywords, TemplateKeywords | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting, Tensor, get_dtype | |||
| class MulMapper(ONNXToMindSporeMapper): | |||
| @@ -34,15 +33,6 @@ class MulMapper(ONNXToMindSporeMapper): | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| weights = kwargs.get("weights") | |||
| if not weights: | |||
| return Setting() | |||
| ref, tensor = list(weights.items())[0] | |||
| return Setting(op_extra_tensor=Tensor(shape=tensor.shape, | |||
| dtype=get_dtype(tensor), reference=ref)) | |||
| @staticmethod | |||
| def _generate_snippet_template(**kwargs): | |||
| template, exchange_msg, outputs_list, outputs_mapping = ONNXToMindSporeMapper._generate_snippet_template( | |||
| @@ -16,7 +16,6 @@ | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import reset_init_or_construct | |||
| from mindinsight.mindconverter.graph_based_converter.constant import ExchangeMessageKeywords, TemplateKeywords | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class ReduceMeanMapper(ONNXToMindSporeMapper): | |||
| @@ -36,15 +35,6 @@ class ReduceMeanMapper(ONNXToMindSporeMapper): | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| params = kwargs['params'] | |||
| if params.get('axes'): | |||
| axis = params['axes'][0] if len(params['axes']) == 1 else tuple(params['axes']) | |||
| else: | |||
| axis = tuple() | |||
| return Setting(op_extra_input={'axis': axis}) | |||
| @staticmethod | |||
| def _generate_snippet_template(**kwargs): | |||
| template, exchange_msg, outputs_list, outputs_mapping = ONNXToMindSporeMapper._generate_snippet_template( | |||
| @@ -14,7 +14,6 @@ | |||
| # ============================================================================== | |||
| """Mapper module.""" | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import convert_bytes_string_to_string | |||
| @@ -68,9 +67,3 @@ class ResizeMapper(ONNXToMindSporeMapper): | |||
| @staticmethod | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| if kwargs.get("weights", None): | |||
| return Setting() | |||
| return Setting() | |||
| @@ -16,7 +16,6 @@ | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import reset_init_or_construct | |||
| from mindinsight.mindconverter.graph_based_converter.constant import ExchangeMessageKeywords, TemplateKeywords | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class SliceMapper(ONNXToMindSporeMapper): | |||
| @@ -34,16 +33,6 @@ class SliceMapper(ONNXToMindSporeMapper): | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| weights = list(kwargs.get("weights").values()) # start, end, axis | |||
| opt_shape = kwargs["params"].get("output_shape") | |||
| if not weights: | |||
| raise ValueError("Cannot get required params from slice.") | |||
| starts = sorted(zip(weights[0].tolist(), weights[2].tolist()), key=lambda x: x[1], reverse=False) | |||
| return Setting(op_extra_input={"begin": tuple([i[0] for i in starts]), | |||
| "size": tuple(opt_shape)}) | |||
| @staticmethod | |||
| def _generate_snippet_template(**kwargs): | |||
| template, exchange_msg, outputs_list, outputs_mapping = ONNXToMindSporeMapper._generate_snippet_template( | |||
| @@ -16,7 +16,6 @@ | |||
| from mindinsight.mindconverter.graph_based_converter.common.utils import reset_init_or_construct | |||
| from mindinsight.mindconverter.graph_based_converter.constant import ExchangeMessageKeywords, TemplateKeywords | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.base import ONNXToMindSporeMapper | |||
| from mindinsight.mindconverter.graph_based_converter.mapper.gen_setting import Setting | |||
| class TransposeMapper(ONNXToMindSporeMapper): | |||
| @@ -34,17 +33,6 @@ class TransposeMapper(ONNXToMindSporeMapper): | |||
| def _convert_trained_weights(**kwargs): | |||
| return dict() | |||
| @staticmethod | |||
| def _convert_settings(**kwargs): | |||
| converted_params = {} | |||
| params = kwargs.get('params') | |||
| perm = params.get('perm') | |||
| if perm and isinstance(perm, list): | |||
| perm = tuple(perm) | |||
| converted_params['input_perm'] = perm | |||
| return Setting(op_extra_input=converted_params) | |||
| @staticmethod | |||
| def _generate_snippet_template(**kwargs): | |||
| template, exchange_msg, outputs_list, outputs_mapping = ONNXToMindSporeMapper._generate_snippet_template( | |||