diff --git a/README.md b/README.md index 53c68b3fec225d06702d793bca0fa30184ac9e9b..ba699039ba4bbc11655228b6b44d2ed7db046f67 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,16 @@ ![python version](https://img.shields.io/badge/python-3.5+-orange.svg) ## 简介 -X2Paddle用于不同框架模型或项目到PaddlePaddle框架模型或项目的迁移,旨在为飞桨开发者提升框架间迁移的效率。 -X2Paddle支持Caffe/TensorFlow/ONNX/PyTorch的预测模型,一步转换至PaddlePaddle预测模型;同时,支持PyTorch训练项目,转换至PaddlePaddle项目,助力用户在PaddlePaddlePaddle上进行模型训练。 - -### 架构设计 -X2Paddle的架构设计着重考虑了对多深度学习框架的的支持以及代码的易读性、易扩展性,并且在多个层面的对转换后OP进行优化处理。 -![](./docs/images/frame.png) +X2Paddle用于不同框架模型或项目到PaddlePaddle框架模型或项目的转换,旨在为飞桨开发者提升框架间转换的效率。 +X2Paddle主要有***2大功能***: +1. ***预测模型转换***:X2Paddle支持Caffe/TensorFlow/ONNX/PyTorch的预测模型,一步转换至PaddlePaddle预测模型。 +2. ***训练项目转换***:heart::heart::PyTorch训练项目,转换至PaddlePaddle项目,助力用户在PaddlePaddlePaddle上进行模型训练。 ### 特性 -- **支持主流深度学习框架**:目前已经支持Caffe/TensorFlow/ONNX/PyTorch四大框架的迁移,涵盖目前市面主流深度学习框架。 +- **支持主流深度学习框架**:目前已经支持Caffe/TensorFlow/ONNX/PyTorch四大框架的预测模型的转换,PyTorch训练项目的转换,涵盖了目前市面主流深度学习框架。 -- **支持的模型丰富**:在主流的CV和NLP模型上均支持转换,涵盖了19+个Caffe模型转换、27+个TensorFlow模型转换、32+个ONNX模型转换、27+个PyTorch模型转换、2+个PyTorch项目转换。 +- **支持的模型丰富**:在主流的CV和NLP模型上均支持转换,涵盖了19+个Caffe预测模型转换、27+个TensorFlow预测模型转换、32+个ONNX预测模型转换、27+个PyTorch预测模型转换、2+个PyTorch训练项目转换,详见 ***[支持列表](./docs/introduction/x2paddle_model_zoo.md)***。 - **简洁易用**:一条命令行或者一个API即可完成模型转换。 @@ -30,7 +28,7 @@ X2Paddle的架构设计着重考虑了对多深度学习框架的的支持以及 - tensorflow : tensorflow == 1.14.0 - caffe : 无 - onnx : onnx >= 1.6.0 -- pytorch:torch >=1.5.0 (script方式暂不支持1.7.0) +- pytorch:torch >=1.5.0 (预测模型转换中的script方式暂不支持1.7.0+) ## 安装 ### 方式一:源码安装 @@ -47,7 +45,7 @@ python setup.py install pip install x2paddle --index https://pypi.python.org/simple/ ``` ## 快速开始 -### 预测模型转换 +### 功能一:预测模型转换 | 参数 | 作用 | | -------------------- | ------------------------------------------------------------ | | --framework | 源模型类型 (tensorflow、caffe、onnx) | @@ -56,63 +54,61 @@ pip install x2paddle --index https://pypi.python.org/simple/ | --save_dir | 指定转换后的模型保存目录路径 | | --model | 当framework为tensorflow/onnx时,该参数指定tensorflow的pb模型文件或onnx模型路径 | | --caffe_proto | **[可选]** 由caffe.proto编译成caffe_pb2.py文件的存放路径,当存在自定义Layer时使用,默认为None | -| --define_input_shape | **[可选]** For TensorFlow, 当指定该参数时,强制用户输入每个Placeholder的shape,见[文档Q2](./docs/user_guides/FAQ.md) | -| --paddle_type | **[可选]** 该参数指定转换为动态图代码(dygraph)或者静态图代码(static),默认为dygraph | +| --define_input_shape | **[可选]** For TensorFlow, 当指定该参数时,强制用户输入每个Placeholder的shape,见[文档Q2](./docs/inference_model_convertor/FAQ.md) | #### TensorFlow +```shell +x2paddle --framework=tensorflow --model=tf_model.pb --save_dir=pd_model ``` -x2paddle --framework=tensorflow --model=tf_model.pb --save_dir=pd_model --paddle_type dygraph -``` +【注意】目前只支持FrozenModel格式的TensorFlow模型到PaddlePaddle模型的转换,若为checkpoint或者SavedModel格式的TensorFlow模型参见[文档](./docs/inference_model_convertor/export_tf_model.md)导出FrozenModel格式模型。 #### Caffe +```shell +x2paddle --framework=caffe --prototxt=deploy.prototxt --weight=deploy.caffemodel --save_dir=pd_model ``` -x2paddle --framework=caffe --prototxt=deploy.prototxt --weight=deploy.caffemodel --save_dir=pd_model --paddle_type dygraph -``` +【注意】若caffe模型中出现自定义层,需要按照[相关流程](./docs/inference_model_convertor/add_caffe_custom_layer.md)自行添加自定义层的转换代码。 #### ONNX +```shell +x2paddle --framework=onnx --model=onnx_model.onnx --save_dir=pd_model ``` -x2paddle --framework=onnx --model=onnx_model.onnx --save_dir=pd_model --paddle_type dygraph -``` - +【注意】如若需要将PyTorch模型转换为ONNX模型,可参见[PyTorch2ONNX转换文档](./docs/inference_model_convertor/pytorch2onnx.md)。 #### PyTorch -PyTorch仅支持API使用方式,详见[PyTorch预测模型转换文档](./docs/user_guides/pytorch2paddle.md)。 +PyTorch仅支持API使用方式,详见[PyTorch预测模型转换文档](./docs/inference_model_convertor/pytorch2paddle.md)。 -### 训练项目转换 +***[预测模型转换常见问题](./docs/inference_model_convertor/FAQ.md)*** -#### PyTorch -【待更新】可安装[分支](https://github.com/PaddlePaddle/X2Paddle/tree/pytorch_project_convertor)源码进行使用。 -详见[PyTorch训练项目转换文档](https://github.com/SunAhong1993/X2Paddle/blob/code_convert_last/docs/pytorch_project_convertor/README.md)。 +### 功能二:训练项目转换:heart: :heart: -## 小工具 -X2Paddle提供了工具解决如下问题,详见[tools/README.md](tools/README.md) -1. 检测模型是否在PaddleLite中支持 -2. 合并模型参数文件 +| 参数 | 作用 | +|----------|--------------| +|--convert_torch_project | 表示使用对PyTorch Project进行转换的功能 | +|--project_dir | PyTorch的项目路径 | +|--save_dir | 指定转换后项目的保存路径 | +|--pretrain_model | **[可选]**需要转换的预训练模型的路径(文件后缀名为“.pth”、“.pt”、“.ckpt”)或者包含预训练模型的文件夹路径,转换后的模型将将保在当前路径,后缀名为“.pdiparams” | +```shell +x2paddle --convert_torch_project --project_dir=torch_project --save_dir=paddle_project --pretrain_model=model.pth +``` +【注意】需要搭配预处理和后处理一起使用,详细可参见[训练项目转换文档](./docs/pytorch_project_convertor/README.md)。 此外,我们为用户提供了:star:[PyTorch-PaddlePaddle API映射表](docs/pytorch_project_convertor/API_docs/README.md):star:供用户查阅。 -## 使用相关文档 -1. [X2Paddle使用过程中常见问题](./docs/user_guides/FAQ.md) -2. [如何导出TensorFlow的Frozen Model](./docs/user_guides/export_tf_model.md) -3. [PyTorch模型导出为ONNX模型](./docs/user_guides/pytorch2onnx.md) -4. [X2Paddle添加内置的Caffe自定义层](./docs/user_guides/add_caffe_custom_layer.md) -5. [转换后PaddlePaddle预测模型简介](./docs/user_guides/pd_folder_introduction.py) -6. [Paddle到ONNX的转换](https://github.com/PaddlePaddle/Paddle2ONNX) -7. [X2Paddle测试模型库](./docs/introduction/x2paddle_model_zoo.md) -8. [X2Paddle支持的op列表](./docs/introduction/op_list.md) +***[训练项目转换常见问题](./docs/pytorch_project_convertor/FAQ.md)*** ## 转换教程 -1. [TensorFlow预测模型转换教程](./docs/demo/tensorflow2paddle.ipynb) -2. [PyTorch预测模型转换教程](./docs/demo/pytorch2paddle.ipynb) +1. [TensorFlow预测模型转换教程](./docs/inference_model_convertor/demo/tensorflow2paddle.ipynb) +2. [PyTorch预测模型转换教程](./docs/inference_model_convertor/demo/pytorch2paddle.ipynb) +3. [PyTorch训练项目转换教程](./docs/pytorch_project_convertor/demo.md) ## 更新历史 -2020.12.09 +**2020.12.09** 1. 新增PyTorch2Paddle转换方式,转换得到Paddle动态图代码,并动转静获得inference_model。 - 方式一:trace方式,转换后的代码有模块划分,每个模块的功能与PyTorch相同。 + 方式一:trace方式,转换后的代码有模块划分,每个模块的功能与PyTorch相同。 方式二:script方式,转换后的代码按执行顺序逐行出现。 2. 新增Caffe/ONNX/Tensorflow到Paddle动态图的转换。 3. 新增TensorFlow op映射(14个):Neg、Greater、FloorMod、LogicalAdd、Prd、Equal、Conv3D、Ceil、AddN、DivNoNan、Where、MirrorPad、Size、TopKv2。 4. 新增Optimizer模块,主要包括op融合、op消除功能,转换后的代码可读性更强,进行预测时耗时更短。 -2021.04.30 +**2021.04.30** 1. 新增支持转换的模型:[SwinTransformer](https://github.com/microsoft/Swin-Transformer/)、[BASNet](https://github.com/xuebinqin/BASNet)、[DBFace](https://github.com/dlunion/DBFace)、[EasyOCR](https://github.com/JaidedAI/EasyOCR)、[CifarNet](https://github.com/tensorflow/models/blob/master/research/slim/nets/cifarnet.py)等。 2. 支持Windows上使用本工具。 3. 新增TensorFlow op映射(4个):SplitV、ReverseV2、BatchToSpaceND、SpaceToBatchND。 @@ -120,6 +116,11 @@ X2Paddle提供了工具解决如下问题,详见[tools/README.md](tools/README 5. 新增ONNX op映射(1个):DepthToSpace。 6. 新增Caffe op映射(1个):MemoryData。 -## 贡献代码 +**2021.05.13** +- 新增PyTorch训练项目功能: +支持转换的项目有[StarGAN](https://github.com/yunjey/stargan)、[Ultra-Light-Fast-Generic-Face-Detector-1MB](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB)。 + + +## :hugs:贡献代码:hugs: -我们非常欢迎您为X2Paddle贡献代码或者提供使用建议。如果您可以修复某个issue或者增加一个新功能,欢迎给我们提交Pull Requests。 +我们非常欢迎您为X2Paddle贡献代码或者提供使用建议。如果您可以修复某个issue或者增加一个新功能,欢迎给我们提交Pull Requests,如果有PyTorch训练项目转换需求欢迎随时提issue~ diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 656efbee055b7c4206df655cfeb8e5b1110dee9b..0000000000000000000000000000000000000000 --- a/docs/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# 一、 introduction -1. op_list.md:当前转换各个框架支持的op。 -2. x2paddle_model_zoo.md:测试过的模型列表。 -# 二、 user_guides -1. FQA.md:常见问题集合。 -2. add_caffe_custom_layer.md:添加caffe自定义Layer的方法,以及当前支持的自定义Layer列表。 -3. export_tf_model.md:导出本工具支持的TensorFlow模型。 -4. pytorch2onnx.md:将PyTorch导出为ONNX。 -5. pytorch2paddle.md:将PyTorch模型转换为Paddle模型。 diff --git a/docs/inference_model_convertor/FAQ.md b/docs/inference_model_convertor/FAQ.md new file mode 100644 index 0000000000000000000000000000000000000000..a016e52b7354b139940717e7d816bf0992328070 --- /dev/null +++ b/docs/inference_model_convertor/FAQ.md @@ -0,0 +1,37 @@ +## 常见问题 + +**Q1. TensorFlow模型转换过程中,提示『Unknown shape for input tensor[tensor name: "input"], Please define shape of input here』?** +A:该提示信息表示无法从TensorFlow的pb模型中获取到输入tensor(tensor名为"input:)的shape信息,所以需要用户手动在提示后输入详细的shape信息,如None,224,224,3 其中None表示Batch。 + + +**Q2. TensorFlow模型转换失败怎么解决?** +A: 如果并非是由缺少OP导致,那可能是由于TensorFlow模型转换时(NHWC->NCHW格式转换导致),在这种情况下,采用如下方式进行转换,同时固化输入大小的方式,继续尝试转换,见如下命令,转换过程中,根据提示,输入相应tensor的固化shape大小。 +``` +x2paddle -f tensorflow -m tf.pb -s pd-model --define_input_shape +``` + +**Q3. ONNX模型转换过程中,提示『Unknown shape for input tensor[tensor name: "input"] -> shape: ['batch', 'sequence'], Please define shape of input here』** +A:该提示信息表示从ONNX的模型中获取到输入tensor(tensor名为"input:)的shape是语义象征性的['batch', 'sequence'],而不是dim为int类型的shape,从而可能会因为部分node的shape无法推理,导致转换失败。所以用户可以尝试手动在提示后输入详细的shape信息,如:-1,3,224,224 其中-1表示Batch。 + +**Q4. 如果我的tensorflow模型是checkpoint或者SavedModel格式,怎么办?** +A:我们提供相关文档将export_tf_model.md + + +**Q4. 进行动态图转换时,提示『Fail to generate inference model! Problem happend while export inference model from python code...』** +A: 此提示为无法将动态图代码转换为静态图模型,有两种可能: +> 使用动态图代码确认转换后的代码是否正确,可使用如下代码进行确认: +``` +import paddle +import numpy as np +np.random.seed(6) +# ipt为输入数据 +ipt = np.random.rand(1, 3, 224, 224).astype("float32") +paddle.disable_static() +# pd_model_dygraph为保存路径(其中的”/“用”.“替换) +from pd_model_dygraph.x2paddle_code import main +out =main(ipt) +``` +> 若运行代码无误,则说明代码中有op不支持动转静,我们将会再未来支持;若报错,则说明pytorch2paddle转换出错,请提issue,我们将及时回复。 + +**Q5. 目前支持了哪些op的转换呢?** +A: 可详见[X2Paddle支持的op列表](./docs/introduction/op_list.md)。 diff --git a/docs/user_guides/add_caffe_custom_layer.md b/docs/inference_model_convertor/add_caffe_custom_layer.md similarity index 96% rename from docs/user_guides/add_caffe_custom_layer.md rename to docs/inference_model_convertor/add_caffe_custom_layer.md index 575be41a577fbd03247c3fe51a6e68286d41bf96..58dc16c6fa748314107ed85f3983c0882a39c579 100644 --- a/docs/user_guides/add_caffe_custom_layer.md +++ b/docs/inference_model_convertor/add_caffe_custom_layer.md @@ -1,6 +1,6 @@ ## 如何转换Caffe自定义Layer -本文档介绍如何将Caffe自定义Layer转换为PaddlePaddle模型中的对应实现, 用户可根据自己需要,添加代码实现自定义层,从而支持模型的完整转换。 +本文档介绍如何将Caffe自定义Layer转换为PaddlePaddle模型中的对应实现, 用户可根据自己需要,添加代码实现自定义层,从而支持模型的完整转换。 目前,代码中已经提供了10个非官方op(不在[官网](http://caffe.berkeleyvision.org/tutorial/layers)上的op)的转换,这些op对应的Caffe实现源码如下: | op | 该版本实现源码 | @@ -49,7 +49,7 @@ def Permute(self, node): node.inputs) == 1, "The count of Permute node\'s input is not 1." input = self.graph.get_input_node(node, idx=0, copy=True) params = node.layer.permute_param - order = list(params.order) + order = list(params.order) self.paddle_graph.add_layer( "paddle.transpose", inputs={"x": input.name}, @@ -80,7 +80,7 @@ def shape_permute(layer, input_shape): - 方式二: 1. 进入./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_custom_layer,创建.py文件,例如mylayer.py 2. 仿照./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_custom_layer中的其他文件,在mylayer.py中主要需要实现1个类,下面以roipooling.py为例分析代码: - + ```python class ROIPooling(object): def __init__(self, pooled_height, pooled_width, spatial_scale): @@ -90,10 +90,10 @@ class ROIPooling(object): "spatial_scale": spatial_scale} def __call__(self, x0, x1): - slice_x1 = paddle.slice(input=x1, axes=[1], + slice_x1 = paddle.slice(input=x1, axes=[1], starts=[1], ends=[5]) - out = fluid.layers.roi_pool(input=x0, - rois=slice_x1, + out = fluid.layers.roi_pool(input=x0, + rois=slice_x1, **self.roipooling_layer_attrs) return out ``` diff --git a/docs/demo/dog_pt.png b/docs/inference_model_convertor/demo/dog_pt.png similarity index 100% rename from docs/demo/dog_pt.png rename to docs/inference_model_convertor/demo/dog_pt.png diff --git a/docs/demo/dog_tf.png b/docs/inference_model_convertor/demo/dog_tf.png similarity index 100% rename from docs/demo/dog_tf.png rename to docs/inference_model_convertor/demo/dog_tf.png diff --git a/docs/demo/pytorch2paddle.ipynb b/docs/inference_model_convertor/demo/pytorch2paddle.ipynb similarity index 100% rename from docs/demo/pytorch2paddle.ipynb rename to docs/inference_model_convertor/demo/pytorch2paddle.ipynb diff --git a/docs/demo/tensorflow2paddle.ipynb b/docs/inference_model_convertor/demo/tensorflow2paddle.ipynb similarity index 100% rename from docs/demo/tensorflow2paddle.ipynb rename to docs/inference_model_convertor/demo/tensorflow2paddle.ipynb diff --git a/docs/user_guides/export_tf_model.md b/docs/inference_model_convertor/export_tf_model.md similarity index 99% rename from docs/user_guides/export_tf_model.md rename to docs/inference_model_convertor/export_tf_model.md index 87471afd03db35e640b57a0838d12ffec74580fb..82a5c2c1793533efd36dc95fbe6876fcc84e83a5 100644 --- a/docs/user_guides/export_tf_model.md +++ b/docs/inference_model_convertor/export_tf_model.md @@ -66,7 +66,7 @@ def freeze_model(sess, output_tensor_names, freeze_model_path): f.write(out_graph.SerializeToString()) print("freeze model saved in {}".format(freeze_model_path)) - + # 加载模型参数 # 此处需要修改input_checkpoint(checkpoint的前缀)和save_pb_file(模型导出的文件路径) input_checkpoint = "./tfhub_models/save/model.ckpt" diff --git a/docs/user_guides/pd_folder_introduction.md b/docs/inference_model_convertor/pd_folder_introduction.md similarity index 100% rename from docs/user_guides/pd_folder_introduction.md rename to docs/inference_model_convertor/pd_folder_introduction.md diff --git a/docs/user_guides/pytorch2onnx.md b/docs/inference_model_convertor/pytorch2onnx.md similarity index 100% rename from docs/user_guides/pytorch2onnx.md rename to docs/inference_model_convertor/pytorch2onnx.md diff --git a/docs/user_guides/pytorch2paddle.md b/docs/inference_model_convertor/pytorch2paddle.md similarity index 86% rename from docs/user_guides/pytorch2paddle.md rename to docs/inference_model_convertor/pytorch2paddle.md index 82f2d204f936c8b62f15654ba39dbb8559f88785..74976b5911566b509d8afd69a3b2d8e53a95beb9 100644 --- a/docs/user_guides/pytorch2paddle.md +++ b/docs/inference_model_convertor/pytorch2paddle.md @@ -5,20 +5,20 @@ PyTorch2Paddle支持trace和script两种方式的转换,均是PyTorch动态图 ## 环境依赖 python == 2.7 | python >= 3.5 -paddlepaddle >= 1.8.0 +paddlepaddle >= 2.0.0 pytorch:torch >=1.5.0 (script方式暂不支持1.7.0) -**使用trace方式需安装以下依赖** +**使用trace方式需安装以下依赖** pandas -treelib +treelib ## 使用方式 ``` python from x2paddle.convert import pytorch2paddle -pytorch2paddle(module=torch_module, - save_dir="./pd_model", - jit_type="trace", +pytorch2paddle(module=torch_module, + save_dir="./pd_model", + jit_type="trace", input_examples=[torch_input]) # module (torch.nn.Module): PyTorch的Module。 # save_dir (str): 转换后模型的保存路径。 @@ -46,8 +46,8 @@ torch_module.load_state_dict(torch_state_dict) torch_module.eval() # 进行转换 from x2paddle.convert import pytorch2paddle -pytorch2paddle(torch_module, - save_dir="pd_model_trace", - jit_type="trace", +pytorch2paddle(torch_module, + save_dir="pd_model_trace", + jit_type="trace", input_examples=[torch.tensor(input_data)]) ``` diff --git a/docs/introduction/architecture.md b/docs/introduction/architecture.md new file mode 100644 index 0000000000000000000000000000000000000000..96e08982ea80a1c041a0ccbf6451d205c8386702 --- /dev/null +++ b/docs/introduction/architecture.md @@ -0,0 +1,3 @@ +## 架构设计 +X2Paddle的架构设计着重考虑了对多深度学习框架的的支持以及代码的易读性、易扩展性。预测模型转换在多个层面的对转换后OP进行优化处理,转换后的模型可直接用于预测部署;训练代码转换则运用了AST的方式进行转换,转换后的代码与源代码格式一致。2种方式的转换满足不同人的需求。 +![](../images/frame.png) diff --git a/docs/introduction/x2paddle_model_zoo.md b/docs/introduction/x2paddle_model_zoo.md index 0c26f20d071a481fa5c629769d8a9933ebd98946..6e3db0718db15456b25a94ed217d6f77f7fb07c1 100644 --- a/docs/introduction/x2paddle_model_zoo.md +++ b/docs/introduction/x2paddle_model_zoo.md @@ -1,9 +1,6 @@ -# X2Paddle模型测试库 -> 目前X2Paddle支持80+的TensorFlow OP,30+的Caffe Layer,60+的ONNX OP,110+的PyTorch Aten,10+的PyTorch Prim,覆盖了大部分CV分类模型常用的操作。我们在如下模型列表中测试了X2Paddle的转换。 +# X2Paddle转换库 -**注:** 受限于不同框架的差异,部分模型可能会存在目前无法转换的情况,如TensorFlow中包含控制流的模型,NLP模型等。对于CV常见的模型,如若您发现无法转换或转换失败,存在较大diff等问题,欢迎通过[ISSUE反馈](https://github.com/PaddlePaddle/X2Paddle/issues/new)的方式告知我们(模型名,代码实现或模型获取方式),我们会及时跟进:) - -## TensorFlow +## TensorFlow预测模型 | 模型 | 代码 | |------|----------| @@ -28,7 +25,7 @@ | Bert(chinese_L-12_H-768_A-12) | [code](https://github.com/google-research/bert#pre-trained-models) | | Bert(multi_cased_L-12_H-768_A-12) | [code](https://github.com/google-research/bert#pre-trained-models) | -## Caffe +## Caffe预测模型 | 模型 | 代码 | |-------|--------| @@ -51,7 +48,7 @@ -## ONNX +## ONNX预测模型 **注:** 部分模型来源于PyTorch,PyTorch的转换可参考[pytorch_to_onnx.md](pytorch_to_onnx.md) | 模型 | 来源 | operator version|备注| @@ -72,11 +69,12 @@ | EfficientNet | [pytorch(personal practice)](https://github.com/rwightman/gen-efficientnet-pytorch) |9| | SqueezeNet | [onnx official](https://s3.amazonaws.com/download.onnx/models/opset_9/squeezenet.tar.gz) |9| |Ultra-Light-Fast-Generic-Face-Detector-1MB| [onnx_model](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/models/onnx)|9 | -|BERT| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](../user_guides/FAQ.md)| -|GPT2| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](../user_guides/FAQ.md)| +|BERT| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](../inference_model_convertor/FAQ.md)| +|GPT2| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](../inference_model_convertor/FAQ.md)| +|CifarNet | [tensorflow](https://github.com/tensorflow/models/blob/master/research/slim/nets/cifarnet.py)|9|| -## PyTorch +## PyTorch预测模型 | 模型 | 代码 | 备注 | |------|----------|------| @@ -98,4 +96,15 @@ | XLMRobertaForTokenClassification|[code](https://huggingface.co/transformers/model_doc/xlmroberta.html) |只支持trace模式| | EasyOCR_detector|[code](https://github.com/JaidedAI/EasyOCR/blob/master/easyocr/detection.py) |-| | EasyOCR_recognizer|[code](https://github.com/JaidedAI/EasyOCR/blob/master/easyocr/recognition.py) |-| +| SwinTransformer|[code](https://github.com/microsoft/Swin-Transformer/) |-| +| BASNet|[code](https://github.com/xuebinqin/BASNet) |-| +| DBFace |[code](https://github.com/dlunion/DBFacet) |-| + +## PyTorch训练项目 +| 模型 | 转换前代码 | 转换后代码 | +|------|----------|------| +| StaGAN | [code](https://github.com/yunjey/stargan)|[code](https://github.com/SunAhong1993/stargan/tree/paddle)| +| Ultra-Light-Fast-Generic-Face-Detector | [code](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB) |[code](https://github.com/SunAhong1993/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/paddle)| + +**注:** 受限于不同框架的差异,部分模型可能会存在目前无法转换的情况,如TensorFlow中包含控制流的模型,NLP模型等。对于CV常见的模型,如若您发现无法转换或转换失败,存在较大diff等问题,欢迎通过[ISSUE反馈](https://github.com/PaddlePaddle/X2Paddle/issues/new)的方式告知我们(模型名,代码实现或模型获取方式),我们会及时跟进: diff --git a/docs/pytorch_project_convertor/API_docs/README.md b/docs/pytorch_project_convertor/API_docs/README.md new file mode 100644 index 0000000000000000000000000000000000000000..de77107b096d6549f9c71b264b99311604814adc --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/README.md @@ -0,0 +1,15 @@ +# PyTorch-PaddlePaddle API对应表 +本文档梳理了常用PyTorch 1.8.1 API与PaddlePaddle 2.0.0 API对应关系和差异分析。根据文档对应关系,有PyTorch使用经验的用户,可根据对应关系,快速熟悉PaddlePaddle的API使用。 + +## [基础操作类](./ops/README.md) + +## [组网类](./nn/README.md) + +## [Loss类](./loss/README.md) + +## [工具类](./utils/README.md) + +## [视觉类](./vision/README.md) + + +***持续更新...*** diff --git a/docs/pytorch_project_convertor/API_docs/loss/README.md b/docs/pytorch_project_convertor/API_docs/loss/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7839364e5097770de4d5eca0430fd102f4e6c16c --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/loss/README.md @@ -0,0 +1,13 @@ +***持续更新...***## Loss类 +| 序号 | PyTorch API | PaddlePaddle API | 备注 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | [torch.nn.L1Loss](https://pytorch.org/docs/stable/generated/torch.nn.L1Loss.html?highlight=l1loss#torch.nn.L1Loss) | [paddle.nn.loss.L1Loss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/L1Loss_cn.html#l1loss) | 功能一致,PyTroch存在废弃参数`size_average`和`reduce`。 | +| 2 | [torch.nn.MSELoss](https://pytorch.org/docs/stable/generated/torch.nn.MSELoss.html?highlight=mseloss#torch.nn.MSELoss) | [paddle.nn.MSELoss](https://pytorch.org/docs/stable/generated/torch.nn.MSELoss.html?highlight=mseloss#torch.nn.MSELoss) | 功能一致,PyTroch存在废弃参数`size_average`和`reduce`。 | +| 3 | [torch.nn.CrossEntropyLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/CrossEntropyLoss_cn.html#crossentropyloss) | [paddle.nn.CrossEntropyLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/CrossEntropyLoss_cn.html#crossentropyloss) | [差异对比](torch.nn.CrossEntropyLoss.md) | +| 4 | [torch.nn.KLDivLoss](https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html?highlight=kldivloss#torch.nn.KLDivLoss) | [paddle.nn.KLDivLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/KLDivLoss_cn.html) | [差异对比](torch.nn.KLDivLoss.md) | +| 5 | [torch.nn.BCELoss](https://pytorch.org/docs/stable/generated/torch.nn.BCELoss.html?highlight=bceloss#torch.nn.BCELoss) | [paddle.nn.BCELoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/BCELoss_cn.html#bceloss) | 功能一致,PyTroch存在废弃参数`size_average`和`reduce`。 | +| 6 | [torch.nn.BCEWithLogitsLoss](https://pytorch.org/docs/stable/generated/torch.nn.BCEWithLogitsLoss.html?highlight=bcewithlogitsloss#torch.nn.BCEWithLogitsLoss) | [paddle.nn.BCEWithLogitsLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/BCEWithLogitsLoss_cn.html#bcewithlogitsloss) | 功能一致,PyTroch存在废弃参数`size_average`和`reduce`。 | +| 7 | [torch.nn.SmoothL1Loss](https://pytorch.org/docs/stable/generated/torch.nn.SmoothL1Loss.html?highlight=torch%20nn%20smoothl1loss#torch.nn.SmoothL1Loss) | [paddle.nn.SmoothL1Loss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/SmoothL1Loss_cn.html#smoothl1loss) | 功能一致,参数名不一致,PyTroch存在废弃参数`size_average`和`reduce`。 | + + +***持续更新...*** diff --git a/docs/pytorch_project_convertor/API_docs/loss/torch.nn.CrossEntropyLoss.md b/docs/pytorch_project_convertor/API_docs/loss/torch.nn.CrossEntropyLoss.md new file mode 100644 index 0000000000000000000000000000000000000000..f9c0af6013dc36cbe0c09e326ebc58902f74225e --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/loss/torch.nn.CrossEntropyLoss.md @@ -0,0 +1,33 @@ +## torch.nn.CrossEntropyLoss +### [torch.nn.CrossEntropyLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/CrossEntropyLoss_cn.html#crossentropyloss) +```python +torch.nn.CrossEntropyLoss(weight=None, + size_average=None, + ignore_index=-100, + reduce=None, + reduction='mean') +``` +### [paddle.nn.CrossEntropyLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/CrossEntropyLoss_cn.html#crossentropyloss) +```python +paddle.nn.CrossEntropyLoss(weight=None, + ignore_index=-100, + reduction='mean', + soft_label=False, + axis=-1, + use_softmax=True, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size_average | - | PyTorch废弃参数。 | +| reduce | - | PyTorch废弃参数。 | +| - | use_softmax | 表示在使用交叉熵之前是否计算softmax,PyTorch无此参数。 | +| - | soft_label | 指明label是否为软标签,PyTorch无此参数。 | +| - | axis | 表示进行softmax计算的维度索引,PyTorch无此参数。 | + +### 功能差异 +#### 计算方式 +***PyTorch***:只支持在使用交叉熵之前计算softmax且为硬标签的计算方式。 +***PaddlePaddle***:支持使用交叉熵之前是否计算softmax的设置,且支持软、硬标签两种计算方式,其计算方式可参见[文档](https://www.paddlepaddle.org.cn/documentation/docs/en/api/paddle/nn/layer/loss/CrossEntropyLoss_en.html)。 diff --git a/docs/pytorch_project_convertor/API_docs/loss/torch.nn.KLDivLoss.md b/docs/pytorch_project_convertor/API_docs/loss/torch.nn.KLDivLoss.md new file mode 100644 index 0000000000000000000000000000000000000000..3a5af5928de9aef819f94a6bfab7d7860dfa56fe --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/loss/torch.nn.KLDivLoss.md @@ -0,0 +1,78 @@ +## torch.nn.KLDivLoss +### [torch.nn.KLDivLoss](https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html?highlight=kldivloss#torch.nn.KLDivLoss) +```python +torch.nn.KLDivLoss(size_average=None, + reduce=None, + reduction='mean', + log_target=False) +``` + +### [paddle.nn.KLDivLoss](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/loss/KLDivLoss_cn.html) +```python +paddle.nn.KLDivLoss(reduction='mean') +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size_average | - | PyTorch废弃参数。 | +| reduce | - | PyTorch废弃参数。 | +| log_target | - | 表示是否对目标值进行log处理,PaddlePaddle无此参数。 | + +### 功能差异 +#### 计算方式 +***PyTorch***: +> 当`log_target`为`True`时: +> $ l(input,label)= e^{target}∗(label−input) $ +> +> 当`log_target`为`False`时: +> 1. $ l(input,label)=target*(log(target)-input) $ +> 2. $ l(input,label) $中值小于0的取0 + +***PaddlePaddle***: +> $ l(input,label)=label∗(log(label)−input) $ + +在PaddlePaddle中可使用如下代码组合实现该API。 +```python +import paddle + +# 定义KLDivLoss +class KLDivLoss(paddle.nn.Layer): + def __init__(self, + size_average=None, + reduce=None, + reduction='mean', + log_target=False): + super().__init__() + self.reduction = reduction + self.log_target = log_target + + def forward(self, input, target): + if self.log_target: + out = paddle.exp(target) * (target - input) + else: + out_pos = target * (paddle.log(target) - input) + zeros = paddle.zeros_like(out_pos) + out = paddle.where(target > 0, out_pos, zeros) + out_sum = paddle.sum(out) + if self.reduction == "sum": + return out_sum + elif self.reduction == "batchmean": + n = input.shape[0] + return out_sum / n + elif self.reduction == "mean": + return paddle.mean(out) + else: + return out + +# 构造输入 +import numpy as np +shape = (5, 20) +x = np.random.uniform(-10, 10, shape).astype('float32') +target = np.random.uniform(-10, 10, shape).astype('float32') + +# 计算loss +kldiv_criterion = KLDivLoss() +pred_loss = kldiv_criterion(paddle.to_tensor(x), + paddle.to_tensor(target)) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/README.md b/docs/pytorch_project_convertor/API_docs/nn/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0d11bec42bf80e9edcc254b9d632ce42ec6db3e1 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/README.md @@ -0,0 +1,53 @@ +## 组网类 + +| 序号 | PyTorch API | PaddlePaddle API | 备注 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | [torch.nn.Conv1d](https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html?highlight=torch%20nn%20conv1d#torch.nn.Conv1d) | [paddle.nn.Conv1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv1D_cn.html#conv1d) | [差异对比](torch.nn.Conv1d.md) | +| 2 | [torch.nn.Conv2d](https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html?highlight=conv2d#torch.nn.Conv2d) | [paddle.nn.Conv2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv2D_cn.html#conv2d) | [差异对比](torch.nn.Conv2d.md) | +| 3 | [torch.nn.Conv3d](https://pytorch.org/docs/stable/generated/torch.nn.Conv3d.html?highlight=conv3d#torch.nn.Conv3d) | [paddle.nn.Conv3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv3D_cn.html#conv3d) | [差异对比](torch.nn.Conv3d.md) | +| 4 | [torch.nn.ConvTranspose1d](https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose1d.html?highlight=torch%20nn%20convtranspose1d#torch.nn.ConvTranspose1d) | [paddle.nn.Conv1DTranspose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv1DTranspose_cn.html#conv1dtranspose) | [差异对比](torch.nn.ConvTranspose1d.md) | +| 5 | [torch.nn.ConvTranspose2d](https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose2d.html?highlight=convtranspose2d#torch.nn.ConvTranspose2d) | [paddle.nn.Conv2DTranspose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv2DTranspose_cn.html#conv2dtranspose) | [差异对比](torch.nn.ConvTranspose2d.md) | +| 6 | [torch.nn.ConvTranspose3d](https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose3d.html?highlight=convtranspose3d#torch.nn.ConvTranspose3d) | [paddle.nn.Conv3DTranspose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv3DTranspose_cn.html#conv3dtranspose) | [差异对比](torch.nn.ConvTranspose3d.md) | +| 7 | [torch.nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html?highlight=linear#torch.nn.Linear) | [paddle.nn.Linear](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Linear_cn.html#linear) | [差异对比](torch.nn.Linear.md) | +| 8 | [torch.nn.MaxPool1d](https://pytorch.org/docs/stable/generated/torch.nn.MaxPool1d.html?highlight=maxpool1d#torch.nn.MaxPool1d) | [paddle.nn.MaxPool1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/MaxPool1D_cn.html#maxpool1d) | [差异对比](torch.nn.MaxPool1d.md) | +| 9 | [torch.nn.MaxPool2d](https://pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html?highlight=maxpool2d#torch.nn.MaxPool2d) | [paddle.nn.MaxPool2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/MaxPool2D_cn.html#maxpool2d) | [差异对比](torch.nn.MaxPool2d.md) | +| 10 | [torch.nn.MaxPool3d](https://pytorch.org/docs/stable/generated/torch.nn.MaxPool3d.html?highlight=maxpool3d#torch.nn.MaxPool3d) | [paddle.nn.MaxPool3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/MaxPool3D_cn.html#maxpool3d) | [差异对比](torch.nn.MaxPool3d.md) | +| 11 | [torch.nn.MaxUnpool1d](https://pytorch.org/docs/stable/generated/torch.nn.MaxUnpool1d.html?highlight=unpool#torch.nn.MaxUnpool1d) | 无对应实现 | [组合实现](torch.nn.MaxUnpool1d.md) | +| 12 | [torch.nn.MaxUnpool2d](https://pytorch.org/docs/stable/generated/torch.nn.MaxUnpool2d.html?highlight=unpool#torch.nn.MaxUnpool2d) | 无对应实现 | [组合实现](torch.nn.MaxUnpool2d.md) | +| 13 | [torch.nn.MaxUnpool3d](https://pytorch.org/docs/stable/generated/torch.nn.MaxUnpool3d.html?highlight=unpool#torch.nn.MaxUnpool3d) | 无对应实现 | [组合实现](torch.nn.MaxUnpool3d.md) | +| 14 | [torch.nn.AvgPool1d](https://pytorch.org/docs/stable/generated/torch.nn.AvgPool1d.html?highlight=avgpool1d#torch.nn.AvgPool1d) | [paddle.nn.AvgPool1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AvgPool1D_cn.html#avgpool1d) | [差异对比](torch.nn.AvgPool1d.md) | +| 15 | [torch.nn.AvgPool2d](https://pytorch.org/docs/stable/generated/torch.nn.AvgPool2d.html?highlight=avgpool2d#torch.nn.AvgPool2d) | [paddle.nn.AvgPool2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AvgPool2D_cn.html#avgpool2d) | [差异对比](torch.nn.AvgPool2d.md) | +| 16 | [torch.nn.AvgPool3d](https://pytorch.org/docs/stable/generated/torch.nn.AvgPool3d.html?highlight=avgpool3d#torch.nn.AvgPool3d) | [paddle.nn.AvgPool3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AvgPool3D_cn.html#avgpool3d) | [差异对比](torch.nn.AvgPool3d.md) | +| 17 | [torch.nn.AdaptiveMaxPool1d](https://pytorch.org/docs/stable/generated/torch.nn.AdaptiveMaxPool1d.html?highlight=adaptivemaxpool1d#torch.nn.AdaptiveMaxPool1d) | [paddle.nn.AdaptiveMaxPool1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AdaptiveMaxPool1D_cn.html#adaptivemaxpool1d) | 功能一致,参数名不一致 | +| 18 | [torch.nn.AdaptiveMaxPool2d](https://pytorch.org/docs/stable/generated/torch.nn.AdaptiveMaxPool2d.html?highlight=adaptivemaxpool2d#torch.nn.AdaptiveMaxPool2d) | [paddle.nn.AdaptiveMaxPool2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AdaptiveMaxPool2D_cn.html#adaptivemaxpool2d) | 功能一致,参数名不一致 | +| 19 | [torch.nn.AdaptiveMaxPool3d](https://pytorch.org/docs/stable/generated/torch.nn.AdaptiveMaxPool3d.html?highlight=adaptivemaxpool3d#torch.nn.AdaptiveMaxPool3d) | [paddle.nn.AdaptiveMaxPool3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AdaptiveMaxPool3D_cn.html#adaptivemaxpool3d) | 功能一致,参数名不一致 | +| 20 | [torch.nn.AdaptiveAvgPool1d](https://pytorch.org/docs/stable/generated/torch.nn.AdaptiveAvgPool1d.html?highlight=adaptiveavgpool1d#torch.nn.AdaptiveAvgPool1d) | [paddle.nn.AdaptiveAvgPool1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AdaptiveAvgPool1D_cn.html#adaptiveavgpool1d) | 功能一致,参数名不一致 | +| 21 | [torch.nn.AdaptiveAvgPool2d](https://pytorch.org/docs/stable/generated/torch.nn.AdaptiveAvgPool2d.html?highlight=adaptiveavgpool2d#torch.nn.AdaptiveAvgPool2d) | [paddle.nn.AdaptiveAvgPool2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AdaptiveAvgPool2D_cn.html#adaptiveavgpool2d) | 功能一致,参数名不一致 | +| 22 | [torch.nn.AdaptiveAvgPool3d](https://pytorch.org/docs/stable/generated/torch.nn.AdaptiveAvgPool3d.html?highlight=adaptiveavgpool3d#torch.nn.AdaptiveAvgPool3d) | [paddle.nn.AdaptiveAvgPool3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AdaptiveAvgPool3D_cn.html#adaptiveavgpool3d) | 功能一致,参数名不一致 | +| 23 | [torch.nn.ConstantPad1d](https://pytorch.org/docs/stable/generated/torch.nn.ConstantPad1d.html?highlight=pad#torch.nn.ConstantPad1d) | [paddle.nn.Pad1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad1D_cn.html#pad1d) | [差异对比](torch.nn.ConstantPad1d.md) | +| 24 | [torch.nn.ConstantPad2d](https://pytorch.org/docs/stable/generated/torch.nn.ConstantPad2d.html?highlight=pad#torch.nn.ConstantPad2d) | [paddle.nn.Pad2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad2D_cn.html#pad2d) | [差异对比](torch.nn.ConstantPad2d.md) | +| 25 | [torch.nn.ConstantPad3d](https://pytorch.org/docs/stable/generated/torch.nn.ConstantPad3d.html?highlight=pad#torch.nn.ConstantPad3d) | [paddle.nn.Pad3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad3D_cn.html#pad3d) | [差异对比](torch.nn.ConstantPad3d.md) | +| 26 | [torch.nn.ReflectionPad1d](https://pytorch.org/docs/stable/generated/torch.nn.ReflectionPad1d.html?highlight=pad#torch.nn.ReflectionPad1d) | [paddle.nn.Pad1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad1D_cn.html#pad1d) | [差异对比](torch.nn.ReflectionPad1d.md) | +| 27 | [torch.nn.ReflectionPad2d](https://pytorch.org/docs/stable/generated/torch.nn.ReflectionPad2d.html?highlight=pad#torch.nn.ReflectionPad2d) | [paddle.nn.Pad2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad2D_cn.html#pad2d) | [差异对比](torch.nn.ReflectionPad2d.md) | +| 28 | [torch.nn.ReplicationPad1d](https://pytorch.org/docs/stable/generated/torch.nn.ReplicationPad1d.html?highlight=pad#torch.nn.ReplicationPad1d) | [paddle.nn.Pad1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad1D_cn.html#pad1d) | [差异对比](torch.nn.ReplicationPad1d.md) | +| 29 | [torch.nn.ReplicationPad2d](https://pytorch.org/docs/stable/generated/torch.nn.ReplicationPad2d.html?highlight=pad#torch.nn.ReplicationPad2d) | [paddle.nn.Pad2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad2D_cn.html#pad2d) | [差异对比](torch.nn.ReplicationPad2d.md) | +| 30 | [torch.nn.ReplicationPad3d](https://pytorch.org/docs/stable/generated/torch.nn.ReplicationPad3d.html?highlight=pad#torch.nn.ReplicationPad3d) | [paddle.nn.Pad3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad3D_cn.html#pad3d) | [差异对比](torch.nn.ReplicationPad3d.md) | +| 31 | [torch.nn.BatchNorm1d](https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm1d.html?highlight=torch%20nn%20batchnorm1d#torch.nn.BatchNorm1d) | [paddle.nn.BatchNorm1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/norm/BatchNorm1D_cn.html#batchnorm1d) | [差异对比](torch.nn.BatchNorm1d.md) | +| 32 | [torch.nn.BatchNorm2d](https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm2d.html?highlight=batchnorm2d#torch.nn.BatchNorm2d) | [paddle.nn.BatchNorm2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/norm/BatchNorm2D_cn.html#batchnorm2d) | [差异对比](torch.nn.BatchNorm2d.md) | +| 33 | [torch.nn.BatchNorm3d](https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm3d.html?highlight=torch%20nn%20batchnorm3d#torch.nn.BatchNorm3d) | [paddle.nn.BatchNorm3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/norm/BatchNorm3D_cn.html#batchnorm3d) | [差异对比](torch.nn.BatchNorm3d.md) | +| 34 | [torch.nn.Upsample](https://pytorch.org/docs/stable/generated/torch.nn.Upsample.html?highlight=upsample#torch.nn.Upsample) | [paddle.nn.Upsample](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Upsample_cn.html#upsample) | [差异对比](torch.nn.Upsample.md) | +| 35 | [torch.nn.Dropout](https://pytorch.org/docs/stable/generated/torch.nn.Dropout.html?highlight=dropout#torch.nn.Dropout) | [paddle.nn.Dropout](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout_cn.html#dropout) | [差异对比](torch.nn.Dropout.md) | +| 36 | [torch.nn.Dropout2d](https://pytorch.org/docs/stable/generated/torch.nn.Dropout2d.html?highlight=dropout2d#torch.nn.Dropout2d) | [paddle.nn.Dropout2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout2D_cn.html#dropout2d) | [差异对比](torch.nn.Dropout2d.md) | +| 37 | [torch.nn.Dropout3d](https://pytorch.org/docs/stable/generated/torch.nn.Dropout3d.html?highlight=dropout3d#torch.nn.Dropout3d) | [paddle.nn.Dropout3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout3D_cn.html#dropout3d) | [差异对比](torch.nn.Dropout3d.md) | +| 38 | [torch.nn.LSTM](https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html?highlight=lstm#torch.nn.LSTM) | [paddle.nn.LSTM](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/rnn/LSTM_cn.html#lstm) | [差异对比](torch.nn.LSTM.md) | +| 39 | [torch.nn.GRU](https://pytorch.org/docs/stable/generated/torch.nn.GRU.html?highlight=torch%20nn%20gru#torch.nn.GRU) | [paddle.nn.GRU](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/rnn/GRU_cn.html#gru) | [差异对比](torch.nn.GRU.md) | +| 40 | [torch.nn.Embedding](https://pytorch.org/docs/stable/generated/torch.nn.Embedding.html?highlight=embedding#torch.nn.Embedding) | [paddle.nn.Embedding](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Embedding_cn.html#embedding) | [差异对比](torch.nn.Embedding.md) | +| 41 | [torch.nn.ELU](https://pytorch.org/docs/stable/generated/torch.nn.ELU.html?highlight=elu#torch.nn.ELU) | [paddle.nn.ELU](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/activation/ELU_cn.html#elu) | 功能一致,PaddlePaddle未定义`inplace`参数表示在不更改变量的内存地址的情况下,直接修改变量的值 | +| 42 | [torch.nn.Hardsigmoid](https://pytorch.org/docs/stable/generated/torch.nn.Hardsigmoid.html?highlight=hardsigmoid#torch.nn.Hardsigmoid) | [paddle.nn.Hardsigmoid](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/activation/Hardsigmoid_cn.html#hardsigmoid) | 功能一致,PaddlePaddle未定义`inplace`参数表示在不更改变量的内存地址的情况下,直接修改变量的值 | +| 43 | [torch.nn.LeakyReLU](https://pytorch.org/docs/stable/generated/torch.nn.LeakyReLU.html?highlight=leakyrelu#torch.nn.LeakyReLU) | [paddle.nn.LeakyReLU](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/activation/LeakyReLU_cn.html#leakyrelu) | 功能一致,PaddlePaddle未定义`inplace`参数表示在不更改变量的内存地址的情况下,直接修改变量的值 | +| 44 | [torch.nn.PReLU](https://pytorch.org/docs/stable/generated/torch.nn.PReLU.html?highlight=prelu#torch.nn.PReLU) | [paddle.nn.PReLU](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/activation/PReLU_cn.html#prelu) | 功能一致 | +| 45 | [torch.nn.ReLU](https://pytorch.org/docs/stable/generated/torch.nn.ReLU.html?highlight=relu#torch.nn.ReLU) | [paddle.nn.ReLU](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/activation/ReLU_cn.html#relu) | 功能一致,PaddlePaddle未定义`inplace`参数表示在不更改变量的内存地址的情况下,直接修改变量的值 | +| 46 | [torch.nn.Softmax](https://pytorch.org/docs/stable/generated/torch.nn.Softmax.html?highlight=softmax#torch.nn.Softmax) | [paddle.nn.Softmax](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/activation/Softmax_cn.html#softmax) | 功能一致,参数名不一致 | + + +***持续更新...*** diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool1d.md new file mode 100644 index 0000000000000000000000000000000000000000..c1f9aacc36ce1b30616ac3f8c4010fe7ed64138b --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool1d.md @@ -0,0 +1,27 @@ +# torch.nn.AvgPool1d +### [torch.nn.AvgPool1d](https://pytorch.org/docs/stable/generated/torch.nn.AvgPool1d.html?highlight=avgpool1d#torch.nn.AvgPool1d) + +```python +torch.nn.AvgPool1d(kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True) +``` + +### [paddle.nn.AvgPool1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AvgPool1D_cn.html#avgpool1d) + +```python +paddle.nn.AvgPool1D(kernel_size, + stride=None, + padding=0, + exclusive=True, + ceil_mode=False, + name=None) +``` + +### 功能差异 + +#### 池化方式 +***PyTorch***: 使用`count_include_pad`表示是否使用额外padding的值计算平均池化结果,默认为True。 +***PaddlePaddle***:使用`exclusive`表示是否不使用额外padding的值计算平均池化结果,默认为True。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool2d.md new file mode 100644 index 0000000000000000000000000000000000000000..2f5a47c0530acc4c521f53f0b406a28a4d65de69 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool2d.md @@ -0,0 +1,30 @@ +## torch.nn.AvgPool2d +### [torch.nn.AvgPool2d](https://pytorch.org/docs/stable/generated/torch.nn.AvgPool2d.html?highlight=avgpool2d#torch.nn.AvgPool2d) + +```python +torch.nn.AvgPool2d(kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None) +``` + +### [paddle.nn.AvgPool2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AvgPool2D_cn.html#avgpool2d) + +```python +paddle.nn.AvgPool2D(kernel_size, + stride=None, + padding=0, + ceil_mode=False, + exclusive=True, + divisor_override=None, + data_format='NCHW', + name=None) +``` + +### 功能差异 + +#### 池化方式 +***PyTorch***: 使用`count_include_pad`表示是否使用额外padding的值计算平均池化结果,默认为True。 +***PaddlePaddle***:使用`exclusive`表示是否不使用额外padding的值计算平均池化结果,默认为True。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool3d.md new file mode 100644 index 0000000000000000000000000000000000000000..372e1d3af3b3045289149f9ff509d4955acca608 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.AvgPool3d.md @@ -0,0 +1,30 @@ +## torch.nn.AvgPool3d +### [torch.nn.AvgPool3d](https://pytorch.org/docs/stable/generated/torch.nn.AvgPool3d.html?highlight=avgpool3d#torch.nn.AvgPool3d) + +```python +torch.nn.AvgPool3d(kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None) +``` + +### [paddle.nn.AvgPool3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/AvgPool3D_cn.html#avgpool3d) + +```python +paddle.nn.AvgPool3D(kernel_size, + stride=None, + padding=0, + ceil_mode=False, + exclusive=True, + divisor_override=None, + data_format='NCDHW', + name=None) +``` + +### 功能差异 + +#### 池化方式 +***PyTorch***: 使用`count_include_pad`表示是否使用额外padding的值计算平均池化结果,默认为True。 +***PaddlePaddle***:使用`exclusive`表示是否不使用额外padding的值计算平均池化结果,默认为True。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm1d.md new file mode 100644 index 0000000000000000000000000000000000000000..613cb62e3f28394cf82e35113eb2e664b2551ffa --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm1d.md @@ -0,0 +1,59 @@ +## torch.nn.BatchNorm1d +### [torch.nn.BatchNorm1d](https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm1d.html?highlight=torch%20nn%20batchnorm1d#torch.nn.BatchNorm1d) +```python +torch.nn.BatchNorm1d(num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True) +``` +### [paddle.nn.BatchNorm1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/norm/BatchNorm1D_cn.html#batchnorm1d) +```python +paddle.nn.BatchNorm1D(num_features, + momentum=0.9, + epsilon=1e-05, + weight_attr=None, + bias_attr=None, + data_format='NCL', + use_global_stats=True, + name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| eps | epsilon | 为了数值稳定加在分母上的值。 | +| - | weight_attr | 指定权重参数属性的对象。如果为False, 则表示每个通道的伸缩固定为1,不可改变。默认值为None,表示使用默认的权重参数属性。 | +| - | bias_attr | 指定偏置参数属性的对象。如果为False, 则表示每一个通道的偏移固定为0,不可改变。默认值为None,表示使用默认的偏置参数属性。 | +| affine | - | 是否进行反射变换,PaddlePaddle无此参数。 | +| track_running_stats | use_global_stats | 表示是否已加载的全局均值和方差。 | + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCL`的输入。 +***PaddlePaddle***:支持`NCL`和`NLC`两种格式的输入(通过`data_format`设置)。 + +#### 反射变换设置 +当PyTorch的反射变换设置为`False`,表示weight和bias不进行更新,由于PaddlePaddle不具备这一功能,可使用如下代码组合实现该API。 +```python +class BatchNorm1D(paddle.nn.BatchNorm1D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm2d.md new file mode 100644 index 0000000000000000000000000000000000000000..a13a9e3bf2ed8f697d6713be36a3279ac67095a6 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm2d.md @@ -0,0 +1,59 @@ +## torch.nn.BatchNorm2d +### [torch.nn.BatchNorm2d](https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm2d.html?highlight=batchnorm2d#torch.nn.BatchNorm2d) +```python +torch.nn.BatchNorm2d(num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True) +``` +### [paddle.nn.BatchNorm2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/norm/BatchNorm2D_cn.html#batchnorm2d) +```python +paddle.nn.BatchNorm2D(num_features, + momentum=0.9, + epsilon=1e-05, + weight_attr=None, + bias_attr=None, + data_format='NCHW', + use_global_stats=True, + name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| eps | epsilon | 为了数值稳定加在分母上的值。 | +| - | weight_attr | 指定权重参数属性的对象。如果为False, 则表示每个通道的伸缩固定为1,不可改变。默认值为None,表示使用默认的权重参数属性。 | +| - | bias_attr | 指定偏置参数属性的对象。如果为False, 则表示每一个通道的偏移固定为0,不可改变。默认值为None,表示使用默认的偏置参数属性。 | +| affine | - | 是否进行反射变换,PaddlePaddle无此参数。 | +| track_running_stats | use_global_stats | 表示是否已加载的全局均值和方差。 | + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### 反射变换设置 +当PyTorch的反射变换设置为`False`,表示weight和bias不进行更新,由于PaddlePaddle不具备这一功能,可使用如下代码组合实现该API。 +```python +class BatchNorm2D(paddle.nn.BatchNorm2D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm3d.md new file mode 100644 index 0000000000000000000000000000000000000000..50b9b3f32641e406663700597882df9fae3081fc --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.BatchNorm3d.md @@ -0,0 +1,59 @@ +## torch.nn.BatchNorm3d +### [torch.nn.BatchNorm3d](https://pytorch.org/docs/stable/generated/torch.nn.BatchNorm3d.html?highlight=torch%20nn%20batchnorm3d#torch.nn.BatchNorm3d) +```python +torch.nn.BatchNorm3d(num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True) +``` +### [paddle.nn.BatchNorm3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/norm/BatchNorm3D_cn.html#batchnorm3d) +```python +paddle.nn.BatchNorm3D(num_features, + momentum=0.9, + epsilon=1e-05, + weight_attr=None, + bias_attr=None, + data_format='NCDHW', + use_global_stats=True, + name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| eps | epsilon | 为了数值稳定加在分母上的值。 | +| - | weight_attr | 指定权重参数属性的对象。如果为False, 则表示每个通道的伸缩固定为1,不可改变。默认值为None,表示使用默认的权重参数属性。 | +| - | bias_attr | 指定偏置参数属性的对象。如果为False, 则表示每一个通道的偏移固定为0,不可改变。默认值为None,表示使用默认的偏置参数属性。 | +| affine | - | 是否进行反射变换,PaddlePaddle无此参数。 | +| track_running_stats | use_global_stats | 表示是否已加载的全局均值和方差。 | + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCDHW`的输入。 +***PaddlePaddle***:支持`NCDHW`和`NDHWC`两种格式的输入(通过`data_format`设置)。 + +#### 反射变换设置 +当PyTorch的反射变换设置为`False`,表示weight和bias不进行更新,由于PaddlePaddle不具备这一功能,可使用如下代码组合实现该API。 +```python +class BatchNorm3D(paddle.nn.BatchNorm3D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad1d.md new file mode 100644 index 0000000000000000000000000000000000000000..74091671ce43a408d99a421ed4ae34c26d083eb6 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad1d.md @@ -0,0 +1,58 @@ +## torch.nn.ConstantPad1d +### [torch.nn.ConstantPad1d](https://pytorch.org/docs/stable/generated/torch.nn.ConstantPad1d.html?highlight=pad#torch.nn.ConstantPad1d) +```python +torch.nn.ConstantPad1d(padding, value) +``` + +### [paddle.nn.Pad1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad1D_cn.html#pad1d) +```python +paddle.nn.Pad1D(padding, mode='constant', value=0.0, data_format='NCL', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`constant`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCL`的输入。 +***PaddlePaddle***:支持`NCL`和`NLC`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 2, 3) +pad = 1 +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ConstantPad1d(padding=pad, value=0) +result = my_pad(data) +# 输出 +# tensor([[[0., 1., 2., 3., 0.], +# [0., 4., 5., 6., 0.]]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 2, 3) +pad = [1, 1] +mode = "constant" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad1D(padding=pad, value=0, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 2, 5], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[0., 1., 2., 3., 0.], +# [0., 4., 5., 6., 0.]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad2d.md new file mode 100644 index 0000000000000000000000000000000000000000..ae907cfa63a74ab36ea2cddc5e814d3fe0be8f51 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad2d.md @@ -0,0 +1,64 @@ +## torch.nn.ConstantPad2d +### [torch.nn.ConstantPad2d](https://pytorch.org/docs/stable/generated/torch.nn.ConstantPad2d.html?highlight=pad#torch.nn.ConstantPad2d) +```python +torch.nn.ConstantPad2d(padding, value) +``` + +### [paddle.nn.Pad2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad2D_cn.html#pad2d) +```python +paddle.nn.Pad2D(padding, mode='constant', value=0.0, data_format='NCHW', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`constant`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 1, 2, 3) +pad = [1, 0, 1, 2] +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ConstantPad2d(padding=pad, value=0) +result = my_pad(data) +# 输出 +# tensor([[[[0., 0., 0., 0.], +# [0., 1., 2., 3.], +# [0., 4., 5., 6.], +# [0., 0., 0., 0.], +# [0., 0., 0., 0.]]]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 1, 2, 3) +pad = [1, 0, 1, 2] +mode = "constant" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad2D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 1, 5, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[0., 0., 0., 0.], +# [0., 1., 2., 3.], +# [0., 4., 5., 6.], +# [0., 0., 0., 0.], +# [0., 0., 0., 0.]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad3d.md new file mode 100644 index 0000000000000000000000000000000000000000..b88d522551d84830b8f20b2a0d80dfe8e4d58288 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConstantPad3d.md @@ -0,0 +1,65 @@ +## torch.nn.ConstantPad3d +### [torch.nn.ConstantPad3d](https://pytorch.org/docs/stable/generated/torch.nn.ConstantPad3d.html?highlight=pad#torch.nn.ConstantPad3d) +```python +torch.nn.ConstantPad3d(padding, value) +``` + +### [paddle.nn.Pad3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad3D_cn.html#pad3d) +```python +paddle.nn.Pad3D(padding, mode='constant', value=0.0, data_format='NCDHW', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`constant`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCDHW`的输入。 +***PaddlePaddle***:支持`NCDHW`和`NCDHW`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 1, 1, 2, 3) +pad = [1, 0, 1, 2, 0, 0] +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ConstantPad3d(padding=pad, value=0) +result = my_pad(data) +# 输出 +# tensor([[[[[0., 0., 0., 0.], +# [0., 1., 2., 3.], +# [0., 4., 5., 6.], +# [0., 0., 0., 0.], +# [0., 0., 0., 0.]]]]]) + +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 1, 1, 2, 3) +pad = [1, 0, 1, 2, 0, 0] +mode = "constant" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad3D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 1, 1, 5, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[[0., 0., 0., 0.], +# [0., 1., 2., 3.], +# [0., 4., 5., 6.], +# [0., 0., 0., 0.], +# [0., 0., 0., 0.]]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv1d.md new file mode 100644 index 0000000000000000000000000000000000000000..3eeab3aa8c8951297960f3b8e6102ebb74cf69a7 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv1d.md @@ -0,0 +1,44 @@ +# torch.nn.Conv1d +### [torch.nn.Conv1d](https://pytorch.org/docs/stable/generated/torch.nn.Conv1d.html?highlight=conv1d#torch.nn.Conv1d) + +```python +torch.nn.Conv1d(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + padding_mode='zeros') +``` + +### [paddle.nn.Conv1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv1D_cn.html#conv1d) + +```python +paddle.nn.Conv1D(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + padding_mode='zeros', + weight_attr=None, + bias_attr=None, + data_format='NCL') +``` + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCL`的输入。 +***PaddlePaddle***:支持`NCL`和`NLC`两种格式的输入(通过`data_format`设置)。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 + +#### padding的设置 +***PyTorch***:`padding`只能支持list或tuple类型。 +***PaddlePaddle***:`padding`支持list或tuple类型或str类型。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv2d.md new file mode 100644 index 0000000000000000000000000000000000000000..ba2ddb5a9c7cac641e2ab29dfd1c765347864a01 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv2d.md @@ -0,0 +1,51 @@ +# torch.nn.Conv2d +### [torch.nn.Conv2d](https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html?highlight=conv2d#torch.nn.Conv2d) + +```python +torch.nn.Conv2d(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + padding_mode='zeros') +``` + +### [paddle.nn.Conv2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv2D_cn.html#conv2d) + +```python +paddle.nn.Conv2D(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + padding_mode='zeros', + weight_attr=None, + bias_attr=None, + data_format='NCHW') +``` + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 + +#### padding的设置 +***PyTorch***:`padding`只能支持list或tuple类型。它可以有3种格式: +(1)包含4个二元组:\[\[0,0\], \[0,0\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\]\],其中每个元组都可使用整数值替换,代表元组中的2个值相等; +(2)包含2个二元组:\[\[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\]\],其中每个元组都可使用整数值替换,代表元组中的2个值相等; +(3)包含一个整数值,padding_height = padding_width = padding。 +***PaddlePaddle***:`padding`支持list或tuple类型或str类型。如果它是一个list或tuple,它可以有4种格式: +(1)包含4个二元组:当 data_format 为"NCHW"时为 \[\[0,0\], \[0,0\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\]\],当 data_format 为"NHWC"时为\[\[0,0\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\], \[0,0\]\]; +(2)包含4个整数值:\[padding_height_top, padding_height_bottom, padding_width_left, padding_width_right\]; +(3)包含2个整数值:\[padding_height, padding_width\],此时padding_height_top = padding_height_bottom = padding_height, padding_width_left = padding_width_right = padding_width; +(4)包含一个整数值,padding_height = padding_width = padding。如果它为一个字符串时,可以是"VALID"或者"SAME",表示填充算法。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv3d.md new file mode 100644 index 0000000000000000000000000000000000000000..4f5c11176a1e543e1faf78b4e83b193801fc8393 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Conv3d.md @@ -0,0 +1,50 @@ +# torch.nn.Conv3d +### [torch.nn.Conv3d](https://pytorch.org/docs/stable/generated/torch.nn.Conv3d.html?highlight=conv3d#torch.nn.Conv3d) + +```python +torch.nn.Conv3d(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + padding_mode='zeros') +``` + +### [paddle.nn.Conv3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv3D_cn.html#conv3d) + +```python +paddle.nn.Conv3D(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + padding_mode='zeros', + weight_attr=None, + bias_attr=None, + data_format='NCDHW') +``` + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCDHW`和`NDHWC`两种格式的输入(通过`data_format`设置)。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 +#### padding的设置 +***PyTorch***:`padding`只能支持list或tuple类型。它可以有3种格式: +(1)包含4个二元组:\[\[0,0\], \[0,0\], \[padding_depth_front, padding_depth_back\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\]\],其中每个元组都可使用整数值替换,代表元组中的2个值相等; +(2)包含3个二元组:\[\[padding_depth_front, padding_depth_back\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\]\],其中每个元组都可使用整数值替换,代表元组中的2个值相等; +(3)包含一个整数值,padding_height = padding_width = padding。 +***PaddlePaddle***:`padding`支持list或tuple类型或str类型。如果它是一个list或tuple,它可以有4种格式: +(1)包含5个二元组:当 data_format 为"NCDHW"时为 \[\[0,0], \[0,0\], \[padding_depth_front, padding_depth_back\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\]\],当 data_format 为"NDHWC"时为\[\[0,0\], \[padding_depth_front, padding_depth_back\], \[padding_height_top, padding_height_bottom\], \[padding_width_left, padding_width_right\], \[0,0\]\]; +(2)包含6个整数值:\[padding_depth_front, padding_depth_back, padding_height_top, padding_height_bottom, padding_width_left, padding_width_right\]; +(3)包含3个整数值:\[padding_depth, padding_height, padding_width\],此时 padding_depth_front = padding_depth_back = padding_depth, padding_height_top = padding_height_bottom = padding_height, padding_width_left = padding_width_right = padding_width; +(4)包含一个整数值,padding_height = padding_width = padding。如果它为一个字符串时,可以是"VALID"或者"SAME",表示填充算法。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose1d.md new file mode 100644 index 0000000000000000000000000000000000000000..72bc157afef0a2c7b9ee9c66b50180be63af4642 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose1d.md @@ -0,0 +1,45 @@ +## torch.nn.ConvTranspose1d +### [torch.nn.ConvTranspose1d](https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose1d.html?highlight=torch%20nn%20convtranspose1d#torch.nn.ConvTranspose1d) +```python +torch.nn.ConvTranspose1d(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + bias=True, + dilation=1, + padding_mode='zeros') +``` + +### [paddle.nn.Conv1DTranspose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv1DTranspose_cn.html#conv1dtranspose) +```python +paddle.nn.Conv1DTranspose(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + dilation=1, + weight_attr=None, + bias_attr=None, + data_format='NCL') +``` +### 功能差异 +#### 输入格式 +***PyTorch***:只支持`NCL`的输入。 +***PaddlePaddle***:支持`NCL`和`NLC`两种格式的输入(通过`data_format`设置)。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 + +#### padding大小的设置 +***PyTorch***:`padding`只能支持list或tuple类型。 +***PaddlePaddle***:`padding`支持list或tuple类型或str类型。 + +#### padding值的设置 +***PyTorch***:通过设置`padding_mode`确定padding的值。 +***PaddlePaddle***:PaddlePaddle无此参数。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose2d.md new file mode 100644 index 0000000000000000000000000000000000000000..c3bb83a2bbc6eca40eb1ebc63453ad06d090c1e3 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose2d.md @@ -0,0 +1,45 @@ +## torch.nn.ConvTranspose2d +### [torch.nn.ConvTranspose2d](https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose2d.html?highlight=convtranspose2d#torch.nn.ConvTranspose2d) +```python +torch.nn.ConvTranspose1d(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + bias=True, + dilation=1, + padding_mode='zeros') +``` + +### [paddle.nn.Conv2DTranspose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv2DTranspose_cn.html#conv2dtranspose) +```python +paddle.nn.Conv2DTranspose(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + dilation=1, + weight_attr=None, + bias_attr=None, + data_format='NCHW') +``` +### 功能差异 +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 + +#### padding大小的设置 +***PyTorch***:`padding`只能支持list或tuple类型。 +***PaddlePaddle***:`padding`支持list或tuple类型或str类型。 + +#### padding值的设置 +***PyTorch***:通过设置`padding_mode`确定padding的值。 +***PaddlePaddle***:PaddlePaddle无此参数。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose3d.md new file mode 100644 index 0000000000000000000000000000000000000000..8c334d8cb321fe9c16d69ac539d00d04d0efc016 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ConvTranspose3d.md @@ -0,0 +1,45 @@ +## torch.nn.ConvTranspose3d +### [torch.nn.ConvTranspose3d](https://pytorch.org/docs/stable/generated/torch.nn.ConvTranspose3d.html?highlight=convtranspose3d#torch.nn.ConvTranspose3d) +```python +torch.nn.ConvTranspose1d(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + bias=True, + dilation=1, + padding_mode='zeros') +``` + +### [paddle.nn.Conv3DTranspose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/conv/Conv3DTranspose_cn.html#conv3dtranspose) +```python +paddle.nn.Conv2DTranspose(in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + dilation=1, + weight_attr=None, + bias_attr=None, + data_format='NCDHW') +``` +### 功能差异 +#### 输入格式 +***PyTorch***:只支持`NCDHW`的输入。 +***PaddlePaddle***:支持`NCDHW`和`NDHWC`两种格式的输入(通过`data_format`设置)。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 + +#### padding大小的设置 +***PyTorch***:`padding`只能支持list或tuple类型。 +***PaddlePaddle***:`padding`支持list或tuple类型或str类型。 + +#### padding值的设置 +***PyTorch***:通过设置`padding_mode`确定padding的值。 +***PaddlePaddle***:PaddlePaddle无此参数。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout.md new file mode 100644 index 0000000000000000000000000000000000000000..d6a605de2d2be0f4386fc634c26d673bc46ac9fa --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout.md @@ -0,0 +1,24 @@ +## torch.nn.Dropout +### [torch.nn.Dropout](https://pytorch.org/docs/stable/generated/torch.nn.Dropout.html?highlight=dropout#torch.nn.Dropout) +```python +torch.nn.Dropout(p=0.5, inplace=False) +``` + +### [paddle.nn.Dropout](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout_cn.html#dropout) +```python +paddle.nn.Dropout(p=0.5, axis=None, mode="upscale_in_train”, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| inplace | - | 表示在不更改变量的内存地址的情况下,直接修改变量的值,PaddlePaddle无此参数。 | +| - | axis | 指定对输入Tensor进行Dropout操作的轴,PyTorch无此参数。 | +| - | mode | 表示丢弃单元的方式,PyTorch无此参数。| + + +### 功能差异 + +#### 丢弃方式 +***PyTorch***:只支持`upscale_in_train`的丢弃方式。 +***PaddlePaddle***:支持`upscale_in_train`和`downscale_in_infer`两种丢弃方式(通过`mode`设置),计算方法可参考[文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout_cn.html#dropout)。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout2d.md new file mode 100644 index 0000000000000000000000000000000000000000..1a1eaa6608b6ee9b1b239a046443442f16958c5e --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout2d.md @@ -0,0 +1,21 @@ +## torch.nn.Dropout2d +### [torch.nn.Dropout2d](https://pytorch.org/docs/stable/generated/torch.nn.Dropout2d.html?highlight=dropout2d#torch.nn.Dropout2d) +```python +torch.nn.Dropout2d(p=0.5, inplace=False) +``` +### [paddle.nn.Dropout2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout2D_cn.html#dropout2d) +```python +paddle.nn.Dropout2D(p=0.5, data_format='NCHW', name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| inplace | - | 表示在不更改变量的内存地址的情况下,直接修改变量的值,PaddlePaddle无此参数。 | +| - | data_format | 指定对输入的数据格式,PyTorch无此参数。 | + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout3d.md new file mode 100644 index 0000000000000000000000000000000000000000..065f78742c7cfe9cb9fa7d3449f731f30c1e6247 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Dropout3d.md @@ -0,0 +1,21 @@ +## torch.nn.Dropout3d +### [torch.nn.Dropout3d](https://pytorch.org/docs/stable/generated/torch.nn.Dropout3d.html?highlight=dropout3d#torch.nn.Dropout3d) +```python +torch.nn.Dropout3d(p=0.5, inplace=False) +``` +### [paddle.nn.Dropout3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Dropout3D_cn.html#dropout3d) +```python +paddle.nn.Dropout3D(p=0.5, data_format='NCDHW', name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| inplace | - | 表示在不更改变量的内存地址的情况下,直接修改变量的值,PaddlePaddle无此参数。 | +| - | data_format | 指定对输入的数据格式,PyTorch无此参数。 | + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCDHW`的输入。 +***PaddlePaddle***:支持`NCDHW`和`NDHWC`两种格式的输入(通过`data_format`设置)。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Embedding.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Embedding.md new file mode 100644 index 0000000000000000000000000000000000000000..9991020a3429f68c7753212a754b1999a07aed7a --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Embedding.md @@ -0,0 +1,29 @@ +## torch.nn.Embedding +### [torch.nn.Embedding](https://pytorch.org/docs/stable/generated/torch.nn.Embedding.html?highlight=embedding#torch.nn.Embedding) +```python +torch.nn.Embedding(num_embeddings, + embedding_dim, + padding_idx=None, + max_norm=None, + norm_type=2.0, + scale_grad_by_freq=False, + sparse=False) +``` +### [paddle.nn.Embedding](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Embedding_cn.html#embedding) +```python +paddle.nn.Embedding(num_embeddings, + embedding_dim, + padding_idx=None, + sparse=False, + weight_attr=None, + name=None) +``` + +### 功能差异 +#### 归一化设置 +***PyTorch***:当max_norm不为`None`时,如果Embeddding向量的范数(范数的计算方式由norm_type决定)超过了max_norm这个界限,就要再进行归一化。 +***PaddlePaddle***:PaddlePaddle无此要求,因此不需要归一化。 + +#### 梯度缩放设置 +***PyTorch***:若scale_grad_by_freq设置为`True`,会根据单词在mini-batch中出现的频率,对梯度进行放缩。 +***PaddlePaddle***:PaddlePaddle无此功能。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.GRU.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.GRU.md new file mode 100644 index 0000000000000000000000000000000000000000..853926d6630e35c8a9ad3fc6b2401f4165fa175c --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.GRU.md @@ -0,0 +1,36 @@ +## torch.nn.GRU +### [torch.nn.GRU](https://pytorch.org/docs/stable/generated/torch.nn.GRU.html?highlight=torch%20nn%20gru#torch.nn.GRU) +```python +torch.nn.GRU(input_size, + hidden_size, + num_layers=1, + bias=True, + batch_first=False, + dropout=0, + bidirectional=False) +``` + +### [paddle.nn.GRU](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/rnn/GRU_cn.html#gru) +```python +paddle.nn.GRU(input_size, + hidden_size, + num_layers=1, + direction='forward', + dropout=0., + time_major=False, + weight_ih_attr=None, + weight_hh_attr=None, + bias_ih_attr=None, + bias_hh_attr=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| batch_first | time_major | PyTorch表示batch size是否为第一维,PaddlePaddle表示time steps是否为第一位,它们的意义相反。 | +| bidirectional | direction | PyTorch表示是否进行双向LSTM,PyTorch使用字符串表示是双向LSTM(`bidirectional`)还是单向LSTM(`forward`)。 | + +### 功能差异 +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_ih_attr`/`weight_hh_attr`/`bias_ih_attr`/`bias_hh_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_ih_attr`/`bias_hh_attr`设置为bool类型与PyTorch的作用一致。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.LSTM.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.LSTM.md new file mode 100644 index 0000000000000000000000000000000000000000..a14e6ab88750d91da83027dfc64fa7d71ffd3129 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.LSTM.md @@ -0,0 +1,42 @@ +## torch.nn.LSTM +### [torch.nn.LSTM](https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html?highlight=lstm#torch.nn.LSTM) +```python +torch.nn.LSTM(input_size, + hidden_size, + num_layers=1, + bias=True, + batch_first=False, + dropout=0, + bidirectional=False, + proj_size=0) +``` + +### [paddle.nn.LSTM](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/rnn/LSTM_cn.html#lstm) +```python +paddle.nn.LSTM(input_size, + hidden_size, + num_layers=1, + direction='forward', + dropout=0., + time_major=False, + weight_ih_attr=None, + weight_hh_attr=None, + bias_ih_attr=None, + bias_hh_attr=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| batch_first | time_major | PyTorch表示batch size是否为第一维,PaddlePaddle表示time steps是否为第一位,它们的意义相反。 | +| bidirectional | direction | PyTorch表示是否进行双向LSTM,PyTorch使用字符串表示是双向LSTM(`bidirectional`)还是单向LSTM(`forward`)。 | +| proj_size | - | 表示LSTM后将映射到对应的大小,PaddlePaddle无此功能。 | + +### 功能差异 + +#### 映射大小的设置 +***PyTorch***:支持将LSTM的结果映射到到对应大小,其具体方式可参见[论文](https://arxiv.org/abs/1402.1128)。 +***PaddlePaddle***:无此功能。 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_ih_attr`/`weight_hh_attr`/`bias_ih_attr`/`bias_hh_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_ih_attr`/`bias_hh_attr`设置为bool类型与PyTorch的作用一致。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Linear.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Linear.md new file mode 100644 index 0000000000000000000000000000000000000000..9c1bab085689793cad993743b22c4d58d7094b97 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Linear.md @@ -0,0 +1,18 @@ +## torch.nn.Linear +### [torch.nn.Linear](https://pytorch.org/docs/stable/generated/torch.nn.Linear.html?highlight=linear#torch.nn.Linear) + +```python +torch.nn.Linear(in_features, out_features, bias=True) +``` + +### [paddle.nn.Linear](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Linear_cn.html#linear) + +```python + paddle.nn.Linear(in_features, out_features, weight_attr=None, bias_attr=None, name=None) +``` + +### 功能差异 + +#### 更新参数设置 +***PyTorch***:`bias`默认为True,表示使用可更新的偏置参数。 +***PaddlePaddle***:`weight_attr`/`bias_attr`默认使用默认的权重/偏置参数属性,否则为指定的权重/偏置参数属性,具体用法参见[ParamAttr](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/param_attr/ParamAttr_cn.html#cn-api-fluid-paramattr);当`bias_attr`设置为bool类型与PyTorch的作用一致。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool1d.md new file mode 100644 index 0000000000000000000000000000000000000000..2b9d1ab6846fcfa10530b0b3f43c69a91202bfb8 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool1d.md @@ -0,0 +1,33 @@ +## torch.nn.MaxPool1d +### [torch.nn.MaxPool1d](https://pytorch.org/docs/stable/generated/torch.nn.MaxPool1d.html?highlight=maxpool1d#torch.nn.MaxPool1d) + +```python +torch.nn.MaxPool1d(kernel_size, + stride=None, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False) +``` + +### [paddle.nn.MaxPool1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/MaxPool1D_cn.html#maxpool1d) + +```python +paddle.nn.MaxPool1D(kernel_size, + stride=None, + padding=0, + return_mask=False, + ceil_mode=False, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| dilation | - | 设置空洞池化的大小,PaddlePaddle无此参数。 | + +### 功能差异 + +#### 池化方式 +***PyTorch***:可以使用空洞池化。 +***PaddlePaddle***:无此池化方式。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool2d.md new file mode 100644 index 0000000000000000000000000000000000000000..c2c200e8a723c8e105edc6f5cc23f7cf74d68c59 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool2d.md @@ -0,0 +1,33 @@ +## torch.nn.MaxPool2d +### [torch.nn.MaxPool2d](https://pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html?highlight=maxpool2d#torch.nn.MaxPool2d) + +```python +torch.nn.MaxPool2d(kernel_size, + stride=None, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False) +``` + +### [paddle.nn.MaxPool2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/MaxPool2D_cn.html#maxpool2d) + +```python +paddle.nn.MaxPool2D(kernel_size, + stride=None, + padding=0, + return_mask=False, + ceil_mode=False, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| dilation | - | 设置空洞池化的大小,PaddlePaddle无此参数。 | + +### 功能差异 + +#### 池化方式 +***PyTorch***:可以使用空洞池化。 +***PaddlePaddle***:无此池化方式。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool3d.md new file mode 100644 index 0000000000000000000000000000000000000000..1455fb11c5b5962aab797b3e39a438148bed83d4 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxPool3d.md @@ -0,0 +1,34 @@ +## torch.nn.MaxPool3d +### [torch.nn.MaxPool3d](https://pytorch.org/docs/stable/generated/torch.nn.MaxPool3d.html?highlight=maxpool3d#torch.nn.MaxPool3d) + +```python +torch.nn.MaxPool3d(kernel_size, + stride=None, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False) +``` + +### [paddle.nn.MaxPool3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/pooling/MaxPool3D_cn.html#maxpool3d) + +```python +paddle.nn.MaxPool3D(kernel_size, + stride=None, + padding=0, + ceil_mode=False, + return_mask=False, + data_format='NCDHW', + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| dilation | - | 设置空洞池化的大小,PaddlePaddle无此参数。 | + +### 功能差异 + +#### 池化方式 +***PyTorch***:可以使用空洞池化。 +***PaddlePaddle***:无此池化方式。 diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool1d.md new file mode 100644 index 0000000000000000000000000000000000000000..53f0d92d7ef9d1409d84c21368117c3d2dbea963 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool1d.md @@ -0,0 +1,78 @@ +## torch.nn.MaxUnpool1d +### [torch.nn.MaxUnpool1d](https://pytorch.org/docs/stable/generated/torch.nn.MaxUnpool1d.html?highlight=unpool#torch.nn.MaxUnpool1d) +```python +torch.nn.MaxUnpool1d(kernel_size, stride=None, padding=0) +``` +### 功能介绍 +用于实现一维反池化,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +import paddle +import paddle.nn as nn +TYPE_MAPPER = {"fp16": "float16", "fp32": "float32", "fp64": "float64"} + +# 定义MaxUnpool1D +class MaxUnpool1D(paddle.nn.Layer): + def __init__(self, kernel_size, stride=None, padding=0): + super().__init__() + if isinstance(stride, int): + self.kernel_size = [kernel_size] + else: + self.kernel_size = kernel_size + if stride is None: + self.stride = self.kernel_size + else: + if isinstance(stride, int): + self.stride = [stride] + else: + self.stride = stride + if isinstance(padding, int): + self.padding = [padding] + else: + self.padding = padding + + def forward(self, input, indices, output_size=None): + if output_size is None: + n, c, l = input.shape + out_l = (l - 1) * self.stride[0] - 2 * self.padding[0] + self.kernel_size[0] + output_size = (n, c, out_l) + else: + if len(output_size) == len(self.kernel_size) + 2: + output_size = output_size[2:] + t = str(input.dtype).lower().strip().split(".")[-1] + t = TYPE_MAPPER[t] + out = paddle.zeros(output_size, dtype=t) + flatten_out = paddle.flatten(out) + for i in range(indices.shape[0]): + for j in range(indices.shape[1]): + for k in range(indices.shape[2]): + indices[i, j, k] = (out.shape[1] * out.shape[2]) * i + out.shape[2] * j + indices[i, j, k] + flatten_indices = paddle.flatten(indices) + flatten_input = paddle.flatten(input) + for i in range(flatten_indices.shape[0]): + flatten_out[flatten_indices[i].tolist()] = flatten_input[i].tolist() + out = paddle.reshape(flatten_out, out.shape) + return out + + +# 组网 +pool = nn.MaxPool1D(2, stride=2, return_mask=True) +unpool = MaxUnpool1D(2, stride=2) + +# 构造输入 +input = paddle.to_tensor([[[ 1., 2, 3, 4]]]) + +# 进行池化 +pool_res, indices = pool(input) +# pool_res: +# Tensor(shape=[1, 1, 2], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[2., 4.]]]) +# indices: +# Tensor(shape=[1, 1, 2], dtype=int32, place=CPUPlace, stop_gradient=True, +# [[[1, 3]]]) + +# 进行反池化 +res = unpool(pool_res, indices) +# res: +# Tensor(shape=[1, 1, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[0., 2., 0., 4.]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool2d.md new file mode 100644 index 0000000000000000000000000000000000000000..5efb8eca643d26b6cc376fca005b0dbf6e6647e7 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool2d.md @@ -0,0 +1,90 @@ +## torch.nn.MaxUnpool2d +### [torch.nn.MaxUnpool2d](https://pytorch.org/docs/stable/generated/torch.nn.MaxUnpool2d.html?highlight=maxunpool2d#torch.nn.MaxUnpool2d) +```python +torch.nn.MaxUnpool2d(kernel_size, stride=None, padding=0) +``` +### 功能介绍 +用于实现一维反池化,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 + +```python +import paddle +import paddle.nn as nn +TYPE_MAPPER = {"fp16": "float16", "fp32": "float32", "fp64": "float64"} + +# 定义MaxUnpool2D +class MaxUnpool2D(paddle.nn.Layer): + def __init__(self, kernel_size, stride=None, padding=0): + super().__init__() + if isinstance(stride, int): + self.kernel_size = (kernel_size, kernel_size) + else: + self.kernel_size = kernel_size + if stride is None: + self.stride = self.kernel_size + else: + if isinstance(stride, int): + self.stride = (stride, stride) + else: + self.stride = stride + if isinstance(padding, int): + self.padding = (padding, padding) + else: + self.padding = padding + + def forward(self, input, indices, output_size=None): + if output_size is None: + n, c, h, w = input.shape + out_h = (h - 1) * self.stride[0] - 2 * self.padding[0] + self.kernel_size[0] + out_w = (w - 1) * self.stride[1] - 2 * self.padding[1] + self.kernel_size[1] + output_size = (n, c, out_h, out_w) + else: + if len(output_size) == len(self.kernel_size) + 2: + output_size = output_size[2:] + t = str(input.dtype).lower().strip().split(".")[-1] + t = TYPE_MAPPER[t] + out = paddle.zeros(output_size, dtype=t) + flatten_out = paddle.flatten(out) + for i in range(indices.shape[0]): + for j in range(indices.shape[1]): + for k in range(indices.shape[2]): + for m in range(indices.shape[3]): + indices[i, j, k, m] = (out.shape[1] * out.shape[2] * out.shape[3]) * i + \ + (out.shape[2] * out.shape[3]) * j + indices[i, j, k, m] + flatten_indices = paddle.flatten(indices) + flatten_input = paddle.flatten(input) + for i in range(flatten_indices.shape[0]): + flatten_out[flatten_indices[i].tolist()] = flatten_input[i].tolist() + out = paddle.reshape(flatten_out, out.shape) + return out + + +# 组网 +pool = nn.MaxPool2D(2, stride=2, return_mask=True) +unpool = MaxUnpool2D(2, stride=2) + +# 构造输入 +input = paddle.to_tensor([[[[ 1., 2, 3, 4], + [ 5, 6, 7, 8], + [ 9, 10, 11, 12], + [13, 14, 15, 16]]]]) + +# 进行池化 +pool_res, indices = pool(input) +# pool_res: +# Tensor(shape=[1, 1, 2, 2], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[6. , 8. ], +# [14., 16.]]]]) +# indices: +# Tensor(shape=[1, 1, 2, 2], dtype=int32, place=CPUPlace, stop_gradient=True, +# [[[[5 , 7 ], +# [13, 15]]]]) + +# 进行反池化 +res = unpool(pool_res, indices) +# res: +# Tensor(shape=[1, 1, 4, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[0. , 0. , 0. , 0. ], +# [0. , 6. , 0. , 8. ], +# [0. , 0. , 0. , 0. ], +# [0. , 14., 0. , 16.]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool3d.md new file mode 100644 index 0000000000000000000000000000000000000000..8e4e778ee7fb571e3c37ee0bc60be43929387a92 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.MaxUnpool3d.md @@ -0,0 +1,89 @@ +## torch.nn.MaxUnpool3d +### [torch.nn.MaxUnpool3d](https://pytorch.org/docs/stable/generated/torch.nn.MaxUnpool3d.html?highlight=maxunpool3d#torch.nn.MaxUnpool3d) +```python +torch.nn.MaxUnpool3d(kernel_size, stride=None, padding=0) +``` +### 功能介绍 +用于实现一维反池化,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +import paddle +import paddle.nn as nn +TYPE_MAPPER = {"fp16": "float16", "fp32": "float32", "fp64": "float64"} + +# 定义MaxUnpool3D +class MaxUnpool3D(paddle.nn.Layer): + def __init__(self, kernel_size, stride=None, padding=0): + super().__init__() + if isinstance(stride, int): + self.kernel_size = (kernel_size, kernel_size, kernel_size) + else: + self.kernel_size = kernel_size + if stride is None: + self.stride = self.kernel_size + else: + if isinstance(stride, int): + self.stride = (stride, stride, stride) + else: + self.stride = stride + if isinstance(padding, int): + self.padding = (padding, padding, padding) + else: + self.padding = padding + + def forward(self, input, indices, output_size=None): + if output_size is None: + n, c, d, h, w = input.shape + out_d = (d - 1) * self.stride[0] - 2 * self.padding[0] + self.kernel_size[0] + out_h = (h - 1) * self.stride[1] - 2 * self.padding[1] + self.kernel_size[1] + out_w = (w - 1) * self.stride[2] - 2 * self.padding[2] + self.kernel_size[2] + output_size = (n, c, out_d, out_h, out_w) + else: + if len(output_size) == len(self.kernel_size) + 2: + output_size = output_size[2:] + t = str(input.dtype).lower().strip().split(".")[-1] + t = TYPE_MAPPER[t] + out = paddle.zeros(output_size, dtype=t) + flatten_out = paddle.flatten(out) + for i in range(indices.shape[0]): + for j in range(indices.shape[1]): + for k in range(indices.shape[2]): + for m in range(indices.shape[3]): + for n in range(indices.shape[4]): + indices[i, j, k, m, n] = (out.shape[1] * out.shape[2] * out.shape[3] * out.shape[4]) * i + \ + (out.shape[2] * out.shape[3] * out.shape[4]) * j + \ + indices[i, j, k, m, n] + flatten_indices = paddle.flatten(indices) + flatten_input = paddle.flatten(input) + for i in range(flatten_indices.shape[0]): + flatten_out[flatten_indices[i].tolist()] = flatten_input[i].tolist() + out = paddle.reshape(flatten_out, out.shape) + return out + + +# 组网 +pool = nn.MaxPool3D(2, stride=2, padding=0, return_mask=True) +unpool = MaxUnpool3D(2, stride=2, padding=0) + +# 构造输入 +input = paddle.to_tensor([[[[[ 1., 2, 3, 4], + [ 5, 6, 7, 8]], + [[ 1., 2, 3, 4], + [ 5, 6, 7, 8]]]]]) +# 进行池化 +pool_res, indices = pool(input) +# pool_res: +# Tensor(shape=[1, 1, 1, 1, 2], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[[6., 8.]]]]]) +# indices +# Tensor(shape=[1, 1, 1, 1, 2], dtype=int32, place=CPUPlace, stop_gradient=True, +# [[[[[5, 7]]]]]) + +# 进行反池化 +res = unpool(pool_res, indices) +# res: +# Tensor(shape=[1, 1, 2, 2, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[[0., 0., 0., 0.], +# [0., 6., 0., 8.]], +# [[0., 0., 0., 0.], +# [0., 0., 0., 0.]]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReflectionPad1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReflectionPad1d.md new file mode 100644 index 0000000000000000000000000000000000000000..7277ebd79fd54ff02b1c85f6a6ef2605304b9ab7 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReflectionPad1d.md @@ -0,0 +1,58 @@ +## torch.nn.ReflectionPad1d +### [torch.nn.ReflectionPad1d](https://pytorch.org/docs/stable/generated/torch.nn.ReflectionPad1d.html?highlight=pad#torch.nn.ReflectionPad1d) +```python +torch.nn.ReflectionPad1d(padding) +``` + +### [paddle.nn.Pad1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad1D_cn.html#pad1d) +```python +paddle.nn.Pad1D(padding, mode='constant', value=0.0, data_format='NCL', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`reflect`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCL`的输入。 +***PaddlePaddle***:支持`NCL`和`NLC`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 2, 3) +pad = 2 +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ReflectionPad1d(padding=pad) +result = my_pad(data) +# 输出 +# tensor([[[3., 2., 1., 2., 3., 2., 1.], +# [6., 5., 4., 5., 6., 5., 4.]]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 2, 3) +pad = [2, 2] +mode = "reflect" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad1D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 2, 7], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[3., 2., 1., 2., 3., 2., 1.], +# [6., 5., 4., 5., 6., 5., 4.]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReflectionPad2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReflectionPad2d.md new file mode 100644 index 0000000000000000000000000000000000000000..8fed9c1caef904756a977a31af42f36cac57ebc3 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReflectionPad2d.md @@ -0,0 +1,60 @@ +## torch.nn.ReflectionPad2d +### [torch.nn.ReflectionPad2d](https://pytorch.org/docs/stable/generated/torch.nn.ReflectionPad2d.html?highlight=pad#torch.nn.ReflectionPad2d) +```python +torch.nn.ReflectionPad2d(padding) +``` + +### [paddle.nn.Pad2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad2D_cn.html#pad2d) +```python +paddle.nn.Pad2D(padding, mode='constant', value=0.0, data_format='NCHW', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`reflect`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 1, 2, 3) +pad = [1, 0, 1, 0] +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ReflectionPad2d(padding=pad) +result = my_pad(data) +# 输出 +# tensor([[[[5., 4., 5., 6.], +# [2., 1., 2., 3.], +# [5., 4., 5., 6.]]]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 1, 2, 3) +pad = [1, 0, 1, 0] +mode = "reflect" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad2D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 1, 3, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[5., 4., 5., 6.], +# [2., 1., 2., 3.], +# [5., 4., 5., 6.]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad1d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad1d.md new file mode 100644 index 0000000000000000000000000000000000000000..9959d78fc2dec93a6eda21840a016846be26e899 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad1d.md @@ -0,0 +1,57 @@ +## torch.nn.ReplicationPad1d +### [torch.nn.ReplicationPad1d](https://pytorch.org/docs/stable/generated/torch.nn.ReplicationPad1d.html?highlight=pad#torch.nn.ReplicationPad1d) +```python +torch.nn.ReplicationPad1d(padding) +``` +### [paddle.nn.Pad1D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad1D_cn.html#pad1d) +```python +paddle.nn.Pad1D(padding, mode='constant', value=0.0, data_format='NCL', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`replicate`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCL`的输入。 +***PaddlePaddle***:支持`NCL`和`NLC`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 2, 3) +pad = 2 +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ReplicationPad1d(padding=pad) +result = my_pad(data) +# 输出 +# tensor([[[1., 1., 1., 2., 3., 3., 3.], +# [4., 4., 4., 5., 6., 6., 6.]]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 2, 3) +pad = [2, 2] +mode = "replicate" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad1D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 2, 7], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[1., 1., 1., 2., 3., 3., 3.], +# [4., 4., 4., 5., 6., 6., 6.]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad2d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad2d.md new file mode 100644 index 0000000000000000000000000000000000000000..f7989339a0423d5e936881a96709663d0365663d --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad2d.md @@ -0,0 +1,59 @@ +## torch.nn.ReplicationPad2d +### [torch.nn.ReplicationPad2d](https://pytorch.org/docs/stable/generated/torch.nn.ReplicationPad2d.html?highlight=pad#torch.nn.ReplicationPad2d) +```python +torch.nn.ReplicationPad2d(padding) +``` +### [paddle.nn.Pad2D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad2D_cn.html#pad2d) +```python +paddle.nn.Pad2D(padding, mode='constant', value=0.0, data_format='NCHW', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`replicate`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +import torch.nn as nn +import numpy as np +input_shape = (1, 1, 2, 3) +pad = [1, 0, 1, 0] +data = torch.arange(np.prod(input_shape), dtype=torch.float32).reshape(input_shape) + 1 +my_pad = nn.ReplicationPad2d(padding=pad) +result = my_pad(data) +# 输出 +# tensor([[[[1., 1., 2., 3.], +# [1., 1., 2., 3.], +# [4., 4., 5., 6.]]]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 1, 2, 3) +pad = [1, 0, 1, 0] +mode = "replicate" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad2D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 1, 3, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[1., 1., 2., 3.], +# [1., 1., 2., 3.], +# [4., 4., 5., 6.]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad3d.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad3d.md new file mode 100644 index 0000000000000000000000000000000000000000..eab4cd397c401f73371c15c8bcec14e15a71b700 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.ReplicationPad3d.md @@ -0,0 +1,65 @@ +## torch.nn.ReplicationPad3d +### [torch.nn.ReplicationPad3d](https://pytorch.org/docs/stable/generated/torch.nn.ReplicationPad3d.html?highlight=pad#torch.nn.ReplicationPad3d) +```python +torch.nn.ReplicationPad3d(padding) +``` +### [paddle.nn.Pad3D](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Pad3D_cn.html#pad3d) +```python +paddle.nn.Pad3D(padding, mode='constant', value=0.0, data_format='NCDHW', name=None) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***:只支持`replicate`方式的Pad方式。 +***PaddlePaddle***:支持`constant`、`reflect`、`replicate`、`circular`四种格式的输入(通过`mode`设置)。 + +#### 输入格式 +***PyTorch***:只支持`NCDHW`的输入。 +***PaddlePaddle***:支持`NCDHW`和`NCDHW`两种格式的输入(通过`data_format`设置)。 + +#### padding的设置 +***PyTorch***:padding参数的类型只能为int或tuple。 +***PaddlePaddle***:padding参数的类型只能为Tensor或list。 + + +### 代码示例 +``` python +# PyTorch示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 1, 1, 2, 3) +pad = [1, 0, 1, 2, 0, 0] +mode = "constant" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad3D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# tensor([[[[[1., 1., 2., 3.], +# [1., 1., 2., 3.], +# [4., 4., 5., 6.], +# [4., 4., 5., 6.], +# [4., 4., 5., 6.]]]]]) + +``` + +``` python +# PaddlePaddle示例: +import paddle +import paddle.nn as nn +import numpy as np +input_shape = (1, 1, 1, 2, 3) +pad = [1, 0, 1, 2, 0, 0] +mode = "constant" +data = paddle.arange(np.prod(input_shape), dtype="float32").reshape(input_shape) + 1 +my_pad = nn.Pad3D(padding=pad, mode=mode) +result = my_pad(data) +# 输出 +# Tensor(shape=[1, 1, 1, 5, 4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[[[[1., 1., 2., 3.], +# [1., 1., 2., 3.], +# [4., 4., 5., 6.], +# [4., 4., 5., 6.], +# [4., 4., 5., 6.]]]]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Upsample.md b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Upsample.md new file mode 100644 index 0000000000000000000000000000000000000000..4c5cf8d37fe07555ab8b4276fc59c1410070755e --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/nn/torch.nn.Upsample.md @@ -0,0 +1,29 @@ +## torch.nn.Upsample +### [torch.nn.Upsample](https://pytorch.org/docs/stable/generated/torch.nn.Upsample.html?highlight=upsample#torch.nn.Upsample) +```python +torch.nn.Upsample(size=None, + scale_factor=None, + mode='nearest', + align_corners=False) +``` +### [paddle.nn.Upsample](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Upsample_cn.html#upsample) +```python +paddle.nn.Upsample(size=None, + scale_factor=None, + mode='nearest', + align_corners=False, + align_mode=0, + data_format='NCHW', + name=None) +``` + +### 功能差异 + +#### 输入格式 +***PyTorch***:只支持`NCHW`的输入。 +***PaddlePaddle***:支持`NCHW`和`NHWC`两种格式的输入(通过`data_format`设置)。 + +#### 计算方式 +***PyTorch***:在mode为`bilinear`或`trilinear`时,只支持align_mode为0的上采样。 +***PaddlePaddle***:在mode为`bilinear`或`trilinear`时,支持align_mode为0和1的上采样。 +【注意】align_mode为0或1时的上采样方式可参见[文档](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/nn/layer/common/Upsample_cn.html#upsample)。 diff --git a/docs/pytorch_project_convertor/API_docs/ops/README.md b/docs/pytorch_project_convertor/API_docs/ops/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fd8374d21365f283c50bd8ad15e65d92f613b18a --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/README.md @@ -0,0 +1,93 @@ +## PyTorch-PaddlePaddle 基础操作类API对应表 + +| 序号 | PyTorch API | PaddlePaddle API | 备注 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | [torch.set\_default\_dtype](https://pytorch.org/docs/stable/generated/torch.set_default_dtype.html) | [paddle.set\_default\_dtype](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/framework/set_default_dtype_cn.html) | 功能一致 | +| 2 | [torch.get\_default\_dtype](https://pytorch.org/docs/stable/generated/torch.get_default_dtype.html) | [paddle.get\_default\_dtype](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/framework/get_default_dtype_cn.html#get-default-dtype) | 功能一致 | +| 3 | [torch.numel](https://pytorch.org/docs/stable/generated/torch.numel.html) | [paddle.numel](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/stat/numel_cn.html#numel) | 功能一致,参数名不一致 | +| 4 | [torch.tensor](https://pytorch.org/docs/stable/generated/torch.tensor.html?highlight=tensor#torch.tensor) | [paddle.to\_tensor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/to_tensor_cn.html#to-tensor) | [差异对比](torch.tensor.md) | +| 5 | [torch.from\_numpy](https://pytorch.org/docs/stable/generated/torch.from_numpy.html?highlight=from_numpy#torch.from_numpy) | [paddle.to\_tensor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/to_tensor_cn.html#to-tensor) | [差异对比](torch.from_numpy.md) | +| 6 | [torch.zeros](https://pytorch.org/docs/stable/generated/torch.zeros.html?highlight=zeros#torch.zeros) | [paddle.zeros](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/zeros_cn.html#zeros) | [差异对比](torch.zeros.md) | +| 7 | [torch.zeros_like](https://pytorch.org/docs/stable/generated/torch.zeros_like.html?highlight=zeros_like#torch.zeros_like) | [paddle.zeros_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/zeros_like_cn.html#zeros-like) | [差异对比](torch.zeros_like.md) | +| 8 | [torch.ones](https://pytorch.org/docs/stable/generated/torch.ones.html?highlight=ones#torch.ones) | [paddle.ones](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/ones_cn.html#ones) | [差异对比](torch.ones.md) | +| 9 | [torch.ones_like](https://pytorch.org/docs/stable/generated/torch.zeros_like.html?highlight=zeros_like#torch.zeros_like) | [paddle.ones_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/zeros_like_cn.html#zeros-like) | [差异对比](torch.ones_like.md) | +| 10 | [torch.empty](https://pytorch.org/docs/stable/generated/torch.empty.html?highlight=empty#torch.empty) | [paddle.empty](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/empty_cn.html#empty) | [差异对比](torch.empty.md) | +| 11 | [torch.empty_like](https://pytorch.org/docs/stable/generated/torch.empty_like.html?highlight=empty_like#torch.empty_like) | [paddle.empty_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/empty_like_cn.html#empty-like) | [差异对比](torch.empty_like.md) | +| 12 | [torch.full](https://pytorch.org/docs/stable/generated/torch.full.html?highlight=full#torch.full) | [paddle.full](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/full_cn.html#full) | 功能一致,[参数不一致](torch.full.md) | +| 13 | [torch.full_like](https://pytorch.org/docs/stable/generated/torch.full_like.html?highlight=full_like#torch.full_like) | [paddle.full_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/full_like_cn.html#full-like) | [差异对比](torch.full_like.md) | +| 14 | [torch.arange](https://pytorch.org/docs/stable/generated/torch.arange.html?highlight=arange#torch.arange) | [paddle.arange](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/arange_cn.html#arange) | 功能一致,[参数不一致](torch.arange.md) | +| 15 | [torch.range](https://pytorch.org/docs/stable/generated/torch.range.html?highlight=range#torch.range) | [paddle.arange](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/arange_cn.html#arange) | 功能一致,[参数不一致](torch.range.md) | +| 16 | [torch.linspace](https://pytorch.org/docs/stable/generated/torch.linspace.html?highlight=linspace#torch.linspace) | [paddle.linspace](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/linspace_cn.html#linspace) | 功能一致,[参数不一致](torch.linspace.md) | +| 17 | [torch.eye](https://pytorch.org/docs/stable/generated/torch.eye.html?highlight=eye#torch.eye) | [paddle.eye](https://pytorch.org/docs/stable/generated/torch.eye.html?highlight=eye#torch.eye) | 功能一致,[参数不一致](torch.eye.md) | +| 18 | [torch.cat](https://pytorch.org/docs/stable/generated/torch.cat.html?highlight=torch%20cat#torch.cat) | [paddle.concat](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/concat_cn.html#concat) | 功能一致,参数名不一致 | +| 19 | [torch.chunk](https://pytorch.org/docs/stable/generated/torch.chunk.html?highlight=chunk#torch.chunk) | [paddle.chunk](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/chunk_cn.html#chunk) | 功能一致,参数名不一致 | +| 20 | [torch.gather](https://pytorch.org/docs/stable/generated/torch.gather.html?highlight=gather#torch.gather) | [paddle.gather](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/gather_cn.html#gather) | [差异对比](torch.gather.md) | +| 21 | [torch.index\_select](https://pytorch.org/docs/stable/generated/torch.index_select.html?highlight=index_select#torch.index_select) | [paddle.index\_select](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/search/index_select_cn.html#index-select) | 功能一致,参数名不一致 | +| 22 | [torch.masked\_select](https://pytorch.org/docs/stable/generated/torch.masked_select.html?highlight=masked_sel#torch.masked_select) | [paddle.masked\_select](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/search/masked_select_cn.html#masked-select) | 功能一致,参数名不一致 | +| 23 | [torch.narrow](https://pytorch.org/docs/stable/generated/torch.narrow.html?highlight=narrow#torch.narrow) | [paddle.slice](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/slice_cn.html#slice) | [差异对比](torch.narrow.md) | +| 24 | [torch.nonzero](https://pytorch.org/docs/stable/generated/torch.nonzero.html?highlight=nonzero#torch.nonzero) | [paddle.nonzero](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/search/nonzero_cn.html#nonzero) | 功能一致,参数名不一致 | +| 25 | [torch.reshape](https://pytorch.org/docs/stable/generated/torch.reshape.html?highlight=reshape#torch.reshape) | [paddle.reshape](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/reshape_cn.html#reshape) | 功能一致,参数名不一致 | +| 26 | [torch.split](https://pytorch.org/docs/stable/generated/torch.split.html?highlight=split#torch.split) | [paddle.split](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/split_cn.html#split) | 功能一致,参数名不一致 | +| 27 | [torch.squeeze](https://pytorch.org/docs/stable/generated/torch.squeeze.html?highlight=squeeze#torch.squeeze) | [paddle.squeeze](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/squeeze_cn.html#squeeze) | 功能一致,参数名不一致 | +| 28 | [torch.stack](https://pytorch.org/docs/stable/generated/torch.stack.html?highlight=stack#torch.stack) | [paddle.stack](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/stack_cn.html#stack) | 功能一致,参数名不一致 | +| 29 | [torch.t](https://pytorch.org/docs/stable/generated/torch.t.html) | [paddle.t](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/linalg/t_cn.html#t) | 功能一致,参数名不一致 | +| 30 | [torch.transpose](https://pytorch.org/docs/stable/generated/torch.transpose.html?highlight=transpose#torch.transpose) | [paddle.transpose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/transpose_cn.html#transpose) | [差异对比](torch.transpose.md) | +| 31 | [torch.unbind](https://pytorch.org/docs/stable/generated/torch.unbind.html?highlight=unbind#torch.unbind) | [paddle.unbind](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/unbind_cn.html#unbind) | 功能一致,参数名不一致 | +| 32 | [torch.unsqueeze](https://pytorch.org/docs/stable/generated/torch.unsqueeze.html?highlight=unsqueeze#torch.unsqueeze) | [paddle.unsqueeze](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/unsqueeze_cn.html#unsqueeze) | 功能一致,参数名不一致 | +| 33 | [torch.where](https://pytorch.org/docs/stable/generated/torch.where.html?highlight=where#torch.where) | [paddle.where](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/search/where_cn.html#where) | 功能一致 | +| 34 | [torch.bernoulli](https://pytorch.org/docs/stable/generated/torch.bernoulli.html?highlight=bernoulli#torch.bernoulli) | [paddle.bernoulli](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/bernoulli_cn.html#bernoulli) | 功能一致,[参数不一致](torch.bernoulli.md) | +| 35 | [torch.multinomial](https://pytorch.org/docs/stable/generated/torch.multinomial.html?highlight=multinomial#torch.multinomial) | [paddle.multinomial](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/multinomial_cn.html#multinomial) | 功能一致,[参数不一致](torch.multinomial.md) | +| 36 | [torch.normal](https://pytorch.org/docs/stable/generated/torch.normal.html?highlight=normal#torch.normal) | [paddle.normal](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/normal_cn.html#normal) | [差异对比](torch.normal.md) | +| 37 | [torch.rand](https://pytorch.org/docs/stable/generated/torch.rand.html?highlight=rand#torch.rand) | [paddle.rand](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/rand_cn.html#rand) | [差异对比](torch.rand.md) | +| 38 | [torch.randint](https://pytorch.org/docs/stable/generated/torch.randint.html?highlight=randint#torch.randint) | [paddle.randint](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/randint_cn.html#randint) | 功能一致,[参数不一致](torch.randint.md) | +| 39 | [torch.randn](https://pytorch.org/docs/stable/generated/torch.randn.html?highlight=ran%20dn#torch.randn) | [paddle.randn](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/randn_cn.html#randn) | [差异对比](torch.randn.md) | +| 40 | [torch.randperm](https://pytorch.org/docs/stable/generated/torch.randperm.html?highlight=randperm#torch.randperm) | [paddle.randperm](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/randperm_cn.html#randperm) | 功能一致,[参数不一致](torch.randperm.md) | +| 41 | [torch.save](https://pytorch.org/docs/stable/generated/torch.save.html?highlight=save#torch.save) | [paddle.save](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/framework/io/save_cn.html#save) | [差异对比](torch.save.md) | +| 42 | [torch.load](https://pytorch.org/docs/stable/generated/torch.load.html?highlight=load#torch.load) | [paddle.load](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/framework/io/load_cn.html#load) | [差异对比](torch.load.md) | +| 43 | [torch.abs](https://pytorch.org/docs/stable/generated/torch.abs.html?highlight=abs#torch.abs) | [paddle.abs](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/abs_cn.html#abs) | 功能一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 44 | [torch.absolute](https://pytorch.org/docs/stable/generated/torch.absolute.html?highlight=absolute#torch.absolute) | [paddle.abs](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/abs_cn.html#abs) | 功能一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 45 | [torch.acos](https://pytorch.org/docs/stable/generated/torch.acos.html?highlight=torch%20acos#torch.acos) | [paddle.acos](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/acos_cn.html#acos) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 46 | [torch.arccos](https://pytorch.org/docs/stable/generated/torch.arccos.html?highlight=arccos#torch.arccos) | [paddle.acos](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/acos_cn.html#acos) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 47 | [torch.add](https://pytorch.org/docs/stable/generated/torch.add.html?highlight=add#torch.add) | [padle.add](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/add_cn.html#add) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 48 | [torch.asin](https://pytorch.org/docs/stable/generated/torch.asin.html?highlight=asin#torch.asin) | [paddle.asin](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/asin_cn.html#asin) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 49 | [torch.arcsin](https://pytorch.org/docs/stable/generated/torch.arcsin.html?highlight=arcsin#torch.arcsin) | [paddle.asin](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/asin_cn.html#asin) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 50 | [torch.atan](https://pytorch.org/docs/stable/generated/torch.atan.html?highlight=atan#torch.atan) | [paddle.atan](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/atan_cn.html#atan) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 51 | [torch.arctan](https://pytorch.org/docs/stable/generated/torch.arctan.html?highlight=arctan#torch.arctan) | [paddle.atan](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/atan_cn.html#atan) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 52 | [torch.ceil](https://pytorch.org/docs/stable/generated/torch.ceil.html?highlight=ceil#torch.ceil) | [paddle.ceil](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/ceil_cn.html#ceil) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 53 | [torch.clamp](https://pytorch.org/docs/stable/generated/torch.clamp.html#torch.clamp) | [paddle.clip](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/clip_cn.html#clip) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 54 | [torch.conj](https://pytorch.org/docs/stable/generated/torch.conj.html?highlight=conj#torch.conj) | [paddle.conj](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/conj_cn.html#conj) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 55 | [torch.cos](https://pytorch.org/docs/stable/generated/torch.cos.html?highlight=cos#torch.cos) | [paddle.cos](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/cos_cn.html#cos) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 56 | [torch.cosh](https://pytorch.org/docs/stable/generated/torch.cosh.html?highlight=cosh#torch.cosh) | [paddle.cosh](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/cosh_cn.html#cosh) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 57 | [torch.div](https://pytorch.org/docs/stable/generated/torch.div.html?highlight=div#torch.div) | [paddle.divide](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/divide_cn.html#divide) | [差异对比](torch.div.md) | +| 58 | [torch.divide](https://pytorch.org/docs/stable/generated/torch.divide.html?highlight=divide#torch.divide) | [paddle.divide](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/divide_cn.html#divide) | [差异对比](torch.divide.md) | +| 59 | [torch.erf](https://pytorch.org/docs/stable/generated/torch.erf.html?highlight=erf#torch.erf) | [paddle.erf](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/erf_cn.html#erf) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 60 | [torch.exp](https://pytorch.org/docs/stable/generated/torch.exp.html?highlight=exp#torch.exp) | [paddle.exp](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/exp_cn.html#exp) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 61 | [torch.floor](https://pytorch.org/docs/stable/generated/torch.floor.html?highlight=floor#torch.floor) | [paddle.floor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/floor_cn.html#floor) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 62 | [torch.floor_divide](https://pytorch.org/docs/stable/generated/torch.floor_divide.html?highlight=floor_divide#torch.floor_divide) | [paddle.floor_divide](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/floor_divide_cn.html#floor-divide) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 63 | [torch.fmod](https://pytorch.org/docs/stable/generated/torch.fmod.html?highlight=fmod#torch.fmod) | [paddle.mod](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/remainder_cn.html#mod) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 64 | [torch.log](https://pytorch.org/docs/stable/generated/torch.log.html?highlight=log#torch.log) | [paddle.log](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/log_cn.html#log) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 65 | [torch.log10](https://pytorch.org/docs/stable/generated/torch.log10.html?highlight=log10#torch.log10) | [paddle.log10](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/log10_cn.html#log10) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 66 | [torch.log1p](https://pytorch.org/docs/stable/generated/torch.log1p.html?highlight=log1p#torch.log1p) | [paddle.log1p](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/log1p_cn.html#log1p) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 67 | [torch.log2](https://pytorch.org/docs/stable/generated/torch.log2.html?highlight=log2#torch.log2) | [paddle.log2](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/log2_cn.html#log2) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 68 | [torch.logical_and](https://pytorch.org/docs/stable/generated/torch.logical_and.html?highlight=logical_and#torch.logical_and) | [paddle.logical_and](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/logical_and_cn.html#logical-and) | 功能一致,参数名不一致 | +| 69 | [torch.logical_not](https://pytorch.org/docs/stable/generated/torch.logical_not.html?highlight=logical_not#torch.logical_not) | [paddle.logical_not](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/logical_not_cn.html#logical-not) | 功能一致,参数名不一致 | +| 70 | [torch.logical_or](https://pytorch.org/docs/stable/generated/torch.logical_or.html?highlight=logical_or#torch.logical_or) | [paddle.logical_or](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/logical_or_cn.html#logical-or) | 功能一致,参数名不一致 | +| 71 | [torch.logical_xor](https://pytorch.org/docs/stable/generated/torch.logical_xor.html?highlight=logical_xor#torch.logical_xor) | [paddle.logical_xor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/logical_xor_cn.html#logical-xor) | 功能一致,参数名不一致 | +| 72 | [torch.mul](https://pytorch.org/docs/stable/generated/torch.mul.html?highlight=torch%20mul#torch.mul) | [paddle.multiply](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/multiply_cn.html#multiply) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 73 | [torch.multiply](https://pytorch.org/docs/stable/generated/torch.multiply.html?highlight=multiply#torch.multiply) | [paddle.multiply](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/multiply_cn.html#multiply) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 74 | [torch.pow](https://pytorch.org/docs/stable/generated/torch.pow.html?highlight=pow#torch.pow) | [paddle.pow](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/pow_cn.html#pow) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 75 | [torch.real](https://pytorch.org/docs/stable/generated/torch.real.html?highlight=real#torch.real) | [paddle.real](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/attribute/real_cn.html#real) | 功能一致,参数名不一致 | +| 76 | [torch.reciprocal](https://pytorch.org/docs/stable/generated/torch.reciprocal.html?highlight=reciprocal#torch.reciprocal) | [paddle.reciprocal](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/reciprocal_cn.html#reciprocal) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 77 | [torch.remainder](https://pytorch.org/docs/stable/generated/torch.remainder.html?highlight=remainder#torch.remainder) | [paddle.mod](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/remainder_cn.html#cn-api-tensor-remainder) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 78 | [torch.round](https://pytorch.org/docs/stable/generated/torch.round.html?highlight=round#torch.round) | [paddle.round](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/round_cn.html#round) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 79 | [torch.rsqrt](https://pytorch.org/docs/stable/generated/torch.rsqrt.html?highlight=rsqrt#torch.rsqrt) | [paddle.rsqrt](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/rsqrt_cn.html#rsqrt) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 80 | [torch.sign](https://pytorch.org/docs/stable/generated/torch.sign.html?highlight=sign#torch.sign) | [paddle.sign](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/sign_cn.html#sign) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 81 | [torch.sin](https://pytorch.org/docs/stable/generated/torch.sin.html?highlight=sin#torch.sin) | [paddle.sin](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/sin_cn.html#sin) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 82 | [torch.sinh](https://pytorch.org/docs/stable/generated/torch.sinh.html?highlight=sinh#torch.sinh) | [paddle.sinh](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/sinh_cn.html#sinh) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 83 | [torch.sqrt](https://pytorch.org/docs/stable/generated/torch.sqrt.html?highlight=sqrt#torch.sqrt) | [paddle.sqrt](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/sqrt_cn.html#sqrt) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 84 | [torch.argmax](https://pytorch.org/docs/stable/generated/torch.argmax.html?highlight=argmax#torch.argmax) | [paddle.argmax](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/search/argmax_cn.html#argmax) | 功能一致,参数名不一致 | +| 85 | [torch.argmin](https://pytorch.org/docs/stable/generated/torch.argmin.html?highlight=argmin#torch.argmin) | [paddle.argmin](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/search/argmin_cn.html#argmin) | 功能一致,参数名不一致 | +| 86 | [torch.max](https://pytorch.org/docs/stable/generated/torch.max.html?highlight=max#torch.max) | [paddle.max](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/max_cn.html#max) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | +| 87 | [torch.min](https://pytorch.org/docs/stable/generated/torch.min.html?highlight=min#torch.min) | [paddle.min](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/min_cn.html#min) | 功能一致,参数名不一致,PaddlePaddle未定义`out`参数代表输出Tensor | + +***持续更新...*** diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.arange.md b/docs/pytorch_project_convertor/API_docs/ops/torch.arange.md new file mode 100644 index 0000000000000000000000000000000000000000..f230b8a1b3e3cea965e32257ae84eff86e7e89bc --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.arange.md @@ -0,0 +1,30 @@ +## torch.arange + +### [torch.arange](https://pytorch.org/docs/stable/generated/torch.arange.html?highlight=arange#torch.arange) +```python +torch.arange(start=0, + end, + step=1, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` +### [paddle.arange](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/arange_cn.html#arange) +```python +paddle.arange(start=0, + end=None, + step=1, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.bernoulli.md b/docs/pytorch_project_convertor/API_docs/ops/torch.bernoulli.md new file mode 100644 index 0000000000000000000000000000000000000000..a36228c5d5a7e91034ab00408f47e0bf3933ff1b --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.bernoulli.md @@ -0,0 +1,23 @@ +## torch.bernoulli +### [torch.bernoulli](https://pytorch.org/docs/stable/generated/torch.bernoulli.html?highlight=bernoulli#torch.bernoulli) +```python +torch.bernoulli(input, *, generator=None, out=None) +``` +### [paddle.bernoulli](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/bernoulli_cn.html#bernoulli) +```python +paddle.bernoulli(x, name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| generator | - | 用于采样的伪随机数生成器,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | + +***【注意】*** 这类生成器的用法如下: +```python +G = torch.Generator() +G.manual_seed(1) +# 生成指定分布Tensor +torch.randperm(5, generator=G) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.div.md b/docs/pytorch_project_convertor/API_docs/ops/torch.div.md new file mode 100644 index 0000000000000000000000000000000000000000..6f6afa3793541f9267b0dff327237b230f55b176 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.div.md @@ -0,0 +1,49 @@ +## torch.div +### [torch.div](https://pytorch.org/docs/stable/generated/torch.div.html?highlight=div#torch.div) +```python +torch.div(input, other, *, rounding_mode=None, out=None) +``` + +### [paddle.divide](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/divide_cn.html#divide) +```python +paddle.divide(x, y, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| rounding_mode | - | 表示舍入模式,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | + +### 功能差异 + +#### 舍入模式设置 +***PyTorch***:可以通过`rounding_mode`设置舍入模式,`"trunc"`表示向0取整,`"floor"`表示向下取整,默认值为`None`表示不进行任何舍入操作。 +***PaddlePaddle***:PaddlePaddle无此功能,需要组合实现。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +a = torch.tensor([ 0.3810, 1.2774, -0.3719, 0.4637]) +b = torch.tensor([ 1.8032, 0.2930, 0.5091, -0.1392]) +out = torch.div(a, b, rounding_mode='trunc') +# 输出 +# tensor([ 0., 4., -0., -3.]) +``` + +``` python +# PaddlePaddle示例: +import paddle +a = paddle.to_tensor([0.3810, 1.2774, -0.3719, 0.4637], dtype="float32") +b = paddle.to_tensor([1.8032, 0.2930, 0.5091, -0.1392], dtype="float32") +ipt = paddle.divide(a, b) +sign_ipt = paddle.sign(ipt) +abs_ipt = paddle.abs(ipt) +abs_ipt = paddle.floor(abs_ipt) +out = paddle.multiply(sign_ipt, abs_ipt) +# 输出 +# Tensor(shape=[4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [ 0., 4., -0., -3.]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.divide.md b/docs/pytorch_project_convertor/API_docs/ops/torch.divide.md new file mode 100644 index 0000000000000000000000000000000000000000..c28fdb41f106493c82a4ab7bf2c79377fbe977f0 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.divide.md @@ -0,0 +1,49 @@ +## torch.divide +### [torch.divide](https://pytorch.org/docs/stable/generated/torch.divide.html?highlight=divide#torch.divide) +```python +torch.divide(input, other, *, rounding_mode=None, out=None) +``` + +### [paddle.divide](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/math/divide_cn.html#divide) +```python +paddle.divide(x, y, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| rounding_mode | - | 表示舍入模式,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | + +### 功能差异 + +#### 舍入模式设置 +***PyTorch***:可以通过`rounding_mode`设置舍入模式,`"trunc"`表示向0取整,`"floor"`表示向下取整,默认值为`None`表示不进行任何舍入操作。 +***PaddlePaddle***:PaddlePaddle无此功能,需要组合实现。 + + +### 代码示例 +``` python +# PyTorch示例: +import torch +a = torch.tensor([ 0.3810, 1.2774, -0.3719, 0.4637]) +b = torch.tensor([ 1.8032, 0.2930, 0.5091, -0.1392]) +out = torch.divide(a, b, rounding_mode='trunc') +# 输出 +# tensor([ 0., 4., -0., -3.]) +``` + +``` python +# PaddlePaddle示例: +import paddle +a = paddle.to_tensor([0.3810, 1.2774, -0.3719, 0.4637], dtype="float32") +b = paddle.to_tensor([1.8032, 0.2930, 0.5091, -0.1392], dtype="float32") +ipt = paddle.divide(a, b) +sign_ipt = paddle.sign(ipt) +abs_ipt = paddle.abs(ipt) +abs_ipt = paddle.floor(abs_ipt) +out = paddle.multiply(sign_ipt, abs_ipt) +# 输出 +# Tensor(shape=[4], dtype=float32, place=CPUPlace, stop_gradient=True, +# [ 0., 4., -0., -3.]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.empty.md b/docs/pytorch_project_convertor/API_docs/ops/torch.empty.md new file mode 100644 index 0000000000000000000000000000000000000000..ff1e733002827629b134182cb1e78c2ab5678f4b --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.empty.md @@ -0,0 +1,57 @@ +## torch.empty +### [torch.empty](https://pytorch.org/docs/stable/generated/torch.empty.html?highlight=empty#torch.empty) + +```python +torch.empty(*size, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False, + pin_memory=False) +``` + +### [paddle.empty](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/empty_cn.html#empty) + +```python +paddle.empty(shape, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | +| pin_memeory | - | 表示是否使用锁页内存,PaddlePaddle无此参数。 | + + +### 功能差异 + +#### 使用方式 +***PyTorch***:生成Tensor的形状大小以可变参数的方式传入。 +***PaddlePaddle***:生成Tensor的形状大小以list的方式传入。 + + +### 代码示例 +``` python +# PyTorch示例: +torch.empty(2, 3) +# 输出 +# tensor([[9.1835e-41, 0.0000e+00, 0.0000e+00], +# [0.0000e+00, 0.0000e+00, 0.0000e+00]]) +``` + +``` python +# PaddlePaddle示例: +paddle.empty([2, 3]) +# 输出 +# Tensor(shape=[2, 3], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[0., 0., 0.], +# [0., 0., 0.]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.empty_like.md b/docs/pytorch_project_convertor/API_docs/ops/torch.empty_like.md new file mode 100644 index 0000000000000000000000000000000000000000..598754d1aa7b961008d88c539ce6e2c4d9a27ffd --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.empty_like.md @@ -0,0 +1,27 @@ +## torch.empty_like +### [torch.empty_like](https://pytorch.org/docs/stable/generated/torch.empty_like.html?highlight=empty_like#torch.empty_like) + +```python +torch.empty_like(input, + *, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=torch.preserve_format) +``` + +### [paddle.empty_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/empty_like_cn.html#empty-like) + +```python +paddle.empty_like(x, dtype=None, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | +| pin_memeory | - | 表示是否使用锁页内存,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.eye.md b/docs/pytorch_project_convertor/API_docs/ops/torch.eye.md new file mode 100644 index 0000000000000000000000000000000000000000..4a82a705fdd8c3ed2a1bada8f3417692310e7aa6 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.eye.md @@ -0,0 +1,31 @@ +## torch.eye + +### [torch.eye](https://pytorch.org/docs/stable/generated/torch.eye.html?highlight=eye#torch.eye) +```python +torch.eye(n, + m=None, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.eye](https://pytorch.org/docs/stable/generated/torch.eye.html?highlight=eye#torch.eye) +```python +paddle.eye(num_rows, + num_columns=None, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| n | num_rows | 生成2-D Tensor的行数。 | +| m | num_columns | 生成2-D Tensor的列数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.from_numpy.md b/docs/pytorch_project_convertor/API_docs/ops/torch.from_numpy.md new file mode 100644 index 0000000000000000000000000000000000000000..3da5367de509cd5a822e7101ae578ab0ad59d4cc --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.from_numpy.md @@ -0,0 +1,29 @@ +## torch.tensor +### [torch.from_numpy](https://pytorch.org/docs/stable/generated/torch.from_numpy.html?highlight=from_numpy#torch.from_numpy) + +```python +torch.from_numpy(ndarray) +``` + +### [paddle.to_tensor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/to_tensor_cn.html#to-tensor) + +```python +paddle.to_tensor(data, + dtype=None, + place=None, + stop_gradient=True) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| ndarray | data | 表示需要转换的数据。 | +| - | dtype | 表示数据类型,PyTorch无此参数。 | +| - | place | 表示Tensor存放位置,PyTorch无此参数。 | +| - | stop_gradient | 表示是否阻断梯度传导,PyTorch无此参数。 | + +### 功能差异 + +#### 使用方式 +***PyTorch***:只能传入一个numpy.ndarray。 +***PaddlePaddle***:可以传入scalar、list、tuple、numpy.ndarray、paddle.Tensor。 diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.full.md b/docs/pytorch_project_convertor/API_docs/ops/torch.full.md new file mode 100644 index 0000000000000000000000000000000000000000..40f863dc7268a4dcaf03ac7939cd851b2f0162a0 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.full.md @@ -0,0 +1,31 @@ +## torch.full + +### [torch.full](https://pytorch.org/docs/stable/generated/torch.full.html?highlight=full#torch.full) +```python +torch.full(size, + fill_value, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.full](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/full_cn.html#full) +```python +paddle.full(shape, + fill_value, + dtype=None, + name=None) +``` + + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.full_like.md b/docs/pytorch_project_convertor/API_docs/ops/torch.full_like.md new file mode 100644 index 0000000000000000000000000000000000000000..8e4a0a98b7c6f0483f3c950ce1f0139bf9e95452 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.full_like.md @@ -0,0 +1,27 @@ +## torch.full_like +### [torch.full_like](https://pytorch.org/docs/stable/generated/torch.full_like.html?highlight=full_like#torch.full_like) + +```python +torch.full_like(input, + fill_value, + *, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False, + memory_format=torch.preserve_format) +``` + +### [paddle.full_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/full_like_cn.html#full-like) + +```python +paddle.full_like(x, fill_value, dtype=None, name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否阻断梯度传导,PaddlePaddle无此参数。 | +| memory_format | - | 表示是内存格式,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.gather.md b/docs/pytorch_project_convertor/API_docs/ops/torch.gather.md new file mode 100644 index 0000000000000000000000000000000000000000..9dfa688dad257df267c7cd39921158a1b509c820 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.gather.md @@ -0,0 +1,68 @@ +## torch.gather +### [torch.gather](https://pytorch.org/docs/stable/generated/torch.gather.html?highlight=gather#torch.gather) + +```python +torch.gather(input, dim, index, *, sparse_grad=False, out=None) +``` + +### [paddle.gather](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/manipulation/gather_cn.html#gather) + +```python +paddle.gather(x, index, axis=None, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| dim | axis | 用于指定index获取输入的维度,PyTorch中类型仅能为int,PaddlePaddle中类型可以为int32/int64/Tensor。 | +| sparse_grad | - | 表示是否对梯度稀疏化,PaddlePaddle无此参数。 | +| out | - | 表示目标Tensor,PaddlePaddle无此参数。 | + +### 功能差异 +#### 使用方式 +***PyTorch***:索引(index)的维度数和输入(input)的维度数一致,索引(index)的形状大小要小于等于输入(input)的形状大小。 +***PaddlePaddle***:索引(index)的秩有且只能等于1。 + +#### 计算方式 +***PyTorch***:沿指定的轴(dim)收集值。以2-D Tensor输入为例,其输出结果如下: +``` +if dim == 0: + out[i][j] = input[index[i][j]][j] +if dim == 1: + out[i][j] = input[i][index[i][j]] +``` +***PaddlePaddle***:根据索引(index)获取输入(x)的指定维度(axis)的条目,并将它们拼接在一起。以2-D Tensor输入为例,其输出结果如下: +``` +if axis == 0: + tensor_list = list() + for i in index: + tensor_list.append(index[i, :]) + 将tensor_list中的tensor沿axis轴拼接 +if axis == 1: + tensor_list = list() + for i in index: + tensor_list.append(index[:, i]) + 将tensor_list中的tensor沿axis轴拼接 +``` + + +### 代码示例 +``` python +# PyTorch示例: +t = torch.tensor([[1, 2], [3, 4]]) +torch.gather(t, 1, torch.tensor([[0, 0], [1, 0]])) +# 输出 +# tensor([[ 1, 1], +# [ 4, 3]]) +``` + +``` python +# PaddlePaddle示例: +t = paddle.to_tensor([[1, 2], [3, 4]]) +paddle.gather(t, paddle.to_tensor([1, 0]), 1) +# 输出 +# Tensor(shape=[2, 2], dtype=int64, place=CPUPlace, stop_gradient=True, +# [[2, 1], +# [4, 3]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.linspace.md b/docs/pytorch_project_convertor/API_docs/ops/torch.linspace.md new file mode 100644 index 0000000000000000000000000000000000000000..b797a3e5232deb39b4b0793a85ac4e8434f20c26 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.linspace.md @@ -0,0 +1,30 @@ +## torch.linspace +### [torch.linspace](https://pytorch.org/docs/stable/generated/torch.linspace.html?highlight=linspace#torch.linspace) +```python +torch.linspace(start, + end, + steps, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.linspace](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/linspace_cn.html#linspace) +```python +paddle.linspace(start, + stop, + num, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.load.md b/docs/pytorch_project_convertor/API_docs/ops/torch.load.md new file mode 100644 index 0000000000000000000000000000000000000000..3d160160e260e9fdf7d00d7233e0f11654fc6e38 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.load.md @@ -0,0 +1,44 @@ +## torch.load +### [torch.load](https://pytorch.org/docs/stable/generated/torch.load.html?highlight=load#torch.load) + +```python +torch.load(f, + map_location=None, + pickle_module=pickle, + **pickle_load_args) +``` + +### [paddle.load](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/framework/io/load_cn.html#load) + +```python +paddle.load(path, **configs) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| pickle_module | - | 表示用于unpickling元数据和对象的模块,PaddlePaddle无此参数。 | +| map_location | - | 表示加载模型的位置,PaddlePaddle无此参数。 | + + +### 功能差异 + +#### 加载类型 +***PyTorch***:可从文件或者内存中的读缓冲区(例如`io.BytesIO`、`io.StringIO`)中加载。 +***PaddlePaddle***:只能从文件中加载。 + +#### 加载内容 +***PyTorch***:可以加载`torch.Tensor`、`torch.nn.Module`、优化器等多个类型的数据。 +***PaddlePaddle***:只能加载`paddle.nn.Layer`、优化器这两个类型的数据。 + + +### 代码示例 +``` python +# PyTorch示例: +torch.load('tensors.pt', map_location=torch.device('cpu')) +``` + +``` python +# PaddlePaddle示例: +load_layer_state_dict = paddle.load("emb.pdparams") +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.multinomial.md b/docs/pytorch_project_convertor/API_docs/ops/torch.multinomial.md new file mode 100644 index 0000000000000000000000000000000000000000..1e651b736ea8a6caf2d379d67d04e4eac6f21fee --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.multinomial.md @@ -0,0 +1,23 @@ +## torch.multinomial +### [torch.multinomial](https://pytorch.org/docs/stable/generated/torch.multinomial.html?highlight=multinomial#torch.multinomial) +```python +torch.multinomial(input, num_samples, replacement=False, *, generator=None, out=None) +``` +### [paddle.multinomial](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/multinomial_cn.html#multinomial) +```python +paddle.multinomial(x, num_samples=1, replacement=False, name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| generator | - | 用于采样的伪随机数生成器,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | + +***【注意】*** 这类生成器的用法如下: +```python +G = torch.Generator() +G.manual_seed(1) +# 生成指定分布Tensor +torch.randperm(5, generator=G) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.narrow.md b/docs/pytorch_project_convertor/API_docs/ops/torch.narrow.md new file mode 100644 index 0000000000000000000000000000000000000000..d47af11da7eec803fa37ed25dc88cce14ed05583 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.narrow.md @@ -0,0 +1,38 @@ +## torch.narrow +### [torch.narrow](https://pytorch.org/docs/stable/generated/torch.narrow.html?highlight=narrow#torch.narrow) +```python +torch.narrow(input, dim, start, length) +``` +### [paddle.slice](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/slice_cn.html#slice) +```python +paddle.slice(input, axes, starts, ends) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| dim | axes | 表示切片的轴。 | +| start | starts | 表示起始位置。 | +### 功能差异 +#### 使用方式 +***PyTorch***:只能在一个维度上进行切割,`dim`、`start`、`length`传入的值均只能为int型;使用该维度输出长度(`length`)来定位结束位置。 +***PaddlePaddle***:可以在多个维度进行切割,`axes`、`starts`、`ends`传入的值为list/tuple(`starts`、`ends`传入的值可以为tensor);直接使用结束位置(`end`)来定位结束位置。 + +### 代码示例 +``` python +# PyTorch示例: +x = torch.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +torch.narrow(x, 0, 0, 2) +# 输出 +# tensor([[ 1, 2, 3], +# [ 4, 5, 6]]) +``` + +``` python +# PaddlePaddle示例: +x = paddle.to_tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) +paddle.slice(x, [0], [0], [2]) +# 输出 +# Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True, +# [[1, 2, 3], +# [4, 5, 6]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.normal.md b/docs/pytorch_project_convertor/API_docs/ops/torch.normal.md new file mode 100644 index 0000000000000000000000000000000000000000..1b93d0baa77659167f01664b9e0cb346222d3927 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.normal.md @@ -0,0 +1,30 @@ +## torch.normal +### [torch.normal](https://pytorch.org/docs/stable/generated/torch.normal.html?highlight=normal#torch.normal) +```python +torch.normal(mean, std, *, generator=None, out=None) +``` +### [paddle.normal](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/normal_cn.html#normal) +```python +paddle.normal(mean=0.0, std=1.0, shape=None, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| - | shape | 表示输出Tensor的形状。 | +| generator | - | 用于采样的伪随机数生成器,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | + +***【注意】*** 这类生成器的用法如下: +```python +G = torch.Generator() +G.manual_seed(1) +# 生成指定分布Tensor +torch.randperm(5, generator=G) +``` + +### 功能差异 + +#### 使用方式 +***PyTorch***: `mean`和`std`只能是Tensor,表示输出Tensor中每个元素的正态分布的均值和标准差。 +***PaddlePaddle***: `mean`和`std`既能是Tensor,也能是float,当为float时,则表示输出Tensor中所有元素的正态分布的均值和标准差,同时需要设置`shape`,表示生成的随机Tensor的形状。 diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.ones.md b/docs/pytorch_project_convertor/API_docs/ops/torch.ones.md new file mode 100644 index 0000000000000000000000000000000000000000..61332f46822985a8c02c0b50811670f0cdfdc375 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.ones.md @@ -0,0 +1,54 @@ +## torch.ones +### [torch.ones](https://pytorch.org/docs/stable/generated/torch.ones.html?highlight=ones#torch.ones) + +```python +torch.ones(*size, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.ones](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/ones_cn.html#ones) + +```python +paddle.ones(shape, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | + + +### 功能差异 + +#### 使用方式 +***PyTorch***:生成Tensor的形状大小以可变参数的方式传入。 +***PaddlePaddle***:生成Tensor的形状大小以list或tuple的方式传入。 + +### 代码示例 +``` python +# PyTorch示例: +torch.ones(2, 3) +# 输出 +# tensor([[ 1., 1., 1.], +# [ 1., 1., 1.]]) +``` + +``` python +# PaddlePaddle示例: +paddle.ones([2, 3]) +# 输出 +# Tensor(shape=[2, 3], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[1., 1., 1.], +# [1., 1., 1.]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.ones_like.md b/docs/pytorch_project_convertor/API_docs/ops/torch.ones_like.md new file mode 100644 index 0000000000000000000000000000000000000000..f89c0ea9399e708f1cc2f7c83ee625b15a5e3509 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.ones_like.md @@ -0,0 +1,26 @@ +## torch.ones_like +### [torch.ones_like](https://pytorch.org/docs/stable/generated/torch.ones_like.html?highlight=ones_like#torch.ones_like) + +```python +torch.ones_like(input, + *, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=torch.preserve_format) +``` + +### [paddle.ones_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/ones_like_cn.html#ones-like) + +```python +paddle.ones_like(x, dtype=None, name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | +| memory_format | - | 表示内存格式,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.rand.md b/docs/pytorch_project_convertor/API_docs/ops/torch.rand.md new file mode 100644 index 0000000000000000000000000000000000000000..2328898adaef3aa3f468f56b605706262335d5fa --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.rand.md @@ -0,0 +1,52 @@ +### [torch.rand](https://pytorch.org/docs/stable/generated/torch.rand.html?highlight=rand#torch.rand) + +```python +torch.rand(*size, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.rand](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/rand_cn.html#rand) + +```python +paddle.rand(shape, + dtype=None, + name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | + +### 功能差异 + +#### 使用方式 +***PyTorch***:生成Tensor的形状大小以可变参数的方式传入。 +***PaddlePaddle***:生成Tensor的形状大小以list或tuple的方式传入。 + + +### 代码示例 +``` python +# PyTorch示例: +torch.rand(2, 3) +# 输出 +# tensor([[0.0860, 0.2757, 0.3211], +# [0.5872, 0.5267, 0.4184]]) +``` + +``` python +# PaddlePaddle示例: +paddle.rand([2, 3]) +# 输出 +# Tensor(shape=[2, 3], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[0.18905126, 0.56219709, 0.00808361], +# [0.78120756, 0.32112977, 0.90572405]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.randint.md b/docs/pytorch_project_convertor/API_docs/ops/torch.randint.md new file mode 100644 index 0000000000000000000000000000000000000000..ab49d0a3be7712b88bc35f1ba62848b6c7ebbc05 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.randint.md @@ -0,0 +1,42 @@ +## torch.randint +### [torch.randint](https://pytorch.org/docs/stable/generated/torch.randint.html?highlight=randint#torch.randint) +```python +torch.randint(low=0, + high, + size, + *, + generator=None, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.randint](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/randint_cn.html#randint) +```python +paddle.randint(low=0, + high=None, + shape=[1], + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| generator | - | 用于采样的伪随机数生成器,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | + + +***【注意】*** 这类生成器的用法如下: +```python +G = torch.Generator() +G.manual_seed(1) +# 生成指定分布Tensor +torch.randperm(5, generator=G) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.randn.md b/docs/pytorch_project_convertor/API_docs/ops/torch.randn.md new file mode 100644 index 0000000000000000000000000000000000000000..14a79a0b5fa1e47591aeacad4ee0552fd5a1238b --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.randn.md @@ -0,0 +1,52 @@ +### [torch.randn](https://pytorch.org/docs/stable/generated/torch.randn.html?highlight=randn#torch.randn) + +```python +torch.randn(*size, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.randn](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/randn_cn.html#randn) + +```python +paddle.randn(shape, + dtype=None, + name=None) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | + + +### 功能差异 + +#### 使用方式 +***PyTorch***:生成Tensor的形状大小以可变参数的方式传入。 +***PaddlePaddle***:生成Tensor的形状大小以list或tuple的方式传入。 + +### 代码示例 +``` python +# PyTorch示例: +torch.randn(2, 3) +# 输出 +# tensor([[ 1.3290, 1.4679, -1.2373], +# [-0.2354, -0.9818, 0.0877]]) +``` + +``` python +# PaddlePaddle示例: +paddle.randn([2, 3]) +# 输出 +# Tensor(shape=[2, 3], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[-1.74181163, -0.50677234, -0.14707172], +# [ 1.18375409, 1.52477348, -0.73248941]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.randperm.md b/docs/pytorch_project_convertor/API_docs/ops/torch.randperm.md new file mode 100644 index 0000000000000000000000000000000000000000..dd8d92a467be494604346b05fbbcb9f4331c41c2 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.randperm.md @@ -0,0 +1,36 @@ +## torch.randperm +### [torch.randperm](https://pytorch.org/docs/stable/generated/torch.randperm.html?highlight=randperm#torch.randperm) +```python +torch.randperm(n, + *, + generator=None, + out=None, + dtype=torch.int64, + layout=torch.strided, + device=None, + requires_grad=False, + pin_memory=False) +``` +### [paddle.randperm](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/random/randperm_cn.html#randperm) +```python +paddle.randperm(n, dtype='int64', name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| generator | - | 用于采样的伪随机数生成器,PaddlePaddle无此参数。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | +| pin_memeory | - | 表示是否使用锁页内存,PaddlePaddle无此参数。 | + + +***【注意】*** 这类生成器的用法如下: +```python +G = torch.Generator() +G.manual_seed(1) +# 生成指定分布Tensor +torch.randperm(5, generator=G) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.range.md b/docs/pytorch_project_convertor/API_docs/ops/torch.range.md new file mode 100644 index 0000000000000000000000000000000000000000..46d66730d9609b9e8993ba918b45d3351657512b --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.range.md @@ -0,0 +1,30 @@ +## torch.range + +### [torch.range](https://pytorch.org/docs/stable/generated/torch.arange.html?highlight=arange#torch.range) +```python +torch.range(start=0, + end, + step=1, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` +### [paddle.arange](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/arange_cn.html#arange) +```python +paddle.arange(start=0, + end=None, + step=1, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.save.md b/docs/pytorch_project_convertor/API_docs/ops/torch.save.md new file mode 100644 index 0000000000000000000000000000000000000000..3c324cffd530365ba0eafbc2abbd28e541e7f95d --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.save.md @@ -0,0 +1,53 @@ +## torch.save +### [torch.save](https://pytorch.org/docs/stable/generated/torch.save.html?highlight=save#torch.save) + +```python +torch.save(obj, + f, + pickle_module=pickle, + pickle_protocol=2) +``` + +### [paddle.save](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/framework/io/save_cn.html#save) + +```python +paddle.save(obj, path, pickle_protocol=2) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| f | path | 表示存储的路径。 | +| pickle_module | - | 表示用于pickling元数据和对象的模块,PaddlePaddle无此参数。 | + + +### 功能差异 + +#### 存储类型 +***PyTorch***:可存储到文件或者内存中的写缓冲区(例如`io.BytesIO`、`io.StringIO`)。 +***PaddlePaddle***:只能存储到文件中。 + +#### 存储内容 +***PyTorch***:可以存储`torch.Tensor`、`torch.nn.Module`、优化器等多个类型的数据。 +***PaddlePaddle***:只能存储`paddle.nn.Layer`、优化器这两个类型的数据。 + + +### 代码示例 +``` python +# PyTorch示例: +x = torch.tensor([0, 1, 2, 3, 4]) +buffer = io.BytesIO() +torch.save(x, buffer) +``` + +``` python +# PaddlePaddle示例: +x = paddle.to_tensor([0, 1, 2, 3, 4]) +padle.save(x, "tensor.pdiparams") +# 报错: +# NotImplementedError: Now only supports save state_dict of Layer or Optimizer, expect dict, but received . +emb = paddle.nn.Embedding(10, 10) +layer_state_dict = emb.state_dict() +paddle.save(layer_state_dict, "emb.pdparams") +# 正常保存 +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.tensor.md b/docs/pytorch_project_convertor/API_docs/ops/torch.tensor.md new file mode 100644 index 0000000000000000000000000000000000000000..cc61cb6f2caa8b051e76c9475a73d4eb2acf5cd0 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.tensor.md @@ -0,0 +1,25 @@ +## torch.tensor +### [torch.tensor](https://pytorch.org/docs/stable/generated/torch.tensor.html?highlight=tensor#torch.tensor) + +```python +torch.tensor(data, + dtype=None, + device=None, + requires_grad=False, + pin_memory=False) +``` + +### [paddle.to_tensor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/to_tensor_cn.html#to-tensor) + +```python +paddle.to_tensor(data, + dtype=None, + place=None, + stop_gradient=True) +``` +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| device | place | 表示Tensor存放位置。 | +| requires_grad | stop_gradient | PyTorch表示是否不阻断梯度传导,PaddlePaddle表示是否阻断梯度传导。 | +| pin_memeory | - | 表示是否使用锁页内存,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.transpose.md b/docs/pytorch_project_convertor/API_docs/ops/torch.transpose.md new file mode 100644 index 0000000000000000000000000000000000000000..1c190d3792c6e348c83c1eb093528645cf47de03 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.transpose.md @@ -0,0 +1,36 @@ +## torch.transpose +### [torch.transpose](https://pytorch.org/docs/stable/generated/torch.transpose.html?highlight=transpose#torch.transpose) + +```python +torch.transpose(input, dim0, dim1) +``` + +### [paddle.transpose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/layers/transpose_cn.html#transpose) + +```python +paddle.transpose(x, perm, name=None) +``` + +### 功能差异 +#### 使用方式 +***PyTorch***:需要设置2个维度值(`dim0`和`dim1`)表示需要交换的维度。 +***PaddlePaddle***:需要设置一个重排顺序(`perm`),类型为list或者tuple。 + +### 代码示例 +``` python +# PyTorch示例: +x = torch.ones((10,20,30)) +out = torch.transpose(x, 0, 2) +out.shape +# 输出 +# torch.Size([30, 20, 10]) +``` + +``` python +# PaddlePaddle示例: +x = paddle.ones((10,20,30)) +out = paddle.transpose(x, (2, 1, 0)) +out.shape +# 输出 +# [30, 20, 10] +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.zeros.md b/docs/pytorch_project_convertor/API_docs/ops/torch.zeros.md new file mode 100644 index 0000000000000000000000000000000000000000..c2b481f0f82f501a717c77b9263a2c283ad1470a --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.zeros.md @@ -0,0 +1,56 @@ +## torch.zeros +### [torch.zeros](https://pytorch.org/docs/stable/generated/torch.zeros.html?highlight=zeros#torch.zeros) + +```python +torch.zeros(*size, + *, + out=None, + dtype=None, + layout=torch.strided, + device=None, + requires_grad=False) +``` + +### [paddle.zeros](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/zeros_cn.html#zeros) + +```python +paddle.zeros(shape, + dtype=None, + name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| size | shape | 表示输出形状大小。 | +| out | - | 表示输出的Tensor,PaddlePaddle无此参数。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | + + + +### 功能差异 + +#### 使用方式 +***PyTorch***:生成Tensor的形状大小以可变参数的方式传入。 +***PaddlePaddle***:生成Tensor的形状大小以list的方式传入。 + + +### 代码示例 +``` python +# PyTorch示例: +torch.zeros(2, 3) +# 输出 +# tensor([[ 0., 0., 0.], +# [ 0., 0., 0.]]) +``` + +``` python +# PaddlePaddle示例: +paddle.zeros([2, 3]) +# 输出 +# Tensor(shape=[2, 3], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[0., 0., 0.], +# [0., 0., 0.]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/ops/torch.zeros_like.md b/docs/pytorch_project_convertor/API_docs/ops/torch.zeros_like.md new file mode 100644 index 0000000000000000000000000000000000000000..52048da0fb134c4c0cd28762e50168ddecd7a00e --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/ops/torch.zeros_like.md @@ -0,0 +1,27 @@ +## torch.zeros_like +### [torch.zeros_like](https://pytorch.org/docs/stable/generated/torch.zeros_like.html?highlight=zeros_like#torch.zeros_like) + +```python +torch.zeros_like(input, + *, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=torch.preserve_format) +``` + +### [paddle.zeros_like](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/tensor/creation/zeros_like_cn.html#zeros-like) + +```python +paddle.zeros_like(x, dtype=None, name=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| input | x | 表示输入Tensor。 | +| layout | - | 表示布局方式,PaddlePaddle无此参数。 | +| device | - | 表示Tensor存放位置,PaddlePaddle无此参数。 | +| requires_grad | - | 表示是否不阻断梯度传导,PaddlePaddle无此参数。 | +| memory_format | - | 表示内存格式,PaddlePaddle无此参数。 | diff --git a/docs/pytorch_project_convertor/API_docs/utils/README.md b/docs/pytorch_project_convertor/API_docs/utils/README.md new file mode 100644 index 0000000000000000000000000000000000000000..4416ef62dfb76edd699d5f98655c5c46b0f28ad4 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/README.md @@ -0,0 +1,14 @@ +## 工具类 +| 序号 | PyTorch API | PaddlePaddle API | 备注 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | [torch.nn.DataParallel](https://pytorch.org/docs/stable/generated/torch.nn.DataParallel.html?highlight=dataparallel#torch.nn.DataParallel) | [paddle.DataParallel](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dygraph/parallel/DataParallel_cn.html#dataparallel) | [差异对比](torch.nn.DataParallel.md) | +| 2 | [torch.nn.parameter.Parameter](https://pytorch.org/docs/stable/generated/torch.nn.parameter.Parameter.html?highlight=torch%20nn%20parameter#torch.nn.parameter.Parameter) | [paddle.create_parameter](https://github.com/PaddlePaddle/Paddle/blob/ce2bdb0afdc2a09a127e8d9aa394c8b00a877364/python/paddle/fluid/layers/tensor.py#L77) | [差异对比](torch.nn.parameter.Parameter.md) | +| 3 | [torch.nn.utils.clip_grad_value_](https://pytorch.org/docs/stable/generated/torch.nn.utils.clip_grad_value_.html?highlight=clip_grad_value_#torch.nn.utils.clip_grad_value_) | 无对应实现 | [组合实现](torch.nn.utils.clip_grad_value_.md) | +| 4 | [torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader) | [paddle.io.DataLoader](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/reader/DataLoader_cn.html#dataloader) | [差异对比](torch.utils.data.DataLoader.md) | +| 5 | [torch.utils.data.random_split](https://pytorch.org/docs/stable/data.html?highlight=random_split#torch.utils.data.random_split) | 无对应实现 | [组合实现](torch.utils.data.random_split.md) | +| 6 | [torch.utils.data.distributed.DistributedSampler](https://pytorch.org/docs/stable/data.html?highlight=distributedsampler#torch.utils.data.distributed.DistributedSampler) | 无对应实现 | [组合实现](torch.utils.data.distributed.DistributedSampler.md) | +| 7 | [torch.utils.data.Dataset](https://pytorch.org/docs/stable/data.html?highlight=torch%20utils%20data%20dataset#torch.utils.data.Dataset) | [paddle.io.Dataset](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dataloader/dataset/Dataset_cn.html#dataset) | 功能一致 | +| 8 | [torch.utils.data.BatchSampler](https://pytorch.org/docs/stable/data.html?highlight=batchsampler#torch.utils.data.BatchSampler) | [paddle.io.BatchSampler](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dataloader/batch_sampler/BatchSampler_cn.html#batchsampler) | [差异对比](torch.utils.data.BatchSampler.md) | +| 9 | [torch.utils.data.Sampler](https://pytorch.org/docs/stable/data.html?highlight=sampler#torch.utils.data.Sampler) | [paddle.io.Sampler](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dataloader/sampler/Sampler_cn.html#sampler) | 功能一致 | + +***持续更新...*** diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.nn.DataParallel.md b/docs/pytorch_project_convertor/API_docs/utils/torch.nn.DataParallel.md new file mode 100644 index 0000000000000000000000000000000000000000..d15958659e36c3939d8f0afa94b41b4efa81d05e --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.nn.DataParallel.md @@ -0,0 +1,28 @@ +## torch.nn.DataParallel +### [torch.nn.DataParallel](https://pytorch.org/docs/stable/generated/torch.nn.DataParallel.html?highlight=dataparallel#torch.nn.DataParallel) +```python +torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0) +``` + +### [paddle.DataParallel](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dygraph/parallel/DataParallel_cn.html#dataparallel) +```python +paddle.DataParallel(layers, strategy=None, comm_buffer_size=25, last_comm_buffer_size=1) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| module | layers | 需要通过数据并行方式执行的模型。 | +| device_ids | - | 表示训练在哪几块GPU上,PaddlePaddle无此参数。 | +| output_device | - | 表示结果输出在哪一块GPU上,PaddlePaddle无此参数。 | +| dim | - | 表示哪一维度上的数据进行划分,PaddlePaddle无此参数。 | +| - | strategy | PaddlePaddle即将废弃参数。 | +| - | comm_buffer_size | 它是通信调用(如NCCLAllReduce)时,参数梯度聚合为一组的内存大小(MB),PyTorch无此参数。 | +| - | last_comm_buffer_size | 它限制通信调用中最后一个缓冲区的内存大小(MB),PyTorch无此参数。 | + +### 功能差异 +#### 使用差异 +***PyTorch***:在API中即可通过设置参数使用的GPU id。 +***PaddlePaddle***:只能在启动代码时设置GPU id,设置方式如下: +> python -m paddle.distributed.launch –selected_gpus=0,1 demo.py +> 其中 demo.py 脚本的代码可以是下面的示例代码。 diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.nn.parameter.Parameter.md b/docs/pytorch_project_convertor/API_docs/utils/torch.nn.parameter.Parameter.md new file mode 100644 index 0000000000000000000000000000000000000000..7c07248390f90bd0a1e02842c3806d9357a90515 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.nn.parameter.Parameter.md @@ -0,0 +1,58 @@ +## torch.nn.parameter.Parameter +### [torch.nn.parameter.Parameter](https://pytorch.org/docs/stable/generated/torch.nn.parameter.Parameter.html?highlight=torch%20nn%20parameter#torch.nn.parameter.Parameter) +```python +torch.nn.parameter.Parameter(data, requires_grad=True) +``` + +## [paddle.create_parameter](https://github.com/PaddlePaddle/Paddle/blob/ce2bdb0afdc2a09a127e8d9aa394c8b00a877364/python/paddle/fluid/layers/tensor.py#L77) +```python +paddle.create_parameter(shape, + dtype, + name=None, + attr=None, + is_bias=False, + default_initializer=None) +``` + + + +### 功能差异 + +#### 使用方式 +***PyTorch***:通过设置`data`将Tensor赋给Parameter。 +***PaddlePaddle***:有2种方式创建Parameter。方式一:通过设置`attr`将ParamAttr赋给Parameter;方式二:通过设置`shape`(大小)、`dtype`(类型)、`default_initializer`(初始化方式)设置Parameter。 + +#### 梯度设置 +***PyTorch***:通过设置`requires_grad`确定是否进行梯度反传。 +***PaddlePaddle***:PaddlePaddle无此功能。 + + + +### 代码示例 +``` python +# PyTorch示例: +import torch +x = torch.zeros(2, 3) +param = torch.nn.parameter.Parameter(x, requires_grad=False) + +# 输出 +# Parameter containing: +# tensor([[0., 0., 0.], +# [0., 0., 0.]]) +``` + +``` python +# PaddlePaddle示例: +import paddle +x = paddle.zeros([2, 3], dtype="float32") +param = paddle.create_parameter(shape=x.shape, + dtype=str(x.numpy().dtype), + default_initializer=paddle.nn.initializer.Assign(x)) +param.stop_gradient = True + +# 输出 +# Parameter containing: +# Tensor(shape=[2, 3], dtype=float32, place=CPUPlace, stop_gradient=True, +# [[0., 0., 0.], +# [0., 0., 0.]]) +``` diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.nn.utils.clip_grad_value_.md b/docs/pytorch_project_convertor/API_docs/utils/torch.nn.utils.clip_grad_value_.md new file mode 100644 index 0000000000000000000000000000000000000000..f42a9d8a7dd3c3e3a5b4d8c39fc843fd43072a12 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.nn.utils.clip_grad_value_.md @@ -0,0 +1,17 @@ +## torch.nn.utils.clip_grad_value_ +### [torch.nn.utils.clip_grad_value_](https://pytorch.org/docs/stable/generated/torch.nn.utils.clip_grad_value_.html?highlight=clip_grad_value_#torch.nn.utils.clip_grad_value_) + +```python +torch.nn.utils.clip_grad_value_(parameters, clip_value) +``` + +### 功能介绍 +用于梯度裁剪,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +def clip_grad_value_(parameters, clip_value): + if isinstance(parameters, paddle.Tensor): + parameters = [parameters] + clip_value = float(clip_value) + for p in filter(lambda p: p.grad is not None, parameters): + paddle.clip(p.grad, min=-clip_value, max=clip_value) +``` diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.BatchSampler.md b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.BatchSampler.md new file mode 100644 index 0000000000000000000000000000000000000000..d2ab6c4bdbae797a57cd4df38f852d33c471eb6f --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.BatchSampler.md @@ -0,0 +1,15 @@ +## torch.utils.data.BatchSampler +### [torch.utils.data.BatchSampler](https://pytorch.org/docs/stable/data.html?highlight=batchsampler#torch.utils.data.BatchSampler) +```python +torch.utils.data.BatchSampler(sampler, batch_size, drop_last) +``` + +### [paddle.io.BatchSampler](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dataloader/batch_sampler/BatchSampler_cn.html#batchsampler) +```python +paddle.io.BatchSampler(dataset=None, sampler=None, shuffle=Fasle, batch_size=1, drop_last=False) +``` + +### 功能差异 +#### 使用方式 +***PyTorch***:只能使用`sampler`来构建BatchSampler。 +***PaddlePaddle***:能使用`sampler`和`dataset`来构建BatchSampler。 diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.DataLoader.md b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.DataLoader.md new file mode 100644 index 0000000000000000000000000000000000000000..aa057cd08af17bea6e452ae5896d9462f6245dae --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.DataLoader.md @@ -0,0 +1,93 @@ +## torch.utils.data.DataLoader +### [torch.utils.data.DataLoader](https://pytorch.org/docs/stable/data.html?highlight=dataloader#torch.utils.data.DataLoader) +```python +torch.utils.data.DataLoader(dataset, + batch_size=1, + shuffle=False, + sampler=None, + batch_sampler=None, + num_workers=0, + collate_fn=None, + pin_memory=False, + drop_last=False, + timeout=0, + worker_init_fn=None, + multiprocessing_context=None, + generator=None, + prefetch_factor=2, + persistent_workers=False) +``` + +### [paddle.io.DataLoader](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/reader/DataLoader_cn.html#dataloader) +```python +paddle.io.DataLoader(dataset, + feed_list=None, + places=None, + return_list=False, + batch_sampler=None, + batch_size=1, + shuffle=False, + drop_last=False, + collate_fn=None, + num_workers=0, + use_buffer_reader=True, + use_shared_memory=False, + timeout=0, + worker_init_fn=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| sampler | - | 表示数据集采集器,PaddlePaddle无此参数。 | +| prefetch_factor | - | 表示每个worker预先加载的数据数量,PaddlePaddle无此参数。 | +| persistent_workers | - | 表示数据集使用一次后,数据加载器将会不会关闭工作进程,PaddlePaddle无此参数。 | +| generator | - | 用于采样的伪随机数生成器,PaddlePaddle无此参数。 | +| pin_memory | - | 表示数据最开始是属于锁页内存,PaddlePaddle无此参数。 | +| - | feed_list | 表示feed变量列表,PyTorch无此参数。 | +| - | use_buffer_reader | 表示是否使用缓存读取器,PyTorch无此参数。 | +| - | use_shared_memory | 表示是否使用共享内存来提升子进程将数据放入进程间队列的速度,PyTorch无此参数。 | + +### 功能差异 +#### 自定义数据采集器 +***PyTorch***:可通过设置`sampler`自定义数据采集器。 +***PaddlePaddle***:PaddlePaddle无此功能,可使用如下代码自定义一个DataLoader实现该功能。 +```python +class DataLoader(paddle.io.DataLoader): + def __init__(self, + dataset, + batch_size=1, + shuffle=False, + sampler=None, + batch_sampler=None, + num_workers=0, + collate_fn=None, + pin_memory=False, + drop_last=False, + timeout=0, + worker_init_fn=None, + multiprocessing_context=None, + generator=None): + if isinstance(dataset[0], (tuple, list)): + return_list = True + else: + return_list = False + return_list = True + super().__init__( + dataset, + feed_list=None, + places=None, + return_list=return_list, + batch_sampler=batch_sampler, + batch_size=batch_size, + shuffle=shuffle, + drop_last=drop_last, + collate_fn=collate_fn, + num_workers=num_workers, + use_buffer_reader=True, + use_shared_memory=False, + timeout=timeout, + worker_init_fn=worker_init_fn) + if sampler is not None: + seld.batch_sampler.sampler = sampler +``` diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.distributed.DistributedSampler.md b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.distributed.DistributedSampler.md new file mode 100644 index 0000000000000000000000000000000000000000..acea7eac0fb3422a6c6d76bbe264bd73c50978dd --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.distributed.DistributedSampler.md @@ -0,0 +1,31 @@ +## torch.utils.data.distributed.DistributedSampler +### [torch.utils.data.distributed.DistributedSampler](https://pytorch.org/docs/stable/data.html?highlight=distributedsampler#torch.utils.data.distributed.DistributedSampler) +```python +torch.utils.data.distributed.DistributedSampler(dataset, + num_replicas=None, + rank=None, + shuffle=True, + seed=0, + drop_last=False) +``` + +### 功能介绍 +用于实现分布式数据采集器,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +import paddle +class DistributedSampler(paddle.io.DistributedBatchSampler): + def __init__(self, + dataset, + num_replicas=None, + rank=None, + shuffle=True, + seed=0, + drop_last=False): + super().__init__( + dataset=dataset, + batch_size=1, + num_replicas=num_replicas, + rank=rank, + shuffle=shuffle, + drop_last=drop_last) +``` diff --git a/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.random_split.md b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.random_split.md new file mode 100644 index 0000000000000000000000000000000000000000..547176c21aac55a0d9f6e22eeb83b02f73faf58d --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/utils/torch.utils.data.random_split.md @@ -0,0 +1,46 @@ +## torch.utils.data.random_split +### [torch.utils.data.random_split](https://pytorch.org/docs/stable/data.html?highlight=random_split#torch.utils.data.random_split) +```python +torch.utils.data.random_split(dataset, lengths, generator=) +``` + +### 功能介绍 +用于实现数据集划分,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 + +```python +import paddle +from paddle.io import Dataset +def _accumulate(iterable, fn=lambda x, y: x + y): + it = iter(iterable) + try: + total = next(it) + except StopIteration: + return + yield total + for element in it: + total = fn(total, element) + yield total + +class Subset(Dataset): + def __init__(self, dataset, indices): + self.dataset = dataset + self.indices = indices + + def __getitem__(self, idx): + return self.dataset[self.indices[idx]] + + def __len__(self): + return len(self.indices) + +def random_split(dataset, lengths, generator=None): + if sum(lengths) != len(dataset): + raise ValueError( + "Sum of input lengths does not equal the length of the input dataset!" + ) + + indices = paddle.randperm(sum(lengths)) + return [ + Subset(dataset, indices[offset - length: offset]) + for offset, length in zip(_accumulate(lengths), lengths) + ] +``` diff --git a/docs/pytorch_project_convertor/API_docs/vision/README.md b/docs/pytorch_project_convertor/API_docs/vision/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7b9c2fe793766b69afbd884807a64a8fdd79073 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/vision/README.md @@ -0,0 +1,23 @@ +## 视觉类 + +| 序号 | PyTorch API | PaddlePaddle API | 备注 | +| ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------- | +| 1 | [torchvision.transforms.Compose](https://pytorch.org/vision/stable/transforms.html?highlight=compose#torchvision.transforms.Compose) | [paddle.vision.transforms.Compose](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/Compose_cn.html#compose) | 功能一致 | +| 2 | [torchvision.transforms.ToPILImage](https://pytorch.org/vision/stable/transforms.html?highlight=topilimage#torchvision.transforms.ToPILImage) | 无对应实现 | [组合实现](torchvision.transforms.ToPILImage.md) | +| 3 | [torchvision.transforms.Resize](https://pytorch.org/vision/stable/transforms.html?highlight=resize#torchvision.transforms.Resize) | [paddle.vision.transforms.Resize](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/Resize_cn.html#resize) | 功能一致 | +| 4 | [torchvision.transforms.ToTensor](https://pytorch.org/vision/stable/transforms.html?highlight=totensor#torchvision.transforms.ToTensor) | [paddle.vision.transforms.ToTensor](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/ToTensor_cn.html#totensor) | 功能一致 | +| 5 | [torchvision.transforms.RandomHorizontalFlip](https://pytorch.org/vision/stable/transforms.html?highlight=randomhorizontalflip#torchvision.transforms.RandomHorizontalFlip) | [paddle.vision.transforms.RandomHorizontalFlip](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/RandomHorizontalFlip_cn.html#randomhorizontalflip) | 功能一致 | +| 6 | [torchvision.transforms.CenterCrop](https://pytorch.org/vision/stable/transforms.html?highlight=centercrop#torchvision.transforms.CenterCrop) | [paddle.vision.transforms.CenterCrop](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/CenterCrop_cn.html#centercrop) | 功能一致 | +| 7 | [torchvision.transforms.ColorJitter](https://pytorch.org/vision/stable/transforms.html?highlight=colorjitter#torchvision.transforms.ColorJitter) | [paddle.vision.transforms.ColorJitter](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/ColorJitter_cn.html#colorjitter) | 功能一致 | +| 8 | [torchvision.transforms.Grayscale](https://pytorch.org/vision/stable/transforms.html?highlight=grayscale#torchvision.transforms.Grayscale) | [paddle.vision.transforms.Grayscale](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/Grayscale_cn.html#grayscale) | 功能一致 | +| 9 | [torchvision.transforms.Normalize](https://pytorch.org/vision/stable/transforms.html?highlight=normalize#torchvision.transforms.Normalize) | [paddle.vision.transforms.Normalize](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/Normalize_cn.html#normalize) | [差异对比](torchvision.transforms.Normalize.md) | +| 10 | [torchvision.transforms.RandomResizedCrop](https://pytorch.org/vision/stable/transforms.html?highlight=randomresizedcrop#torchvision.transforms.RandomResizedCrop) | [paddle.vision.transforms.RandomResizedCrop](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/RandomResizedCrop_cn.html#randomresizedcrop) | 功能一致 | +| 11 | [torchvision.transforms.Pad](https://pytorch.org/vision/stable/transforms.html?highlight=pad#torchvision.transforms.Pad) | [paddle.vision.transforms.Pad](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/Pad_cn.html#pad) | 功能一致 | +| 12 | [torchvision.transforms.RandomCrop](https://pytorch.org/vision/stable/transforms.html?highlight=randomcrop#torchvision.transforms.RandomCrop) | [paddle.vision.transforms.RandomCrop](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/RandomCrop_cn.html#randomcrop) | 功能一致 | +| 13 | [torchvision.transforms.RandomRotation](https://pytorch.org/vision/stable/transforms.html?highlight=randomrotation#torchvision.transforms.RandomRotation) | [paddle.vision.transforms.RandomRotation](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/RandomRotation_cn.html#daimashili) | 功能一致 | +| 14 | [torchvision.transforms.RandomVerticalFlip](https://pytorch.org/vision/stable/transforms.html?highlight=randomverticalflip#torchvision.transforms.RandomVerticalFlip) | [paddle.vision.transforms.RandomVerticalFlip](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/RandomVerticalFlip_cn.html#randomverticalflip) | 功能一致 | +| 15 | [torchvision.transforms.Lambda](https://pytorch.org/vision/stable/transforms.html?highlight=lambda#torchvision.transforms.Lambda) | 无对应实现 | [组合实现](torchvision.transforms.Lambda.md) | +| 17 | [torchvision.utils.save_image](https://pytorch.org/vision/stable/utils.html?highlight=save_image#torchvision.utils.save_image) | 无对应实现 | [组合实现](torchvision.utils.save_image.md) | +| 18 | [torchvision.models 系列模型](https://pytorch.org/vision/stable/models.html?highlight=torchvision%20models) | X2Paddle提供 | [使用方式](torchvision.models.md) | + +***持续更新...*** diff --git a/docs/pytorch_project_convertor/API_docs/vision/torchvision.models.md b/docs/pytorch_project_convertor/API_docs/vision/torchvision.models.md new file mode 100644 index 0000000000000000000000000000000000000000..e97f1adbfb041340b64d2b107767d8e4e415a995 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/vision/torchvision.models.md @@ -0,0 +1,36 @@ +## [torchvision.models](https://pytorch.org/vision/stable/models.html?highlight=torchvision%20models) +目前PaddlePaddle官方提供的模型参数与PyTorch不一致,为此X2Paddle提供了一套与torchvision模型参数一致且使用方式一致的模型库,以resnet18为例,具体使用方式如下: + +```python +from x2paddle import models +# 构造权重随机初始化的模型: +resnet18 = models.resnet18_pth() +x = paddle.rand([1, 3, 224, 224]) +out = model(x) + +# 构造预训练模型: +resnet18 = models.resnet18_pth(pretrained=True) +x = paddle.rand([1, 3, 224, 224]) +out = model(x) +``` + +目前支持的模型为: +| PyTorch模型 | Paddle模型 | +| ------------------------------------------------------------ | -------------------------------- | +| [torchvision.models.resnet18](https://pytorch.org/vision/stable/models.html#torchvision.models.resnet18) | x2paddle.models.resnet18_pth | +| [torchvision.models.resnet34](https://pytorch.org/vision/stable/models.html#torchvision.models.resnet34) | x2paddle.models.resnet34_pth | +| [torchvision.models.resnet50](https://pytorch.org/vision/stable/models.html#torchvision.models.resnet50) | x2paddle.models.resnet50_pth | +| [torchvision.models.resnet101](https://pytorch.org/vision/stable/models.html#torchvision.models.resnet101) | x2paddle.models.resnet101_pth | +| [torchvision.models.resnet152](https://pytorch.org/vision/stable/models.html#torchvision.models.resnet152) | x2paddle.models.resnet152_pth | +| [torchvision.models.resnext50_32x4d](https://pytorch.org/vision/stable/models.html#torchvision.models.resnext50_32x4d) | x2paddle.models.resnext50_32x4d_pth | +| [torchvision.models.resnext101_32x8d](https://pytorch.org/vision/stable/models.html#torchvision.models.resnext101_32x8d) | x2paddle.resnext101_32x8d_pth | +| [torchvision.models.wide_resnet50_2](https://pytorch.org/vision/stable/models.html#torchvision.models.wide_resnet50_2) | x2paddle.models.wide_resnet50_2_pth | +| [torchvision.models.wide_resnet101_2](https://pytorch.org/vision/stable/models.html#torchvision.models.wide_resnet101_2) | x2paddle.models.wide_resnet101_2_pth | +| [torchvision.models.vgg11](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg11) | x2paddle.models.vgg11_pth | +| [torchvision.models.vgg11_bn](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg11_bn) | x2paddle.models.vgg11_bn_pth | +| [torchvision.models.vgg13](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg13) | x2paddle.models.vgg13_pth | +| [torchvision.models.vgg13_bn](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg13_bn) | x2paddle.models.vgg13_bn_pth | +| [torchvision.models.vgg16](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg16) | x2paddle.models.vgg16_pth | +| [torchvision.models.vgg16_bn](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg16_bn) | x2paddle.models.vgg16_bn_pth | +| [torchvision.models.vgg19](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg19) | x2paddle.models.vgg19_pth | +| [torchvision.models.vgg19_bn](https://pytorch.org/vision/stable/models.html#torchvision.models.vgg19_bn) | x2paddle.models.vgg19_bn_pth | diff --git a/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.Lambda.md b/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.Lambda.md new file mode 100644 index 0000000000000000000000000000000000000000..e6ad9be49cd944efeb86874a94cc7424a380495d --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.Lambda.md @@ -0,0 +1,20 @@ +## torchvision.transforms.Lambda +### [torchvision.transforms.Lambda](https://pytorch.org/vision/stable/transforms.html?highlight=lambda#torchvision.transforms.Lambda) +```python +torchvision.transforms.Lambda(lambd) +``` + +### 功能介绍 +用于使用lamda定义的函数对数据进行预处理,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +import paddle +from paddle.vision.transforms import BaseTransform +class Lambda(BaseTransform): + def __init__(self, lambd): + if not callable(lambd): + raise TypeError("Argument lambd should be callable, got {}".format(repr(type(lambd).__name__))) + self.lambd = lambd + + def _apply_image(self, img): + return self.lambd(img) +``` diff --git a/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.Normalize.md b/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.Normalize.md new file mode 100644 index 0000000000000000000000000000000000000000..4e43a0771ce5cc21b6f89d713f7404cc13201e70 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.Normalize.md @@ -0,0 +1,22 @@ +## torchvision.transforms.Normalize +### [torchvision.transforms.Normalize](https://pytorch.org/vision/stable/transforms.html?highlight=normalize#torchvision.transforms.Normalize) +```python +torchvision.transforms.Normalize(mean, std, inplace=False) +``` + +### [paddle.vision.transforms.Normalize](https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/vision/transforms/transforms/Normalize_cn.html#normalize) +```python +paddle.vision.transforms.Normalize(mean=0.0, std=1.0, data_format='CHW', to_rgb=False, keys=None) +``` + +### 参数差异 +| PyTorch | PaddlePaddle | 备注 | +| ------------- | ------------ | ------------------------------------------------------ | +| inplace | - | 表示表示在不更改变量的内存地址的情况下,直接修改变量,PaddlePaddle无此参数。 | +| - | data_format | 表示数据的格式,PyTorch无此参数。 | +| - | to_rgb | 表示是否是否转换为rgb的格式,PyTorch无此参数。 | + +### 功能差异 +#### 使用方式 +***PyTorch***:只支持`CHW`的输入数据,同时不支持转换为`rgb`。 +***PaddlePaddle***:支持`CHW`和`HWC`的输入数据,同时支持转换为`rgb`。 diff --git a/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.ToPILImage.md b/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.ToPILImage.md new file mode 100644 index 0000000000000000000000000000000000000000..09e6d53380cf2c9a7a1698daf45915b2274d0bce --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/vision/torchvision.transforms.ToPILImage.md @@ -0,0 +1,114 @@ +## torchvision.transforms.ToPILImage +### [torchvision.transforms.ToPILImage](https://pytorch.org/vision/stable/transforms.html?highlight=topilimage#torchvision.transforms.ToPILImage) +```python +torchvision.transforms.ToPILImage(mode=None) +``` + +### 功能介绍 +用于根据`mode`返回PIL类型的图像,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +import paddle +import PIL +import numbers +import numpy as np +from PIL import Image +from paddle.vision.transforms import BaseTransform +from paddle.vision.transforms import functional as F + + +class ToPILImage(BaseTransform): + def __init__(self, mode=None, keys=None): + super(ToTensor, self).__init__(keys) + self.data_format = data_format + + def _apply_image(self, pic): + """ + Args: + pic (Tensor|np.ndarray): Image to be converted to PIL Image. + Returns: + PIL: Converted image. + """ + if not (isinstance(pic, paddle.Tensor) or isinstance(pic, np.ndarray)): + raise TypeError('pic should be Tensor or ndarray. Got {}.'.format( + type(pic))) + + elif isinstance(pic, paddle.Tensor): + if pic.ndimension() not in {2, 3}: + raise ValueError( + 'pic should be 2/3 dimensional. Got {} dimensions.'.format( + pic.ndimension())) + + elif pic.ndimension() == 2: + # if 2D image, add channel dimension (CHW) + pic = pic.unsqueeze(0) + + elif isinstance(pic, np.ndarray): + if pic.ndim not in {2, 3}: + raise ValueError( + 'pic should be 2/3 dimensional. Got {} dimensions.'.format( + pic.ndim)) + + elif pic.ndim == 2: + # if 2D image, add channel dimension (HWC) + pic = np.expand_dims(pic, 2) + + npimg = pic + if isinstance(pic, paddle.Tensor) and "float" in str(pic.numpy( + ).dtype) and mode != 'F': + pic = pic.mul(255).byte() + if isinstance(pic, paddle.Tensor): + npimg = np.transpose(pic.numpy(), (1, 2, 0)) + + if not isinstance(npimg, np.ndarray): + raise TypeError( + 'Input pic must be a paddle.Tensor or NumPy ndarray, ' + + 'not {}'.format(type(npimg))) + + if npimg.shape[2] == 1: + expected_mode = None + npimg = npimg[:, :, 0] + if npimg.dtype == np.uint8: + expected_mode = 'L' + elif npimg.dtype == np.int16: + expected_mode = 'I;16' + elif npimg.dtype == np.int32: + expected_mode = 'I' + elif npimg.dtype == np.float32: + expected_mode = 'F' + if mode is not None and mode != expected_mode: + raise ValueError( + "Incorrect mode ({}) supplied for input type {}. Should be {}" + .format(mode, np.dtype, expected_mode)) + mode = expected_mode + + elif npimg.shape[2] == 2: + permitted_2_channel_modes = ['LA'] + if mode is not None and mode not in permitted_2_channel_modes: + raise ValueError("Only modes {} are supported for 2D inputs". + format(permitted_2_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'LA' + + elif npimg.shape[2] == 4: + permitted_4_channel_modes = ['RGBA', 'CMYK', 'RGBX'] + if mode is not None and mode not in permitted_4_channel_modes: + raise ValueError("Only modes {} are supported for 4D inputs". + format(permitted_4_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'RGBA' + else: + permitted_3_channel_modes = ['RGB', 'YCbCr', 'HSV'] + if mode is not None and mode not in permitted_3_channel_modes: + raise ValueError("Only modes {} are supported for 3D inputs". + format(permitted_3_channel_modes)) + if mode is None and npimg.dtype == np.uint8: + mode = 'RGB' + + if mode is None: + raise TypeError('Input type {} is not supported'.format( + npimg.dtype)) + + return Image.fromarray(npimg, mode=mode) +``` diff --git a/docs/pytorch_project_convertor/API_docs/vision/torchvision.utils.save_image.md b/docs/pytorch_project_convertor/API_docs/vision/torchvision.utils.save_image.md new file mode 100644 index 0000000000000000000000000000000000000000..3a5096b04a6ef1cfba39a610e080a23e8170cd29 --- /dev/null +++ b/docs/pytorch_project_convertor/API_docs/vision/torchvision.utils.save_image.md @@ -0,0 +1,112 @@ +## torchvision.utils.save_image +### [torchvision.utils.save_image](https://pytorch.org/vision/stable/utils.html?highlight=save_image#torchvision.utils.save_image) +```python +torchvision.utils.save_image(tensor: Union[torch.Tensor, List[torch.Tensor]], + fp: Union[str, pathlib.Path, BinaryIO], + format: Union[str, NoneType] = None, + **kwargs) +``` + +### 功能介绍 +用于将Tensor保存至图像中,PaddlePaddle目前无对应API,可使用如下代码组合实现该API。 +```python +import pathlib +import paddle +import warnings +import math +import numpy as np +from PIL import Image +from typing import Union, Optional, List, Tuple, Text, BinaryIO + + +@paddle.no_grad() +def make_grid(tensor: Union[paddle.Tensor, List[paddle.Tensor]], + nrow: int=8, + padding: int=2, + normalize: bool=False, + value_range: Optional[Tuple[int, int]]=None, + scale_each: bool=False, + pad_value: int=0, + **kwargs) -> paddle.Tensor: + if not (isinstance(tensor, paddle.Tensor) or + (isinstance(tensor, list) and all( + isinstance(t, paddle.Tensor) for t in tensor))): + raise TypeError( + f'tensor or list of tensors expected, got {type(tensor)}') + + if "range" in kwargs.keys(): + warning = "range will be deprecated, please use value_range instead." + warnings.warn(warning) + value_range = kwargs["range"] + + # if list of tensors, convert to a 4D mini-batch Tensor + if isinstance(tensor, list): + tensor = paddle.stack(tensor, axis=0) + + if tensor.dim() == 2: # single image H x W + tensor = tensor.unsqueeze(0) + if tensor.dim() == 3: # single image + if tensor.size(0) == 1: # if single-channel, convert to 3-channel + tensor = paddle.concat((tensor, tensor, tensor), 0) + tensor = tensor.unsqueeze(0) + + if tensor.dim() == 4 and tensor.size(1) == 1: # single-channel images + tensor = paddle.concat((tensor, tensor, tensor), 1) + + if normalize is True: + if value_range is not None: + assert isinstance(value_range, tuple), \ + "value_range has to be a tuple (min, max) if specified. min and max are numbers" + + def norm_ip(img, low, high): + img.clip(min=low, max=high) + img = img - low + img = img / max(high - low, 1e-5) + + def norm_range(t, value_range): + if value_range is not None: + norm_ip(t, value_range[0], value_range[1]) + else: + norm_ip(t, float(t.min()), float(t.max())) + + if scale_each is True: + for t in tensor: # loop over mini-batch dimension + norm_range(t, value_range) + else: + norm_range(tensor, value_range) + + if tensor.size(0) == 1: + return tensor.squeeze(0) + + # make the mini-batch of images into a grid + nmaps = tensor.size(0) + xmaps = min(nrow, nmaps) + ymaps = int(math.ceil(float(nmaps) / xmaps)) + height, width = int(tensor.shape[2] + padding), int(tensor.shape[3] + + padding) + num_channels = tensor.shape[1] + grid = paddle.full((num_channels, height * ymaps + padding, + width * xmaps + padding), pad_value) + k = 0 + for y in range(ymaps): + for x in range(xmaps): + if k >= nmaps: + break + grid[:, y * height + padding:(y + 1) * height, x * width + padding:( + x + 1) * width] = tensor[k] + k = k + 1 + return grid + + +@paddle.no_grad() +def save_image(tensor: Union[paddle.Tensor, List[paddle.Tensor]], + fp: Union[Text, pathlib.Path, BinaryIO], + format: Optional[str]=None, + **kwargs) -> None: + grid = make_grid(tensor, **kwargs) + ndarr = paddle.clip(grid * 255 + 0.5, 0, 255).transpose( + [1, 2, 0]).cast("uint8").numpy() + im = Image.fromarray(ndarr) + im.save(fp, format=format) + +``` diff --git a/docs/pytorch_project_convertor/FAQ.md b/docs/pytorch_project_convertor/FAQ.md new file mode 100644 index 0000000000000000000000000000000000000000..50572b784282312bd1ccdf497879d58f80fc9bb1 --- /dev/null +++ b/docs/pytorch_project_convertor/FAQ.md @@ -0,0 +1,24 @@ +## 常见问题 +1.出现如下提示如何处理? +> The no support Api are: [torchvision.transforms.RandomErasing, torchvision.transforms.functional, torchvision.transforms.RandomCrop.get_params, torch.all, torch.as_tensor]. + +A:这一提示说明仍有API未支持转换,用户可自行添加相应API的支持,具体添加流程参照[添加示例](./add_api.md),或及时提issue与我们联系。 + +2.运行时,出现如下2种错误,如何处理? +> AttributeError: 'Tensor' object has no attribute 'XX' +> AttributeError: 'Layer' object has no attribute 'XX' + +A:这一提示说明`paddle.nn.Tensor`或`paddle.nn.Layer`仍有attribute未支持转换,用户可自行添加相应API的支持,具体添加流程参照[添加示例](./add_api.md),或及时提issue与我们联系。 + + +3.运行时,出现DataLoader的报错异常,如何查找原因? +A: +步骤一:查看对应自定义Dataset中\_\_getiem\_\_的返回值是否为numpy; +步骤二:如若当前的设备为GPU,是否未将`num_workers`设置为0; +步骤三:查看图像预处理的transform中是否有使用出错。 + +4.当前是否支持torch.jit的转换? +A:不支持。 + +5.如何查看PyTorch与PaddlePaddle API的差异? +A:我们提供了[PyTorch-PaddlePaddle API对应表](./API_docs/README.md),您可从中获取对应关系。 diff --git a/docs/pytorch_project_convertor/README.md b/docs/pytorch_project_convertor/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a8a3b00add657bfb152d67ef8c39dbb3da82a32b --- /dev/null +++ b/docs/pytorch_project_convertor/README.md @@ -0,0 +1,26 @@ +# PyTorch训练项目转换 + +支持将PyTorch代码及预训练模型转换为PaddlePaddle代码及预训练模型。 + +## 使用方法 +### 第一步:转换前代码预处理 +由于部分PyTorch操作是目前PaddlePaddle暂不支持的操作(例如:不支持TensorBoard、自动下载模型等),因此我们需要手动将这部分操作去除或者修改,具体可参见[转换前代码预处理](./before_convert.md)。 + + + +### 第二步:转换 +``` shell +x2paddle --convert_torch_project --project_dir=torch_project --save_dir=paddle_project --pretrain_model=model.pth +``` +| 参数 | | +|----------|--------------| +|--convert_torch_project | 当前方式为对PyTorch Project进行转换 | +|--project_dir | PyTorch的项目路径 | +|--save_dir | 指定转换后项目的保存路径 | +|--pretrain_model | **[可选]**需要转换的预训练模型的路径(文件后缀名为“.pth”、“.pt”、“.ckpt”)或者包含预训练模型的文件夹路径,转换后的模型将将保在当前路径,后缀名为“.pdiparams” | + + +### 第三步:转换后代码后处理 +PaddlePaddle在使用上有部分限制(例如:自定义Dataset必须继承自`paddle.io.Dataset`、部分情况下DataLoader的num_worker只能为0等),用户需要手动修改代码,使代码运行,具体可参见[转换后代码后处理](./after_convert.md)。 + +***[注意]*** 转换前后相应操作可以参考[转换示例](./demo.md) diff --git a/docs/pytorch_project_convertor/add_api.md b/docs/pytorch_project_convertor/add_api.md new file mode 100644 index 0000000000000000000000000000000000000000..8e89b18413de12c20e58819b723405ce43534db7 --- /dev/null +++ b/docs/pytorch_project_convertor/add_api.md @@ -0,0 +1,234 @@ +# 添加API映射方式 + +在3种情况下需要添加的API映射,本文档将对添加方式逐一进行介绍,3种情况如下表所示: + +| | 对应情况 | +| -------------------- | ------------------------------------------------------------ | +| [情况1](#situation1) | 在运行代码时出现错误:`AttributeError: 'Tensor' object has no attribute 'XX'`。 | +| [情况2](#situation2) | 在运行代码时出现错误:`AttributeError: 'Layer' object has no attribute 'XX'`。 | +| [情况3](#situation3) | 在转换代码时出现提示:`Can not convert the file XX.py. The unsupported packages or operators are: [torch.nn.Tanh, torch.nn.utils.spectral_norm].`。
[3.1](#situation3.1) PaddlePaddle存在对应API,功能完全一致,参数一致。
[3.2](#situation3.2) PaddlePaddle存在对应API,功能基本一致,参数不一致。
[3.3](#situation3.3) PaddlePaddle不存在对应API。 | + +需要修改的文件在[x2paddle/project_convertor/pytorch](../../x2paddle/project_convertor/pytorch)中,具体文件如下所示: + +> . +> |── api_mapper # 存放映射处理相关操作 +>           |── \_\_init\_\_.py +>           |── learning_rate_scheduler.py # 学习率类API映射操作 +>           |── nn.py # 组网、损失相关类API映射操作 +>           |── ops.py # paddle.Tensor处理类API映射操作 +>           |── torchvision.py # 图像处理相关的API映射操作 +>           └── utils.py # 基础操作 +> |── mapper.py # 存放映射关系 +> └── torch2paddle # 存放需要重新封装实现的API +>           |──\_\_init\_\_.py +>           |── device.py # 实现设备相关的操作 +>           |── io.py # 实现数据相关的操作 +>           |── layer.py # 实现paddle.nn.Layer类内方法/属性的操作 +>           |── nn_functional.py # 实现组网OP的操作 +>           |── nn_utils.py # 实现组网参数相关的操作 +>           |── ops.py # 实现Tensor处理OP的操作 +>           |── optimizer.py # 实现优化相关的操作 +>           |── tensor.py # 实现paddle.Tensor类内方法/属性操作 +>           |── varbase.py # 实现paddle.Tensor取值的操作 +>           |── vision_transforms.py # 实现视觉transform的操作 +>           └── vision_utils.py # 实现视觉基础的操作 + +***[注意]*** 添加完映射后,需要重新安装X2Paddle: +``` +cd X2Paddle +rm -rf bulid dist x2paddle.egg-info +pip uninstall x2paddle +python setup.py install +``` + +### 情况1 + +该情况出现的原因为paddle.Tensor缺乏类内方法/属性,因此需要将torch.Tensor的类内方法/属性注册为paddle.Tensor的类内方法/属性,在[x2paddle/project_convertor/pytorch/torch2paddle/tensor.py](../../x2paddle/project_convertor/pytorch/torch2paddle/tensor.py)添加相应代码。以item类内方法为例,PyTorch中item方法的作用是提取Scalar中的数值,以避免耗费内存和计算量,因此需要添加如下代码: + +```python +# 添加注册装饰器 +@add_tensor_function +def item(self): + # 实现item方法的对应功能 + return self.numpy()[0] +``` + +当torch.Tensor的类内方法/属性与paddle.Tensor的内置方法/属性命名一致,但实现功能不一致,也需要重新实现该类内方法/属性。以reshape类内方法为例,PyTorch传入的为可变参数,而PaddlePaddle出入的参数为一个list,因此需要添加的代码如下: + +```python +# 对原始的reshape进行重命名,此处添加"_tmp",防止与其他类内函数重名 +reshape_tmp = partial(paddle.Tensor.reshape) +# 添加注册装饰器 +@add_tensor_function +def reshape(self, *shape): + # 实现reshape方法的对应功能 + return reshape_tmp(self, shape) +``` + +### 情况2 + +该情况出现的原因为paddle.nn.Layer缺乏类内方法/属性,因此需要将torch.nn.Module的类内方法/属性注册为paddle.nn.Layer的类内方法/属性,在[x2paddle/project_convertor/pytorch/torch2paddle/layer.py](../../x2paddle/project_convertor/pytorch/torch2paddle/layer.py)添加相应代码。以cuda类内方法为例,PyTorch的网络可以设置运行的的设备为cuda,而PaddlePaddle则不需要此操作,因此需要添加如下代码返回原网络即可: + +```python +# 添加注册装饰器 +@add_layer_function +def cuda(self): + return self +``` + +当torch.nn.Module的类内方法/属性与paddle.nn.Layer的内置方法/属性命名一致,但实现功能不一致,也需要重新实现该类内方法/属性。以train类内方法为例,PyTorch可以设置train的模式是train还是eval,PaddlePaddle则需要组合实现,因此需要添加的代码如下: + +```python +# 对原始的train进行重命名,此处添加"_tmp",防止与其他类内函数重名 +train_tmp = partial(paddle.nn.Layer.train) +# 添加注册装饰器 +@add_layer_function +def train(self, mode=True): + # 实现train方法的对应功能 + if mode: + return train_tmp(self) + else: + return paddle.nn.Layer.eval(self) +``` + +### 情况3 + +### 3.1 PaddlePaddle存在对应API,功能完全一致,参数一致 + +该情况直接在[x2paddle/project_convertor/pytorch/mapper.py](../../x2paddle/project_convertor/pytorch/mapper.py)中对应的MAPPER中添加PyTorch API的字符串以及Paddle API的字符串,无需添加进行参数映射所需调用的类,具体实现如下: + +```python +# key为PyTorch API字符串; +# value为列表,由Paddle API字符串和None组合而成。 +... +NN_MAPPER = { + ... + "torch.nn.Sequential": + ["paddle.nn.Sequential", None], + "torch.nn.utils": + ["paddle.nn.utils", None], + ... +} +... +``` + +### 3.2 PaddlePaddle存在对应API,功能基本一致,参数不一致 + +该情况需要完成以下几个步骤: + +***步骤1*** 在[x2paddle/project_convertor/pytorch/mapper.py](.../../x2paddle/project_convertor/pytorch/mapper.py)中对应的MAPPER中添加PyTorch API的字符串以及Paddle API的字符串、映射处理类,具体实现如下: + +```python +# key为PyTorch API字符串; +# value为列表,由Paddle API字符串和参映射处理类组合而成。 +... +NN_MAPPER = { + ... + "torch.nn.Conv2d": + ["paddle.nn.Conv2D", ClassConv2D], + ... + "torch.nn.functional.relu": + ["paddle.nn.functional.relu", FuncRelu], + ... + } +... +# 类名以Class或Func开始,Class代表Paddle API为一个类,Func代表Paddle API为一个方法。 +``` + +***步骤2*** 在[x2paddle/project_convertor/pytorch/api_mapper/](../../x2paddle/project_convertor/pytorch/api_mapper)文件夹中找到对应的文件并在其中添加映射处理类,类型中用户需要重写process_attrs、delete_attrs、check_attrs以及run这三个函数,其中run只需要修改对应的x2paddle封装的API命名即可。以`torch.matmul`和`paddle.matmul`的映射为例,二者的参数名不一致,因此需要添加的代码如下所示: + +```python +class FuncMatmul(Mapper): + def __init__(self, func_name, pytorch_api_name, args, kwargs, target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + """ 更新参数。 + """ + rename_key(self.kwargs, "input", "x") + rename_key(self.kwargs, "other", "y") + + def delete_attrs(self): + """ 删除参数。 + """ + delete_key(self.kwargs, "out") + + def check_attrs(self): + """ 确认参数的值。 + """ + pass + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.matmul"): + # 作用:当出现可变参数或关键字参数,无法对参数进行处理; + # 需要根据x2paddle封装的对应API命名生成代码(x2paddle封装的对应API相关代码在步骤3中实现) + return [], generate_api_code(self.func_name, self.args, self.kwargs), [] + else: + # 作用:将paddle与pytorch不同的可变参数替换成字典参数,并生成相应代码 + self.convert_args2kwargs() + return self.convert_to_paddle() +``` + +其中,使用到的几个方法介绍如下: + +| 方法 | 参数 | 作用 | +| ------------------------------------ | ------------------------------------------------------------ | ----------------------------------------------- | +| rename_key(kwargs, old_key, new_key) | kwargs:PyTorch API的关键字参数; old_key:PyTorch API的关键字参数的key; new_key:Paddle API的关键字参数的key。 | 若old_key存在于kwargs,将old_key替换为new_key。 | +| delete_key(kwargs, old_key) | kwargs:PyTorch API的关键字参数;old_key:PyTorch API的关键字参数的key。 | 删除kwargs中的old_key。 | + + + +***步骤3*** 当PyTorch API传入的是可变参数或关键字参数,映射处理类无法对参数进行处理,此时只能调用x2paddle封装的API,所以需要在[x2paddle/project_convertor/pytorch/torch2paddle/](../../x2paddle/project_convertor/pytorch/torch2paddle)文件夹中找到对应的文件并在其中添加x2paddle API实现,其函数名或类名与步骤2中的`torch2paddle_func_name`命名一致,同样以`torch.matmul`和`paddle.matmul`的映射为例,其实现代码如下: + +```python +def matmul(input, other, *, out=None): + return paddle.matmul(input, other) +``` + +### 3.3 PaddlePaddle不存在对应API + +### 3.3.1 API代码为必要代码 + +当前API在代码中必须存在,需要添加转换,因此要完成以下2个步骤: + +***步骤1*** 在[x2paddle/project_convertor/pytorch/mapper.py](../../x2paddle/project_convertor/pytorch/mapper.py)中对应的MAPPER中添加PyTorch API的字符串以及Paddle API的字符串,具体实现如下: + +```python +# key为PyTorch API字符串; +# value为列表,由x2paddle自行实现API字符串和None组合而成。 +... +UTILS_MAPPER = { + ... + "torch.utils.data.random_split": + ["x2paddle.torch2paddle.random_split", None], + ... + "torch.utils.data.ConcatDataset": + ["x2paddle.torch2paddle.ConcatDataset", None] + ... + } +... +``` + +***步骤2*** 在[x2paddle/project_convertor/pytorch/torch2paddle/](../../x2paddle/project_convertor/pytorch/torch2paddle)文件夹中找到对应的文件并在其中添加x2paddle API实现,其函数名或类名与步骤1中字典 value值中list的第一个值一致,以`torch.utils.data.random_split`的实现为例,其作用为划分数据集,因此需要添加的代码如下所示: + +```python +def random_split(dataset, lengths, generator=None): + if sum(lengths) != len(dataset): + raise ValueError("Sum of input lengths does not equal the length of the input dataset!") + indices = paddle.randperm(sum(lengths)) + return [Subset(dataset, indices[offset - length : offset]) for offset, length in zip(_accumulate(lengths), lengths)] +setattr(paddle.io, "random_split", random_split) +``` + +### 3.3.2 API代码为不必要代码 + +当前API为PaddlePaddle不需要的代码,应进行删除,因此需要在[x2paddle/project_convertor/pytorch/mapper.py](../../x2paddle/project_convertor/pytorch/mapper.py)中REMOVE_API中添加需要去除的PyTorch API,具体实现如下: + +```python +REMOVE_API =["torch.backends.cudnn", + "torch.backends.cudnn.benchmark"] +``` + +### 3.3.3 API代码为可替换代码 + +若当前API可用其他PyTorch API`torch.YY`(`torch.YY`在[已支持映射列表](./supported_API.md)中)代替且替换后精度影响不大,可在原PyTorch代码中将当前API替换为`torch.YY`,再进行转换。 diff --git a/docs/pytorch_project_convertor/after_convert.md b/docs/pytorch_project_convertor/after_convert.md new file mode 100644 index 0000000000000000000000000000000000000000..c0c5ecbbbd3f02d4e82313bceac3e80aca9d92a9 --- /dev/null +++ b/docs/pytorch_project_convertor/after_convert.md @@ -0,0 +1,42 @@ +# 转换后代码后处理 +1. 若需要使用GPU,且预处理中使用了Tensor,`x2paddle.torch2paddle.DataLoader`中的`num_workers`必须设置为0。 + +2. 修改自定义Dataset(继承自`paddle.io.Dataset`)中的`__getitem__`的返回值,若返回值中存在Tensor,需添加相应代码将Tensor修改为numpy。 + +``` +# 原始代码 +class VocDataset(paddle.io.Dataset): + ... + def __getitem__(self): + ... + return out1, out2 + ... +# 替换后代码 +class VocDataset(paddle.io.Dataset): + ... + def __getitem__(self): + ... + if isinstance(out1, paddle.Tensor): + out1 = out1.numpy() + if isinstance(out2, paddle.Tensor): + out2 = out2.numpy() + return out1, out2 + ... +``` + +3. 若存在Tensor对比操作(包含==、!=、<、<=、>、>=操作符),在对比操作符前添加对Tensor类型的判断,如果为bool型强转为int型,并在对比后转换回bool型。 + +``` +# 原始代码(其中c_trg是Tensor) +c_trg = c_trg == 0 +# 替换后代码 +is_bool = False +if str(c_trg.dtype) == "VarType.BOOL": + c_trg = c_trg.cast("int32") + is_bool = True +c_trg = c_trg == 0 +if is_bool: + c_trg = c_trg.cast("bool") +``` + +4. 如若转换后的运行代码的入口为sh脚本文件,且其中有预训练模型路径,应将其中的预训练模型的路径字符串中的“.pth”、“.pt”、“.ckpt”替换为“.pdiparams”。 diff --git a/docs/pytorch_project_convertor/before_convert.md b/docs/pytorch_project_convertor/before_convert.md new file mode 100644 index 0000000000000000000000000000000000000000..35b59a718a59392b7bb26d57b020599beabdea64 --- /dev/null +++ b/docs/pytorch_project_convertor/before_convert.md @@ -0,0 +1,35 @@ +# 转换前代码预处理 +1. 去除TensorBoard相关的操作。 + +2. 将PyTorch中Tensor逐位逻辑与、或、异或运算操作符替换为对应的API的操作: +> | 替换为 torch.bitwise_or +> & 替换为 torch.bitwise_and +> ^ 替换为 torch.bitwise_xor + +``` python +# 原始代码: +pos_mask | neg_mask +# 替换后代码 +torch.bitwise_or(pos_mask, neg_mask) +``` + +3. 若自定义的`DataSet`(用于加载数据模块,作为`torch.utils.data.DataLoader`的参数)未继承`torch.utils.data.Dataset`,则需要添加该继承关系。 + +``` +# 原始代码 +class VocDataset: +# 替换后代码 +import torch +class VocDataset(torch.utils.data.Dataset): +``` + +4. 若预训练模型需要下载,去除下载预训练模型相关代码,在转换前将预训练模型下载至本地,并修改加载预训练模型参数相关代码的路径为预训练模型本地保存路径。 + +5. 若在数据预处理中出现Tensor与float型/int型对比大小,则需要将float型/int型修改为Tensor,例如下面代码为一段未数据预处理中一段代码,修改如下: +``` python +# 原始代码: +mask = best_target_per_prior < 0.5 +# 替换后代码 +threshold_tensor = torch.full_like(best_target_per_prior, 0.5) +mask = best_target_per_prior < threshold_tensor +``` diff --git a/docs/pytorch_project_convertor/demo.md b/docs/pytorch_project_convertor/demo.md new file mode 100644 index 0000000000000000000000000000000000000000..f675a869d5025e900db2a1f336b175b073dbd845 --- /dev/null +++ b/docs/pytorch_project_convertor/demo.md @@ -0,0 +1,197 @@ +# PyTorch项目转换示例 +## [StarGAN](https://github.com/yunjey/stargan) +### 准备工作 +``` shell +# 下载项目 +git clone https://github.com/yunjey/stargan.git +git checkout 30867d6f85a3bb99c38ae075de651004747c42d4 +# 下载预训练模型 +cd stargan +bash download.sh pretrained-celeba-128x128 +# 下载数据集 +bash download.sh celeba +``` +### 第一步:转换前代码预处理 +1. 规避使用TensorBoard,在[config处](https://github.com/yunjey/stargan/blob/master/main.py#L109)设置不使用tensorboard,具体添加代码如下: +``` python +... +parser.add_argument('--lr_update_step', type=int, default=1000) +config = parser.parse_args() +# 第5行为添加不使用tensorboard的相关代码 +config.use_tensorboard = False +print(config) +main(config) +``` +### 第二步:转换 +``` shell +cd ../ +x2paddle --convert_torch_project --project_dir=stargan --save_dir=paddle_project --pretrain_model=stargan/stargan_celeba_128/models/ +``` +【注意】此示例中的`pretrain_model`是训练后的PyTorch模型,转换后则为PaddlePaddle训练后的模型,用户可修改转换后代码将其作为预训练模型,也可直接用于预测。 +### 第三步:转换后代码后处理 +**需要修改的文件位于paddle_project文件夹中,其中文件命名与原始stargan文件夹中文件命名一致。** +1. DataLoader的`num_workers`设置为0,在[config处](https://github.com/SunAhong1993/stargan/blob/paddle/main.py#L116)设置强制设置`num_workers`,具体添加代码如下: +``` python +... +parser.add_argument('--lr_update_step', type=int, default=1000) +config = parser.parse_args() +config.use_tensorboard = False +# 第6行添加设置num_workers为0 +config.num_workers = 0 +print(config) +main(config) +``` + +2. 修改自定义Dataset中的[\_\_getitem\_\_的返回值](https://github.com/SunAhong1993/stargan/blob/paddle/data_loader.py#L63),将Tensor修改为numpy,修改代码如下: +``` python +... +class CelebA(data.Dataset): + ... + def __getitem__(self, index): + """Return one image and its corresponding attribute label.""" + dataset = (self.train_dataset if self.mode == 'train' else self. + test_dataset) + filename, label = dataset[index] + image = Image.open(os.path.join(self.image_dir, filename)) + # return self.transform(image), torch2paddle.create_float32_tensor(label) + # 将原来的return替换为如下12-17行 + out1 = self.transform(image) + if isinstance(out1, paddle.Tensor): + out1 = out1.numpy() + out2 = torch2paddle.create_float32_tensor(label) + if isinstance(out2, paddle.Tensor): + out2 = out2.numpy() + return out1, out2 + ... +``` + +3. 在[Tensor对比操作](https://github.com/SunAhong1993/stargan/blob/paddle/solver.py#L156)中对Tensor进行判断,判断是否为bool型,如果为bool类型需要强制转换,修改代码如下: +``` python +... +class Solver(object): + ... + def create_labels(self, c_org, c_dim=5, dataset='CelebA', selected_attrs=None): + ... + for i in range(c_dim): + if dataset == 'CelebA': + c_trg = c_org.clone() + if i in hair_color_indices: + c_trg[:, i] = 1 + for j in hair_color_indices: + if j != i: + c_trg[:, j] = 0 + else: + # 如果为bool型,需要强转为int32, + # 在17-20行实现 + is_bool = False + if str(c_trg.dtype) == "VarType.BOOL": + c_trg = c_trg.cast("int32") + is_bool = True + c_trg[:, i] = (c_trg[:, i] == 0) + # 如果为bool类型转换为原类型 + # 在23-24行实现 + if is_bool: + c_trg = c_trg.cast("bool") + ... + ... + ... +... +``` + +### 运行训练代码 +``` shell +cd paddle_project/stargan +python main.py --mode train --dataset CelebA --image_size 128 --c_dim 5 --sample_dir stargan_celeba/samples --log_dir stargan_celeba/logs --model_save_dir stargan_celeba/models --result_dir stargan_celeba/results --selected_attrs Black_Hair Blond_Hair Brown_Hair Male Young --celeba_image_dir ./data/celeba/images --attr_path ./data/celeba/list_attr_celeba.txt +``` + +***转换后的代码可在[这里](https://github.com/SunAhong1993/stargan/tree/paddle)进行查看。*** + +## [Ultra-Light-Fast-Generic-Face-Detector](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB) + +### 准备工作 +1. 下载项目 +``` shell +# 下载项目 +git clone https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB.git +git checkout 492a02471671b49c56be8d90cda54c94749d2980 +``` +2. 根据Generate VOC format training data set and training process的README.md所示下载数据集,并存放于Ultra-Light-Fast-Generic-Face-Detector-1MB/data/文件夹下。 +### 第一步:转换前代码预处理 +1. 将代码中的[或操作符](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/vision/utils/box_utils.py#L153)替换为如下代码: +``` python +... +def hard_negative_mining(loss, labels, neg_pos_ratio): + ... + # return pos_mask | neg_mask + return torch.bitwise_or(pos_mask, neg_mask) +... +``` + +2. 使自定义的[`DataSet`](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/vision/datasets/voc_dataset.py#L10)继承`torch.utils.data.Dataset`,同时由于代码未导入torch,要添加相关导入的包,修改为如下代码: +``` python +... +# 导入torch +import torch +... +# class VOCDataset +class VOCDataset(torch.utils.data.Dataset): + ... +... +``` +3. 将[数据预处理](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/vision/utils/box_utils.py#L126)Tensor与int型对比,修改为Tensor与Tensor对比,修改如下: +``` python +... +def assign_priors(gt_boxes, gt_labels, corner_form_priors, + iou_threshold): + ... + # labels[best_target_per_prior < iou_threshold] = 0 # the backgournd id + # 将原来的赋值修改为7-8行 + iou_threshold_tensor = torch.full_like(best_target_per_prior, iou_threshold) + labels[best_target_per_prior < iou_threshold_tensor] = 0 + boxes = gt_boxes[best_target_per_prior_index] + return boxes, labels +... +``` + +### 第二步:转换 +```shell +x2paddle --convert_torch_project --project_dir=Ultra-Light-Fast-Generic-Face-Detector-1MB --save_dir=paddle_project +``` +### 第三步:转换后代码后处理 +**需要修改的文件位于paddle_project文件夹中,其中文件命名与原始Ultra-Light-Fast-Generic-Face-Detector-1MB文件夹中文件命名一致。** +1. DataLoader的`num_workers`设置为0,在转换后的[train-version-RFB.sh处](https://github.com/SunAhong1993/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/paddle/train-version-RFB.sh#L27)设置强制设置`num_workers`,具体添加代码如下: +```shell +... + --num_workers \ + #4 \ + 0 \ +... +``` +2.修改自定义Dataset中的[\_\_getitem\_\_的返回值](https://github.com/SunAhong1993/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/paddle/vision/datasets/voc_dataset.py#L56),将Tensor修改为numpy,修改代码如下: +``` python +... +class VOCDataset(data.Dataset): + ... + def __getitem__(self, index): + image_id = self.ids[index] + boxes, labels, is_difficult = self._get_annotation(image_id) + if not self.keep_difficult: + boxes = boxes[is_difficult == 0] + labels = labels[is_difficult == 0] + image = self._read_image(image_id) + if self.transform: + image, boxes, labels = self.transform(image, boxes, labels) + if self.target_transform: + boxes, labels = self.target_transform(boxes, labels) + # return image, boxes, labels + # 将原来的return替换为如下17行 + return image.numpy(), boxes.numpy(), labels.numpy() + ... +``` + +### 运行训练代码 +``` shell +cd paddle_project/Ultra-Light-Fast-Generic-Face-Detector-1MB +sh train-version-RFB.sh +``` +***转换后的代码可在[这里](https://github.com/SunAhong1993/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/paddle)进行查看。*** diff --git a/docs/pytorch_project_convertor/supported_API.md b/docs/pytorch_project_convertor/supported_API.md new file mode 100644 index 0000000000000000000000000000000000000000..6bfb2f71dbfec6af48839eb264b8002ab39b3d73 --- /dev/null +++ b/docs/pytorch_project_convertor/supported_API.md @@ -0,0 +1,90 @@ +# PyTorch训练项目转换支持API列表 +> 目前PyTorch训练项目转换支持6个优化器相关API,40+的NN类API,5个Utils类API,2个Autograd类API,40+的基础操作API以及10+Torchvision API,我们在如下列表中给出了目前的全部API。 + +## 优化器相关API +| 序号 | API | 序号 | API | +| ---- | ------------------------------------------ | ---- | ------------------------------------------ | +| 1 | torch.optim | 2 | torch.optim.lr_scheduler.ReduceLROnPlateau | +| 3 | torch.optim.lr_scheduler.CosineAnnealingLR | 4 | torch.optim.lr_scheduler.MultiStepLR | +| 5 | torch.optim.Adam | 6 | torch.optim.SGD | + +## NN类API + +| 序号 | API | 序号 | API | +| ---- | ---------------------------------------------------- | ---- | --------------------------------- | +| 1 | torch.nn | 2 | torch.nn.Module | +| 3 | torch.nn.ModuleList | 4 | torch.nn.Sequential | +| 5 | torch.nn.utils | 6 | torch.nn.utils.clip_grad_value_ | +| 7 | torch.nn.Parameter | 8 | torch.nn.DataParallel | +| 9 | torch.nn.functional | 10 | torch.nn.BatchNorm1d | +| 11 | torch.nn.BatchNorm2d | 12 | torch.nn.BatchNorm3d | +| 13 | torch.nn.Conv1d | 14 | torch.nn.Conv2d | +| 15 | torch.nn.Conv3d | 16 | torch.nn.ConvTranspose2d | +| 17 | torch.nn.Dropout | 18 | torch.nn.Embedding | +| 19 | torch.nn.InstanceNorm2d | 20 | torch.nn.LeakyReLU | +| 21 | torch.nn.Linear | 22 | torch.nn.MaxPool1d | +| 23 | torch.nn.MaxPool2d | 24 | torch.nn.MaxPool3d | +| 25 | torch.nn.ReLU | 26 | torch.nn.Sigmoid | +| 27 | torch.nn.Softmax | 28 | torch.nn.Tanh | +| 29 | torch.nn.Upsample | 30 | torch.nn.CrossEntropyLoss | +| 31 | torch.nn.BCEWithLogitsLoss | 32 | torch.nn.BCELoss | +| 33 | torch.nn.functional.avg_pool1d | 34 | torch.nn.functional.avg_pool2d | +| 35 | torch.nn.functional.avg_pool3d | 36 | torch.nn.functional.dropout | +| 37 | torch.nn.functional.log_softmax | 38 | torch.nn.functional.pad | +| 39 | torch.sigmoid | 40 | torch.nn.functional.sigmoid | +| 41 | torch.nn.functional.softmax | 42 | torch.nn.init.xavier_uniform_ | +| 43 | torch.nn.functional.binary_cross_entropy_with_logits | 44 | torch.nn.functional.cross_entropy | +| 45 | torch.nn.functional.dropout | 46 | torch.nn.functional.relu | +| 47 | torch.nn.functional.smooth_l1_loss | | | + +## Utils类API + +| 序号 | API | 序号 | API | +| ---- | ------------------------------ | ---- | --------------------------- | +| 1 | torch.utils.data | 2 | torch.utils.data.DataLoader | +| 3 | torch.utils.data.random_split | 4 | torch.utils.data.Dataset | +| 5 | torch.utils.data.ConcatDataset | | | + +## Autograd类API + +| 序号 | API | 序号 | API | +| ---- | ----------------------- | ---- | ------------------- | +| 1 | torch.autograd.Variable | 2 | torch.autograd.grad | + +## 基础操作API + +| 序号 | API | 序号 | API | +| ---- | ----------------------- | ---- | ----------------------- | +| 1 | torch | 2 | torch.Tensor | +| 3 | torch.FloatTensor | 4 | torch.load | +| 5 | torch.save | 6 | torch.device | +| 7 | torch.cat | 8 | torch.cuda.is_available | +| 9 | torch.no_grad | 10 | torch.from_numpy | +| 11 | torch.cuda.device_count | 12 | torch.manual_seed | +| 13 | torch.unsqueeze | 14 | torch.squeeze | +| 15 | torch.sum | 16 | torch.mean | +| 17 | torch.full | 18 | torch.full_like | +| 19 | torch.ones | 20 | torch.ones_like | +| 21 | torch.zeros | 22 | torch.zeros_like | +| 23 | torch.sqrt | 24 | torch.arange | +| 25 | torch.matmul | 26 | torch.set_grad_enabled | +| 27 | torch.tensor | 28 | torch.clamp | +| 29 | torch.exp | 30 | torch.max | +| 31 | torch.min | 32 | torch.argmax | +| 33 | torch.argmin | 34 | torch.stack | +| 35 | torch.log | 36 | torch.randperm | +| 37 | torch.rand | 38 | torch.abs | +| 39 | torch.bitwise_or | 40 | torch.bitwise_xor | +| 41 | torch.bitwise_and | 42 | torch.bitwise_not | + +## Torchvision API + +| 序号 | API | 序号 | API | +| ---- | --------------------------------- | ---- | ------------------------------------------- | +| 1 | torchvision.transforms | 2 | torchvision.transforms.Compose | +| 3 | torchvision.transforms.ToPILImage | 4 | torchvision.transforms.Resize | +| 5 | torchvision.transforms.ToTensor | 6 | torchvision.transforms.RandomHorizontalFlip | +| 7 | torchvision.transforms.CenterCrop | 8 | torchvision.transforms.Normalize | +| 9 | torchvision.utils.save_image | 10 | torchvision.datasets.ImageFolder | + +***持续更新...*** diff --git a/docs/user_guides/FAQ.md b/docs/user_guides/FAQ.md deleted file mode 100644 index 19e3ace0d367f11ccf1141e13bee38db0946349f..0000000000000000000000000000000000000000 --- a/docs/user_guides/FAQ.md +++ /dev/null @@ -1,42 +0,0 @@ -## 常见问题 - -**Q1. TensorFlow模型转换过程中,提示『Unknown shape for input tensor[tensor name: "input"], Please define shape of input here』?** -A:该提示信息表示无法从TensorFlow的pb模型中获取到输入tensor(tensor名为"input:)的shape信息,所以需要用户手动在提示后输入详细的shape信息,如None,224,224,3 其中None表示Batch - - -**Q2. TensorFlow模型转换失败怎么解决?** -A: 如果并非是由缺少OP导致,那可能是由于TensorFlow模型转换时(NHWC->NCHW格式转换导致),在这种情况下,采用如下方式进行转换,同时固化输入大小的方式,继续尝试转换,见如下命令,转换过程中,根据提示,输入相应tensor的固化shape大小 -``` -x2paddle -f tensorflow -m tf.pb -s pd-model --without_data_format_optimization --define_input_shape -``` - -> 1. 目前Tensorflow的CV模型大部分均为`NHWC`的输入格式,而Paddle的默认输入格式为`NCHW`,因此X2Paddle在转换过程中,会对如`axis`, `shape`等参数进行转换,适应Paddle的NCHW格式。但在这种情况下,可能会由于TensorFlow模型太复杂,导致出错。 指定`--without_data_format_optimization`后,会停止对`axis`,`shape`等参数的优化(这可能会带来一定数量的transpose操作) - -**Q3. ONNX模型转换过程中,提示『Unknown shape for input tensor[tensor name: "input"] -> shape: ['batch', 'sequence'], Please define shape of input here』** -A:该提示信息表示从ONNX的模型中获取到输入tensor(tensor名为"input:)的shape是语义象征性的['batch', 'sequence'],而不是dim为int类型的shape,从而可能会因为部分node的shape无法推理,导致转换失败。所以用户可以尝试手动在提示后输入详细的shape信息,如:-1,3,224,224 其中-1表示Batch - -**Q4. Paddle模型转至ONNX模型过程中,提示『The parameter normalized of multiclass_nms OP of Paddle is False, which has diff with ONNX』** -A: 此提示为警告信息,模型仍然会正常进行转换。Paddle中`fluid.layers.multiclass_nms`算子中提供了`normalized`参数,用于表示输入box是否进行了归一化。而ONNX中的NMS算子只支持`normalized`参数为True的情况,当你转换的模型(一般是YOLOv3模型)中该参数为`False`的情况下,转换后的模型可能会与原模型存在diff。 - -**Q5. Paddle模型转至ONNX模型过程中,提示『Converting this model to ONNX need with static input shape, please fix input shape of this model』** -A: 此提示为错误信息,表示该模型的转换需要固定的输入大小: -> 1. 模型来源于PaddleX导出,可以在导出的命令中,指定--fixed_input_shape=[Height,Width],详情可见:[PaddleX模型导出文档](https://github.com/PaddlePaddle/PaddleX/blob/develop/docs/deploy/export_model.md)。 -> 2. 模型来源于PaddleDetection导出,可以在导出模型的时候,指定 TestReader.inputs_def.image_shape=[Channel,Height,Width], 详情可见:[PaddleDetection模型导出文档](https://github.com/PaddlePaddle/PaddleDetection/blob/master/docs/advanced_tutorials/deploy/EXPORT_MODEL.md#设置导出模型的输入大小)。 -> 3. 模型来源于自己构建,可在网络构建的`fluid.data(shape=[])`中,指定shape参数来固定模型的输入大小。 - - -**Q6. 进行动态图转换时,提示『Fail to generate inference model! Problem happend while export inference model from python code...』** -A: 此提示为无法将动态图代码转换为静态图模型,有两种可能: -> 使用动态图代码确认转换后的代码是否正确,可使用如下代码进行确认: -``` -import paddle -import numpy as np -np.random.seed(6) -# ipt为输入数据 -ipt = np.random.rand(1, 3, 224, 224).astype("float32") -paddle.disable_static() -# pd_model_dygraph为保存路径(其中的”/“用”.“替换) -from pd_model_dygraph.x2paddle_code import main -out =main(ipt) -``` -> 若运行代码无误,则说明代码中有op不支持动转静,我们将会再未来支持;若报错,则说明pytorch2paddle转换出错,请提issue,我们将及时回复。 \ No newline at end of file diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index 1a507890bc18ea85906812dec28ad5aea4fec373..0000000000000000000000000000000000000000 --- a/tools/README.md +++ /dev/null @@ -1,18 +0,0 @@ -### 一、PaddleLite部署 -使用X2Paddle转换后的模型均可以使用Paddle Fluid进行预测。但对于PaddleLite上的部署,则需要检查模型中的OP是否都在PaddleLite中支持。使用`check_for_lite.py`可以进行检查。 - -``` -python tools/check_for_lite.py paddle_model/inference_model/__model__ -``` -> 附:check_for_lite工具并不能完全判断模型是否被支持,PaddleLite详细支持的算子请参考[PaddleLite支持算子集](https://github.com/PaddlePaddle/Paddle-Lite/blob/develop/docs/introduction/support_operation_list.md) - -### 二、模型参数合并 -X2Paddle转换后产出的路径下包括两个目录, -1. `model_with_code`: 包含保存的参数文件和模型python代码文件,供用户debug -2. `inference_model`: 参数文件和序列化的模型结构文件,供用户预测部署 - -其中在`inference_model`中,X2Paddle将每个参数独立保存在不同的文件中(文件名和参数名一致),用户可使用`merge_params.py`将参数文件合并成一个文件使用 -``` -python tools/merge_params.py paddle_model/inference_model new_model_dir -``` -合并参数后的模型保存在`new_model_dir`中 diff --git a/tools/check_code_style.sh b/tools/check_code_style.sh deleted file mode 100644 index 235c291e6800fe2c668786c8d422a791aa08c0b2..0000000000000000000000000000000000000000 --- a/tools/check_code_style.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -function abort(){ - echo "Your change doesn't follow X2Paddle's code style." 1>&2 - echo "Please use pre-commit to check what is wrong." 1>&2 - exit 1 -} - -trap 'abort' 0 -set -e -cd $TRAVIS_BUILD_DIR -export PATH=/usr/bin:$PATH -pre-commit install - -if ! pre-commit run -a ; then - git diff - exit 1 -fi - -trap : 0 diff --git a/tools/check_for_lite.py b/tools/check_for_lite.py deleted file mode 100644 index 78fb55f5ee309106daf12d805a2efa95dcdaaad2..0000000000000000000000000000000000000000 --- a/tools/check_for_lite.py +++ /dev/null @@ -1,35 +0,0 @@ -from six.moves import urllib -import sys -from paddle.fluid.framework import Program - -ops_h = "https://raw.githubusercontent.com/PaddlePaddle/Paddle-Lite/develop/lite/api/_paddle_use_ops.h" -try: - fetch = urllib.urlretrieve(ops_h, "./_paddle_use_ops.h") -except: - fetch = urllib.request.urlretrieve(ops_h, "./_paddle_use_ops.h") - -ops = list() -with open("./_paddle_use_ops.h") as f: - for line in f: - if "USE_LITE_OP" in line: - op = line.strip().split('(')[1].split(')')[0] - ops.append(op) - -model_file = sys.argv[1] -with open(model_file, 'rb') as f: - program = Program.parse_from_string(f.read()) - -unsupported_ops = set() -for op in program.blocks[0].ops: - if op.type not in ops: - unsupported_ops.add(op.type) - -nums = len(unsupported_ops) -if len(unsupported_ops) > 0: - print("========= {} OPs are not supported in Paddle-Lite=========".format( - nums)) - for op in unsupported_ops: - print("========= {} ========".format(op)) -else: - print("\n========== Good News! ========") - print("Good! All ops in this model are supported in Paddle-Lite!\n") diff --git a/tools/compile.sh b/tools/compile.sh deleted file mode 100644 index 2d31521baa44450eaefb21791998177adf2f1342..0000000000000000000000000000000000000000 --- a/tools/compile.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -#function: -# script used to generate caffe_pb2.py from caffe.proto using protoc -# - -PROTOC=`which protoc` -if [[ -z $PROTOC ]];then - echo "not found protoc, you should first install it following this[https://github.com/google/protobuf/releases]" - exit 1 -fi - -WORK_ROOT=$1 -PY_NAME="$WORK_ROOT/caffe_pb2.py" -$PROTOC --proto_path=$WORK_ROOT --python_out=$WORK_ROOT $WORK_ROOT/caffe.proto -ret=$? - -if [ -e "$PY_NAME" ];then - echo "succeed to generate [$PY_NAME]" - exit 0 -else - echo "failed to generate [$PY_NAME]" -fi -exit $ret diff --git a/tools/merge_params.py b/tools/merge_params.py deleted file mode 100644 index f85e7ab97396dcdcd247436473c43588e422ede2..0000000000000000000000000000000000000000 --- a/tools/merge_params.py +++ /dev/null @@ -1,20 +0,0 @@ -import paddle -import paddle.fluid as fluid -import sys - -model_dir = sys.argv[1] -new_model_dir = sys.argv[2] -paddle.enable_static() -exe = paddle.static.Executor(paddle.CPUPlace()) -[inference_program, feed_target_names, - fetch_targets] = paddle.static.load_inference_model( - dirname=model_dir, executor=exe) - -print(feed_target_names) -paddle.static.save_inference_model( - dirname=new_model_dir, - feeded_var_names=feed_target_names, - target_vars=fetch_targets, - executor=exe, - main_program=inference_program, - params_filename="__params__") diff --git a/x2paddle/__init__.py b/x2paddle/__init__.py index b124fb6906078fed98498647459dda18ad91bf89..b954f830c0f5bbf9872fefbaf204a2ae64a347b9 100644 --- a/x2paddle/__init__.py +++ b/x2paddle/__init__.py @@ -1,4 +1,4 @@ -__version__ = "1.1.0" +__version__ = "1.2.0" from .core.program import PaddleGraph diff --git a/x2paddle/convert.py b/x2paddle/convert.py index 8d78cc43717111b0036d09cf0de7df3aa20bfd19..b993c54f3fcfb9dcf04d5167db4e566b4c0ac7a0 100644 --- a/x2paddle/convert.py +++ b/x2paddle/convert.py @@ -47,8 +47,10 @@ def arg_parser(): parser.add_argument( "--framework", "-f", - choices=['tensorflow', 'caffe', 'onnx'], - help="define which deeplearning framework(tensorflow/caffe/onnx)") + type=_text_type, + default=None, + help="define which deeplearning framework(tensorflow/caffe/onnx/paddle2onnx)" + ) parser.add_argument( "--caffe_proto", "-c", @@ -71,9 +73,26 @@ def arg_parser(): parser.add_argument( "--paddle_type", "-pt", - choices=['dygraph', 'static'], + type=_text_type, default="dygraph", help="define the paddle model type after converting(dygraph/static)") + parser.add_argument( + "--convert_torch_project", + "-tp", + action='store_true', + help="Convert the PyTorch Project.") + parser.add_argument( + "--project_dir", + "-pd", + type=_text_type, + default=None, + help="define project folder path for pytorch") + parser.add_argument( + "--pretrain_model", + "-pm", + type=_text_type, + default=None, + help="pretrain model file of pytorch model") return parser @@ -220,7 +239,11 @@ def main(): x2paddle.__version__)) return + if not args.convert_torch_project: + assert args.framework is not None, "--framework is not defined(support tensorflow/caffe/onnx)" assert args.save_dir is not None, "--save_dir is not defined" + assert args.paddle_type in ["dygraph", "static" + ], "--paddle_type must be 'dygraph' or 'static'" try: import platform @@ -241,28 +264,34 @@ def main(): "[ERROR] paddlepaddle not installed, use \"pip install paddlepaddle\"" ) - if args.framework == "tensorflow": - assert args.model is not None, "--model should be defined while translating tensorflow model" - define_input_shape = False - if args.define_input_shape: - define_input_shape = True - tf2paddle(args.model, args.save_dir, define_input_shape, - args.paddle_type) + if args.convert_torch_project: + assert args.project_dir is not None, "--project_dir should be defined while translating pytorch project" + from x2paddle.project_convertor.pytorch.convert import main as convert_torch + convert_torch(args) + else: + if args.framework == "tensorflow": + assert args.model is not None, "--model should be defined while translating tensorflow model" + define_input_shape = False + if args.define_input_shape: + define_input_shape = True + tf2paddle(args.model, args.save_dir, define_input_shape, + args.paddle_type) - elif args.framework == "caffe": - assert args.prototxt is not None and args.weight is not None, "--prototxt and --weight should be defined while translating caffe model" - caffe2paddle(args.prototxt, args.weight, args.save_dir, - args.caffe_proto, args.paddle_type) - elif args.framework == "onnx": - assert args.model is not None, "--model should be defined while translating onnx model" - onnx2paddle(args.model, args.save_dir, args.paddle_type) - elif args.framework == "paddle2onnx": - print( - "Paddle to ONNX tool has been migrated to the new github: https://github.com/PaddlePaddle/paddle2onnx" - ) + elif args.framework == "caffe": + assert args.prototxt is not None and args.weight is not None, "--prototxt and --weight should be defined while translating caffe model" + caffe2paddle(args.prototxt, args.weight, args.save_dir, + args.caffe_proto, args.paddle_type) + elif args.framework == "onnx": + assert args.model is not None, "--model should be defined while translating onnx model" + onnx2paddle(args.model, args.save_dir, args.paddle_type) + elif args.framework == "paddle2onnx": + print( + "Paddle to ONNX tool has been migrated to the new github: https://github.com/PaddlePaddle/paddle2onnx" + ) - else: - raise Exception("--framework only support tensorflow/caffe/onnx now") + else: + raise Exception( + "--framework only support tensorflow/caffe/onnx now") if __name__ == "__main__": diff --git a/x2paddle/decoder/caffe_shape_inference.py b/x2paddle/decoder/caffe_shape_inference.py index 334cf5c8a5b69702689dc3dd44db920295576c88..213a8ffdec74689a8e7ab3bf05040b483040f300 100644 --- a/x2paddle/decoder/caffe_shape_inference.py +++ b/x2paddle/decoder/caffe_shape_inference.py @@ -10,7 +10,7 @@ # 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. +# limitations under the License. import math import numbers @@ -313,7 +313,8 @@ def shape_reshape(layer, input_shape): assert input_count % explicit_count == 0, "[Reshape]botom count[%d] "\ "must be divisible by product of the specified dimensions[%d] "\ % (input_count, explicit_count) - output_shape[start_axis + inferred_axis] = int(input_count / explicit_count) + output_shape[start_axis + inferred_axis] = int(input_count / + explicit_count) output_count = count(output_shape) assert output_count == input_count, "[Reshape]output count[%d] must match input count[%d]" % ( @@ -381,7 +382,8 @@ def shape_reduction(layer, input_shape): assert axis <= len(input_shape[0]), 'invalid axis[%d] error' % (axis) return [input_shape[0:axis]] -def shape_axpy(layer, input_shape): + +def shape_axpy(layer, input_shapes): assert len(input_shapes) == 3, "not valid input shape for axpy layer" assert len(input_shapes[0]) == len(input_shapes[1]), 'should have same dims' output_shape = input_shapes[1] @@ -390,12 +392,15 @@ def shape_axpy(layer, input_shape): % (str(output_shape), str(input_shapes[2])) return [output_shape] + def shape_detectionoutput(layer, input_shape): return [[-1, 6]] + def shape_normalize(layer, input_shape): return input_shape + def shape_permute(layer, input_shape): order = layer.permute_param.order inshape = input_shape[0] @@ -406,6 +411,7 @@ def shape_permute(layer, input_shape): output_shape.append(inshape[ii]) return [output_shape] + def shape_priorbox(layer, input_shape): max_size = layer.prior_box_param.max_size aspect_ratio = layer.prior_box_param.aspect_ratio @@ -419,10 +425,12 @@ def shape_priorbox(layer, input_shape): output_shape = [1, 2, 4 * N_bbx] return [output_shape] + def shape_relu6(layer, input_shape): return input_shape -def shape_roipooling(layer, input_shape): + +def shape_roipooling(layer, input_shapes): pooled_w = layer.roi_pooling_param.pooled_w pooled_h = layer.roi_pooling_param.pooled_h base_fea_shape = input_shapes[0] @@ -433,10 +441,12 @@ def shape_roipooling(layer, input_shape): output_shape[3] = pooled_w return [output_shape] + def shape_shufflechannel(layer, input_shape): return input_shape -def shape_upsample(layer, input_shape): + +def shape_upsample(layer, input_shapes): scale = layer.upsample_param.scale assert len(input_shapes) == 1, "not valid input shape for upsample layer" assert type(scale) is int @@ -447,7 +457,8 @@ def shape_upsample(layer, input_shape): output_shape = [input_shape[0], input_shape[1], new_h, new_w] return [output_shape] -def shape_select(layer, input_shape): + +def shape_select(layer, input_shapes): slice_point = layer.select_param.slice_point axis = layer.select_param.axis input_shape = input_shapes[0] @@ -461,4 +472,3 @@ def shape_select(layer, input_shape): output_shape = input_shape output_shape[axis] = end - start return [output_shape] - diff --git a/x2paddle/models.py b/x2paddle/models.py new file mode 100644 index 0000000000000000000000000000000000000000..72e920e6dc81cadde7f5090009796ebc7d788ad0 --- /dev/null +++ b/x2paddle/models.py @@ -0,0 +1,21 @@ +from x2paddle.project_convertor.pytorch import models +resnet_pth_urls = models.resnet.model_urls +resnet18_pth = models.resnet18 +resnet34_pth = models.resnet34 +resnet50_pth = models.resnet50 +resnet101_pth = models.resnet101 +resnet152_pth = models.resnet152 +resnext50_32x4d_pth = models.resnext50_32x4d +resnext101_32x8d_pth = models.resnext101_32x8d +wide_resnet50_2_pth = models.wide_resnet50_2 +wide_resnet101_2_pth = models.wide_resnet101_2 + +vgg_pth_urls = models.vgg.model_urls +vgg11_pth = models.vgg11 +vgg11_bn_pth = models.vgg11_bn +vgg13_pth = models.vgg13 +vgg13_bn_pth = models.vgg13_bn +vgg16_pth = models.vgg16 +vgg16_bn_pth = models.vgg16_bn +vgg19_pth = models.vgg19 +vgg19_bn_pth = models.vgg19_bn diff --git a/x2paddle/project_convertor/__init__.py b/x2paddle/project_convertor/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/x2paddle/project_convertor/pytorch/__init__.py b/x2paddle/project_convertor/pytorch/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/x2paddle/project_convertor/pytorch/api_mapper/__init__.py b/x2paddle/project_convertor/pytorch/api_mapper/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..723559db086c3b1bdefd5eec04b6c9c0be9d1b04 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/api_mapper/__init__.py @@ -0,0 +1,18 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from .learning_rate_scheduler import * +from .nn import * +from .ops import * +from .torchvision import * diff --git a/x2paddle/project_convertor/pytorch/api_mapper/learning_rate_scheduler.py b/x2paddle/project_convertor/pytorch/api_mapper/learning_rate_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..9cbd2e9574870bd9c0e32bf62894ce62241e1533 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/api_mapper/learning_rate_scheduler.py @@ -0,0 +1,55 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from .utils import * + + +class LRScheculerMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + self.useful_attrs = dict() + + def process_attrs(self): + self.kwargs["learning_rate"] = 0.01 + + def delete_attrs(self): + self.useful_attrs["optimizer"] = self.kwargs.pop("optimizer") + + def run(self): + if self.pytorch_api_name == "torch.optim.lr_scheduler.ReduceLROnPlateau" and \ + self.rename_func_name("x2paddle.torch2paddle.ReduceOnPlateau"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.optim.lr_scheduler.CosineAnnealingLR" and \ + self.rename_func_name("x2paddle.torch2paddle.CosineAnnealingDecay"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.optim.lr_scheduler.MultiStepLR" and \ + self.rename_func_name("x2paddle.torch2paddle.MultiStepDecay"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs() + self.check_attrs() + self.process_attrs() + self.delete_attrs() + insert_code = "{}._learning_rate = {}".format( + self.useful_attrs["optimizer"], self.target_name) + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [insert_code] diff --git a/x2paddle/project_convertor/pytorch/api_mapper/nn.py b/x2paddle/project_convertor/pytorch/api_mapper/nn.py new file mode 100644 index 0000000000000000000000000000000000000000..d80f5d972ccb9a8f8a222bc2241aa7f9c1f6d0ac --- /dev/null +++ b/x2paddle/project_convertor/pytorch/api_mapper/nn.py @@ -0,0 +1,659 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from .utils import * +from x2paddle.utils import * + + +class AvgPoolModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + if "count_include_pad" in self.kwargs: + if isinstance(self.kwargs["count_include_pad"], bool): + self.kwargs["exclusive"] = not self.kwargs["count_include_pad"] + else: + self.kwargs["exclusive"] = "not {}".format(self.kwargs[ + "count_include_pad"]) + if len(self.args) > 4: + if isinstance(self.args[4], bool): + self.args[4] = not self.args[4] + else: + self.args[4] = "not {}".format(self.args[4]) + + def delete_attrs(self): + delete_key(self.kwargs, "count_include_pad") + + def run(self): + if self.pytorch_api_name == "torch.nn.AvgPool1d" and self.rename_func_name( + "x2paddle.torch2paddle.AvgPool1D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.AvgPool2d" and self.rename_func_name( + "x2paddle.torch2paddle.AvgPool2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.AvgPool3d" and self.rename_func_name( + "x2paddle.torch2paddle.AvgPool3d"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class BatchNormModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "num_channels", "num_features") + rename_key(self.kwargs, "eps", "epsilon") + rename_key(self.kwargs, "track_running_stats", "use_global_stats") + if "momentum" in self.kwargs: + if isinstance(self.kwargs["momentum"], float): + self.kwargs["momentum"] = 1 - self.kwargs["momentum"] + else: + self.kwargs["momentum"] = "1 - {}".format(self.kwargs[ + "momentum"]) + if "affine" in self.kwargs and not self.kwargs["affine"]: + for key in ["weight_attr", "bias_attr"]: + self.kwargs[key] = "paddle.ParamAttr(learning_rate=0.0)" + + def delete_attrs(self): + delete_key(self.kwargs, "affine") + delete_key(self.kwargs, "process_group") + + def run(self): + if self.pytorch_api_name == "torch.nn.InstanceNorm2d": + delete_key(self.kwargs, "track_running_stats") + if self.pytorch_api_name == "torch.nn.BatchNorm1d" and self.rename_func_name( + "x2paddle.torch2paddle.BatchNorm1D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.BatchNorm2d" and self.rename_func_name( + "x2paddle.torch2paddle.BatchNorm2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.BatchNorm3d" and self.rename_func_name( + "x2paddle.torch2paddle.BatchNorm3D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.SyncBatchNorm" and self.rename_func_name( + "x2paddle.torch2paddle.SyncBatchNorm"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.InstanceNorm2d" and self.rename_func_name( + "x2paddle.torch2paddle.InstanceNorm2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(1) + if "affine" in self.kwargs and not (isinstance(self.kwargs["affine"], bool) \ + or (isinstance(self.kwargs["affine"], str) and self.kwargs["affine"].strip() in ["True", "False"])): + print(self.kwargs["affine"], self.func_name) + if self.pytorch_api_name == "torch.nn.BatchNorm1D": + self.func_name = "x2paddle.torch2paddle.BatchNorm1D" + elif self.pytorch_api_name == "torch.nn.BatchNorm2D": + self.func_name = "x2paddle.torch2paddle.BatchNorm2D" + elif self.pytorch_api_name == "torch.nn.BatchNorm3D": + self.func_name = "x2paddle.torch2paddle.BatchNorm3D" + elif self.pytorch_api_name == "torch.nn.SyncBatchNorm": + self.func_name = "x2paddle.torch2paddle.SyncBatchNorm" + elif self.pytorch_api_name == "torch.nn.InstanceNorm2D": + self.func_name = "x2paddle.torch2paddle.InstanceNorm2D" + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + return self.convert_to_paddle() + + +class ConvModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "bias", "bias_attr") + + def run(self): + if self.pytorch_api_name == "torch.nn.Conv1d" and self.rename_func_name( + "x2paddle.torch2paddle.Conv1D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.Conv2d" and self.rename_func_name( + "x2paddle.torch2paddle.Conv2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.Conv3d" and self.rename_func_name( + "x2paddle.torch2paddle.Conv3D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(7) + return self.convert_to_paddle() + + +class DropoutModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "inplace") + + +class EmbeddingModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "max_norm") + delete_key(self.kwargs, "norm_type") + delete_key(self.kwargs, "scale_grad_by_freq") + + def check_attrs(self): + assert "max_norm" not in self.kwargs or self.kwargs[ + "max_norm"] is None, "The max_norm is not supported yet in Embedding!" + assert "scale_grad_by_freq" not in self.kwargs or not self.kwargs[ + "scale_grad_by_freq"], "The scale_grad_by_freq must be False in Embedding!" + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.Embedding"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(3) + return self.convert_to_paddle() + + +class GroupNormModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "eps", "epsilon") + if "affine" in self.kwargs and not self.kwargs["affine"]: + for key in ["weight_attr", "bias_attr"]: + self.kwargs[key] = False + + def delete_attrs(self): + delete_key(self.kwargs, "affine") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.GroupNorm"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class LayerNormModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "eps", "epsilon") + if "elementwise_affine" in self.kwargs and not self.kwargs[ + "elementwise_affine"]: + for key in ["weight_attr", "bias_attr"]: + self.kwargs[key] = False + + def delete_attrs(self): + delete_key(self.kwargs, "elementwise_affine") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.LayerNorm"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(1) + return self.convert_to_paddle() + + +class LinearModuleMapper(ConvModuleMapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.Linear"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class LossModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "size_average") + delete_key(self.kwargs, "reduce") + + def run(self): + if self.pytorch_api_name == "torch.nn.CrossEntropyLoss" and \ + self.rename_func_name("x2paddle.torch2paddle.CrossEntropyLoss"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.BCEWithLogitsLoss" and \ + self.rename_func_name("x2paddle.torch2paddle.BCEWithLogitsLoss"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.L1Loss" and \ + self.rename_func_name("x2paddle.torch2paddle.L1Loss"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class MaxPoolModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "return_indices", "return_mask") + + def check_attrs(self): + assert "dilation" not in self.kwargs, "The dilation is not supported yet in MaxPool!" + + def run(self): + if self.pytorch_api_name == "torch.nn.MaxPool1d" and self.rename_func_name( + "x2paddle.torch2paddle.MaxPool1D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.MaxPool2d" and self.rename_func_name( + "x2paddle.torch2paddle.MaxPool12D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.MaxPool3d" and self.rename_func_name( + "x2paddle.torch2paddle.MaxPool13D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(3) + return self.convert_to_paddle() + + +class PadModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + if self.pytorch_api_name == "torch.nn.ReflectionPad2d": + self.kwargs["mode"] = "reflect" + elif self.pytorch_api_name in [ + "torch.nn.ConstantPad2d", "torch.nn.ZeroPad2d" + ]: + self.kwargs["mode"] = "constant" + elif self.pytorch_api_name == "torch.nn.ReplicationPad2d": + self.kwargs["mode"] = "replicate" + + def run(self): + if self.pytorch_api_name == "torch.nn.ReflectionPad2d" and self.rename_func_name( + "x2paddle.torch2paddle.ReflectionPad2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.ConstantPad2d" and self.rename_func_name( + "x2paddle.torch2paddle.ConstantPad2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.ReplicationPad2d" and self.rename_func_name( + "x2paddle.torch2paddle.ReplicationPad2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.ZeroPad2d" and self.rename_func_name( + "x2paddle.torch2paddle.ZeroPad2D"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(1) + return self.convert_to_paddle() + + +class ReLUModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "inplace") + if len(self.args) > 0: + self.args.clear() + + +class SoftmaxModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + + rename_key(self.kwargs, "dim", "axis") + + +class AvgPoolFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + if "count_include_pad" in self.kwargs: + if isinstance(self.kwargs["count_include_pad"], bool): + self.kwargs["exclusive"] = not self.kwargs["count_include_pad"] + else: + self.kwargs["exclusive"] = "not {}".format(self.kwargs[ + "count_include_pad"]) + + def delete_attrs(self): + delete_key(self.kwargs, "count_include_pad") + + def run(self): + if self.pytorch_api_name == "torch.nn.functional.avg_pool1d" and self.rename_func_name( + "x2paddle.torch2paddle.avg_pool1d"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.functional.avg_pool2d" and self.rename_func_name( + "x2paddle.torch2paddle.avg_pool2d"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.functional.avg_pool3d" and self.rename_func_name( + "x2paddle.torch2paddle.avg_pool3d"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(4) + return self.convert_to_paddle() + + +class CrossEntropyFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "target", "label") + + +class DropoutFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + def delete_attrs(self): + delete_key(self.kwargs, "inplace") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.dropout"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class InterpolateFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + def check_attrs(self): + assert "recompute_scale_factor" not in self.kwargs or self.kwargs[ + "recompute_scale_factor"] is None or len( + self.args + ) > 5, "The recompute_scale_factor is not supported yet in interpolate!" + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.interpolate"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class LeaklyReluFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + def delete_attrs(self): + delete_key(self.kwargs, "inplace") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.leaky_relu"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class LogSoftmaxFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "dim", "axis") + rename_key(self.kwargs, "input", "x") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.log_softmax"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class PadFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + +class ReluFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + def delete_attrs(self): + delete_key(self.kwargs, "inplace") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.relu"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(1) + return self.convert_to_paddle() + + +class SigmoidFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + +class LossFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "beta", "delta") + rename_key(self.kwargs, "target", "label") + + def delete_attrs(self): + delete_key(self.kwargs, "size_average") + delete_key(self.kwargs, "reduce") + + def run(self): + if self.pytorch_api_name == "torch.nn.functional.smooth_l1_loss" and self.rename_func_name( + "x2paddle.torch2paddle.smooth_l1_loss"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + elif self.pytorch_api_name == "torch.nn.functional.mse_loss" and self.rename_func_name( + "x2paddle.torch2paddle.mse_loss"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class SoftmaxFuncMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + rename_key(self.kwargs, "dim", "axis") + + def delete_attrs(self): + delete_key(self.kwargs, "_stacklevel") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.softmax"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() diff --git a/x2paddle/project_convertor/pytorch/api_mapper/ops.py b/x2paddle/project_convertor/pytorch/api_mapper/ops.py new file mode 100644 index 0000000000000000000000000000000000000000..6d52812af6c386c5a4e2484dc603ae8e6948f22a --- /dev/null +++ b/x2paddle/project_convertor/pytorch/api_mapper/ops.py @@ -0,0 +1,540 @@ +from .utils import * +from x2paddle.utils import * + + +class SaveMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "pickle_module") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.save"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class LoadMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "pickle_module") + delete_key(self.kwargs, "map_location") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.load"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class HubLoadMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + if len(self.args) == 1: + self.kwargs.clear() + elif len(self.args) == 0: + self.args.append(list(self.kwargs.values())[0]) + self.kwargs.clear() + else: + self.args = self.args[:1] + + def run(self): + if self.pytorch_api_name == "torch.hub.load_state_dict_from_url": + if self.rename_func_name( + "x2paddle.torch2paddle.load_state_dict_from_url"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.utils.model_zoo.load_url": + if self.rename_func_name("x2paddle.torch2paddle.load_url"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + return self.convert_to_paddle() + + +class SetDeviceMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + self.useful_attrs = dict() + + def process_attrs(self): + self.useful_attrs["device"] = self.args[0] + self.args[0] = self.target_name + + def run(self): + self.process_attrs() + insert_codes = list() + insert_codes.append("{} = {}".format(self.target_name, + self.useful_attrs["device"])) + insert_codes.append("{} = {}.replace('cuda', 'gpu')".format( + self.target_name, self.target_name)) + return insert_codes, generate_api_code(self.func_name, self.args, + self.kwargs), [] + + +class DataParallelModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "device_ids") + delete_key(self.kwargs, "output_device") + delete_key(self.kwargs, "dim") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.DataParallel"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class UnSqueezeMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + rename_key(self.kwargs, "dim", "axis") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.unsqueeze"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(2) + return self.convert_to_paddle() + + +class OneMathMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + + def delete_attrs(self): + delete_key(self.kwargs, "out") + + def run(self): + if self.pytorch_api_name == "torch.sqrt": + if self.rename_func_name("x2paddle.torch2paddle.sqrt"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.abs": + if self.rename_func_name("x2paddle.torch2paddle.abs"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.log": + if self.rename_func_name("x2paddle.torch2paddle.log"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.exp": + if self.rename_func_name("x2paddle.torch2paddle.exp"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.clip": + if self.rename_func_name("x2paddle.torch2paddle.clip"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + return self.convert_to_paddle() + + +class ArangeMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + self.useful_attrs = dict() + + def process_attrs(self): + self.useful_attrs["requires_grad"] = self.kwargs[ + "requires_grad"] if "requires_grad" in self.kwargs else False + + def delete_attrs(self): + delete_key(self.kwargs, "out") + delete_key(self.kwargs, "layout") + delete_key(self.kwargs, "device") + delete_key(self.kwargs, "requires_grad") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.arange"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(3) + out1, out2, out3 = self.convert_to_paddle() + if isinstance(self.useful_attrs["requires_grad"], + str) or not self.useful_attrs["requires_grad"]: + out2 = "{}.requires_grad_({})".format( + out2, self.useful_attrs["requires_grad"]) + return out1, out2, out3 + + +class TwoMathMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + rename_key(self.kwargs, "other", "y") + + def delete_attrs(self): + delete_key(self.kwargs, "out") + + def run(self): + if self.pytorch_api_name == "torch.matmul": + if self.rename_func_name("x2paddle.torch2paddle.matmul"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.add": + if self.rename_func_name("x2paddle.torch2paddle.add"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.mul": + if self.rename_func_name("x2paddle.torch2paddle.mul"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + return self.convert_to_paddle() + + +class CreateParamModuleMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "data", "value") + + def delete_attrs(self): + delete_key(self.kwargs, "requires_grad") + + def check_attrs(self): + assert "requires_grad" not in self.kwargs or self.kwargs[ + "requires_grad"], "The requires_grad must be True in Parameter!" + + def run(self): + if self.rename_func_name(self.func_name): + if "*" in self.args[0] and "**" not in self.args[0]: + param_name = self.args[0][1:] + elif "**" in self.args[0]: + param_name = "{}['data']".format(self.args[0][2:]) + elif "*" not in self.args[0] and "**" in self.args[1]: + param_name = self.args[0] + else: + self.check_attrs() + self.process_attrs() + self.delete_attrs() + if len(self.args) == 1: + param_name = self.args[0] + else: + param_name = self.kwargs["value"] + code = "paddle.create_parameter(shape={}.shape, dtype=str({}.numpy().dtype), default_initializer = paddle.nn.initializer.Assign({}))".format( + param_name, param_name, param_name) + return [], code, ["{}.stop_gradient = False".format(self.target_name)] + + +class NoGradMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + self.args.clear() + self.kwargs.clear() + + +class LogicalMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + rename_key(self.kwargs, "other", "y") + + def run(self): + if self.pytorch_api_name == "torch.bitwise_or": + if self.rename_func_name("x2paddle.torch2paddle.logical_or"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.bitwise_and": + if self.rename_func_name("x2paddle.torch2paddle.logical_and"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.bitwise_xor": + if self.rename_func_name("x2paddle.torch2paddle.logical_xor"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.bitwise_not": + if self.rename_func_name("x2paddle.torch2paddle.logical_not"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + return self.convert_to_paddle() + + +class StackMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "tensors", "x") + rename_key(self.kwargs, "dim", "axis") + + def delete_attrs(self): + delete_key(self.kwargs, "out") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.stack"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class RandpermMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def delete_attrs(self): + delete_key(self.kwargs, "out") + delete_key(self.kwargs, "layout") + delete_key(self.kwargs, "device") + delete_key(self.kwargs, "requires_grad") + delete_key(self.kwargs, "pin_memory") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.randperm"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() + + +class TensorBuilderMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + self.useful_attrs = dict() + + def process_attrs(self): + if self.pytorch_api_name in ["torch.ones", "torch.zeros"]: + if len(self.args) > 1: + new_args = list() + for arg in self.args: + if isinstance(arg, int): + new_args.append(str(arg)) + else: + new_args.append(arg) + shape = ", ".join(new_args) + shape = "[{}]".format(shape) + self.args.clear() + self.args.append(shape) + rename_key(self.kwargs, "size", "shape") + self.useful_attrs["requires_grad"] = self.kwargs[ + "requires_grad"] if "requires_grad" in self.kwargs else False + + def delete_attrs(self): + delete_key(self.kwargs, "out") + delete_key(self.kwargs, "layout") + delete_key(self.kwargs, "device") + delete_key(self.kwargs, "requires_grad") + + def run(self): + if self.pytorch_api_name == "torch.full": + if self.rename_func_name("x2paddle.torch2paddle.full"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.zeros": + if self.rename_func_name("x2paddle.torch2paddle.zeros"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.ones": + if self.rename_func_name("x2paddle.torch2paddle.ones"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + out1, out2, out3 = self.convert_to_paddle() + if isinstance(self.useful_attrs["requires_grad"], + str) or not self.useful_attrs["requires_grad"]: + out2 = "{}.requires_grad_({})".format( + out2, self.useful_attrs["requires_grad"]) + return out1, out2, out3 + + +class TensorLikeMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + self.useful_attrs = dict() + + def process_attrs(self): + rename_key(self.kwargs, "input", "x") + self.useful_attrs["requires_grad"] = self.kwargs[ + "requires_grad"] if "requires_grad" in self.kwargs else False + + def delete_attrs(self): + delete_key(self.kwargs, "out") + delete_key(self.kwargs, "layout") + delete_key(self.kwargs, "device") + delete_key(self.kwargs, "requires_grad") + + def run(self): + if self.pytorch_api_name == "torch.full_like": + if self.rename_func_name("x2paddle.torch2paddle.full_like"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.zeros_like": + if self.rename_func_name("x2paddle.torch2paddle.zeros_like"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + if self.pytorch_api_name == "torch.ones_like": + if self.rename_func_name("x2paddle.torch2paddle.ones_like"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + out1, out2, out3 = self.convert_to_paddle() + if isinstance(self.useful_attrs["requires_grad"], + str) or not self.useful_attrs["requires_grad"]: + out2 = "{}.requires_grad_({})".format( + out2, self.useful_attrs["requires_grad"]) + return out1, out2, out3 + + +class SplitMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def process_attrs(self): + rename_key(self.kwargs, "tensor", "x") + rename_key(self.kwargs, "split_size_or_sections", "num_or_sections") + rename_key(self.kwargs, "dim", "axis") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.split"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(3) + return self.convert_to_paddle() + + +class LinspaceMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + self.useful_attrs = dict() + + def process_attrs(self): + rename_key(self.kwargs, "end", "stop") + rename_key(self.kwargs, "steps", "num") + self.useful_attrs["requires_grad"] = self.kwargs[ + "requires_grad"] if "requires_grad" in self.kwargs else False + + def delete_attrs(self): + delete_key(self.kwargs, "out") + delete_key(self.kwargs, "layout") + delete_key(self.kwargs, "device") + delete_key(self.kwargs, "requires_grad") + + def run(self): + if self.rename_func_name("x2paddle.torch2paddle.linspace"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(1) + out1, out2, out3 = self.convert_to_paddle() + if isinstance(self.useful_attrs["requires_grad"], + str) or not self.useful_attrs["requires_grad"]: + out2 = "{}.requires_grad_({})".format( + out2, self.useful_attrs["requires_grad"]) + return out1, out2, out3 diff --git a/x2paddle/project_convertor/pytorch/api_mapper/torchvision.py b/x2paddle/project_convertor/pytorch/api_mapper/torchvision.py new file mode 100644 index 0000000000000000000000000000000000000000..b6d7d6ae65e416d1f00687bfd8754b14c585bee5 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/api_mapper/torchvision.py @@ -0,0 +1,37 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from .utils import * + + +class ImageFolderMapper(Mapper): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + super().__init__(func_name, pytorch_api_name, args, kwargs, target_name) + + def check_attrs(self): + assert "target_transform" not in self.kwargs, "The target_transform is not supported yet in ImageFolder!" + + def run(self): + if self.pytorch_api_name == "torchvision.datasets.ImageFolder" and \ + self.rename_func_name("x2paddle.torch2paddle.ImageFolder"): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + self.convert_args2kwargs(1) + return self.convert_to_paddle() diff --git a/x2paddle/project_convertor/pytorch/api_mapper/utils.py b/x2paddle/project_convertor/pytorch/api_mapper/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d8f190583d14a2ba69d60f46176ab3bebcda949f --- /dev/null +++ b/x2paddle/project_convertor/pytorch/api_mapper/utils.py @@ -0,0 +1,156 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import inspect + + +def api_args2kwargs(pytorch_api_name, args, first_same_attr_count): + """ 将每个OP的args转为kwargs。 + + Args: + pytorch_api_name (str): OP的类型名字。 + args (list): 参数列表。 + first_same_attr_count (int): PyTorch与Paddle前first_same_attr_count个完全相同的参数。 + """ + + def get_default_args(obj): + if inspect.isbuiltin(obj): + demp_str = obj.__doc__.split("->")[0].strip()[:-1] + demp_str = demp_str.split("(")[-1] + demp_str_seg = demp_str.split(",") + default_args = list() + for seg in demp_str_seg: + seg = seg.strip().replace("*", "") + if seg == "": + continue + if "=" in seg: + seg = seg.split("=")[0] + default_args.append(seg) + return default_args + else: + signature = inspect.signature(obj) + return [k for k, v in signature.parameters.items()] + + if pytorch_api_name.startswith("torchvision"): + import torchvision + obj = torchvision + else: + import torch + obj = torch + for i, part in enumerate(pytorch_api_name.split(".")): + if i == 0: + continue + obj = getattr(obj, part) + default_args = get_default_args(obj) + new_kwargs = dict() + for i, default_k in enumerate(default_args): + if i >= first_same_attr_count and i < len(args): + new_kwargs[default_k] = args[i] + return new_kwargs + + +def rename_key(kwargs, old_key, new_key): + if old_key in kwargs: + v = kwargs.pop(old_key) + kwargs[new_key] = v + + +def delete_key(kwargs, old_key): + if old_key in kwargs: + kwargs.pop(old_key) + + +def generate_api_code(func_name, args, kwargs): + for i, arg in enumerate(args): + if not isinstance(args[i], str): + args[i] = str(args[i]) + args_str = ", ".join(args) + kwargs_str_list = list() + for k, v in kwargs.items(): + kwargs_str_list.append("{}={}".format(k, v)) + kwargs_str = ", ".join(kwargs_str_list) + if len(args_str) > 0: + code = "{}({}, {})".format(func_name, args_str, kwargs_str) + else: + code = "{}({})".format(func_name, kwargs_str) + return code + + +class Mapper(object): + def __init__(self, + func_name, + pytorch_api_name, + args, + kwargs, + target_name=None): + self.func_name = func_name + self.pytorch_api_name = pytorch_api_name + self.args = args + self.kwargs = kwargs + self.target_name = target_name + + def process_attrs(self): + """ 更新参数。 + """ + pass + + def delete_attrs(self): + """ 删除参数。 + """ + pass + + def check_attrs(self): + """ 确认参数的值。 + """ + pass + + def rename_func_name(self, torch2paddle_func_name=None): + """ 判断是否为可变参数或者关键字参数, + 若为可变参数或者关键字参数,则替换参数名。 + """ + if torch2paddle_func_name is not None and \ + (len(self.args) > 0 and isinstance(self.args[0], str) and self.args[0].startswith("*")) or \ + (len(self.args) > 1 and isinstance(self.args[-1], str) and self.args[-1].startswith("**")): + self.func_name = torch2paddle_func_name + return True + else: + return False + + def convert_to_paddle(self): + """ 1. 通过执行check、process、delete转换为paddle的参数; + 2. 生成paddle相关代码。 + """ + self.check_attrs() + self.process_attrs() + self.delete_attrs() + return [], generate_api_code(self.func_name, self.args, self.kwargs), [] + + def convert_args2kwargs(self, first_same_attr_count=0): + """ 将args转换为kwargs。 + """ + if len(self.args) > first_same_attr_count: + new_kwargs = api_args2kwargs(self.pytorch_api_name, self.args, + first_same_attr_count) + self.kwargs.update(new_kwargs) + self.args = self.args[:first_same_attr_count] + + def run(self, torch2paddle_func_name=None): + """ 如果存在可变参数或者关键字参数,直接替换函数名为x2paddle的API; + 反之,调用convert_to_paddle。 + """ + if self.rename_func_name(torch2paddle_func_name): + return [], generate_api_code(self.func_name, self.args, + self.kwargs), [] + else: + return self.convert_to_paddle() diff --git a/x2paddle/project_convertor/pytorch/ast_update.py b/x2paddle/project_convertor/pytorch/ast_update.py new file mode 100644 index 0000000000000000000000000000000000000000..bff91121d3407efa4ca4bdce202a82501ace0ffc --- /dev/null +++ b/x2paddle/project_convertor/pytorch/ast_update.py @@ -0,0 +1,623 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import ast +import astor +import sys +from x2paddle.project_convertor.pytorch.mapper import * +import copy +import os.path as osp +from .utils import get_dep_file_path + + +class DepInfo: + """ + 依赖包信息。 + PT_FROM代表pytorch from信息的字符串,例如:torch; + PD_FROM代表paddle from信息的字符串,例如:paddle; + PT_IMPORT代表pytorch import信息系的字符串,例如:nn.functional; + PD_IMPORT代表paddle import信息系的字符串,例如:nn.functional; + AS代表as信息的字符串,例如:F; + PT_DEPENDENCY代表由PT_FROM、PT_IMPORT、AS三者组成的字符串,例如:from torch import nn.functional as F。 + PD_DEPENDENCY代表由PD_FROM、PD_IMPORT、AS三者组成的字符串,例如:from paddle import nn.functional as F。 + """ + PT_FROM = None + PD_FROM = None + PT_IMPORT = None + PD_IMPORT = None + AS = None + PT_DEPENDENCY = None + PD_DEPENDENCY = None + + +class AstUpdater(ast.NodeVisitor): + """ 更新ast树,将ast树中PyTorch相关的节点转为Paddle相关的节点。 + Args: + py_file_path (str): python文件的绝对值路径。 + file_dependencies (dict): 当前已经统计的依赖信息,key为python文件的绝对值路径, + value为key文件所对应的依赖信息组成的list。 + """ + + def __init__(self, py_file_path, file_dependencies): + self.py_file_path = py_file_path + self.root = ast.parse(open(py_file_path, "rb").read()) + self.file_dependencies = file_dependencies + self.scopes_and_dependencies = list() # 作用域和依赖组成的stack + self.nodes = list() # ast节点组成的stack + self.no_support_apis = list() # 不支持的API列表 + self.is_import_torch2paddle = False # 是否添加import torch2paddle + self.is_import_paddle = True # 是否添加import padddle + self.is_import_x2paddle = False # 是否添加import x2paddle + + def _get_scope_node(self): + """ 获取当前节点的作用域。 + """ + scope_node = None + for i in range(len(self.scopes_and_dependencies)): + i = -(i + 1) + sd = self.scopes_and_dependencies[i] + if not isinstance(sd, DepInfo) and not isinstance(sd, ast.Assign): + scope_node = sd + break + return scope_node + + def _get_current_index(self, scope_node, node): + """ 获取当前节点在其作用域中的索引序号。 + """ + current_id = 0 + for i, n in enumerate(scope_node.body): + if node == n: + current_id = i + break + return current_id + + def _get_father_node(self): + """ 获取父节点。 + """ + return self.nodes[-2] + + def _get_complete_api(self, api_part_name): + """ 根据部分api名字获取PyTorch的api全名。 + 情况1:依赖是DepInfo,但其PD_IMPORT为None(非PyTorch的依赖),则pytorch_api为None。 + 情况2:依赖是DepInfo,且DepInfo的部分PyTorch属性以“torch”开头,则pytorch_api为完整api。 + 情况3:依赖是ast.Assign节点,则pytorch_api为None。 + """ + pytorch_api = None + dep_info = None + if api_part_name is None: + return pytorch_api, dep_info + for i in range(len(self.scopes_and_dependencies)): + i = -(i + 1) + dep_info = self.scopes_and_dependencies[i] + if isinstance(dep_info, DepInfo): + if dep_info.PT_IMPORT is None: + continue + if (dep_info.PT_FROM is not None and "torch" in dep_info.PT_FROM) or \ + (dep_info.PT_IMPORT is not None and "torch" in dep_info.PT_IMPORT): + replace_str = None + if dep_info.AS is not None and api_part_name.startswith( + dep_info.AS + "."): + replace_str = dep_info.AS + elif dep_info.AS is None and api_part_name.startswith( + dep_info.PT_IMPORT): + replace_str = dep_info.PT_IMPORT + if replace_str is not None: + pytorch_api = api_part_name.replace( + replace_str, dep_info.PT_DEPENDENCY, 1) + if "torch2paddle" in pytorch_api: + # 说明当前节点是插入的已经替换过的node + pytorch_api = None + break + elif isinstance(dep_info, ast.Assign): + is_customized = False + for s in astor.to_source(dep_info.targets[0]).split(","): + if api_part_name.split(".")[0] == s.strip(): + is_customized = True + break + if is_customized: + break + return pytorch_api, dep_info + + def _rename(self, name, dep_info, pytorch_api, paddle_api): + """ 对函数名进行重命名。 + 例如:将nn.Conv2d替换为nn.Conv2D。 + """ + pytorch_api_seg = pytorch_api.split(dep_info.PT_IMPORT) + if ".models." in paddle_api: + self.is_import_x2paddle = True + if paddle_api.startswith(dep_info.PD_IMPORT + ".") or \ + paddle_api.endswith("." + dep_info.PD_IMPORT) or \ + "." + dep_info.PD_IMPORT + "." in paddle_api: + paddle_api_seg = paddle_api.split(dep_info.PD_IMPORT) + if dep_info.AS is None: + name = name.replace(dep_info.PT_IMPORT + pytorch_api_seg[-1], + dep_info.PD_IMPORT + paddle_api_seg[-1]) + else: + name = name.replace(pytorch_api_seg[-1], paddle_api_seg[-1]) + elif "torch2paddle." in paddle_api: + name = "torch2paddle." + paddle_api.split("torch2paddle.")[-1] + self.is_import_torch2paddle = True + else: + name = paddle_api + return name + + def run(self): + self.scopes_and_dependencies.append(self.root) + self.visit(self.root) + for i, node in enumerate(self.root.body): + if isinstance(node, ast.Import) or isinstance(node, ast.ImportFrom): + if self.is_import_torch2paddle: + self.root.body.insert( + i, + ast.parse("from x2paddle import torch2paddle").body[0]) + if self.is_import_x2paddle: + self.root.body.insert(i, + ast.parse("import x2paddle").body[0]) + if self.is_import_paddle: + self.root.body.insert(i, ast.parse("import paddle").body[0]) + break + + def visit(self, node): + self.nodes.append(node) + out = super(AstUpdater, self).visit(node) + self.nodes.pop() + if out is not None: + return out + else: + # 出现字符串或者if等节点需要返回字符串 + try: + return astor.to_source(node) + except Exception: + return None + + def visit_ImportFrom(self, node): + """ 1. 遍历子节点。 + 2. 将当前from依赖中的多个import拆分成多个import。 + 例如:from torch import nn, utils 这个node + 拆分为:node1:from torch import nn + node2:from torch import utils + 拆分原因: + 在paddle中父依赖包可能不一致。 + """ + scope_node = self._get_scope_node() + current_id = self._get_current_index(scope_node, node) + scope_node.body.pop(current_id) + son_nodes = node.names + for i, son_node in enumerate(son_nodes): + copy_node = copy.deepcopy(node) + copy_node.names = [son_node] + if i == 0: + is_remove = self.visit_alias(son_node, copy_node, node.module, + node.level) + if not is_remove: + scope_node.body.insert(current_id, copy_node) + else: + scope_node.body.insert(current_id + i, copy_node) + + def visit_Import(self, node): + """ 遍历子节点。 + """ + son_nodes = getattr(node, "names") + for son_node in son_nodes: + self.visit_alias(son_node, node) + + def visit_alias(self, + node, + father_node=None, + from_name=None, + from_level=None): + """ 构建DepInfo并将其放入scopes_and_dependencies。 + 如果import字符串为“*”,获取依赖包所在文件的依赖信息并转换为DepInfo加入当前的scopes_and_dependencies; + 反之,直接在scopes_and_dependencies中加入DepInfo。 + """ + is_remove = False + dep_info = DepInfo() + dep_info.PT_FROM = from_name + dep_info.PT_IMPORT = getattr(node, "name") + dep_info.AS = getattr(node, "asname", None) + if dep_info.PT_IMPORT == "*": + import_file_path = get_dep_file_path(self.py_file_path, from_level, + from_name) + pytorch_dependencies = self.file_dependencies[import_file_path] + for pytorch_dep_info in pytorch_dependencies: + current_dep_info = DepInfo() + if not isinstance(pytorch_dep_info, str): + current_dep_info.PT_FROM = pytorch_dep_info.FROM + current_dep_info.PT_IMPORT = pytorch_dep_info.IMPORT + current_dep_info.AS = pytorch_dep_info.AS + current_dep_info.PT_DEPENDENCY = pytorch_dep_info.DEPENDENCY + if "torch" in current_dep_info.PT_DEPENDENCY: + if current_dep_info.PT_DEPENDENCY in API_MAPPER: + current_dep_info.PD_DEPENDENCY = \ + API_MAPPER[current_dep_info.PT_DEPENDENCY][0] + if current_dep_info.PT_DEPENDENCY == "torch" and \ + isinstance(self._get_scope_node(), ast.Module): + self.is_import_paddle = False + if current_dep_info.PT_FROM is not None: + seg = current_dep_info.PD_DEPENDENCY.split(".") + current_dep_info.PD_IMPORT = seg[-1] + current_dep_info.PD_FROM = \ + current_dep_info.PD_DEPENDENCY.replace("." + seg[-1], "") + else: + current_dep_info.PD_IMPORT = \ + current_dep_info.PD_DEPENDENCY + elif current_dep_info.PT_DEPENDENCY in REMOVE_API: + scope_node = self._get_scope_node() + for i, n in enumerate(scope_node.body): + if father_node == n: + scope_node.body[i] = ast.parse("\n").body + is_remove = True + else: + self.no_support_apis.append( + current_dep_info.PT_DEPENDENCY) + else: + current_dep_info.PD_DEPENDENCY = pytorch_dep_info + self.scopes_and_dependencies.append(current_dep_info) + return is_remove + dependency_str_list = list() + if dep_info.PT_FROM is None and from_level is not None: + dependency_str_list.append("." * from_level) + elif dep_info.PT_FROM is not None: + dependency_str_list.append(dep_info.PT_FROM) + dependency_str_list.append(dep_info.PT_IMPORT) + dep_info.PT_DEPENDENCY = ".".join(dependency_str_list) + if dep_info.PT_DEPENDENCY.startswith("torch"): + if dep_info.PT_DEPENDENCY in API_MAPPER: + dep_info.PD_DEPENDENCY = API_MAPPER[dep_info.PT_DEPENDENCY][0] + if dep_info.PT_DEPENDENCY == "torch": + self.is_import_paddle = False + if dep_info.PT_FROM is not None: + seg = dep_info.PD_DEPENDENCY.split(".") + setattr(node, "name", seg[-1]) + setattr(father_node, "module", + dep_info.PD_DEPENDENCY.replace("." + seg[-1], "")) + dep_info.PD_IMPORT = seg[-1] + dep_info.PD_FROM = dep_info.PD_DEPENDENCY.replace( + "." + seg[-1], "") + else: + setattr(node, "name", dep_info.PD_DEPENDENCY) + dep_info.PD_IMPORT = dep_info.PD_DEPENDENCY + elif dep_info.PT_DEPENDENCY in REMOVE_API: + scope_node = self._get_scope_node() + for i, n in enumerate(scope_node.body): + if father_node == n: + scope_node.body[i] = ast.parse("\n").body + is_remove = True + elif dep_info.PT_DEPENDENCY.startswith("torch"): + self.no_support_apis.append(dep_info.PT_DEPENDENCY) + else: + dep_info.PD_DEPENDENCY = dep_info.PT_DEPENDENCY + self.scopes_and_dependencies.append(dep_info) + return is_remove + + def visit_Name(self, node): + """ 获取字符串名字。 + """ + pytorch_api, dep_info = self._get_complete_api(getattr(node, "id")) + father_node = self._get_father_node() + if pytorch_api in API_MAPPER: + paddle_api = API_MAPPER[pytorch_api][0] + if isinstance(father_node, ast.Call) and getattr( + father_node.func, "id", None) in ("getattr", "setattr", + "hasattr"): + paddle_api = self._rename(paddle_api, dep_info, pytorch_api, + paddle_api) + for i, arg_node in enumerate(father_node.args): + if astor.to_source(arg_node).strip() == getattr(node, "id"): + father_node.args[i] = ast.parse(paddle_api).body[ + 0].value + return getattr(node, "id") + + def visit_Attribute(self, node): + """ 对属性字符串满足以下4种情况时进行替换: + 情况1 —— Class A(nn.Module):将nn.Module替换为nn.Layer; + 情况2 —— a = (1, 2, nn.Module):将nn.Module替换为nn.Layer; + 情况3 —— def a() -> torch.Tensor:将torch.Tensor替换为paddle.Tensor; + 情况4 —— def a(x: torch.Tensor):将torch.Tensor替换为paddle.Tensor; + 情况5 —— isinstance(a, nn.Module):将nn.Module替换为nn.Layer; + 情况6 —— torch.float32:将torch.float32替换为"float32"; + """ + value_node = node.value + attr = node.attr + name = self.visit(value_node) + attr_str = name + "." + attr + pytorch_api, dep_info = self._get_complete_api(attr_str) + father_node = self._get_father_node() + if pytorch_api in API_MAPPER: + paddle_api = API_MAPPER[pytorch_api][0] + if isinstance(father_node, ast.ClassDef): + attr_str = self._rename(attr_str, dep_info, pytorch_api, + paddle_api) + if node in father_node.bases: + father_node.bases[0] = ast.parse(attr_str).body[0].value + return attr_str + elif isinstance(father_node, ast.arguments): + attr_str = self._rename(attr_str, dep_info, pytorch_api, + paddle_api) + for i, default_n in enumerate(father_node.defaults): + if default_n == node: + father_node.defaults[i] = ast.parse(attr_str).body[ + 0].value + return attr_str + elif isinstance(father_node, ast.Tuple): + paddle_api = self._rename(paddle_api, dep_info, pytorch_api, + paddle_api) + for i, elts_node in enumerate(father_node.elts): + if astor.to_source(elts_node).strip() == attr_str: + father_node.elts[i] = ast.parse(paddle_api).body[ + 0].value + return paddle_api + elif isinstance(father_node, ast.FunctionDef): + paddle_api = self._rename(paddle_api, dep_info, pytorch_api, + paddle_api) + father_node.returns = ast.parse(paddle_api).body[0].value + return paddle_api + elif isinstance(father_node, ast.arg): + attr_str = self._rename(attr_str, dep_info, pytorch_api, + paddle_api) + father_node.annotation = ast.parse(attr_str).body[0].value + return attr_str + elif isinstance(father_node, ast.Call) and getattr( + father_node.func, "id", None) == "isinstance": + paddle_api = self._rename(paddle_api, dep_info, pytorch_api, + paddle_api) + for i, arg_node in enumerate(father_node.args): + if astor.to_source(arg_node).strip() == attr_str: + father_node.args[i] = ast.parse(paddle_api).body[ + 0].value + return paddle_api + elif not isinstance(father_node, ast.Call): + # 对torch.float32的处理 + for k, v in father_node.__dict__.items(): + if v == node: + father_node.k = ast.parse(paddle_api).body[0].value + break + return attr_str + elif pytorch_api in REMOVE_API: + if isinstance( + father_node, + (ast.Assign, ast.If, ast.FunctionDef, ast.ClassDef, ast.Call)): + scope_node = self._get_scope_node() + for i, n in enumerate(scope_node.body): + if father_node == n: + scope_node.body.pop(i) + return None + elif isinstance(father_node, ast.BoolOp): + for i, n in enumerate(father_node.values): + if node == n: + father_node.values[i] = ast.parse("False").body[0].value + return None + else: + if isinstance(pytorch_api, str) and pytorch_api.startswith( + "torch" + ) and "(" not in pytorch_api and "[" not in pytorch_api: + if not isinstance(father_node, ast.Attribute): + self.no_support_apis.append(pytorch_api) + return attr_str + + def visit_Num(self, node): + """ 返回数值。 + """ + return getattr(node, "n") + + def visit_keyword(self, node): + """ 返回键值对。 + 【注意】当value是API_MAPPER中的key时,需要替换为API_MAPPER中对应的Paddle API。 + """ + key = getattr(node, "arg") + value_node = getattr(node, "value") + value = self.visit(value_node) + if value in API_MAPPER: + value = API_MAPPER[value][0] + elif isinstance(value, str) and value.startswith( + "torch") and "(" not in value and "[" not in value: + self.no_support_apis.append(value) + return {key: value} + + def visit_Tuple(self, node): + """ 返回tuple。 + """ + elts_nodes = getattr(node, "elts") + elts = list() + for elts_node in elts_nodes: + elts.append(self.visit(elts_node)) + elts = tuple(elts) + return elts + + def visit_Assign(self, node): + """ 1. 将Assign节点加入scopes_and_dependencies; + 2. 遍历Assign节点的子节点。 + """ + self.scopes_and_dependencies.append(node) + self.generic_visit(node) + + def visit_Call(self, node): + """ 1. 获取原始函数名并更新为新的函数名。 + 2. 获取args和kwargs。 + 3. 根据API_MAPPER映射需要更新的操作,对参数进行处理。 + 4. 如果有前缀代码和后缀代码,则需要添加相应节点。 + """ + # 获取函数名 + func_node = node.func + if isinstance(func_node, ast.Attribute) and isinstance(func_node.value, + ast.Call): + func_name = None + else: + func_name = self.visit(func_node) + pytorch_api, dep_info = self._get_complete_api(func_name) + if pytorch_api is None: + self.generic_visit(node) + return + if pytorch_api not in API_MAPPER: + if pytorch_api.startswith( + "torch" + ) and "[" not in pytorch_api and "(" not in pytorch_api: + self.no_support_apis.append(pytorch_api) + return + paddle_api = API_MAPPER[pytorch_api][0] + func_name = self._rename(func_name, dep_info, pytorch_api, paddle_api) + setattr(node, "func", ast.parse(func_name).body[0].value) + + # 获取args + args_nodes = getattr(node, "args") + args_list = list() + for args_node in args_nodes: + args_list.append(self.visit(args_node)) + + # 获取keywords + keywords_nodes = getattr(node, "keywords") + kw_dict = dict() + for keywords_node in keywords_nodes: + if list(self.visit(keywords_node).keys())[0] is None: + args_list.append("**{}".format( + list(self.visit(keywords_node).values())[0])) + else: + kw_dict.update(self.visit(keywords_node)) + + if API_MAPPER[pytorch_api][1] is None: + return + + target_name = None + father_node = self._get_father_node() + if isinstance(father_node, ast.Assign): + target_node = father_node.targets[0] + target_name = self.visit(target_node) + mapper = API_MAPPER[pytorch_api][1](func_name, pytorch_api, args_list, + kw_dict, target_name) + prefix_insert_codes, new_code, suffix_insert_codes = mapper.run() + scope_node = self._get_scope_node() + if isinstance(ast.parse(new_code).body[0], ast.Assign): + node_index = self._get_current_index(scope_node, node) + scope_node.body[node_index] = ast.parse( + new_code.replace("\n", "")).body[0] + else: + new_call_node = ast.parse(new_code).body[0].value + setattr(node, "func", new_call_node.func) # 修改了fun_name + setattr(node, "args", new_call_node.args) + setattr(node, "keywords", new_call_node.keywords) + for i, n in enumerate(scope_node.body): + if father_node == n: + for code in prefix_insert_codes: + scope_node.body.insert( + i, ast.parse(code.replace("\n", "")).body[0]) + i += 1 + break + for i, n in enumerate(scope_node.body): + if father_node == n: + j = i + 1 + for code in suffix_insert_codes: + scope_node.body.insert( + j, ast.parse(code.replace("\n", "")).body[0]) + j += 1 + break + + def visit_Subscript(self, node): + value_node = node.value + value_name = self.visit(value_node) + pytorch_api, dep_info = self._get_complete_api(value_name) + if pytorch_api in API_MAPPER: + paddle_api = API_MAPPER[pytorch_api][0] + value_name = self._rename(value_name, dep_info, pytorch_api, + paddle_api) + node.value = ast.parse(value_name).body[0] + else: + if isinstance(pytorch_api, str) and pytorch_api.startswith( + "torch") and "(" not in pytorch_api: + self.no_support_apis.append(pytorch_api) + self.visit(node.slice) + self.visit(node.ctx) + + def visit_FunctionDef(self, node): + """ 1. 将FunctionDef节点加入scopes_and_dependencies; + 2. 遍历FunctionDef节点的子节点; + 3. 去除scopes_and_dependencies中FunctionDef节点以及之后的节点。 + """ + self.scopes_and_dependencies.append(node) + self.generic_visit(node) + last_node = self.scopes_and_dependencies.pop(-1) + while not isinstance(last_node, ast.FunctionDef): + last_node = self.scopes_and_dependencies.pop(-1) + + def visit_ClassDef(self, node): + """ 1. 将ClassDef节点加入scopes_and_dependencies; + 2. 遍历ClassDef节点的子节点; + 3. 去除scopes_and_dependencies中ClassDef节点以及之后的节点。 + """ + self.scopes_and_dependencies.append(node) + self.generic_visit(node) + last_node = self.scopes_and_dependencies.pop(-1) + while not isinstance(last_node, ast.ClassDef): + last_node = self.scopes_and_dependencies.pop(-1) + + def visit_If(self, node): + """ 1. 将If节点加入scopes_and_dependencies; + 2. 遍历If节点的子节点; + 3. 去除scopes_and_dependencies中If节点以及之后的节点。 + """ + self.scopes_and_dependencies.append(node) + self.generic_visit(node) + last_node = self.scopes_and_dependencies.pop(-1) + while not isinstance(last_node, ast.If): + last_node = self.scopes_and_dependencies.pop(-1) + + def visit_While(self, node): + """ 1. 将While节点加入scopes_and_dependencies; + 2. 遍历Try节点的子节点; + 3. 去除scopes_and_dependencies中Try节点以及之后的节点。 + """ + self.scopes_and_dependencies.append(node) + self.generic_visit(node) + last_node = self.scopes_and_dependencies.pop(-1) + while not isinstance(last_node, ast.While): + last_node = self.scopes_and_dependencies.pop(-1) + + def visit_Try(self, node): + """ 1. 将Try节点加入scopes_and_dependencies; + 2. 遍历Try节点的子节点; + 3. 去除scopes_and_dependencies中Try节点以及之后的节点。 + """ + self.scopes_and_dependencies.append(node) + self.generic_visit(node) + last_node = self.scopes_and_dependencies.pop(-1) + while not isinstance(last_node, ast.Try): + last_node = self.scopes_and_dependencies.pop(-1) + + def visit_ExtSlice(self, node): + """ 将Index节点替换替换为Num。 + """ + dim_nodes = node.dims + for i, dim_node in enumerate(dim_nodes): + if isinstance(dim_node, ast.Index): + dim_nodes[i] = dim_node.value + else: + self.visit(dim_node) + + def visit_Str(self, node): + """ 修改模型参数的后缀名。 + """ + setattr(node, "s", + node.s.replace(".pth", ".pdiparams").replace( + ".pt", ".pdiparams").replace(".ckpt", ".pdiparams")) + + +def update(py_file_path, file_dependencies): + updater = AstUpdater(py_file_path, file_dependencies) + updater.run() + if len(updater.no_support_apis) > 0: + print("Can not convert the file {}.".format(py_file_path)) + print("The unsupported packages or operators are: [{}].".format( + ", ".join(set(updater.no_support_apis)))) + return None + else: + return updater.root diff --git a/x2paddle/project_convertor/pytorch/convert.py b/x2paddle/project_convertor/pytorch/convert.py new file mode 100644 index 0000000000000000000000000000000000000000..4665f85ca9e5bcaaa4d14b6f021e5aa4a79a6728 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/convert.py @@ -0,0 +1,117 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import ast +import astor +import os +import os.path as osp +import shutil +import argparse +from six import text_type as _text_type + +from .dependency_analyzer import analyze +from .ast_update import update +from .utils import * + + +def write_file(path, tree): + code = astor.to_source(tree) + code = code.replace("(...)", "...") + code = add_line_continuation_symbol(code) + f = open(path, "w") + f.write(code) + f.close() + + +def generate_dependencies(folder_path, file_dependencies): + for name in os.listdir(folder_path): + current_path = osp.join(folder_path, name) + if osp.isfile(current_path) and current_path.endswith(".py"): + if current_path in file_dependencies: + continue + analyze(current_path, file_dependencies) + elif osp.isdir(current_path): + generate_dependencies(current_path, file_dependencies) + + +def convert_code(folder_path, new_folder_path, file_dependencies): + for name in os.listdir(folder_path): + current_path = osp.join(folder_path, name) + new_current_path = osp.join(new_folder_path, name) + if osp.isfile(current_path) and current_path.endswith(".py"): + print(current_path) + root = update(current_path, file_dependencies) + if root is not None: + write_file(new_current_path, root) + elif osp.isdir(current_path): + if not osp.exists(new_current_path): + os.makedirs(new_current_path) + convert_code(current_path, new_current_path, file_dependencies) + elif osp.isfile(current_path) and osp.splitext(current_path)[ + -1] in [".pth", ".pt", ".ckpt"]: + continue + elif osp.isfile(current_path) and current_path.endswith(".pyc"): + continue + elif osp.isdir(current_path) and current_path == "__pycache__": + continue + elif osp.isdir(current_path) and current_path == ".ipynb_checkpoints": + continue + else: + shutil.copyfile(current_path, new_current_path) + + +def convert_params(params_path): + import torch + import paddle + params = torch.load(params_path, map_location=torch.device('cpu')) + new_params = dict() + bn_w_name_list = list() + for k, v in params.items(): + if k.endswith(".running_mean"): + new_params[k.replace(".running_mean", "._mean")] = v.detach().numpy( + ) + elif k.endswith(".running_var"): + new_params[k.replace(".running_var", "._variance")] = v.detach( + ).numpy() + bn_w_name_list.append(k.replace(".running_var", ".weight")) + else: + new_params[k] = v.detach().numpy() + for k, v in new_params.items(): + if len(v.shape) == 2 and k.endswith( + ".weight") and k not in bn_w_name_list: + new_params[k] = v.T + paddle.save(new_params, + params_path.replace(".pth", ".pdiparams").replace( + ".pt", ".pdiparams").replace(".ckpt", ".pdiparams")) + + +def main(args): + project_path = args.project_dir + save_path = args.save_dir + params_path = args.pretrain_model + if params_path is not None: + params_path = osp.abspath(params_path) + if osp.isdir(params_path): + for file in os.listdir(params_path): + if osp.splitext(file)[-1] in [".pth", ".pt", ".ckpt"]: + convert_params(osp.join(params_path, file)) + else: + convert_params(params_path) + project_path = osp.abspath(project_path) + file_dependencies = dict() + sys.path.append(project_path) + generate_dependencies(project_path, file_dependencies) + if not osp.exists(save_path): + os.makedirs(save_path) + convert_code(project_path, save_path, file_dependencies) diff --git a/x2paddle/project_convertor/pytorch/dependency_analyzer.py b/x2paddle/project_convertor/pytorch/dependency_analyzer.py new file mode 100644 index 0000000000000000000000000000000000000000..b5baaf98fef8d56f75fefb8b7c3167e738eda2e2 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/dependency_analyzer.py @@ -0,0 +1,145 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. +import sys +import os.path as osp +import ast +import astor +from x2paddle.project_convertor.pytorch.mapper import * +import copy +from .utils import get_dep_file_path + + +class PtDepInfo: + """ + PyTorch依赖包信息。 + FROM代表from信息的字符串,例如:torch; + IMPORT代表import信息系的字符串,例如:nn.functional; + AS代表as信息的字符串,例如:F; + DEPENDENCY代表由FROM、IMPORT、AS三者组成的字符串,例如:from torch import nn.functional as F。 + """ + FROM = None + IMPORT = None + AS = None + DEPENDENCY = None + + +class DependencyAnalyzer(ast.NodeVisitor): + """ 获取python文件的依赖信息。 + 依赖信息由4部分组成:(1)import相关的依赖;(2)赋值;(3)函数;(4)类。 + + Args: + py_file_path (str): python文件的绝对值路径。 + file_dependencies (dict): 当前已经统计的依赖信息,key为python文件的绝对值路径, + value为key文件所对应的依赖信息组成的list。 + """ + + def __init__(self, py_file_path, file_dependencies): + self.py_file_path = py_file_path + self.file_dependencies = file_dependencies + self.root = ast.parse(open(py_file_path, "rb").read()) + self.scopes_and_dependencies = list() # 作用域和依赖组成的stack + self.file_dependencies[self.py_file_path] = list() + + def _get_scope_node(self): + """ 获取当前节点的作用域。 + """ + scope_node = None + for i in range(len(self.scopes_and_dependencies)): + i = -(i + 1) + sd = self.scopes_and_dependencies[i] + if not isinstance(sd, PtDepInfo) and not isinstance(sd, ast.Assign): + scope_node = sd + break + return scope_node + + def run(self): + self.scopes_and_dependencies.append(self.root) + self.visit(self.root) + + def visit(self, node): + out = super(DependencyAnalyzer, self).visit(node) + + def visit_ImportFrom(self, node): + """ 遍历子节点。 + """ + son_nodes = node.names + for son_node in son_nodes: + self.visit_alias(son_node, node.module, node.level) + + def visit_Import(self, node): + """ 遍历子节点。 + """ + son_nodes = getattr(node, "names") + for son_node in son_nodes: + self.visit_alias(son_node) + + def visit_alias(self, node, from_name=None, from_level=None): + """ 构建PtDepInfo并将当前的PtDepInfo放入scopes_and_dependencies。 + 如果import字符串为“*”,获取依赖包所在文件的依赖信息加入当前的dependencies; + 反之,直接在dependencies中加入PtDepInfo。 + """ + dep_info = PtDepInfo() + dep_info.FROM = from_name + dep_info.IMPORT = getattr(node, "name") + dep_info.AS = getattr(node, "asname", None) + if dep_info.IMPORT == "*": + import_file_path = get_dep_file_path(self.py_file_path, from_level, + dep_info.FROM) + if import_file_path not in self.file_dependencies: + analyzer = DependencyAnalyzer(import_file_path, + self.file_dependencies) + analyzer.run() + self.file_dependencies[self.py_file_path].extend( + self.file_dependencies[import_file_path]) + else: + dependency_str_list = list() + if dep_info.FROM is None and from_level is not None: + dependency_str_list.append("." * from_level) + elif dep_info.FROM is not None: + dependency_str_list.append(dep_info.FROM) + dependency_str_list.append(dep_info.IMPORT) + dep_info.DEPENDENCY = ".".join(dependency_str_list) + self.file_dependencies[self.py_file_path].append(dep_info) + self.scopes_and_dependencies.append(dep_info) + + def visit_FunctionDef(self, node): + """ 当作用域为ast的根节点时,把函数名放入dependencies。 + """ + if isinstance(self._get_scope_node(), ast.Module): + self.scopes_and_dependencies.append(node) + self.file_dependencies[self.py_file_path].append(node.name) + + def visit_ClassDef(self, node): + """ 当作用域为ast的根节点时,把类名放入dependencies。 + """ + if isinstance(self._get_scope_node(), ast.Module): + self.scopes_and_dependencies.append(node) + self.file_dependencies[self.py_file_path].append(node.name) + + def visit_Assign(self, node): + """ 当作用域为ast的根节点时,把赋值名放入dependencies。 + """ + if isinstance(self._get_scope_node(), ast.Module): + self.scopes_and_dependencies.append(node) + for target in node.targets: + if isinstance(target, ast.Tuple): + for ele in target.elts: + self.file_dependencies[self.py_file_path].append(ele.id) + elif isinstance(target, ast.Name): + self.file_dependencies[self.py_file_path].append(target.id) + + +def analyze(py_file_path, file_dependencies): + analyzer = DependencyAnalyzer(py_file_path, file_dependencies) + analyzer.run() diff --git a/x2paddle/project_convertor/pytorch/mapper.py b/x2paddle/project_convertor/pytorch/mapper.py new file mode 100644 index 0000000000000000000000000000000000000000..81ceec3685d3363b9bd284a40d4cf0179536dcd7 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/mapper.py @@ -0,0 +1,315 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from x2paddle.project_convertor.pytorch.api_mapper import * +from x2paddle.utils import * + +OPTIMIZER_MAPPER = { + "torch.optim": ["paddle.optimizer", None], + "torch.optim.lr_scheduler.ReduceLROnPlateau": + ["paddle.optimizer.lr.ReduceOnPlateau", LRScheculerMapper], + "torch.optim.lr_scheduler.CosineAnnealingLR": + ["paddle.optimizer.lr.CosineAnnealingDecay", LRScheculerMapper], + "torch.optim.lr_scheduler.MultiStepLR": + ["paddle.optimizer.lr.MultiStepDecay", LRScheculerMapper], + "torch.optim.Adam": ["x2paddle.torch2paddle.Adam", None], + "torch.optim.SGD": ["x2paddle.torch2paddle.Momentum", None] +} + +NN_MAPPER = { + # basic + "torch.nn": ["paddle.nn", None], + "torch.nn.Module": ["paddle.nn.Layer", None], + "torch.nn.ModuleList": ["paddle.nn.LayerList", None], + "torch.nn.Sequential": ["paddle.nn.Sequential", None], + "torch.nn.utils": ["paddle.nn.utils", None], + "torch.nn.utils.clip_grad_value_": + ["x2paddle.torch2paddle.clip_grad_value_", None], + "torch.nn.utils.spectral_norm": + ["x2paddle.torch2paddle.spectral_norm", None], + "torch.nn.Parameter": ["paddle.create_parameter", CreateParamModuleMapper], + "torch.nn.parallel": ["paddle", None], + "torch.nn.DataParallel": ["paddle.DataParallel", DataParallelModuleMapper], + "torch.nn.parallel.DistributedDataParallel": + ["paddle.DataParallel", DataParallelModuleMapper], + "torch.nn.functional": ["paddle.nn.functional", None], + # nn_net + "torch.nn.AdaptiveAvgPool1d": ["paddle.nn.AdaptiveAvgPool1D", None], + "torch.nn.AdaptiveAvgPool2d": ["paddle.nn.AdaptiveAvgPool2D", None], + "torch.nn.AdaptiveAvgPool3d": ["paddle.nn.AdaptiveAvgPool3D", None], + "torch.nn.AvgPool1d": ["paddle.nn.AvgPool1D", AvgPoolModuleMapper], + "torch.nn.AvgPool2d": ["paddle.nn.AvgPool2D", AvgPoolModuleMapper], + "torch.nn.AvgPool3d": ["paddle.nn.AvgPool3D", AvgPoolModuleMapper], + "torch.nn.BatchNorm1d": ["paddle.nn.BatchNorm1D", BatchNormModuleMapper], + "torch.nn.BatchNorm2d": ["paddle.nn.BatchNorm2D", BatchNormModuleMapper], + "torch.nn.BatchNorm3d": ["paddle.nn.BatchNorm3D", BatchNormModuleMapper], + "torch.nn.ConstantPad2d": ["paddle.nn.Pad2D", PadModuleMapper], + "torch.nn.Conv1d": ["paddle.nn.Conv1D", ConvModuleMapper], + "torch.nn.Conv2d": ["paddle.nn.Conv2D", ConvModuleMapper], + "torch.nn.Conv3d": ["paddle.nn.Conv3D", ConvModuleMapper], + "torch.nn.ConvTranspose2d": + ["x2paddle.torch2paddle.Conv2DTranspose", None], + "torch.nn.Dropout": ["paddle.nn.Dropout", DropoutModuleMapper], + "torch.nn.Dropout2d": ["paddle.nn.Dropout", DropoutModuleMapper], + "torch.nn.Embedding": ["paddle.nn.Embedding", EmbeddingModuleMapper], + "torch.nn.GELU": ["paddle.nn.GELU", None], + "torch.nn.GroupNorm": ["paddle.nn.GroupNorm", None], + "torch.nn.Identity": ["x2paddle.torch2paddle.Identity", None], + "torch.nn.InstanceNorm2d": + ["paddle.nn.InstanceNorm2D", BatchNormModuleMapper], + "torch.nn.LeakyReLU": ["paddle.nn.LeakyReLU", None], + "torch.nn.LayerNorm": ["paddle.nn.LayerNorm", BatchNormModuleMapper], + "torch.nn.Linear": ["paddle.nn.Linear", LinearModuleMapper], + "torch.nn.MaxPool1d": ["paddle.nn.MaxPool1D", MaxPoolModuleMapper], + "torch.nn.MaxPool2d": ["paddle.nn.MaxPool2D", MaxPoolModuleMapper], + "torch.nn.MaxPool3d": ["paddle.nn.MaxPool3D", MaxPoolModuleMapper], + "torch.nn.MaxUnpool2d": ["x2paddle.torch2paddle.MaxUnpool2d", None], + "torch.nn.ReflectionPad2d": ["paddle.nn.Pad2D", PadModuleMapper], + "torch.nn.ReplicationPad2d": ["paddle.nn.Pad2D", PadModuleMapper], + "torch.nn.PReLU": ["paddle.nn.PReLU", None], + "torch.nn.ReLU": ["paddle.nn.ReLU", ReLUModuleMapper], + "torch.nn.ReLU6": ["paddle.nn.ReLU6", ReLUModuleMapper], + "torch.nn.Sigmoid": ["paddle.nn.Sigmoid", None], + "torch.nn.Softmax": ["paddle.nn.Softmax", SoftmaxModuleMapper], + "torch.nn.SyncBatchNorm": + ["paddle.nn.SyncBatchNorm", BatchNormModuleMapper], + "torch.nn.Tanh": ["paddle.nn.Tanh", None], + "torch.nn.Upsample": ["paddle.nn.Upsample", None], + "torch.nn.ZeroPad2d": ["paddle.nn.Pad2D", PadModuleMapper], + # nn_loss + "torch.nn.CrossEntropyLoss": + ["paddle.nn.CrossEntropyLoss", LossModuleMapper], + "torch.nn.BCEWithLogitsLoss": + ["paddle.nn.BCEWithLogitsLoss", LossModuleMapper], + "torch.nn.BCELoss": ["paddle.nn.BCELoss", None], + "torch.nn.KLDivLoss": ["x2paddle.torch2paddle.KLDivLoss", None], + "torch.nn.L1Loss": ["paddle.nn.loss.L1Loss", LossModuleMapper], + # functional_net + "torch.nn.functional.avg_pool1d": + ["paddle.nn.functional.avg_pool1d", AvgPoolFuncMapper], + "torch.nn.functional.avg_pool2d": + ["paddle.nn.functional.avg_pool2d", AvgPoolFuncMapper], + "torch.nn.functional.avg_pool3d": + ["paddle.nn.functional.avg_pool3d", AvgPoolFuncMapper], + "torch.nn.functional.dropout": + ["paddle.nn.functional.dropout", DropoutFuncMapper], + "torch.nn.functional.interpolate": + ["paddle.nn.functional.interpolate", InterpolateFuncMapper], + "torch.nn.functional.leaky_relu": + ["paddle.nn.functional.leaky_relu", LeaklyReluFuncMapper], + "torch.nn.functional.log_softmax": + ["paddle.nn.functional.log_softmax", LogSoftmaxFuncMapper], + "torch.nn.functional.pad": ["paddle.nn.functional.pad", PadFuncMapper], + "torch.nn.functional.relu": ["paddle.nn.functional.relu", ReluFuncMapper], + "torch.sigmoid": ["paddle.nn.functional.sigmoid", SigmoidFuncMapper], + "torch.nn.functional.sigmoid": + ["paddle.nn.functional.sigmoid", SigmoidFuncMapper], + "torch.nn.functional.softmax": + ["paddle.nn.functional.softmax", SoftmaxFuncMapper], + "torch.nn.functional.tanh": ["paddle.tanh", None], + # init + "torch.nn.init": ["x2paddle.torch2paddle", None], + "torch.nn.init.kaiming_normal_": + ["x2paddle.torch2paddle.kaiming_normal_", None], + "torch.nn.init.kaiming_normal": + ["x2paddle.torch2paddle.kaiming_normal_", None], + "torch.nn.init.xavier_uniform_": + ["x2paddle.torch2paddle.xavier_normal_", None], + "torch.nn.init.xavier_normal_": + ["x2paddle.torch2paddle.xavier_uniform_", None], + "torch.nn.init.constant_": ["x2paddle.torch2paddle.constant_init_", None], + "torch.nn.init.normal_": ["x2paddle.torch2paddle.normal_init_", None], + "torch.nn.init.ones_": ["x2paddle.torch2paddle.ones_init_", None], + "torch.nn.init.zeros_": ["x2paddle.torch2paddle.zeros_init_", None], + "torch.nn.init.orthogonal_": + ["x2paddle.torch2paddle.normal_init_", None], # syf(TODO) + # functional_loss + "torch.nn.functional.binary_cross_entropy_with_logits": + ["x2paddle.torch2paddle.binary_cross_entropy_with_logits", None], + "torch.nn.functional.cross_entropy": + ["paddle.nn.functional.cross_entropy", CrossEntropyFuncMapper], + "torch.nn.functional.mse_loss": + ["paddle.nn.functional.mse_loss", LossFuncMapper], + "torch.nn.functional.smooth_l1_loss": + ["paddle.nn.functional.smooth_l1_loss", LossFuncMapper], +} + +UTILS_MAPPER = { + "torch.utils.data": ["paddle.io", None], + "torch.utils.data.DataLoader": ["x2paddle.torch2paddle.DataLoader", None], + "torch.utils.data.random_split": + ["x2paddle.torch2paddle.random_split", None], + "torch.utils.data.Dataset": ["paddle.io.Dataset", None], + "torch.utils.data.ConcatDataset": + ["x2paddle.torch2paddle.ConcatDataset", None], + "torch.utils.data.distributed": ["x2paddle.torch2paddle", None], + "torch.utils.data.distributed.DistributedSampler": + ["x2paddle.torch2paddle.DistributedSampler", None], + "torch.utils.model_zoo": ["paddle", None], + "torch.utils.model_zoo.load_url": ["paddle.load", HubLoadMapper], +} + +DIST_MAPPER = { + "torch.multiprocessing": ["paddle.distributed", None], + "torch.multiprocessing.spawn": ["paddle.distributed.spawn", None], + "torch.distributed": ["x2paddle.torch2paddle", None], + "torch.distributed.init_process_group": + ["x2paddle.torch2paddle.init_process_group", None] +} + +DTYPE_MAPPER = { + "torch.float32": [string("float32"), None], + "torch.long": [string("int64"), None], + "torch.bool": [string("bool"), None] +} + +TORCHVISION_MAPPER = { + "torchvision": ["paddle.vision", None], + # transforms + "torchvision.transforms": ["paddle.vision.transforms", None], + "torchvision.transforms.Compose": + ["paddle.vision.transforms.Compose", None], + "torchvision.transforms.ToPILImage": + ["x2paddle.torch2paddle.ToPILImage", None], + "torchvision.transforms.Resize": ["paddle.vision.transforms.Resize", None], + "torchvision.transforms.ToTensor": + ["x2paddle.torch2paddle.ToTensor", None], + "torchvision.transforms.RandomHorizontalFlip": + ["paddle.vision.transforms.RandomHorizontalFlip", None], + "torchvision.transforms.CenterCrop": + ["paddle.vision.transforms.CenterCrop", None], + "torchvision.transforms.Normalize": + ["x2paddle.torch2paddle.Normalize", None], + "torchvision.transforms.RandomResizedCrop": + ["paddle.vision.transforms.RandomResizedCrop", None], + "torchvision.transforms.Lambda": ["x2paddle.torch2paddle.Lambda", None], + # utils + "torchvision.utils": ["x2paddle.torch2paddle", None], + "torchvision.utils.save_image": ["x2paddle.torch2paddle.save_image", None], + # datasets + "torchvision.datasets": ["paddle.vision.datasets", None], + "torchvision.datasets.ImageFolder": + ["x2paddle.torch2paddle.ImageFolder", None], + # models + "torchvision.models": ["x2paddle.models", None], + "torchvision.models.vgg.model_urls": + ["x2paddle.models.vgg_pth_urls", None], + "torchvision.models.vgg11": ["x2paddle.models.vgg11_pth", None], + "torchvision.models.vgg13": ["x2paddle.models.vgg13_pth", None], + "torchvision.models.vgg16": ["x2paddle.models.vgg16_pth", None], + "torchvision.models.vgg19": ["x2paddle.models.vgg19_pth", None], + "torchvision.models.vgg11_bn": ["x2paddle.models.vgg11_bn_pth", None], + "torchvision.models.vgg13_bn": ["x2paddle.models.vgg13_bn_pth", None], + "torchvision.models.vgg16_bn": ["x2paddle.models.vgg16_bn_pth", None], + "torchvision.models.vgg19_bn": ["x2paddle.models.vgg19_bn_pth", None], + "torchvision.models.resnet.model_urls": + ["x2paddle.models.resnet_pth_urls", None], + "torchvision.models.resnet18": ["x2paddle.models.resnet18_pth", None], + "torchvision.models.resnet34": ["x2paddle.models.resnet34_pth", None], + "torchvision.models.resnet50": ["x2paddle.models.resnet50_pth", None], + "torchvision.models.resnet101": ["x2paddle.models.resnet101_pth", None], + "torchvision.models.resnet152": ["x2paddle.models.resnet152_pth", None], + "torchvision.models.resnext50_32x4d": + ["x2paddle.models.resnext50_32x4d_pth", None], + "torchvision.models.resnext101_32x8d": + ["x2paddle.models.resnext101_32x8d_pth", None], + "torchvision.models.wide_resnet50_2": + ["x2paddle.models.wide_resnet50_2_pth", None], + "torchvision.models.wide_resnet101_2": + ["x2paddle.models.wide_resnet101_2_pth", None], +} + +AUTOGRAD_MAPPER = { + "torch.autograd.Variable": ["paddle.to_tensor", None], # TODO(syf): 确认是否一致 + "torch.autograd.grad": ["paddle.grad", None], +} + +API_MAPPER = { + "torch": ["paddle", None], + "torch.Tensor": ["x2paddle.torch2paddle.create_tensor", None], + "torch.FloatTensor": ["x2paddle.torch2paddle.create_float32_tensor", None], + "torch.cuda.FloatTensor": + ["x2paddle.torch2paddle.create_float32_tensor", None], + "torch.ByteTensor": ["x2paddle.torch2paddle.create_uint8_tensor", None], + "torch.cuda.ByteTensor": + ["x2paddle.torch2paddle.create_uint8_tensor", None], + "torch.load": ["paddle.load", LoadMapper], + "torch.save": ["paddle.save", SaveMapper], + "torch.device": ["paddle.set_device", SetDeviceMapper], + "torch.cat": ["x2paddle.torch2paddle.concat", None], + "torch.cuda.is_available": ["paddle.is_compiled_with_cuda", None], + "torch.cuda.set_device": ["x2paddle.torch2paddle.set_cuda_device", None], + "torch.no_grad": ["paddle.no_grad", None], + "torch.from_numpy": ["paddle.to_tensor", None], + "torch.cuda.device_count": ["x2paddle.torch2paddle.device_count", None], + "torch.manual_seed": ["paddle.seed", None], + "torch.unsqueeze": ["paddle.unsqueeze", UnSqueezeMapper], + "torch.squeeze": ["paddle.squeeze", UnSqueezeMapper], + "torch.sum": ["x2paddle.torch2paddle.sum", None], + "torch.mean": ["x2paddle.torch2paddle.mean", None], + "torch.full": ["paddle.full", TensorBuilderMapper], + "torch.full_like": ["paddle.full_like", TensorLikeMapper], + "torch.ones": ["paddle.ones", TensorBuilderMapper], + "torch.ones_like": ["paddle.full_like", TensorLikeMapper], + "torch.zeros": ["paddle.zeros", TensorBuilderMapper], + "torch.zeros_like": ["paddle.full_like", TensorLikeMapper], + "torch.sqrt": ["paddle.sqrt", OneMathMapper], + "torch.arange": ["paddle.arange", ArangeMapper], + "torch.matmul": ["paddle.matmul", TwoMathMapper], + "torch.set_grad_enabled": ["paddle.no_grad", NoGradMapper], + "torch.tensor": ["paddle.to_tensor", None], + "torch.clamp": ["paddle.clip", OneMathMapper], + "torch.exp": ["paddle.exp", OneMathMapper], + "torch.max": ["x2paddle.torch2paddle.max", None], + "torch.min": ["x2paddle.torch2paddle.min", None], + "torch.argmax": ["paddle.argmax", OneMathMapper], + "torch.argmin": ["paddle.argmin", OneMathMapper], + "torch.stack": ["paddle.stacks", StackMapper], + "torch.log": ["paddle.log", OneMathMapper], + "torch.randperm": ["paddle.randperm", RandpermMapper], + "torch.rand": ["x2paddle.torch2paddle.rand", None], + "torch.randn_like": ["x2paddle.torch2paddle.randn_like", None], + "torch.abs": ["paddle.abs", OneMathMapper], + "torch.bitwise_or": ["paddle.logical_or", LogicalMapper], + "torch.bitwise_xor": ["paddle.logical_xor", LogicalMapper], + "torch.bitwise_and": ["paddle.logical_and", LogicalMapper], + "torch.bitwise_not": ["paddle.logical_not", LogicalMapper], + "torch.split": ["paddle.split", SplitMapper], + "torch.hub.load_state_dict_from_url": ["paddle.load", HubLoadMapper], + "torch.randn": ["x2paddle.torch2paddle.randn", None], + "torch.add": ["paddle.add", TwoMathMapper], + "torch.mul": ["paddle.multiply", TwoMathMapper], + "torch.einsum": ["paddlenlp.ops.einsum ", None], + "torch.linspace": ["paddle.linspace", LinspaceMapper], +} +INVALID_API = { + "torch.channels_last": ["None", None], + "torch.cuda.empty_cache": ["x2paddle.torch2paddle.invalid", None], +} + +API_MAPPER.update(OPTIMIZER_MAPPER) +API_MAPPER.update(NN_MAPPER) +API_MAPPER.update(UTILS_MAPPER) +API_MAPPER.update(DTYPE_MAPPER) +API_MAPPER.update(DIST_MAPPER) +API_MAPPER.update(TORCHVISION_MAPPER) +API_MAPPER.update(AUTOGRAD_MAPPER) +API_MAPPER.update(INVALID_API) + +REMOVE_API = [ + "torch.backends.cudnn", + "torch.backends.cudnn.benchmark", + "torch.backends.cudnn.enabled", + "torch.backends.cudnn.deterministic", +] diff --git a/x2paddle/project_convertor/pytorch/models/__init__.py b/x2paddle/project_convertor/pytorch/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f06ff6ece35d9163c9d3c5f14ee79d3f671b5748 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/models/__init__.py @@ -0,0 +1,22 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from . import vgg +from . import resnet + +from .vgg import * +from .resnet import * + +__all__ = vgg.__all__ +__all__.extend(resnet.__all__) diff --git a/x2paddle/project_convertor/pytorch/models/resnet.py b/x2paddle/project_convertor/pytorch/models/resnet.py new file mode 100644 index 0000000000000000000000000000000000000000..83eb628b04fb659c81e1431933e2d2d68f52c60e --- /dev/null +++ b/x2paddle/project_convertor/pytorch/models/resnet.py @@ -0,0 +1,435 @@ +import paddle +import paddle.nn as nn +from paddle import Tensor +from paddle.utils.download import get_weights_path_from_url +from typing import Type, Any, Callable, Union, List, Optional +from x2paddle import torch2paddle + +__all__ = [ + 'ResNet', 'resnet18', 'resnet34', 'resnet50', 'resnet101', 'resnet152', + 'resnext50_32x4d', 'resnext101_32x8d', 'wide_resnet50_2', 'wide_resnet101_2' +] + +model_urls = { + 'resnet18': + 'https://x2paddle.bj.bcebos.com/vision/models/resnet18-pt.pdparams', + 'resnet34': + 'https://x2paddle.bj.bcebos.com/vision/models/resnet34-pt.pdparams', + 'resnet50': + 'https://x2paddle.bj.bcebos.com/vision/models/resnet50-pt.pdparams', + 'resnet101': + 'https://x2paddle.bj.bcebos.com/vision/models/resnet101-pt.pdparams', + 'resnet152': + 'https://x2paddle.bj.bcebos.com/vision/models/resnet152-pt.pdparams', + 'resnext50_32x4d': + 'https://x2paddle.bj.bcebos.com/vision/models/resnext50_32x4d-pt.pdparams', + 'resnext101_32x8d': + 'https://x2paddle.bj.bcebos.com/vision/models/resnext101_32x8d-pt.pdparams', + 'wide_resnet50_2': + 'https://x2paddle.bj.bcebos.com/vision/models/wide_resnet50_2-pt.pdparams', + 'wide_resnet101_2': + 'https://x2paddle.bj.bcebos.com/vision/models/wide_resnet101_2-pt.pdparams', +} + + +def conv3x3(in_planes: int, + out_planes: int, + stride: int=1, + groups: int=1, + dilation: int=1) -> nn.Conv2D: + """3x3 convolution with padding""" + return nn.Conv2D( + in_planes, + out_planes, + kernel_size=3, + stride=stride, + padding=dilation, + groups=groups, + bias_attr=False, + dilation=dilation) + + +def conv1x1(in_planes: int, out_planes: int, stride: int=1) -> nn.Conv2D: + """1x1 convolution""" + return nn.Conv2D( + in_planes, out_planes, kernel_size=1, stride=stride, bias_attr=False) + + +class BasicBlock(nn.Layer): + expansion: int = 1 + + def __init__(self, + inplanes: int, + planes: int, + stride: int=1, + downsample: Optional[nn.Layer]=None, + groups: int=1, + base_width: int=64, + dilation: int=1, + norm_layer: Optional[Callable[..., nn.Layer]]=None) -> None: + super(BasicBlock, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2d + if groups != 1 or base_width != 64: + raise ValueError( + 'BasicBlock only supports groups=1 and base_width=64') + if dilation > 1: + raise NotImplementedError( + "Dilation > 1 not supported in BasicBlock") + # Both self.conv1 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv3x3(inplanes, planes, stride) + self.bn1 = norm_layer(planes) + self.relu = nn.ReLU() + self.conv2 = conv3x3(planes, planes) + self.bn2 = norm_layer(planes) + self.downsample = downsample + self.stride = stride + + def forward(self, x: Tensor) -> Tensor: + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + + +class Bottleneck(nn.Layer): + # Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2) + # while original implementation places the stride at the first 1x1 convolution(self.conv1) + # according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385. + # This variant is also known as ResNet V1.5 and improves accuracy according to + # https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch. + + expansion: int = 4 + + def __init__(self, + inplanes: int, + planes: int, + stride: int=1, + downsample: Optional[nn.Layer]=None, + groups: int=1, + base_width: int=64, + dilation: int=1, + norm_layer: Optional[Callable[..., nn.Layer]]=None) -> None: + super(Bottleneck, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2D + width = int(planes * (base_width / 64.)) * groups + # Both self.conv2 and self.downsample layers downsample the input when stride != 1 + self.conv1 = conv1x1(inplanes, width) + self.bn1 = norm_layer(width) + self.conv2 = conv3x3(width, width, stride, groups, dilation) + self.bn2 = norm_layer(width) + self.conv3 = conv1x1(width, planes * self.expansion) + self.bn3 = norm_layer(planes * self.expansion) + self.relu = nn.ReLU() + self.downsample = downsample + self.stride = stride + + def forward(self, x: Tensor) -> Tensor: + identity = x + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + if self.downsample is not None: + identity = self.downsample(x) + + out += identity + out = self.relu(out) + + return out + + +class ResNet(nn.Layer): + def __init__(self, + block: Type[Union[BasicBlock, Bottleneck]], + layers: List[int], + num_classes: int=1000, + zero_init_residual: bool=False, + groups: int=1, + width_per_group: int=64, + replace_stride_with_dilation: Optional[List[bool]]=None, + norm_layer: Optional[Callable[..., nn.Layer]]=None) -> None: + super(ResNet, self).__init__() + if norm_layer is None: + norm_layer = nn.BatchNorm2D + self._norm_layer = norm_layer + + self.inplanes = 64 + self.dilation = 1 + if replace_stride_with_dilation is None: + # each element in the tuple indicates if we should replace + # the 2x2 stride with a dilated convolution instead + replace_stride_with_dilation = [False, False, False] + if len(replace_stride_with_dilation) != 3: + raise ValueError("replace_stride_with_dilation should be None " + "or a 3-element tuple, got {}".format( + replace_stride_with_dilation)) + self.groups = groups + self.base_width = width_per_group + self.conv1 = nn.Conv2D( + 3, + self.inplanes, + kernel_size=7, + stride=2, + padding=3, + bias_attr=False) + self.bn1 = norm_layer(self.inplanes) + self.relu = nn.ReLU() + self.maxpool = nn.MaxPool2D(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, layers[0]) + self.layer2 = self._make_layer( + block, + 128, + layers[1], + stride=2, + dilate=replace_stride_with_dilation[0]) + self.layer3 = self._make_layer( + block, + 256, + layers[2], + stride=2, + dilate=replace_stride_with_dilation[1]) + self.layer4 = self._make_layer( + block, + 512, + layers[3], + stride=2, + dilate=replace_stride_with_dilation[2]) + self.avgpool = nn.AdaptiveAvgPool2D((1, 1)) + self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.sublayers(): + if isinstance(m, nn.Conv2D): + torch2paddle.kaiming_normal_( + m.weight, mode='fan_out', nonlinearity='relu') + elif isinstance(m, (nn.BatchNorm2D, nn.GroupNorm)): + torch2paddle.constant_init_(m.weight, 1) + torch2paddle.constant_init_(m.bias, 0) + + # Zero-initialize the last BN in each residual branch, + # so that the residual branch starts with zeros, and each residual block behaves like an identity. + # This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677 + if zero_init_residual: + for m in self.sublayers(): + if isinstance(m, Bottleneck): + torch2paddle.constant_init_(m.bn3.weight, + 0) # type: ignore[arg-type] + elif isinstance(m, BasicBlock): + torch2paddle.constant_init_(m.bn2.weight, + 0) # type: ignore[arg-type] + + def _make_layer(self, + block: Type[Union[BasicBlock, Bottleneck]], + planes: int, + blocks: int, + stride: int=1, + dilate: bool=False) -> nn.Sequential: + norm_layer = self._norm_layer + downsample = None + previous_dilation = self.dilation + if dilate: + self.dilation *= stride + stride = 1 + if stride != 1 or self.inplanes != planes * block.expansion: + downsample = nn.Sequential( + conv1x1(self.inplanes, planes * block.expansion, stride), + norm_layer(planes * block.expansion), ) + + layers = [] + layers.append( + block(self.inplanes, planes, stride, downsample, self.groups, + self.base_width, previous_dilation, norm_layer)) + self.inplanes = planes * block.expansion + for _ in range(1, blocks): + layers.append( + block( + self.inplanes, + planes, + groups=self.groups, + base_width=self.base_width, + dilation=self.dilation, + norm_layer=norm_layer)) + + return nn.Sequential(*layers) + + def _forward_impl(self, x: Tensor) -> Tensor: + # See note [TorchScript super()] + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + + x = self.avgpool(x) + x = paddle.flatten(x, 1) + x = self.fc(x) + + return x + + def forward(self, x: Tensor) -> Tensor: + return self._forward_impl(x) + + +def _resnet(arch: str, + block: Type[Union[BasicBlock, Bottleneck]], + layers: List[int], + pretrained: bool, + **kwargs: Any) -> ResNet: + model = ResNet(block, layers, **kwargs) + if pretrained: + state_dict = get_weights_path_from_url(model_urls[arch]) + model.load_dict(state_dict) + return model + + +def resnet18(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNet-18 model from + `"Deep Residual Learning for Image Recognition" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _resnet('resnet18', BasicBlock, [2, 2, 2, 2], pretrained, **kwargs) + + +def resnet34(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNet-34 model from + `"Deep Residual Learning for Image Recognition" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _resnet('resnet34', BasicBlock, [3, 4, 6, 3], pretrained, **kwargs) + + +def resnet50(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNet-50 model from + `"Deep Residual Learning for Image Recognition" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, **kwargs) + + +def resnet101(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNet-101 model from + `"Deep Residual Learning for Image Recognition" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, **kwargs) + + +def resnet152(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNet-152 model from + `"Deep Residual Learning for Image Recognition" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _resnet('resnet152', Bottleneck, [3, 8, 36, 3], pretrained, **kwargs) + + +def resnext50_32x4d(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNeXt-50 32x4d model from + `"Aggregated Residual Transformation for Deep Neural Networks" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + kwargs['groups'] = 32 + kwargs['width_per_group'] = 4 + return _resnet('resnext50_32x4d', Bottleneck, [3, 4, 6, 3], pretrained, + **kwargs) + + +def resnext101_32x8d(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""ResNeXt-101 32x8d model from + `"Aggregated Residual Transformation for Deep Neural Networks" `_. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + kwargs['groups'] = 32 + kwargs['width_per_group'] = 8 + return _resnet('resnext101_32x8d', Bottleneck, [3, 4, 23, 3], pretrained, + **kwargs) + + +def wide_resnet50_2(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""Wide ResNet-50-2 model from + `"Wide Residual Networks" `_. + + The model is the same as ResNet except for the bottleneck number of channels + which is twice larger in every block. The number of channels in outer 1x1 + convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048 + channels, and in Wide ResNet-50-2 has 2048-1024-2048. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + kwargs['width_per_group'] = 64 * 2 + return _resnet('wide_resnet50_2', Bottleneck, [3, 4, 6, 3], pretrained, + **kwargs) + + +def wide_resnet101_2(pretrained: bool=False, progress: bool=True, + **kwargs: Any) -> ResNet: + r"""Wide ResNet-101-2 model from + `"Wide Residual Networks" `_. + + The model is the same as ResNet except for the bottleneck number of channels + which is twice larger in every block. The number of channels in outer 1x1 + convolutions is the same, e.g. last block in ResNet-50 has 2048-512-2048 + channels, and in Wide ResNet-50-2 has 2048-1024-2048. + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + kwargs['width_per_group'] = 64 * 2 + return _resnet('wide_resnet101_2', Bottleneck, [3, 4, 23, 3], pretrained, + **kwargs) diff --git a/x2paddle/project_convertor/pytorch/models/vgg.py b/x2paddle/project_convertor/pytorch/models/vgg.py new file mode 100644 index 0000000000000000000000000000000000000000..5e4316f4daa3829319bbd04757c30917f5f331bf --- /dev/null +++ b/x2paddle/project_convertor/pytorch/models/vgg.py @@ -0,0 +1,202 @@ +import paddle +import paddle.nn as nn +from paddle.utils.download import get_weights_path_from_url +from typing import Union, List, Dict, Any, cast +from x2paddle import torch2paddle + +__all__ = [ + 'VGG', + 'vgg11', + 'vgg11_bn', + 'vgg13', + 'vgg13_bn', + 'vgg16', + 'vgg16_bn', + 'vgg19_bn', + 'vgg19', +] + +model_urls = { + 'vgg11': 'https://x2paddle.bj.bcebos.com/vision/models/vgg11-pt.pdparams', + 'vgg13': 'https://x2paddle.bj.bcebos.com/vision/models/vgg13-pt.pdparams', + 'vgg16': 'https://x2paddle.bj.bcebos.com/vision/models/vgg16-pt.pdparams', + 'vgg19': 'https://x2paddle.bj.bcebos.com/vision/models/vgg19-pt.pdparams', + 'vgg11_bn': + 'https://x2paddle.bj.bcebos.com/vision/models/vgg11_bn-pt.pdparams', + 'vgg13_bn': + 'https://x2paddle.bj.bcebos.com/vision/models/vgg13_bn-pt.pdparams', + 'vgg16_bn': + 'https://x2paddle.bj.bcebos.com/vision/models/vgg16_bn-pt.pdparams', + 'vgg19_bn': + 'https://x2paddle.bj.bcebos.com/vision/models/vgg19_bn-pt.pdparams', +} + + +class VGG(nn.Layer): + def __init__(self, features, num_classes=1000, init_weights=True): + super(VGG, self).__init__() + self.features = features + self.avgpool = nn.AdaptiveAvgPool2D((7, 7)) + self.classifier = nn.Sequential( + nn.Linear(512 * 7 * 7, 4096), + nn.ReLU(), + nn.Dropout(), + nn.Linear(4096, 4096), + nn.ReLU(), + nn.Dropout(), + nn.Linear(4096, num_classes), ) + if init_weights: + self._initialize_weights() + + def forward(self, x): + x = self.features(x) + x = self.avgpool(x) + x = paddle.flatten(x, 1) + x = self.classifier(x) + return x + + def _initialize_weights(self): + for m in self.modules(): + if isinstance(m, nn.Conv2D): + torch2paddle.kaiming_normal_( + m.weight, mode='fan_out', nonlinearity='relu') + if m.bias is not None: + torch2paddle.constant_init_(m.bias, 0) + elif isinstance(m, nn.BatchNorm2D): + torch2paddle.constant_init_(m.weight, 1) + torch2paddle.constant_init_(m.bias, 0) + elif isinstance(m, nn.Linear): + torch2paddle.normal_init_(m.weight, 0, 0.01) + torch2paddle.constant_init_(m.bias, 0) + + +def make_layers(cfg: List[Union[str, int]], + batch_norm: bool=False) -> nn.Sequential: + layers: List[nn.Layer] = [] + in_channels = 3 + for v in cfg: + if v == 'M': + layers += [nn.MaxPool2D(kernel_size=2, stride=2)] + else: + v = cast(int, v) + conv2d = nn.Conv2D(in_channels, v, kernel_size=3, padding=1) + if batch_norm: + layers += [conv2d, nn.BatchNorm2D(v), nn.ReLU()] + else: + layers += [conv2d, nn.ReLU()] + in_channels = v + return nn.Sequential(*layers) + + +cfgs: Dict[str, List[Union[str, int]]] = { + 'A': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], + 'B': + [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'], + 'D': [ + 64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, + 512, 512, 'M' + ], + 'E': [ + 64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, + 'M', 512, 512, 512, 512, 'M' + ], +} + + +def _vgg(arch: str, cfg: str, batch_norm: bool, pretrained: bool, + **kwargs: Any) -> VGG: + if pretrained: + kwargs['init_weights'] = False + model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs) + if pretrained: + state_dict = get_weights_path_from_url(model_urls[arch]) + model.load_dict(state_dict) + return model + + +def vgg11(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 11-layer model (configuration "A") from + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg11', 'A', False, pretrained, **kwargs) + + +def vgg11_bn(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 11-layer model (configuration "A") with batch normalization + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg11_bn', 'A', True, pretrained, **kwargs) + + +def vgg13(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 13-layer model (configuration "B") + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg13', 'B', False, pretrained, **kwargs) + + +def vgg13_bn(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 13-layer model (configuration "B") with batch normalization + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg13_bn', 'B', True, pretrained, **kwargs) + + +def vgg16(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 16-layer model (configuration "D") + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg16', 'D', False, pretrained, **kwargs) + + +def vgg16_bn(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 16-layer model (configuration "D") with batch normalization + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg16_bn', 'D', True, pretrained, **kwargs) + + +def vgg19(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 19-layer model (configuration "E") + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg19', 'E', False, pretrained, **kwargs) + + +def vgg19_bn(pretrained: bool=False, progress: bool=True, **kwargs: Any) -> VGG: + r"""VGG 19-layer model (configuration 'E') with batch normalization + `"Very Deep Convolutional Networks For Large-Scale Image Recognition" `._ + + Args: + pretrained (bool): If True, returns a model pre-trained on ImageNet + progress (bool): If True, displays a progress bar of the download to stderr + """ + return _vgg('vgg19_bn', 'E', True, pretrained, **kwargs) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/__init__.py b/x2paddle/project_convertor/pytorch/torch2paddle/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4733ace140f79fe7cd170dae520a781ee4570091 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/__init__.py @@ -0,0 +1,30 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from .io import * +from .layer import * +from .tensor import * +from .optimizer import * +from .nn import * +from .nn_utils import * +from .nn_functional import * +from .nn_init import * +from .varbase import * +from .vision_transforms import * +from .device import * +from .vision_utils import * +from .vision_datasets import * +from .ops import * +from .learning_rate_scheduler import * +from .parambase import * diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/device.py b/x2paddle/project_convertor/pytorch/torch2paddle/device.py new file mode 100644 index 0000000000000000000000000000000000000000..173c7fb0c7edc95460a03bfe0e84761fb6123199 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/device.py @@ -0,0 +1,26 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import os +import paddle + + +def device_count(): + gpu_useful = paddle.get_device().startswith("gpu") + if gpu_useful: + device_str = os.environ["CUDA_VISIBLE_DEVICES"] + seg = device_str.split(",") + return len(seg) + else: + return 0 diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/distributed.py b/x2paddle/project_convertor/pytorch/torch2paddle/distributed.py new file mode 100644 index 0000000000000000000000000000000000000000..7890eee76f45cd98d15e8877e03ab109f9e2af94 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/distributed.py @@ -0,0 +1,28 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import os +import paddle + + +def init_process_group(backend, + init_method=None, + timeout=datetime.timedelta(0, 1800), + world_size=-1, + rank=-1, + store=None, + group_name=''): + paddle.distributed.init_parallel_env() + os.environ['PADDLE_TRAINERS_NUM'] = world_size if world_size > 0 else 1 + os.environ['PADDLE_TRAINER_ID'] = rank if rank > 0 else 1 diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/io.py b/x2paddle/project_convertor/pytorch/torch2paddle/io.py new file mode 100644 index 0000000000000000000000000000000000000000..0ca6c6a2bcb8bfd5920ef1f719e02fbebb629b3f --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/io.py @@ -0,0 +1,189 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from paddle.io import Dataset, IterableDataset +import paddle +import warnings +import bisect + + +class ConcatDataset(Dataset): + r"""Dataset as a concatenation of multiple datasets. + + This class is useful to assemble different existing datasets. + + Arguments: + datasets (sequence): List of datasets to be concatenated + """ + + @staticmethod + def cumsum(sequence): + r, s = [], 0 + for e in sequence: + l = len(e) + r.append(l + s) + s += l + return r + + def __init__(self, datasets): + super(ConcatDataset, self).__init__() + assert len(datasets) > 0, 'datasets should not be an empty iterable' + self.datasets = list(datasets) + for d in self.datasets: + assert not isinstance( + d, IterableDataset + ), "ConcatDataset does not support IterableDataset" + self.cumulative_sizes = self.cumsum(self.datasets) + + def __len__(self): + return self.cumulative_sizes[-1] + + def __getitem__(self, idx): + if idx < 0: + if -idx > len(self): + raise ValueError( + "absolute value of index should not exceed dataset length") + idx = len(self) + idx + dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) + if dataset_idx == 0: + sample_idx = idx + else: + sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] + return self.datasets[dataset_idx][sample_idx] + + @property + def cummulative_sizes(self): + warnings.warn( + "cummulative_sizes attribute is renamed to " + "cumulative_sizes", + DeprecationWarning, + stacklevel=2) + return self.cumulative_sizes + + +def _accumulate(iterable, fn=lambda x, y: x + y): + # _accumulate([1,2,3,4,5]) --> 1 3 6 10 15 + # _accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120 + it = iter(iterable) + try: + total = next(it) + except StopIteration: + return + yield total + for element in it: + total = fn(total, element) + yield total + + +class Subset(Dataset): + r""" + Subset of a dataset at specified indices. + + Arguments: + dataset (Dataset): The whole Dataset + indices (sequence): Indices in the whole set selected for subset + """ + + def __init__(self, dataset, indices): + self.dataset = dataset + self.indices = indices + + def __getitem__(self, idx): + return self.dataset[self.indices[idx]] + + def __len__(self): + return len(self.indices) + + +def random_split(dataset, lengths, generator=None): + r""" + Randomly split a dataset into non-overlapping new datasets of given lengths. + Optionally fix the generator for reproducible results, e.g.: + + >>> random_split(range(10), [3, 7], generator=torch.Generator().manual_seed(42)) + + Arguments: + dataset (Dataset): Dataset to be split + lengths (sequence): lengths of splits to be produced + generator (Generator): from torch import default_generator, which is not use in paddle. + """ + if sum(lengths) != len(dataset): + raise ValueError( + "Sum of input lengths does not equal the length of the input dataset!" + ) + + indices = paddle.randperm(sum(lengths)) + return [ + Subset(dataset, indices[offset - length:offset]) + for offset, length in zip(_accumulate(lengths), lengths) + ] + + +setattr(paddle.io, "random_split", random_split) + + +class DataLoader(paddle.io.DataLoader): + def __init__(self, + dataset, + batch_size=1, + shuffle=False, + sampler=None, + batch_sampler=None, + num_workers=0, + collate_fn=None, + pin_memory=False, + drop_last=False, + timeout=0, + worker_init_fn=None, + multiprocessing_context=None, + generator=None): + if isinstance(dataset[0], (tuple, list)): + return_list = True + else: + return_list = False + return_list = True + super().__init__( + dataset, + feed_list=None, + places=None, + return_list=return_list, + batch_sampler=batch_sampler, + batch_size=batch_size, + shuffle=shuffle, + drop_last=drop_last, + collate_fn=collate_fn, + num_workers=num_workers, + use_buffer_reader=True, + use_shared_memory=False, + timeout=timeout, + worker_init_fn=worker_init_fn) + if sampler is not None: + seld.batch_sampler.sampler = sampler + + +class DistributedSampler(paddle.io.DistributedBatchSampler): + def __init__(self, + dataset, + num_replicas=None, + rank=None, + shuffle=True, + seed=0, + drop_last=False): + super().__init__( + dataset=dataset, + batch_size=1, + num_replicas=num_replicas, + rank=rank, + shuffle=shuffle, + drop_last=drop_last) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/layer.py b/x2paddle/project_convertor/pytorch/torch2paddle/layer.py new file mode 100644 index 0000000000000000000000000000000000000000..d6a6e8414c2eb829a8a52e06912aa5e56be65a00 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/layer.py @@ -0,0 +1,86 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +from functools import partial + + +def add_layer_function(func): + setattr(paddle.nn.Layer, func.__name__, func) + + +@property +def module(self): + if hasattr(self, "_layers"): + return self._layers + else: + return self + + +setattr(paddle.nn.Layer, "module", module) + + +@add_layer_function +def load_state_dict(self, state_dict, strict=True): + for key, param in self.state_dict().items(): + state = state_dict.get(key, None) + if state is None: + if key.endswith(".scale"): + state_dict[key] = state_dict.pop(key[0:-5] + "weight") + self.set_state_dict(state_dict) + + +@add_layer_function +def to(self, *args, **kwargs): + # TODO(syf): for dtype + return self + + +@add_layer_function +def cuda(self): + return self + + +@add_layer_function +def apply(self, func): + func(self) + + +@add_layer_function +def modules(self): + return [self] + self.sublayers() + + +@add_layer_function +def add_module(self, name, module): + self.add_sublayer(name, module) + + +pd_cuda = partial(paddle.nn.Layer.cuda) + + +@add_layer_function +def cuda(self, device=None): + return self + + +pd_train = partial(paddle.nn.Layer.train) + + +@add_layer_function +def train(self, mode=True): + if mode: + return pd_train(self) + else: + return paddle.nn.Layer.eval(self) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/learning_rate_scheduler.py b/x2paddle/project_convertor/pytorch/torch2paddle/learning_rate_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..891d28ca4291ce9001ffc89548fd43785f2dc2c8 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/learning_rate_scheduler.py @@ -0,0 +1,63 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle + + +class ReduceOnPlateau(paddle.optimizer.lr.ReduceOnPlateau): + def __init__(self, + optimizer, + mode='min', + factor=0.1, + patience=10, + threshold=0.0001, + threshold_mode='rel', + cooldown=0, + min_lr=0, + eps=1e-08, + verbose=False): + super().__init__( + learning_rate=0.01, + mode=mode, + factor=factor, + patience=patience, + threshold=threshold, + threshold_mode=threshold_mode, + cooldown=cooldown, + min_lr=min_lr, + epsilon=eps, + verbose=verbose) + optimizer._learning_rate = self + + +class CosineAnnealingDecay(paddle.optimizer.lr.CosineAnnealingDecay): + def __init__(self, + optimizer, + T_max, + eta_min=0, + last_epoch=-1, + verbose=False): + super().__init__(0.01, T_max, eta_min, last_epoch, verbose) + optimizer._learning_rate = self + + +class MultiStepDecay(paddle.optimizer.lr.MultiStepDecay): + def __init__(self, + optimizer, + milestones, + gamma=0.1, + last_epoch=-1, + verbose=False): + super().__init__(0.01, milestones, gamma, last_epoch1, verbose) + optimizer._learning_rate = self diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/nn.py b/x2paddle/project_convertor/pytorch/torch2paddle/nn.py new file mode 100644 index 0000000000000000000000000000000000000000..79b07625a6036fb920d0e1e0418cc94ca37ec9cf --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/nn.py @@ -0,0 +1,572 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +from .utils import * + + +class AvgPool1D(paddle.nn.AvgPool1D): + def __init__(self, + kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None): + super().__init__( + kernel_size=kernel_size, + stride=stride, + padding=padding, + ceil_mode=padding, + exclusive=count_include_pad, + divisor_override=divisor_override) + + +class AvgPool2D(paddle.nn.AvgPool2D): + def __init__(self, + kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None): + super().__init__( + kernel_size=kernel_size, + stride=stride, + padding=padding, + ceil_mode=padding, + exclusive=count_include_pad, + divisor_override=divisor_override) + + +class AvgPool3D(paddle.nn.AvgPool3D): + def __init__(self, + kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None): + super().__init__( + kernel_size=kernel_size, + stride=stride, + padding=padding, + ceil_mode=padding, + exclusive=count_include_pad, + divisor_override=divisor_override) + + +class BatchNorm1D(paddle.nn.BatchNorm1D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) + + +class BatchNorm2D(paddle.nn.BatchNorm2D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) + + +class BatchNorm3D(paddle.nn.BatchNorm3D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) + + +class BCEWithLogitsLoss(paddle.nn.BCEWithLogitsLoss): + def __init__(self, + weight=None, + size_average=None, + reduce=None, + reduction='mean', + pos_weight=None): + super().__init__(weight, reduction=reduction, pos_weight=pos_weight) + + +@property +def in_channels(self): + return self._in_channels + + +setattr(paddle.nn.layer.conv._ConvNd, "in_channels", in_channels) + + +@property +def out_channels(self): + return self._out_channels + + +setattr(paddle.nn.layer.conv._ConvNd, "out_channels", out_channels) + + +@property +def kernel_size(self): + return self._kernel_size + + +setattr(paddle.nn.layer.conv._ConvNd, "kernel_size", kernel_size) + + +@property +def stride(self): + return self._stride + + +setattr(paddle.nn.layer.conv._ConvNd, "stride", stride) + + +@property +def padding(self): + return self._padding + + +setattr(paddle.nn.layer.conv._ConvNd, "padding", padding) + + +@property +def dilation(self): + return self._dilation + + +setattr(paddle.nn.layer.conv._ConvNd, "dilation", dilation) + + +@property +def groups(self): + return self._groups + + +setattr(paddle.nn.layer.conv._ConvNd, "groups", groups) + + +class ConstantPad2D(paddle.nn.Pad2D): + def __init__(self, padding, value): + super().__init__(padding, value=value) + + +class Conv1D(paddle.nn.Conv1D): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + padding_mode='zeros'): + super().__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + padding_mode=padding_mode, + bias_attr=bias if not bias else None) + + +class Conv2D(paddle.nn.Conv2D): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + padding_mode='zeros'): + super().__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + padding_mode=padding_mode, + bias_attr=bias if not bias else None) + + +class Conv3D(paddle.nn.Conv3D): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + dilation=1, + groups=1, + bias=True, + padding_mode='zeros'): + super().__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + dilation=dilation, + groups=groups, + padding_mode=padding_mode, + bias_attr=bias if not bias else None) + + +class Conv2DTranspose(paddle.nn.Conv2DTranspose): + def __init__(self, + in_channels, + out_channels, + kernel_size, + stride=1, + padding=0, + output_padding=0, + groups=1, + bias=True, + dilation=1, + padding_mode='zeros'): + super().__init__( + in_channels, + out_channels, + kernel_size, + stride=stride, + padding=padding, + output_padding=output_padding, + groups=groups, + dilation=dilation, + bias_attr=bias if not bias else None) + assert padding_mode == 'zeros', "The padding_mode must be zero in Conv2DTranspose." + + +class CrossEntropyLoss(paddle.nn.CrossEntropyLoss): + def __init__(self, + weight=None, + size_average=None, + ignore_index=-100, + reduce=None, + reduction='mean'): + super().__init__(weight, reduction=reduction, ignore_index=ignore_index) + + +class Dropout(paddle.nn.Dropout): + def __init__(self, p=0.5, inplace=False): + super().__init__(p) + + +class Embedding(paddle.nn.Embedding): + def __init__(self, + num_embeddings, + embedding_dim, + padding_idx=None, + max_norm=None, + norm_type=2.0, + scale_grad_by_freq=False, + sparse=False, + _weight=None): + super().__init__( + num_embeddings, + embedding_dim, + padding_idx=padding_idx, + sparse=sparse) + assert max_norm is None, "The max_norm must be None in Embedding!" + assert not scale_grad_by_freq, "The scale_grad_by_freq must False None in Embedding!" + + +class Identity(paddle.nn.Layer): + def __init__(self, *args, **kwargs): + super().__init__() + + def forward(self, input): + return input + + +class GroupNorm(paddle.nn.GroupNorm): + def __init__(num_groups, num_channels, eps=1e-05, affine=True): + if not affine: + weight_attr = False + bias_attr = False + else: + weight_attr = None + bias_attr = None + super().__init__(num_groups, num_channels, eps, weight_attr, bias_attr) + + +class InstanceNorm2D(paddle.nn.InstanceNorm2D): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=False, + track_running_stats=False): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr) + + +class KLDivLoss(paddle.nn.Layer): + def __init__(self, + size_average=None, + reduce=None, + reduction='mean', + log_target=False): + super().__init__() + self.reduction = reduction + self.log_target = log_target + + def forward(self, input, target): + if self.log_target: + out = paddle.exp(target) * (target - input) + else: + out_pos = target * (paddle.log(target) - input) + zeros = paddle.zeros_like(out_pos) + out = paddle.where(target > 0, out_pos, zeros) + out_sum = paddle.sum(out) + if self.reduction == "sum": + return out_sum + elif self.reduction == "batchmean": + n = input.shape[0] + return out_sum / n + elif self.reduction == "mean": + return paddle.mean(out) + else: + return out + + +class LayerNorm(paddle.nn.LayerNorm): + def __init__(self, normalized_shape, eps=1e-05, elementwise_affine=True): + if not elementwise_affine: + weight_attr = False + bias_attr = False + else: + weight_attr = None + bias_attr = None + super().__init__(normalized_shape, eps, weight_attr, bias_attr) + + +class Linear(paddle.nn.Linear): + def __init__(self, in_features, out_features, bias=True): + super().__init__( + in_features, out_features, bias_attr=bias if not bias else None) + + +class L1Loss(paddle.nn.L1Loss): + def __init__(self, size_average=None, reduce=None, reduction='mean'): + super().__init__(reduction=reduction) + + +class MaxPool1D(paddle.nn.MaxPool1D): + def __init__(self, + kernel_size, + stride=None, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False): + super().__init__( + kernel_size, + stride=stride, + padding=padding, + ceil_mode=ceil_mode, + return_mask=return_indices) + assert dilation == 1, "The dilation must be 1 in MaxPool1D." + + +class MaxPool2D(paddle.nn.MaxPool2D): + def __init__(self, + kernel_size, + stride=None, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False): + super().__init__( + kernel_size, + stride=stride, + padding=padding, + ceil_mode=ceil_mode, + return_mask=return_indices) + assert dilation == 1, "The dilation must be 1 in MaxPool2D." + + +class MaxPool3D(paddle.nn.MaxPool3D): + def __init__(self, + kernel_size, + stride=None, + padding=0, + dilation=1, + return_indices=False, + ceil_mode=False): + super().__init__( + kernel_size, + stride=stride, + padding=padding, + ceil_mode=ceil_mode, + return_mask=return_indices) + assert dilation == 1, "The dilation must be 1 in MaxPool3D." + + +import paddle +import paddle.nn as nn +TYPE_MAPPER = {"fp16": "float16", "fp32": "float32", "fp64": "float64"} + + +class MaxUnpool2D(paddle.nn.Layer): + def __init__(self, kernel_size, stride=None, padding=0): + super().__init__() + if isinstance(stride, int): + self.kernel_size = (kernel_size, kernel_size) + else: + self.kernel_size = kernel_size + if stride is None: + self.stride = self.kernel_size + else: + if isinstance(stride, int): + self.stride = (stride, stride) + else: + self.stride = stride + if isinstance(padding, int): + self.padding = (padding, padding) + else: + self.padding = padding + + def forward(self, input, indices, output_size=None): + if output_size is None: + n, c, h, w = input.shape + out_h = ( + h - 1 + ) * self.stride[0] - 2 * self.padding[0] + self.kernel_size[0] + out_w = ( + w - 1 + ) * self.stride[1] - 2 * self.padding[1] + self.kernel_size[1] + output_size = (n, c, out_h, out_w) + else: + if len(output_size) == len(self.kernel_size) + 2: + output_size = output_size[2:] + t = str(input.dtype).lower().strip().split(".")[-1] + t = TYPE_MAPPER[t] + out = paddle.zeros(output_size, dtype=t) + flatten_out = paddle.flatten(out) + for i in range(indices.shape[0]): + for j in range(indices.shape[1]): + for k in range(indices.shape[2]): + for m in range(indices.shape[3]): + indices[i, j, k, m] = (out.shape[1] * out.shape[2] * out.shape[3]) * i + \ + (out.shape[2] * out.shape[3]) * j + indices[i, j, k, m] + flatten_indices = paddle.flatten(indices) + flatten_input = paddle.flatten(input) + for i in range(flatten_indices.shape[0]): + flatten_out[flatten_indices[i].tolist()] = flatten_input[i].tolist() + out = paddle.reshape(flatten_out, out.shape) + return out + + +class ReflectionPad2D(paddle.nn.Pad2D): + def __init__(self, padding): + super().__init__(padding, mode="reflect") + + +class ReplicationPad2D(paddle.nn.Pad2D): + def __init__(self, padding): + super().__init__(padding, mode="replicate") + + +class Softmax(paddle.nn.Softmax): + def __init__(self, dim=None): + super().__init__(axis=dim) + + +class SyncBatchNorm(paddle.nn.SyncBatchNorm): + def __init__(self, + num_features, + eps=1e-05, + momentum=0.1, + affine=True, + track_running_stats=True, + process_group=None): + momentum = 1 - momentum + weight_attr = None + bias_attr = None + if not affine: + weight_attr = paddle.ParamAttr(learning_rate=0.0) + bias_attr = paddle.ParamAttr(learning_rate=0.0) + super().__init__( + num_features, + momentum=momentum, + epsilon=eps, + weight_attr=weight_attr, + bias_attr=bias_attr, + use_global_stats=track_running_stats) + + +class ZeroPad2D(paddle.nn.Pad2D): + def __init__(self, padding): + super().__init__(padding) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/nn_functional.py b/x2paddle/project_convertor/pytorch/torch2paddle/nn_functional.py new file mode 100644 index 0000000000000000000000000000000000000000..d378dd3183ebe1734a96a1fdd89a3f1604eeae68 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/nn_functional.py @@ -0,0 +1,141 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +import copy +from .utils import * + + +def binary_cross_entropy_with_logits(input, + target, + weight=None, + size_average=None, + reduce=None, + reduction='mean', + pos_weight=None): + if not reduce or not size_average: + reduction = "sum" + input_t = str(input.dtype).lower().strip().split(".")[-1] + if input_t in TYPE_MAPPER: + input_t = TYPE_MAPPER[input_t] + input_index = TYPE_ORDER.index(input_t) + target_t = str(target.dtype).lower().strip().split(".")[-1] + if target_t in TYPE_MAPPER: + target_t = TYPE_MAPPER[target_t] + target_index = TYPE_ORDER.index(target_t) + if input_index < target_index: + real_type = TYPE_ORDER[target_index] + input = input.cast(real_type) + else: + real_type = TYPE_ORDER[input_index] + target = target.cast(real_type) + return paddle.nn.functional.binary_cross_entropy_with_logits( + input, target, weight, reduction, pos_weight) + + +def avg_pool1d(input, + kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True): + return paddle.nn.functional.avg_pool1d( + input, + kernel_size, + stride=stride, + padding=padding, + ceil_mode=ceil_mode, + exclusive=not count_include_pad) + + +def avg_pool2d(input, + kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None): + return paddle.nn.functional.avg_pool2d( + input, + kernel_size, + stride=stride, + padding=padding, + ceil_mode=ceil_mode, + exclusive=not count_include_pad, + divisor_override=divisor_override) + + +def avg_pool3d(input, + kernel_size, + stride=None, + padding=0, + ceil_mode=False, + count_include_pad=True, + divisor_override=None): + return paddle.nn.functional.avg_pool3d( + input, + kernel_size, + stride=stride, + padding=padding, + ceil_mode=ceil_mode, + exclusive=not count_include_pad, + divisor_override=divisor_override) + + +def dropout(input, p=0.5, training=True, inplace=False): + return paddle.nn.functional.dropout(input, p=p, training=training) + + +def interpolate(input, + size=None, + scale_factor=None, + mode='nearest', + align_corners=None, + recompute_scale_factor=None): + return paddle.nn.functional.interpolate( + input, + size=size, + scale_factor=scale_factor, + mode=mode, + align_corners=align_corners) + + +def leaky_relu(input, negative_slope=0.01, inplace=False): + return paddle.nn.functional.leaky_relu(input, negative_slope=negative_slope) + + +def log_softmax(input, dim=None, _stacklevel=3, dtype=None): + return paddle.nn.functional.log_softmax(input, axis=dim, dtype=None) + + +def mse_loss(input, target, size_average=None, reduce=None, reduction='mean'): + paddle.nn.functional.mse_loss(input, target, reduction=reduction) + + +def relu(input, inplace=False): + return paddle.nn.functional.relu(input) + + +def smooth_l1_loss(input, + target, + size_average=None, + reduce=None, + reduction='mean', + beta=1.0): + paddle.nn.functional.smooth_l1_loss( + input, target, reduction=reduction, delta=beta) + + +def softmax(input, dim=None, _stacklevel=3, dtype=None): + return paddle.nn.functional.softmax(input, axis=dim, dtype=dtype) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/nn_init.py b/x2paddle/project_convertor/pytorch/torch2paddle/nn_init.py new file mode 100644 index 0000000000000000000000000000000000000000..01d9ccb4ff1a901ea13332895083668440ad49c2 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/nn_init.py @@ -0,0 +1,317 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import math +from functools import reduce +import paddle +from paddle.fluid import framework +from paddle.fluid.core import VarDesc +from paddle.fluid.initializer import XavierInitializer, MSRAInitializer +from paddle.fluid.data_feeder import check_variable_and_dtype + + +def _calculate_fan_in_and_fan_out(var): + dimensions = var.dim() + if dimensions < 2: + raise ValueError( + "Fan in and fan out can not be computed for var with fewer than 2 dimensions" + ) + num_input_fmaps = var.shape[0] + num_output_fmaps = var.shape[1] + receptive_field_size = 1 + if var.dim() > 2: + receptive_field_size = reduce(lambda x, y: x * y, var.shape[2:]) + fan_in = num_input_fmaps * receptive_field_size + fan_out = num_output_fmaps * receptive_field_size + return fan_in, fan_out + + +def _calculate_correct_fan(var, mode): + mode = mode.lower() + valid_modes = ['fan_in', 'fan_out'] + if mode not in valid_modes: + raise ValueError("Mode {} not supported, please use one of {}".format( + mode, valid_modes)) + fan_in, fan_out = _calculate_fan_in_and_fan_out(var) + return fan_in if mode == 'fan_in' else fan_out + + +def _calculate_gain(nonlinearity, param=None): + linear_fns = [ + 'linear', 'conv1d', 'conv2d', 'conv3d', 'conv_transpose1d', + 'conv_transpose2d', 'conv_transpose3d' + ] + if nonlinearity in linear_fns or nonlinearity == 'sigmoid': + return 1 + elif nonlinearity == 'tanh': + return 5.0 / 3 + elif nonlinearity == 'relu': + return math.sqrt(2.0) + elif nonlinearity == 'leaky_relu': + if param is None: + negative_slope = 0.01 + elif not isinstance(param, bool) and isinstance( + param, int) or isinstance(param, float): + # True/False are instances of int, hence check above + negative_slope = param + else: + raise ValueError("negative_slope {} not a valid number".format( + param)) + return math.sqrt(2.0 / (1 + negative_slope**2)) + elif nonlinearity == 'selu': + return 3.0 / 4 # Value found empirically (https://github.com/pytorch/pytorch/pull/50664) + else: + raise ValueError("Unsupported nonlinearity {}".format(nonlinearity)) + + +class KaimingNormal(MSRAInitializer): + def __init__(self, a=0, mode='fan_in', nonlinearity='leaky_relu'): + super(KaimingNormal, self).__init__(uniform=False, fan_in=None, seed=0) + self.a = a + self.mode = mode + self.nonlinearity = nonlinearity + + def __call__(self, var, block=None): + """Initialize the input tensor with MSRA initialization. + Args: + var(Tensor): Tensor that needs to be initialized. + block(Block, optional): The block in which initialization ops + should be added. Used in static graph only, default None. + Returns: + The initialization op + """ + block = self._check_block(block) + + assert isinstance(var, framework.Variable) + assert isinstance(block, framework.Block) + f_in, f_out = self._compute_fans(var) + + if self._seed == 0: + self._seed = block.program.random_seed + + # to be compatible of fp16 initalizers + if var.dtype == VarDesc.VarType.FP16: + out_dtype = VarDesc.VarType.FP32 + out_var = block.create_var( + name=unique_name.generate(".".join( + ['masra_init', var.name, 'tmp'])), + shape=var.shape, + dtype=out_dtype, + type=VarDesc.VarType.LOD_TENSOR, + persistable=False) + else: + out_dtype = var.dtype + out_var = var + + fan = _calculate_correct_fan(var, self.mode) + gain = _calculate_gain(self.nonlinearity, self.a) + std = gain / math.sqrt(fan) + op = block._prepend_op( + type="gaussian_random", + outputs={"Out": out_var}, + attrs={ + "shape": out_var.shape, + "dtype": int(out_dtype), + "mean": 0.0, + "std": std, + "seed": self._seed + }, + stop_gradient=True) + + if var.dtype == VarDesc.VarType.FP16: + block.append_op( + type="cast", + inputs={"X": out_var}, + outputs={"Out": var}, + attrs={"in_dtype": out_var.dtype, + "out_dtype": var.dtype}) + + if not framework.in_dygraph_mode(): + var.op = op + return op + + +def kaiming_normal_(param, a=0, mode='fan_in', nonlinearity='leaky_relu'): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=KaimingNormal( + a=a, mode=mode, nonlinearity=nonlinearity)) + paddle.assign(param, replaced_param) + + +class XavierNormal(XavierInitializer): + def __init__(self, gain=1.0): + super(XavierNormal, self).__init__( + uniform=True, fan_in=None, fan_out=None, seed=0) + self._gain = gain + + def __call__(self, var, block=None): + block = self._check_block(block) + assert isinstance(block, framework.Block) + check_variable_and_dtype(var, "Out", ["float16", "float32", "float64"], + "xavier_init") + + fan_in, fan_out = _calculate_fan_in_and_fan_out(var) + + if self._seed == 0: + self._seed = block.program.random_seed + + # to be compatible of fp16 initalizers + if var.dtype == VarDesc.VarType.FP16: + out_dtype = VarDesc.VarType.FP32 + out_var = block.create_var( + name=unique_name.generate(".".join( + ['xavier_init', var.name, 'tmp'])), + shape=var.shape, + dtype=out_dtype, + type=VarDesc.VarType.LOD_TENSOR, + persistable=False) + else: + out_dtype = var.dtype + out_var = var + + std = self._gain * math.sqrt(2.0 / float(fan_in + fan_out)) + op = block._prepend_op( + type="uniform_random", + inputs={}, + outputs={"Out": out_var}, + attrs={ + "shape": out_var.shape, + "dtype": out_dtype, + "min": 0, + "max": std, + "seed": self._seed + }, + stop_gradient=True) + if var.dtype == VarDesc.VarType.FP16: + block.append_op( + type="cast", + inputs={"X": out_var}, + outputs={"Out": var}, + attrs={"in_dtype": out_var.dtype, + "out_dtype": var.dtype}) + if not framework.in_dygraph_mode(): + var.op = op + return op + + +def xavier_normal_(param, gain=1.0): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=XavierNormal(gain=gain)) + paddle.assign(param, replaced_param) + + +class XavierUniform(XavierInitializer): + def __init__(self, gain=1.0): + super(XavierUniform, self).__init__( + uniform=True, fan_in=None, fan_out=None, seed=0) + self._gain = gain + + def __call__(self, var, block=None): + block = self._check_block(block) + assert isinstance(block, framework.Block) + check_variable_and_dtype(var, "Out", ["float16", "float32", "float64"], + "xavier_init") + + fan_in, fan_out = _calculate_fan_in_and_fan_out(var) + + if self._seed == 0: + self._seed = block.program.random_seed + + # to be compatible of fp16 initalizers + if var.dtype == VarDesc.VarType.FP16: + out_dtype = VarDesc.VarType.FP32 + out_var = block.create_var( + name=unique_name.generate(".".join( + ['xavier_init', var.name, 'tmp'])), + shape=var.shape, + dtype=out_dtype, + type=VarDesc.VarType.LOD_TENSOR, + persistable=False) + else: + out_dtype = var.dtype + out_var = var + + std = self._gain * math.sqrt(2.0 / float(fan_in + fan_out)) + limit = math.sqrt(3.0) * std + op = block._prepend_op( + type="uniform_random", + inputs={}, + outputs={"Out": out_var}, + attrs={ + "shape": out_var.shape, + "dtype": out_dtype, + "min": -limit, + "max": limit, + "seed": self._seed + }, + stop_gradient=True) + if var.dtype == VarDesc.VarType.FP16: + block.append_op( + type="cast", + inputs={"X": out_var}, + outputs={"Out": var}, + attrs={"in_dtype": out_var.dtype, + "out_dtype": var.dtype}) + if not framework.in_dygraph_mode(): + var.op = op + return op + + +def xavier_uniform_(param, gain=1.0): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=XavierUniform(gain=gain)) + paddle.assign(param, replaced_param) + + +def constant_init_(param, val): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.full(param.shape, val, param.dtype))) + paddle.assign(param, replaced_param) + + +def normal_init_(param, mean=0.0, std=1.0): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.normal( + mean=mean, std=std, shape=param.shape))) + paddle.assign(param, replaced_param) + + +def ones_init_(param): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.ones(param.shape, param.dtype))) + paddle.assign(param, replaced_param) + + +def zeros_init_(param): + replaced_param = paddle.create_parameter( + shape=param.shape, + dtype=param.dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.zeros(param.shape, param.dtype))) + paddle.assign(param, replaced_param) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/nn_utils.py b/x2paddle/project_convertor/pytorch/torch2paddle/nn_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a0107e2d618412fe08660c510cb09f40122ecf4c --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/nn_utils.py @@ -0,0 +1,56 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +from .utils import * + + +def clip_grad_value_(parameters, clip_value): + r"""Clips gradient of an iterable of parameters at specified value. + + Gradients are modified in-place. + + Arguments: + parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a + single Tensor that will have gradients normalized + clip_value (float or int): maximum allowed value of the gradients. + The gradients are clipped in the range + :math:`\left[\text{-clip\_value}, \text{clip\_value}\right]` + """ + if isinstance(parameters, paddle.Tensor): + parameters = [parameters] + clip_value = float(clip_value) + for p in filter(lambda p: p.grad is not None, parameters): + paddle.clip(p.grad, min=-clip_value, max=clip_value) + + +def spectral_norm(module, + name='weight', + n_power_iterations=1, + eps=1e-12, + dim=None): + input = getattr(module, name) + if dim is None: + if isinstance(module, + (paddle.nn.Conv1DTranspose, pdddle.nn.Conv2DTranspose, + paddle.nn.Conv3DTranspose)): + dim = 1 + else: + dim = 0 + t = str(input.dtype).lower().strip().split(".")[-1] + t = TYPE_MAPPER[t] + spectral_norm = paddle.nn.SpectralNorm( + input.shape, dim=dim, power_iters=n_power_iterations, eps=eps, dtype=t) + out = spectral_norm(input) + return out diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/ops.py b/x2paddle/project_convertor/pytorch/torch2paddle/ops.py new file mode 100644 index 0000000000000000000000000000000000000000..e437a51deabdf3c9efeb6686a0c8a098b5c3a61c --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/ops.py @@ -0,0 +1,371 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +import copy +import warnings +from .utils import * + + +def abs(input, *, out=None): + return paddle.abs(input) + + +def add(input, other, *, out=None): + return paddle.add(input, other) + + +def arange(start, + end, + step=1, + *, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if requires_grad: + return paddle.arange(start, end, step, dtype).requires_grad_(True) + else: + return paddle.arange(start, end, step, dtype) + + +def clip(input, min, max, *, out=None): + return paddle.clip(input, min, max) + + +def concat(tensors, dim=0): + x = tensors + last_index = -1 + for ele in x: + t = str(ele.dtype).lower().strip().split(".")[-1] + if t in TYPE_MAPPER: + t = TYPE_MAPPER[t] + index = TYPE_ORDER.index(t) + if last_index < index: + last_index = index + real_type = TYPE_ORDER[last_index] + x = list(x) + for i in range(len(x)): + x[i] = x[i].cast(real_type) + return paddle.concat(x, dim) + + +def create_tensor(*size): + if len(size) > 1: + return paddle.zeros(size, dtype="float32") + else: + return paddle.to_tensor(size[0]) + + +def create_float32_tensor(*size): + if len(size) > 1: + return paddle.zeros(size, dtype="float32") + else: + out = paddle.to_tensor(size[0]) + out = paddle.cast(out, "float32") + return out + + +def create_uint8_tensor(*size): + if len(size) > 1: + return paddle.zeros(size, dtype="uint8") + else: + out = paddle.to_tensor(size[0]) + out = paddle.cast(out, "uint8") + return out + + +def exp(input, *, out=None): + return paddle.exp(input) + + +def full(size, + fill_value, + *, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if requires_grad: + return paddle.full(size, fill_value, dtype).requires_grad_(True) + else: + return paddle.full(size, fill_value, dtype) + + +def full_like(input, + fill_value, + *, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=None): + if requires_grad: + return paddle.full_like(input, fill_value, dtype).requires_grad_(True) + else: + return paddle.full_like(input, fill_value, dtype) + + +def linspace(start, + end, + steps, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if requires_grad: + return paddle.linspace(start, end, step, dtype).requires_grad_(True) + else: + return paddle.linspace(start, end, step, dtype) + + +def load(f, map_location=None, pickle_module=None, **pickle_load_args): + return paddle.load(f) + + +def load_state_dict_from_url(url, + model_dir=None, + map_location=None, + progress=True, + check_hash=False, + file_name=None): + return paddle.load(url) + + +def load_url(url, + model_dir=None, + map_location=None, + progress=True, + check_hash=False, + file_name=None): + return paddle.load(url) + + +def log(input, *, out=None): + return paddle.log(input) + + +def logical_and(input, other, *, out=None): + return paddle.logical_and(input, other, out) + + +def logical_not(input, *, out=None): + return paddle.logical_not(input, out) + + +def logical_or(input, other, *, out=None): + return paddle.logical_or(input, other, out) + + +def logical_xor(input, other, *, out=None): + return paddle.logical_xor(input, other, out) + + +def matmul(input, other, *, out=None): + return paddle.matmul(input, other) + + +def mul(input, other, *, out=None): + return paddle.multiply(input, other) + + +def max(input, dim_other=None, keepdim=False, *, out=None): + if dim_other is None: + return paddle.max(input) + elif isinstance(dim_other, paddle.Tensor): + return paddle.maximum(input, dim_other) + else: + return paddle.max(input, axis=dim_other, keepdim=keepdim) + + +def mean(input, dim=None, keepdim=False, *, out=None): + if dim is None: + warnings.warn('The output of paddle.mean is not scalar!') + return paddle.mean(input) + else: + return paddle.mean(input, axis=dim, keepdim=keepdim) + + +def min(input, dim_other=None, keepdim=False, *, out=None): + if dim_other is None: + return paddle.min(input) + elif isinstance(dim_other, paddle.Tensor): + return paddle.minimum(input, dim_other) + else: + return paddle.min(input, axis=dim_other, keepdim=keepdim) + + +def ones(*size, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if len(size) == 1 and isinstance(size[0], (tuple, list)): + shape = size[0] + else: + shape = size + if requires_grad: + return paddle.ones(shape, dtype).requires_grad_(True) + else: + return paddle.ones(shape, dtype) + + +def ones_like(input, + *, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=None): + if requires_grad: + return paddle.ones_like(input, dtype).requires_grad_(True) + else: + return paddle.ones_like(input, dtype) + + +def set_cuda_device(device): + if isinstance(device, int): + return paddle.set_device("gpu:{}".format(device)) + else: + return paddle.set_device("gpu") + + +def rand(*size, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if len(size) == 1 and isinstance(size[0], (tuple, list)): + shape = size[0] + else: + shape = size + if requires_grad: + return paddle.rand(shape, dtype).requires_grad_(True) + else: + return paddle.rand(shape, dtype) + + +def randn(*size, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if len(size) == 1 and isinstance(size[0], (tuple, list)): + shape = size[0] + else: + shape = size + if requires_grad: + return paddle.randn(shape, dtype).requires_grad_(True) + else: + return paddle.randn(shape, dtype) + + +def randn_like(input, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=None): + shape = input.shape + if requires_grad: + return paddle.randn(shape, dtype).requires_grad_(True) + else: + return paddle.randn(shape, dtype) + + +def randperm(n, + *, + generator=None, + out=None, + dtype="int64", + layout=None, + device=None, + requires_grad=False, + pin_memory=False): + if requires_grad: + return paddle.randperm(n, dtype).requires_grad_(True) + else: + return paddle.randperm(n, dtype) + + +def save(obj, f, pickle_module=None, pickle_protocol=2): + return paddle.save(obj, f, pickle_protocol=pickle_protocol) + + +def split(tensor, split_size_or_sections, dim=0): + return paddle.split(tensor, split_size_or_sections, dim) + + +def sqrt(input, *, out=None): + return paddle.sqrt(input) + + +def stack(tensors, dim=0, *, out=None): + return paddle.stack(tensors, dim) + + +def sum(input, dim=None, keepdim=False, *, out=None): + if dim is None: + warnings.warn('The output of paddle.sum is not scalar!') + return paddle.sum(input) + else: + return paddle.sum(input, axis=dim, keepdim=keepdim) + + +def unsqueeze(input, dim): + return paddle.squeeze(input, dim) + + +def zeros(*size, + out=None, + dtype=None, + layout=None, + device=None, + requires_grad=False): + if len(size) == 1 and isinstance(size[0], (tuple, list)): + shape = size[0] + else: + shape = size + if requires_grad: + return paddle.zeros(shape, dtype).requires_grad_(True) + else: + return paddle.zeros(shape, dtype) + + +def zeros_like(input, + *, + dtype=None, + layout=None, + device=None, + requires_grad=False, + memory_format=None): + if requires_grad: + return paddle.zeros_like(input, dtype).requires_grad_(True) + else: + return paddle.zeros_like(input, dtype) + + +class DataParallel(paddle.DataParallel): + def __init__(self, module, device_ids=None, output_device=None, dim=0): + super().__init__(module) + + +def invalid(*args, **kwargs): + return None diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/optimizer.py b/x2paddle/project_convertor/pytorch/torch2paddle/optimizer.py new file mode 100644 index 0000000000000000000000000000000000000000..70b8f7625970bddb36579ce204d209c8bb79fd1e --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/optimizer.py @@ -0,0 +1,233 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +from collections import defaultdict +import warnings + +import paddle +from paddle.regularizer import L2Decay + + +class _RequiredParameter(object): + """Singleton class representing a required parameter for an Optimizer.""" + + def __repr__(self): + return "" + + +required = _RequiredParameter() + + +def update_parameters(parameters, lr, weight_decay): + parameters_list = list() + if parameters is not None: + for items in parameters: + if isinstance(items, dict): + params = items["params"] + if "lr" in items: + for p in params: + p.optimize_attr["learning_rate"] = items[ + "lr"] / lr * p.optimize_attr["learning_rate"] + if "weight_decay" in items: + for p in params: + if isinstance(items["weight_decay"], (float, int)): + p.regularizer = L2Decay(items["weight_decay"]) + else: + p.regularizer = weight_decay + for p in params: + print(p.regularizer) + parameters_list.extend(params) + else: + parameters_list.append(items) + return parameters_list + + +class Momentum(paddle.optimizer.Momentum): + def __init__(self, + params, + lr=0.001, + momentum=0.0, + dampening=0, + weight_decay=0.0, + nesterov=False): + assert dampening == 0, "The dampening must be 0 in Momentum!" + parameters_list = update_parameters(params, lr, weight_decay) + super().__init__( + learning_rate=lr, + momentum=momentum, + parameters=parameters_list, + use_nesterov=nesterov, + weight_decay=weight_decay, + grad_clip=None, + name=None) + + defaults = dict( + lr=lr, + momentum=momentum, + dampening=dampening, + weight_decay=weight_decay, + nesterov=nesterov) + self.defaults = defaults + + self.state = defaultdict(dict) + self.param_groups = [] + + param_groups = list(params) + if len(param_groups) == 0: + raise ValueError("optimizer got an empty parameter list") + if not isinstance(param_groups[0], dict): + param_groups = [{'params': param_groups}] + + for param_group in param_groups: + self.add_param_group(param_group) + + def add_param_group(self, param_group): + assert isinstance(param_group, dict), "param group must be a dict" + + params = param_group['params'] + if isinstance(params, paddle.Tensor): + param_group['params'] = [params] + elif isinstance(params, set): + raise TypeError( + 'optimizer parameters need to be organized in ordered collections, but ' + 'the ordering of tensors in sets will change between runs. Please use a list instead.' + ) + else: + param_group['params'] = list(params) + + for param in param_group['params']: + if not isinstance(param, paddle.Tensor): + raise TypeError("optimizer can only optimize Tensors.") + if not param.is_leaf: + raise ValueError("can't optimize a non-leaf Tensor") + + for name, default in self.defaults.items(): + if default is required and name not in param_group: + raise ValueError( + "parameter group didn't specify a value of required optimization parameter " + + name) + else: + param_group.setdefault(name, default) + + params = param_group['params'] + if len(params) != len(set(params)): + warnings.warn( + "optimizer contains a parameter group with duplicate parameters; " + "in future, this will cause an error; ", + stacklevel=3) + + param_set = set() + for group in self.param_groups: + param_set.update(set(group['params'])) + + if not param_set.isdisjoint(set(param_group['params'])): + raise ValueError( + "some parameters appear in more than one parameter group") + + self.param_groups.append(param_group) + + def zero_grad(self): + return self.clear_grad() + + +class Adam(paddle.optimizer.Adam): + def __init__(self, + params, + lr=0.001, + betas=(0.9, 0.999), + eps=1e-08, + weight_decay=0, + amsgrad=False): + parameters_list = update_parameters(params, lr, weight_decay) + if weight_decay == 0: + weight_decay = None + super().__init__( + learning_rate=lr, + beta1=betas[0], + beta2=betas[1], + epsilon=eps, + parameters=parameters_list, + weight_decay=weight_decay, + grad_clip=None, + name=None, + lazy_mode=False) + + defaults = dict( + lr=lr, + betas=betas, + eps=eps, + weight_decay=weight_decay, + amsgrad=amsgrad) + self.defaults = defaults + + self.state = defaultdict(dict) + self.param_groups = [] + + param_groups = list(parameters_list) + if len(param_groups) == 0: + print(param_groups) + raise ValueError("optimizer got an empty parameter list") + if not isinstance(param_groups[0], dict): + param_groups = [{'params': param_groups}] + + for param_group in param_groups: + self.add_param_group(param_group) + + def add_param_group(self, param_group): + assert isinstance(param_group, dict), "param group must be a dict" + + params = param_group['params'] + if isinstance(params, paddle.Tensor): + param_group['params'] = [params] + elif isinstance(params, set): + raise TypeError( + 'optimizer parameters need to be organized in ordered collections, but ' + 'the ordering of tensors in sets will change between runs. Please use a list instead.' + ) + else: + param_group['params'] = list(params) + + for param in param_group['params']: + if not isinstance(param, paddle.Tensor): + raise TypeError("optimizer can only optimize Tensors.") + if not param.is_leaf: + raise ValueError("can't optimize a non-leaf Tensor") + + for name, default in self.defaults.items(): + if default is required and name not in param_group: + raise ValueError( + "parameter group didn't specify a value of required optimization parameter " + + name) + else: + param_group.setdefault(name, default) + + params = param_group['params'] + if len(params) != len(set(params)): + warnings.warn( + "optimizer contains a parameter group with duplicate parameters; " + "in future, this will cause an error; ", + stacklevel=3) + + param_set = set() + for group in self.param_groups: + param_set.update(set(group['params'])) + + if not param_set.isdisjoint(set(param_group['params'])): + raise ValueError( + "some parameters appear in more than one parameter group") + + self.param_groups.append(param_group) + + def zero_grad(self): + return self.clear_grad() diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/parambase.py b/x2paddle/project_convertor/pytorch/torch2paddle/parambase.py new file mode 100644 index 0000000000000000000000000000000000000000..372f3519f5c27678b1e73d7cd2e097342d81a179 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/parambase.py @@ -0,0 +1,50 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +from functools import partial + + +def add_parambase_function(func): + setattr(paddle.fluid.framework.ParamBase, func.__name__, func) + + +@add_parambase_function +def normal_(self, mean=0.0, std=1.0): + replaced_param = paddle.create_parameter( + shape=self.shape, + dtype=self.dtype, + default_initializer=paddle.nn.initializer.Normal( + mean=mean, std=std)) + paddle.assign(self, replaced_param) + + +@add_parambase_function +def zero_(self): + replaced_param = paddle.create_parameter( + shape=self.shape, + dtype=self.dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.zeros(self.shape, self.dtype))) + paddle.assign(self, replaced_param) + + +@add_parambase_function +def fill_(self, value): + replaced_param = paddle.create_parameter( + shape=self.shape, + dtype=self.dtype, + default_initializer=paddle.nn.initializer.Assign( + paddle.full(self.shape, value, self.dtype))) + paddle.assign(self, replaced_param) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/tensor.py b/x2paddle/project_convertor/pytorch/torch2paddle/tensor.py new file mode 100644 index 0000000000000000000000000000000000000000..1c9635dba3b46325d4456d88f0a401776469785b --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/tensor.py @@ -0,0 +1,252 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +import copy +from functools import reduce +from functools import partial + + +def add_tensor_function(func): + setattr(paddle.Tensor, func.__name__, func) + + +@property +def data(self): + return self + + +setattr(paddle.Tensor, "data", data) + + +@property +def requires_grad(self): + return not self.stop_gradient + + +setattr(paddle.Tensor, "requires_grad", requires_grad) + + +@add_tensor_function +def requires_grad_(self, requires_grad=True): + self.stop_gradient = not requires_grad + return self + + +@add_tensor_function +def item(self): + return self.numpy()[0] + + +@add_tensor_function +def permute(self, *dims): + return self.transpose(dims) + + +@add_tensor_function +def clamp_(self, min, max): + return self.clip(min, max) + + +@add_tensor_function +def contiguous(self): + return self + + +@add_tensor_function +def view(self, *shape): + return self.reshape(*shape) + + +@add_tensor_function +def repeat(self, *sizes): + return self.tile(sizes) + + +@add_tensor_function +def dim(self): + return self.ndim + + +@add_tensor_function +def long(self, memory_format=None): + return paddle.cast(self, dtype="int64") + + +@add_tensor_function +def float(self, memory_format=None): + return paddle.cast(self, dtype="float32") + + +@add_tensor_function +def size(self, dim=None): + if dim is not None: + return self.shape[dim] + else: + return self.shape + + +@add_tensor_function +def to(self, *args, **kwargs): + if len(args) == 1 and "dtype" not in kwargs: + try: + return paddle.cast(self, dtype=args[0]) + except Exception: + return self + else: + if len(kwargs) > 0: + if "dtype" in kwargs: + return paddle.cast(self, dtype=kwargs["dtype"]) + else: + return self + else: + return self + + +@add_tensor_function +def index_fill_(self, dim, index, val): + x_shape = self.shape + index_shape = index.shape + if dim != 0: + perm_list = list(range(len(x_shape))) + while dim < 0: + dim += len(x_shape) + perm_list.pop(dim) + perm_list = [dim] + perm_list + self = paddle.transpose(self, perm=perm_list) + s = x_shape.pop(dim) + x_shape = [s] + x_shape + updates_shape = index_shape + x_shape[1:] + updates = paddle.full(updates_shape, fill_value=val, dtype=self.dtype) + out = paddle.scatter(self, index, updates) + if dim != 0: + perm_list = list(range(len(x_shape))) + perm_list.pop(0) + perm_list.insert(dim, 0) + out = paddle.transpose(out, perm=perm_list) + paddle.assign(out, output=self) + + +@add_tensor_function +def fill_(self, value): + paddle.assign( + paddle.full_like( + self, value, dtype="float32").cast(self.dtype), + output=self) + + +pd_sum = partial(paddle.Tensor.sum) + + +@add_tensor_function +def sum(self, dim, keepdim=False, dtype=None): + return pd_sum(self, axis=dim, dtype=dtype, keepdim=keepdim) + + +pd_sort = partial(paddle.Tensor.sort) + + +@add_tensor_function +def sort(self, dim=-1, descending=False, out=None): + return pd_sort( + self, axis=dim, descending=descending), paddle.argsort( + self, axis=dim, descending=descending) + + +pd_reshape = partial(paddle.Tensor.reshape) + + +@add_tensor_function +def reshape(self, *shape): + return pd_reshape(self, shape) + + +pd_transpose = partial(paddle.Tensor.transpose) + + +@add_tensor_function +def transpose(self, dim0, dim1=None): + if dim1 is None: + return pd_transpose(self, dim0) + else: + shape = self.shape + perm = list(range(len(shape))) + dim0 = (dim0 + len(shape)) if dim0 < 0 else dim0 + dim1 = (dim1 + len(shape)) if dim1 < 0 else dim1 + perm[dim0] = dim1 + perm[dim1] = dim0 + return pd_transpose(self, perm) + + +pd_max = partial(paddle.Tensor.max) + + +@add_tensor_function +def max(self, dim, keepdim=None): + return pd_max(self, dim, keepdim), paddle.argmax(self, dim, keepdim) + + +pd_min = partial(paddle.Tensor.min) + + +@add_tensor_function +def min(self, dim, keepdim=None): + return pd_min(self, dim, keepdim), paddle.argmin(self, dim, keepdim) + + +pd_expand = partial(paddle.Tensor.expand) + + +@add_tensor_function +def expand(self, *sizes): + return pd_expand(self, sizes) + + +@add_tensor_function +def div(self, value): + return self / value + + +@add_tensor_function +def eq(self, other): + return self.equal(other) + + +@add_tensor_function +def eq_(self, other): + return self.equal(other) + + +@add_tensor_function +def mul(self, value): + return self * value + + +@add_tensor_function +def mul_(self, value): + return self * value + + +pd_cuda = partial(paddle.Tensor.cuda) + + +@add_tensor_function +def cuda(self, device=None, non_blocking=False, memory_format=None): + return self + + +@add_tensor_function +def copy_(self, src, non_blocking=False): + src = paddle.expand(src, self.shape) + paddle.assign(src, self) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/utils.py b/x2paddle/project_convertor/pytorch/torch2paddle/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..8c7557ed1b42ac6efd31dac375ac46f813a32183 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/utils.py @@ -0,0 +1,16 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +TYPE_ORDER = ["bool", "int32", "int64", "float16", "float32", "float64"] +TYPE_MAPPER = {"fp16": "float16", "fp32": "float32", "fp64": "float64"} diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/varbase.py b/x2paddle/project_convertor/pytorch/torch2paddle/varbase.py new file mode 100644 index 0000000000000000000000000000000000000000..7b0e932ed9569a02c6820ec449712818c622e634 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/varbase.py @@ -0,0 +1,115 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +from paddle.fluid.core import VarBase + + +def is_condition_one(idx): + """ + a = paddle.to_tensor(np.array([[1,2,3], [4,5,6]]).astype("float32")) + mask = paddle.to_tensor(np.array([True, False]).astype("bool")) + a[mask, :] + a[mask, ...] + """ + if not (isinstance(idx[0], paddle.Tensor) and + str(idx[0].dtype) == "VarType.BOOL"): + return False + if len(idx) == 1: + return False + if len(idx) > 1: + if idx[1] is Ellipsis: + return True + for ele in idx[1:]: + if isinstance( + ele, slice + ) and ele.start is None and ele.start is None and ele.step is None: + continue + else: + return False + return True + + +def is_condition_two(idx): + """ + a = paddle.to_tensor(np.random.rand(1, 2, 3).astype("float32")) + a[..., :2] + """ + if idx[0] is Ellipsis and (isinstance(idx[1], slice) or isinstance(idx[1], + int)): + return True + return False + + +VarBase.tmp = VarBase.__getitem__ + + +def __getitem__(self, idx): + is_bool = False + if str(self.dtype) == "VarType.BOOL": + self = self.cast("int32") + is_bool = True + if isinstance(idx, paddle.Tensor) and len(idx.shape) == 1: + out = paddle.gather(self, idx) + return out.cast("bool") if is_bool else out + elif isinstance(idx, paddle.Tensor) and str(idx.dtype) == "VarType.BOOL": + idx = paddle.cast(idx, "int32") + idx = paddle.nonzero(idx) + out = paddle.gather_nd(self, idx) + return out.cast("bool") if is_bool else out + elif isinstance(idx, tuple): + if is_condition_one(idx): + first_idx = idx[0] + first_idx = paddle.cast(first_idx, "int32") + first_idx = paddle.nonzero(first_idx) + out = paddle.gather_nd(self, first_idx) + return out.cast("bool") if is_bool else out + elif is_condition_two(idx): + new_idx = list() + for i in range(len(self.shape) - 1): + new_idx.append(slice(None, None, None)) + new_idx.append(list(idx)[-1]) + out = self.tmp(tuple(new_idx)) + return out.cast("bool") if is_bool else out + else: + out = self.tmp(idx) + return out.cast("bool") if is_bool else out + # TODO(syf): 出来为(slice(None, None, None), slice(None, None, None), 0) + else: + out = self.tmp(idx) + if out.shape == [1]: + return out.numpy()[0] + else: + return out + + +VarBase.__getitem__ = __getitem__ + +VarBase.setitem_tmp = VarBase.__setitem__ + + +def __setitem__(self, idx, value): + if isinstance(idx, paddle.Tensor) and str(idx.dtype) == "VarType.BOOL": + """ + a = paddle.to_tensor(np.array([1,2,3]).astype("float32")) + mask = paddle.to_tensor(np.array([True, False, True]).astype("bool")) + a[mask] = 1 + """ + value_tensor = paddle.full(self.shape, value, self.dtype) + paddle.assign(paddle.where(idx, value_tensor, self), self) + else: + return self.setitem_tmp(idx, value) + + +VarBase.__setitem__ = __setitem__ diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/vision_datasets.py b/x2paddle/project_convertor/pytorch/torch2paddle/vision_datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..2797dd2e425f6bddb127fdda1791e35a7d453981 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/vision_datasets.py @@ -0,0 +1,108 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +from PIL import Image +import os +import os.path +from typing import Any, Callable, cast, Dict, List, Optional, Tuple +IMG_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.ppm', '.bmp', '.pgm', '.tif', + '.tiff', '.webp') + + +def has_file_allowed_extension(filename: str, + extensions: Tuple[str, ...]) -> bool: + return filename.lower().endswith(extensions) + + +def make_dataset( + directory: str, + class_to_idx: Dict[str, int], + extensions: Optional[Tuple[str, ...]]=None, + is_valid_file: Optional[Callable[[str], bool]]=None, ) -> List[Tuple[ + str, int]]: + instances = [] + directory = os.path.expanduser(directory) + both_none = extensions is None and is_valid_file is None + both_something = extensions is not None and is_valid_file is not None + if both_none or both_something: + raise ValueError( + "Both extensions and is_valid_file cannot be None or not None at the same time" + ) + if extensions is not None: + + def is_valid_file(x: str) -> bool: + return has_file_allowed_extension(x, + cast(Tuple[str, ...], extensions)) + + is_valid_file = cast(Callable[[str], bool], is_valid_file) + for target_class in sorted(class_to_idx.keys()): + class_index = class_to_idx[target_class] + target_dir = os.path.join(directory, target_class) + if not os.path.isdir(target_dir): + continue + for root, _, fnames in sorted(os.walk(target_dir, followlinks=True)): + for fname in sorted(fnames): + path = os.path.join(root, fname) + if is_valid_file(path): + item = path, class_index + instances.append(item) + return instances + + +class ImageFolder(paddle.vision.datasets.ImageFolder): + def __init__(self, + root, + transform=None, + target_transform=None, + loader=None, + is_valid_file=None): + super().__init__( + root, + loader=loader, + transform=transform, + is_valid_file=is_valid_file) + self.target_transform = target_transform + classes, class_to_idx = self._find_classes(root) + self.samples = self.make_dataset(self.root, class_to_idx, IMG_EXTENSIONS + if is_valid_file is None else None, + is_valid_file) + + def _find_classes(self, dir: str) -> Tuple[List[str], Dict[str, int]]: + classes = [d.name for d in os.scandir(dir) if d.is_dir()] + classes.sort() + class_to_idx = {cls_name: i for i, cls_name in enumerate(classes)} + return classes, class_to_idx + + @staticmethod + def make_dataset( + directory: str, + class_to_idx: Dict[str, int], + extensions: Optional[Tuple[str, ...]]=None, + is_valid_file: Optional[Callable[[str], bool]]=None, ) -> List[ + Tuple[str, int]]: + return make_dataset( + directory, + class_to_idx, + extensions=extensions, + is_valid_file=is_valid_file) + + def __getitem__(self, index): + path, target = self.samples[index] + sample = self.loader(path) + if self.transform is not None: + sample = self.transform(sample) + if self.target_transform is not None: + target = self.target_transform(target) + return [sample, target] diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/vision_transforms.py b/x2paddle/project_convertor/pytorch/torch2paddle/vision_transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..f7f283cd56ff01b63646d591743da9ab0111937d --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/vision_transforms.py @@ -0,0 +1,190 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import paddle +import PIL +import numbers +import numpy as np +from PIL import Image +from paddle.vision.transforms import BaseTransform +from paddle.vision.transforms import functional as F + + +class ToPILImage(BaseTransform): + def __init__(self, mode=None, keys=None): + super(ToTensor, self).__init__(keys) + self.data_format = data_format + + def _apply_image(self, pic): + """ + Args: + pic (Tensor|np.ndarray): Image to be converted to PIL Image. + Returns: + PIL: Converted image. + """ + if not (isinstance(pic, paddle.Tensor) or isinstance(pic, np.ndarray)): + raise TypeError('pic should be Tensor or ndarray. Got {}.'.format( + type(pic))) + + elif isinstance(pic, paddle.Tensor): + if pic.ndimension() not in {2, 3}: + raise ValueError( + 'pic should be 2/3 dimensional. Got {} dimensions.'.format( + pic.ndimension())) + + elif pic.ndimension() == 2: + # if 2D image, add channel dimension (CHW) + pic = pic.unsqueeze(0) + + elif isinstance(pic, np.ndarray): + if pic.ndim not in {2, 3}: + raise ValueError( + 'pic should be 2/3 dimensional. Got {} dimensions.'.format( + pic.ndim)) + + elif pic.ndim == 2: + # if 2D image, add channel dimension (HWC) + pic = np.expand_dims(pic, 2) + + npimg = pic + if isinstance(pic, paddle.Tensor) and "float" in str(pic.numpy( + ).dtype) and mode != 'F': + pic = pic.mul(255).byte() + if isinstance(pic, paddle.Tensor): + npimg = np.transpose(pic.numpy(), (1, 2, 0)) + + if not isinstance(npimg, np.ndarray): + raise TypeError( + 'Input pic must be a paddle.Tensor or NumPy ndarray, ' + + 'not {}'.format(type(npimg))) + + if npimg.shape[2] == 1: + expected_mode = None + npimg = npimg[:, :, 0] + if npimg.dtype == np.uint8: + expected_mode = 'L' + elif npimg.dtype == np.int16: + expected_mode = 'I;16' + elif npimg.dtype == np.int32: + expected_mode = 'I' + elif npimg.dtype == np.float32: + expected_mode = 'F' + if mode is not None and mode != expected_mode: + raise ValueError( + "Incorrect mode ({}) supplied for input type {}. Should be {}" + .format(mode, np.dtype, expected_mode)) + mode = expected_mode + + elif npimg.shape[2] == 2: + permitted_2_channel_modes = ['LA'] + if mode is not None and mode not in permitted_2_channel_modes: + raise ValueError("Only modes {} are supported for 2D inputs". + format(permitted_2_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'LA' + + elif npimg.shape[2] == 4: + permitted_4_channel_modes = ['RGBA', 'CMYK', 'RGBX'] + if mode is not None and mode not in permitted_4_channel_modes: + raise ValueError("Only modes {} are supported for 4D inputs". + format(permitted_4_channel_modes)) + + if mode is None and npimg.dtype == np.uint8: + mode = 'RGBA' + else: + permitted_3_channel_modes = ['RGB', 'YCbCr', 'HSV'] + if mode is not None and mode not in permitted_3_channel_modes: + raise ValueError("Only modes {} are supported for 3D inputs". + format(permitted_3_channel_modes)) + if mode is None and npimg.dtype == np.uint8: + mode = 'RGB' + + if mode is None: + raise TypeError('Input type {} is not supported'.format( + npimg.dtype)) + + return Image.fromarray(npimg, mode=mode) + + +class ToTensor(BaseTransform): + """Convert a ``PIL.Image`` or ``numpy.ndarray`` to ``numpy.ndarray`` with shapr (C x H x W). + Args: + data_format (str, optional): Data format of output tensor, should be 'HWC' or + 'CHW'. Default: 'CHW'. + keys (list[str]|tuple[str], optional): Same as ``BaseTransform``. Default: None. + """ + + def __init__(self, data_format='CHW', keys=None): + super(ToTensor, self).__init__(keys) + self.data_format = data_format + + def _apply_image(self, img): + """ + Args: + img (PIL.Image|np.ndarray): Image to be converted to tensor. + Returns: + np.ndarray: Converted image. + """ + if isinstance(img, PIL.JpegImagePlugin.JpegImageFile) or isinstance( + img, PIL.Image.Image): + img = np.array(img) + img = img / 255.0 + img = img.transpose((2, 0, 1)).astype("float32") + img = paddle.to_tensor(img) + return img + + +class Normalize(BaseTransform): + """Normalize the input data with mean and standard deviation. + Given mean: ``(M1,...,Mn)`` and std: ``(S1,..,Sn)`` for ``n`` channels, + this transform will normalize each channel of the input data. + ``output[channel] = (input[channel] - mean[channel]) / std[channel]`` + Args: + mean (int|float|list): Sequence of means for each channel. + std (int|float|list): Sequence of standard deviations for each channel. + """ + + def __init__(self, mean=0.0, std=1.0, inplace=False): + key = None + super(Normalize, self).__init__(key) + if isinstance(mean, numbers.Number): + mean = [mean, mean, mean] + + if isinstance(std, numbers.Number): + std = [std, std, std] + + self.mean = mean + self.std = std + + def _apply_image(self, img): + if isinstance(img, paddle.Tensor): + img = img.numpy() + return F.normalize(img, self.mean, self.std, 'CHW', False) + + +class Lambda(BaseTransform): + """Apply a user-defined lambda as a transform. This transform does not support torchscript. + Args: + lambd (function): Lambda/function to be used for transform. + """ + + def __init__(self, lambd): + if not callable(lambd): + raise TypeError("Argument lambd should be callable, got {}".format( + repr(type(lambd).__name__))) + self.lambd = lambd + + def _apply_image(self, img): + return self.lambd(img) diff --git a/x2paddle/project_convertor/pytorch/torch2paddle/vision_utils.py b/x2paddle/project_convertor/pytorch/torch2paddle/vision_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0d96bbe13b08ac7f149abceadcc40dcdf60d8f4e --- /dev/null +++ b/x2paddle/project_convertor/pytorch/torch2paddle/vision_utils.py @@ -0,0 +1,145 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import pathlib +import paddle +import warnings +import math +import numpy as np +from PIL import Image +from typing import Union, Optional, List, Tuple, Text, BinaryIO + + +@paddle.no_grad() +def make_grid(tensor: Union[paddle.Tensor, List[paddle.Tensor]], + nrow: int=8, + padding: int=2, + normalize: bool=False, + value_range: Optional[Tuple[int, int]]=None, + scale_each: bool=False, + pad_value: int=0, + **kwargs) -> paddle.Tensor: + """Make a grid of images. + + Args: + tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W) + or a list of images all of the same size. + nrow (int, optional): Number of images displayed in each row of the grid. + The final grid size is ``(B / nrow, nrow)``. Default: ``8``. + padding (int, optional): amount of padding. Default: ``2``. + normalize (bool, optional): If True, shift the image to the range (0, 1), + by the min and max values specified by :attr:`range`. Default: ``False``. + value_range (tuple, optional): tuple (min, max) where min and max are numbers, + then these numbers are used to normalize the image. By default, min and max + are computed from the tensor. + scale_each (bool, optional): If ``True``, scale each image in the batch of + images separately rather than the (min, max) over all images. Default: ``False``. + pad_value (float, optional): Value for the padded pixels. Default: ``0``. + + Example: + See this notebook `here `_ + + """ + if not (isinstance(tensor, paddle.Tensor) or + (isinstance(tensor, list) and all( + isinstance(t, paddle.Tensor) for t in tensor))): + raise TypeError( + f'tensor or list of tensors expected, got {type(tensor)}') + + if "range" in kwargs.keys(): + warning = "range will be deprecated, please use value_range instead." + warnings.warn(warning) + value_range = kwargs["range"] + + # if list of tensors, convert to a 4D mini-batch Tensor + if isinstance(tensor, list): + tensor = paddle.stack(tensor, axis=0) + + if tensor.dim() == 2: # single image H x W + tensor = tensor.unsqueeze(0) + if tensor.dim() == 3: # single image + if tensor.size(0) == 1: # if single-channel, convert to 3-channel + tensor = paddle.concat((tensor, tensor, tensor), 0) + tensor = tensor.unsqueeze(0) + + if tensor.dim() == 4 and tensor.size(1) == 1: # single-channel images + tensor = paddle.concat((tensor, tensor, tensor), 1) + + if normalize is True: + if value_range is not None: + assert isinstance(value_range, tuple), \ + "value_range has to be a tuple (min, max) if specified. min and max are numbers" + + def norm_ip(img, low, high): + img.clip(min=low, max=high) + img = img - low + img = img / max(high - low, 1e-5) + + def norm_range(t, value_range): + if value_range is not None: + norm_ip(t, value_range[0], value_range[1]) + else: + norm_ip(t, float(t.min()), float(t.max())) + + if scale_each is True: + for t in tensor: # loop over mini-batch dimension + norm_range(t, value_range) + else: + norm_range(tensor, value_range) + + if tensor.size(0) == 1: + return tensor.squeeze(0) + + # make the mini-batch of images into a grid + nmaps = tensor.size(0) + xmaps = min(nrow, nmaps) + ymaps = int(math.ceil(float(nmaps) / xmaps)) + height, width = int(tensor.shape[2] + padding), int(tensor.shape[3] + + padding) + num_channels = tensor.shape[1] + grid = paddle.full((num_channels, height * ymaps + padding, + width * xmaps + padding), pad_value) + k = 0 + for y in range(ymaps): + for x in range(xmaps): + if k >= nmaps: + break + grid[:, y * height + padding:(y + 1) * height, x * width + padding:( + x + 1) * width] = tensor[k] + k = k + 1 + return grid + + +@paddle.no_grad() +def save_image(tensor: Union[paddle.Tensor, List[paddle.Tensor]], + fp: Union[Text, pathlib.Path, BinaryIO], + format: Optional[str]=None, + **kwargs) -> None: + """Save a given Tensor into an image file. + + Args: + tensor (Tensor or list): Image to be saved. If given a mini-batch tensor, + saves the tensor as a grid of images by calling ``make_grid``. + fp (string or file object): A filename or a file object + format(Optional): If omitted, the format to use is determined from the filename extension. + If a file object was used instead of a filename, this parameter should always be used. + **kwargs: Other arguments are documented in ``make_grid``. + """ + + grid = make_grid(tensor, **kwargs) + # Add 0.5 after unnormalizing to [0, 255] to round to nearest integer + ndarr = paddle.clip(grid * 255 + 0.5, 0, 255).transpose( + [1, 2, 0]).cast("uint8").numpy() + im = Image.fromarray(ndarr) + im.save(fp, format=format) diff --git a/x2paddle/project_convertor/pytorch/utils.py b/x2paddle/project_convertor/pytorch/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..ebf3c8e3563c4225460e335ee415d0f9c24d0fb0 --- /dev/null +++ b/x2paddle/project_convertor/pytorch/utils.py @@ -0,0 +1,61 @@ +# Copyright (c) 2021 PaddlePaddle Authors. 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. + +import os.path as osp +import sys + + +def get_dep_file_path(current_file_path, from_level, from_str): + """ 根据from信息获取依赖包所在文件。如果from字符串中存在相对路径(出现"."), + 则根据相对路径找到相应的文件;反之,执行import语句找到相应依赖的文件。 + Args: + current_file_path (str): from信息所在文件的路径。 + from_level (int): from信息中父目录级别数。 + from_str (str): from信息中依赖包名字。 + """ + if from_level > 0: + while from_level > 0: + current_file_path, folder_or_file = osp.split(current_file_path) + from_level -= 1 + if from_str is None: + import_file_path = osp.join(current_file_path, "__init__.py") + else: + current_file_path = osp.join(current_file_path, + osp.join(*from_str.split("."))) + if osp.exists(current_file_path + ".py"): + import_file_path = current_file_path + ".py" + else: + import_file_path = osp.join(current_file_path, "__init__.py") + else: + current_abs_path = osp.dirname(current_file_path) + sys.path.append(current_abs_path) + if len(from_str.split(".")) == 1: + key_str = from_str + exec("import {}".format(key_str)) + else: + from_seg = from_str.split(".") + from_str = ".".join(from_seg[0:-1]) + key_str = from_seg[-1] + exec("from {} import {}".format(from_str, key_str)) + sys.path.pop(-1) + import_file_path = locals()[key_str].__file__ + return import_file_path + + +def add_line_continuation_symbol(code): + code_list = code.split("\n") + for i, line in enumerate(code_list): + if line.strip().endswith("="): + code_list[i] = line + "\\" + return "\n".join(code_list) diff --git a/x2paddle/torch2paddle.py b/x2paddle/torch2paddle.py new file mode 100644 index 0000000000000000000000000000000000000000..5f1b1000b448fc65af573b3f8f128136d1060481 --- /dev/null +++ b/x2paddle/torch2paddle.py @@ -0,0 +1 @@ +from x2paddle.project_convertor.pytorch.torch2paddle import * diff --git a/x2paddle/utils.py b/x2paddle/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..7ffa1ec4ea7512f6fa5e30de5d6862302832a408 --- /dev/null +++ b/x2paddle/utils.py @@ -0,0 +1,20 @@ +# -*- coding:UTF-8 -*- +# Copyright (c) 2021 PaddlePaddle Authors. 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. + + +def string(param): + """ 生成字符串。 + """ + return "\'{}\'".format(param)