From f729a29cbe9907cb6fa7700288aac8c30561fadd Mon Sep 17 00:00:00 2001 From: SunAhong1993 Date: Thu, 10 Dec 2020 14:28:07 +0800 Subject: [PATCH] fix the docs --- caffe_custom_layer.md | 14 -- op_list.md => docs/introduction/op_list.md | 47 +++- .../introduction/x2paddle_model_zoo.md | 22 ++ FAQ.md => docs/user_guides/FAQ.md | 17 ++ docs/user_guides/add_caffe_custom_layer.md | 205 ++++++++++++++++++ docs/user_guides/export_tf_model.md | 112 ++++++++++ .../user_guides/pytorch2onnx.md | 0 .../user_guides/pytorch2paddle.md | 2 +- export_tf_model.md | 44 ---- 9 files changed, 403 insertions(+), 60 deletions(-) delete mode 100644 caffe_custom_layer.md rename op_list.md => docs/introduction/op_list.md (58%) rename x2paddle_model_zoo.md => docs/introduction/x2paddle_model_zoo.md (78%) rename FAQ.md => docs/user_guides/FAQ.md (80%) create mode 100644 docs/user_guides/add_caffe_custom_layer.md create mode 100644 docs/user_guides/export_tf_model.md rename pytorch_to_onnx.md => docs/user_guides/pytorch2onnx.md (100%) rename pytorch2paddle.md => docs/user_guides/pytorch2paddle.md (98%) delete mode 100644 export_tf_model.md diff --git a/caffe_custom_layer.md b/caffe_custom_layer.md deleted file mode 100644 index a7bddf2..0000000 --- a/caffe_custom_layer.md +++ /dev/null @@ -1,14 +0,0 @@ -目前,代码中已经提供了10个非官方op(不在[官网](http://caffe.berkeleyvision.org/tutorial/layers)上的op)的转换,这些op对应的Caffe实现源码如下: - -| op | 该版本实现源码 | -|-------|--------| -| PriorBox | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/prior_box_layer.cpp) | -| DetectionOutput | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/detection_output_layer.cpp) | -| ConvolutionDepthwise | [code](https://github.com/farmingyard/caffe-mobilenet/blob/master/conv_dw_layer.cpp) | -| ShuffleChannel | [code](https://github.com/farmingyard/ShuffleNet/blob/master/shuffle_channel_layer.cpp) | -| Permute | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/permute_layer.cpp) | -| Normalize | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/normalize_layer.cpp) | -| ROIPooling | [code](https://github.com/rbgirshick/caffe-fast-rcnn/blob/0dcd397b29507b8314e252e850518c5695efbb83/src/caffe/layers/roi_pooling_layer.cpp) | -| Axpy | [code](https://github.com/hujie-frank/SENet/blob/master/src/caffe/layers/axpy_layer.cpp) | -| ReLU6 | [code](https://github.com/chuanqi305/ssd/blob/ssd/src/caffe/layers/relu6_layer.cpp) | -| Upsample | [code](https://github.com/eric612/MobileNet-YOLO/blob/master/src/caffe/layers/upsample_layer.cpp) | diff --git a/op_list.md b/docs/introduction/op_list.md similarity index 58% rename from op_list.md rename to docs/introduction/op_list.md index 8b47b62..c52bf1b 100644 --- a/op_list.md +++ b/docs/introduction/op_list.md @@ -24,7 +24,11 @@ | 57 | Sqrt | 58 | Softplus | 59 | Erf | 60 | AddV2 | | 61 | LessEqual | 62 | BatchMatMul | 63 | BatchMatMulV2 | 64 | ExpandDims | | 65 | BatchToSpaceND | 66 | SpaceToBatchND | 67 | OneHot | 68 | Pow | -| 69 | All | 70 | GatherV2 | 71 | IteratorV2 | | | +| 69 | All | 70 | GatherV2 | 71 | IteratorV2 | 72 | Neg | +| 73 | Greater | 74 | FloorMod | 75 | LogicalAdd | 76 | Prod | +| 77 | Equal | 78 | Conv3D | 79 | Ceil | 80 | AddN | +| 81 | DivNoNan | 82 | Where | 83 | MirrorPad | 84 | Size | +| 85 | TopKv2 | | | | | | | ## Caffe @@ -58,3 +62,44 @@ | 45 | Squeeze | 46 | Equal | 47 | Identity | 48 | GlobalAveragePool | | 49 | MaxPool | 50 | Conv | 51 | Gemm | 52 | NonZero | | 53 | Abs | 54 | Floor | + +## PyTorch +Aten: +| 序号 | OP | 序号 | OP | 序号 | OP | 序号 | OP | +|------|------|------|------|------|------|------|------| +| 1 | aten::abs | 2 | aten::adaptive_avg_pool2d | 3 | aten::addmm | 4 | aten::add | +| 5 | aten::add\_ | 6 | aten::\_\_and\_\_ | 7 | aten::append | 8 | aten::arange | +| 9 | aten::avg\_pool2d | 10 | aten::avg\_pool3d | 11 | aten::avg_pool1d | 12 | aten::batch_norm | +| 13 | aten::cat | 14 | aten::chunk | 15 | aten::clamp | 16 | aten::\_\_contains\_\_ | +| 17 | aten::constant\_pad\_nd | 18 | aten::contiguous | 19 | aten::conv2d | 20 | aten::\_convolution | +| 21 | aten::conv_transpose2d | 22 | aten::cos | 23 | aten::cumsum | 24 | aten::detach | +| 25 | aten::dict | 26 | aten::dim | 27 | aten::div\_ | 28 | aten::div | +| 29 | aten::dropout | 30 | aten::dropout_ | 31 | aten::embedding | 32 | aten::eq | +| 33 | aten::exp | 34 | aten::expand | 35 | aten::expand_as | 36 | aten::eye | +| 37 | aten::feature_dropout | 38 | aten::flatten | 39 | aten::Float | 40 | aten::floor | +| 41 | aten::floordiv | 42 | aten::floor_divide | 43 | aten::full_like | 44 | aten::gather | +| 45 | aten::gelu | 46 | aten::\_\_getitem\_\_ | 47 | aten::gt | 48 | aten::hardtanh\_ | +| 49 | aten::index\_select | 50 | aten::Int | 51 | aten::\_\_is\_\_ | 52 | aten::\_\_isnot\_\_ | +| 53 | aten::layer\_norm | 54 | aten::le |55|aten::leaky\_relu\_|56|aten::len| +| 57 | aten::log | 58 | aten::lt |59|aten::masked\_fil\l_|60|aten::masked\_fill| +| 61 | aten::max | 62 | aten::max\_pool2d |63|aten::matmul|64|aten\_min| +| 65 | aten::mean | 66 | aten::meshgrid |67|aten::mul|68|aten::mul\_| +| 69 | aten::ne | 70 | aten::neg |71|aten::\_\_not\_\_|72|aten::ones| +| 73 | aten::permute | 74 | aten::pow |75|aten::relu|76|aten::relu\_| +| 77 | aten::relu6 | 78 | aten::repeat |79|aten::reshape|80|aten::rsub| +| 81 | aten::ScalarImplicit | 82 | aten::select |83|aten::\_set\_item|84|aten::sigmoid| +| 85 | aten::sin | 86 | aten::size |87|aten::slice|88|aten::softmax| +| 89 | aten::softplus | 90 | aten::sqrt |91|aten::squeeze|92|aten::stack| +| 93 | aten::sub | 94 | aten::t |95|aten::tanh|96|aten::split| +| 97 | aten::transpose | 98 | aten::to |99|aten::type\_as|100|aten::unsqueeze| +| 101 | aten::upsample\_bilinear2d | 102 | aten::values |103|aten::view|104|aten::warn| +| 105 | aten::where | 106 | aten::zeros |107|aten::zeros\_like||| + +Prim: +| 序号 | OP | 序号 | OP | 序号 | OP | 序号 | OP | +|------|------|------|------|------|------|------|------| +| 1 | prim::Constant | 2 | prim::data | 3 | prim::DictConstruct | 4 | prim::GetAttr | +| 5 | prim::If | 6 | prim::ListConstruct | 7 | prim::ListUnpack | 8 | prim::Loop | +| 9 | prim::min | 10 | prim::NumToTensor | 11 | prim::RaiseException | 12 | prim::requires\_grad | +| 13 | prim::SetAttr | 14 | prim::shape | 15 | prim::TupleConstruct | 16 | prim::TupleUnpack | +| 17 | prim::unchecked\_cast | 18 | prim::Uninitialized | |||| diff --git a/x2paddle_model_zoo.md b/docs/introduction/x2paddle_model_zoo.md similarity index 78% rename from x2paddle_model_zoo.md rename to docs/introduction/x2paddle_model_zoo.md index 871b8c3..7197db5 100644 --- a/x2paddle_model_zoo.md +++ b/docs/introduction/x2paddle_model_zoo.md @@ -74,3 +74,25 @@ |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](FAQ.md)| |GPT2| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](FAQ.md)| + + +## PyTorch + +| 模型 | 代码 | 备注 | +|------|----------|------| +| AlexNet | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/alexnet.py)|-| +| MNasNet | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/mnasnet.py) |-| +| MobileNetV2 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/mobilenet.py) |-| +| ResNet18 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |-| +| ShuffleNetV2 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/shufflenet.py) |-| +| SqueezeNet | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/squeezenet.py) |-| +| VGG16 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/vgg.py) |-| +| InceptionV3 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/inception.py) |-| +| DeepLabv3_ResNet50 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/segmentation/deeplabv3.py) |-| +| FCN_ResNet50 | [code](https://github.com/pytorch/vision/blob/master/torchvision/models/segmentation/fcn.py) |-| +| CamembertForQuestionAnswering | [code](https://huggingface.co/transformers/model_doc/camembert.html) |只支持trace模式| +| DPRContextEncoder | [code](https://huggingface.co/transformers/model_doc/dpr.html) |只支持trace模式| +| ElectraModel | [code](https://huggingface.co/transformers/model_doc/electra.html ) |只支持trace模式| +| FlaubertModel | [code](https://huggingface.co/transformers/model_doc/flaubert.html) |只支持trace模式| +| Roberta| [code](https://huggingface.co/transformers/model_doc/roberta.html) |只支持trace模式| +| XLMRobertaForTokenClassification|[code](https://huggingface.co/transformers/model_doc/xlmroberta.html) |只支持trace模式| diff --git a/FAQ.md b/docs/user_guides/FAQ.md similarity index 80% rename from FAQ.md rename to docs/user_guides/FAQ.md index 4247a15..19e3ace 100644 --- a/FAQ.md +++ b/docs/user_guides/FAQ.md @@ -23,3 +23,20 @@ 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/docs/user_guides/add_caffe_custom_layer.md b/docs/user_guides/add_caffe_custom_layer.md new file mode 100644 index 0000000..4407f21 --- /dev/null +++ b/docs/user_guides/add_caffe_custom_layer.md @@ -0,0 +1,205 @@ +## 如何转换Caffe自定义Layer + +本文档介绍如何将Caffe自定义Layer转换为PaddlePaddle模型中的对应实现, 用户可根据自己需要,添加代码实现自定义层,从而支持模型的完整转换。 +目前,代码中已经提供了10个非官方op(不在[官网](http://caffe.berkeleyvision.org/tutorial/layers)上的op)的转换,这些op对应的Caffe实现源码如下: + +| op | 该版本实现源码 | +|-------|--------| +| PriorBox | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/prior_box_layer.cpp) | +| DetectionOutput | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/detection_output_layer.cpp) | +| ConvolutionDepthwise | [code](https://github.com/farmingyard/caffe-mobilenet/blob/master/conv_dw_layer.cpp) | +| ShuffleChannel | [code](https://github.com/farmingyard/ShuffleNet/blob/master/shuffle_channel_layer.cpp) | +| Permute | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/permute_layer.cpp) | +| Normalize | [code](https://github.com/weiliu89/caffe/blob/ssd/src/caffe/layers/normalize_layer.cpp) | +| ROIPooling | [code](https://github.com/rbgirshick/caffe-fast-rcnn/blob/0dcd397b29507b8314e252e850518c5695efbb83/src/caffe/layers/roi_pooling_layer.cpp) | +| Axpy | [code](https://github.com/hujie-frank/SENet/blob/master/src/caffe/layers/axpy_layer.cpp) | +| ReLU6 | [code](https://github.com/chuanqi305/ssd/blob/ssd/src/caffe/layers/relu6_layer.cpp) | +| Upsample | [code](https://github.com/eric612/MobileNet-YOLO/blob/master/src/caffe/layers/upsample_layer.cpp) | + +添加代码实现自定义层的步骤如下: + +***步骤一 下载代码*** +此处涉及修改源码,应先卸载x2paddle,并且下载源码,主要有以下两步完成: +``` +pip uninstall x2paddle +pip install git+https://github.com/PaddlePaddle/X2Paddle.git@develop +``` + +***步骤二 编译caffe.proto*** +该步骤依赖protobuf编译器,其安装过程有以下两种方式: +> 选择一:pip install protobuf (protobuf >= 3.6.0) +> 选择二:使用[官方源码](https://github.com/protocolbuffers/protobuf)进行编译 + +使用脚本./tools/compile.sh将caffe.proto(包含所需的自定义Layer信息)编译成我们所需的目标语言(Python) +使用方式: +``` +bash ./toos/compile.sh /home/root/caffe/src/caffe/proto +# /home/root/caffe/src/caffe/proto为caffe.proto的存放路径,生成的caffe_pb2.py也将保存在该路径下 +``` + +***步骤三 添加自定义Layer的实现代码*** +**静态图方式:** +- 进入./x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer,创建.py文件,例如mylayer.py +- 仿照./x2paddle/op_mapper/static/caffe2paddle/caffe_custom_layer中的其他文件,在mylayer.py中主要需要实现3个函数,下面以roipooling.py为例分析代码: + 1. `def roipooling_shape(input_shape, pooled_w=None, pooled_h=None)` + 参数: + 1. input_shape(list):其中每个元素代表该层每个输入数据的shape,为必须传入的参数 + 2. pooled_w(int):代表ROI Pooling的kernel的宽,其命名与.prototxt中roi_pooling_param中的key一致 + 3. pooled_h(int):代表ROI Pooling的kernel的高,其命名与.prototxt中roi_pooling_param中的key一致 + + 功能:计算出进行ROI Pooling后的shape + 返回:一个list,其中每个元素代表每个输出数据的shape,由于ROI Pooling的输出数据只有一个,所以其list长度为1 + + 2. `def roipooling_layer(inputs, input_shape=None, name=None, pooled_w=None, pooled_h=None, spatial_scale=None)` + + 参数: + 1. inputs(list):其中每个元素代表该层每个输入数据,为必须传入的参数 + 2. input_shape(list):其中每个元素代表该层每个输入数据的shape,为必须传入的参数 + 3. name(str):ROI Pooling层的名字,为必须传入的参数 + 4. pooled_w(int):代表ROI Pooling的kernel的宽,其命名与.prototxt中roi_pooling_param中的key一致 + 5. pooled_h(int):代表ROI Pooling的kernel的高,其命名与.prototxt中roi_pooling_param中的key一致 + 6. spatial_scale(float):用于将ROI坐标从输入比例转换为池化时使用的比例,其命名与.prototxt中roi_pooling_param中的key一致 + + 功能:运用PaddlePaddle完成组网来实现`roipooling_layer`的功能 + 返回:一个Variable,为组网后的结果 + + 3. `def roipooling_weights(name, data=None)` + + 参数: + 1. name(str):ROI Pooling层的名字,为必须传入的参数 + 2. data(list):由Caffe模型.caffemodel获得的关于roipooling的参数,roipooling的参数为None + + 功能:为每个参数(例如kernel、bias等)命名;同时,若Caffe中该层参数与PaddlePaddle中参数的格式不一致,则变换操作也在该函数中实现。 + 返回:一个list,包含每个参数的名字。 + +- 在roipooling.py中注册`roipooling`,主要运用下述代码实现: + ``` + register(kind='ROIPooling', shape=roipooling_shape, layer=roipooling_layer, weights=roipooling_weights) + # kind为在model.prototxt中roipooling的type + ``` +- 在./x2paddle/op_mapper/caffe_custom_layer/\_\_init\_\_.py中引入该层的使用 + ``` + from . import roipooling + ``` +**动态图方式:** +> 【注意】若Caffe自定义layer与Paddle的op一一对应,使用方式一,否则使用方式二。 + +- 方式一: +1. 仿照./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py中的CaffeOpMapper类中的映射方法(输入为self和node),实现类似的映射方法,以下述的映射方法为例: +```python +def Permute(self, node): + assert len( + 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) + self.paddle_graph.add_layer( + "paddle.transpose", + inputs={"x": input.name}, + outputs=[node.layer_name], + perm=order) +``` +>需完成的步骤: +> a. 获取Caffe Layer的属性,并对应转换为Paddle的属性。 +> b. 获取当前Layer的输入。 +> c. 使用self.paddle_graph.add_layer为PaddleGraph添加layer。其中,第一个参数代表Paddle的kernel;inputs是一个字典,用于存储paddle中的输入的key与其输入名字;outputs是一个列表,用于存储输出的名字;其余参数为属性对应关系。 +2. 仿照./x2paddle/decoder/caffe_shape_inference.py中的shape_xx方法,实现获取当前Layer输出大小的函数,以下述方法为例: +```python +def shape_permute(layer, input_shape): + order = layer.permute_param.order + inshape = input_shape[0] + output_shape = [] + order = list(order) + for ii in order: + assert ii < len(inshape), "invalid order for permute[%s]" % (name) + output_shape.append(inshape[ii]) + return [output_shape] +``` +>参数: +> layer (caffe_pb2.LayerParameter): caffe的Layer,可用于获取当前Layer的属性。 +> input_shape (list): 其中每个元素代表该层每个输入数据的大小。 + + +- 方式二: +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): + self.roipooling_layer_attrs = { + "pooled_height": pooled_height, + "pooled_width": pooled_width, + "spatial_scale": spatial_scale} + + def __call__(self, x0, x1): + slice_x1 = paddle.slice(input=x1, axes=[1], + starts=[1], ends=[5]) + out = fluid.layers.roi_pool(input=x0, + rois=slice_x1, + **self.roipooling_layer_attrs) + return out +``` + +>\_\_init\_\_函数:用于初始化各个属性 +>\_\_call\_\_函数:用于组合实现当前Layer的前向,输入为当前Layer所需要的输入 + + +3. 仿照./x2paddle/op_mapper/dygraph/caffe2paddle/caffe_op_mapper.py中的CaffeOpMapper类中的映射方法(输入为self和node),实现类似的映射方法,以下述的映射方法为例: +```python +def ROIPooling(self, node): + roipooling_name = name_generator("roipooling", self.nn_name2id) + output_name = node.layer_name + layer_outputs = [roipooling_name, output_name] + assert len( + node.inputs) == 2, "The count of ROIPooling node\'s input is not 2." + input0 = self.graph.get_input_node(node, idx=0, copy=True) + input1 = self.graph.get_input_node(node, idx=1, copy=True) + inputs_dict = {} + inputs_dict["x0"] = input0.name + inputs_dict["x1"] = input1.name + params = node.layer.roi_pooling_param + layer_attrs = { + "pooled_height": params.pooled_h, + "pooled_width": params.pooled_w, + "spatial_scale": params.spatial_scale} + self.paddle_graph.add_layer( + "custom_layer:ROIPooling", + inputs=inputs_dict, + outputs=layer_outputs, + **layer_attrs) +``` +>需完成的步骤: +> a. 获取Caffe Layer的属性,并对应转换为Paddle的属性。 +> b. 获取当前Layer的输入。 +> c. 使用self.paddle_graph.add_layer为PaddleGraph添加layer。其中,第一个参数代表Paddle的kernel(此处kernel必须以“custom_layer:“开头);inputs是一个字典,用于存储paddle中的输入的key与其输入名字;outputs是一个列表,用于存储输出的名字;其余参数为属性对应关系。 + +4. 仿照./x2paddle/decoder/caffe_shape_inference.py中的shape_xx方法,实现获取当前Layer输出大小的函数,以下述方法为例: +```python +def shape_roipooling(layer, input_shape): + pooled_w = layer.roi_pooling_param.pooled_w + pooled_h = layer.roi_pooling_param.pooled_h + base_fea_shape = input_shapes[0] + rois_shape = input_shapes[1] + output_shape = base_fea_shape + output_shape[0] = rois_shape[0] + output_shape[2] = pooled_h + output_shape[3] = pooled_w + return [output_shape] + +``` +>参数: +> layer (caffe_pb2.LayerParameter): caffe的Layer,可用于获取当前Layer的属性。 +> input_shape (list): 其中每个元素代表该层每个输入数据的大小。 + +***步骤四 运行转换代码*** +``` +# 在X2Paddle目录下安装x2paddle +python setup.py install +# 运行转换代码 +x2paddle --framework=caffe + --prototxt=deploy.proto + --weight=deploy.caffemodel + --save_dir=pd_model + --caffe_proto=/home/root/caffe/src/caffe/proto/caffe_pb2.py +``` diff --git a/docs/user_guides/export_tf_model.md b/docs/user_guides/export_tf_model.md new file mode 100644 index 0000000..884587f --- /dev/null +++ b/docs/user_guides/export_tf_model.md @@ -0,0 +1,112 @@ +## 如何导出TensorFlow模型 + +本文档介绍如何将TensorFlow模型导出为X2Paddle支持的模型格式。 +TensorFlow目前一般分为3种保存格式(checkpoint、FrozenModel、SavedModel),X2Paddle支持的是FrozenModel(将网络参数和网络结构同时保存到同一个文件中,并且只保存指定的前向计算子图),下面示例展示了如何导出X2Paddle支持的模型格式。 + +***下列代码Tensorflow 1.X下使用*** +- checkpoint模型+代码 +步骤一 下载模型参数文件 +``` +wget http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz +tar xzvf vgg_16_2016_08_28.tar.gz +``` + +步骤二 加载和导出模型 +``` +#coding: utf-8 +import tensorflow.contrib.slim as slim +from tensorflow.contrib.slim.nets import vgg +from tensorflow.python.framework import graph_util +import tensorflow as tf + +# 固化模型函数 +# output_tensor_names: list,指定模型的输出tensor的name +# freeze_model_path: 模型导出的文件路径 +def freeze_model(sess, output_tensor_names, freeze_model_path): + out_graph = graph_util.convert_variables_to_constants( + sess, sess.graph.as_graph_def(), output_tensor_names) + with tf.gfile.GFile(freeze_model_path, 'wb') as f: + f.write(out_graph.SerializeToString()) + + print("freeze model saved in {}".format(freeze_model_path)) + +# 加载模型参数 +sess = tf.Session() +inputs = tf.placeholder(dtype=tf.float32, + shape=[None, 224, 224, 3], + name="inputs") +logits, endpoint = vgg.vgg_16(inputs, num_classes=1000, is_training=False) +load_model = slim.assign_from_checkpoint_fn( + "vgg_16.ckpt", slim.get_model_variables("vgg_16")) +load_model(sess) + +# 导出模型 +freeze_model(sess, ["vgg_16/fc8/squeezed"], "vgg16.pb") +``` +- 纯checkpoint模型 +文件结构: +> |--- checkpoint +> |--- model.ckpt-240000.data-00000-of-00001 +> |--- model.ckpt-240000.index +> |--- model.ckpt-240000.meta + +加载和导出模型: +```python +#coding: utf-8 +from tensorflow.python.framework import graph_util +import tensorflow as tf + +# 固化模型函数 +# output_tensor_names: list,指定模型的输出tensor的name +# freeze_model_path: 模型导出的文件路径 +def freeze_model(sess, output_tensor_names, freeze_model_path): + out_graph = graph_util.convert_variables_to_constants( + sess, sess.graph.as_graph_def(), output_tensor_names) + with tf.gfile.GFile(freeze_model_path, 'wb') as f: + 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" +save_pb_file = "./tfhub_models/save.pb" +sess = tf.Session() +saver = tf.train.import_meta_graph(input_checkpoint + '.meta', clear_devices=True) +saver.restore(sess, input_checkpoint) + +# 此处需要修改freeze_model的第二个参数,指定模型的输出tensor的name +freeze_model(sess, ["vgg_16/fc8/squeezed"], save_pb_file) +``` + +- SavedModel模型 +文件结构: +> |-- variables +> |------ variables.data-00000-of-00001 +> |------ variables.data-00000-of-00001 +> |-- saved_model.pb + +加载和导出模型: +```python +#coding: utf-8 +import tensorflow as tf +sess = tf.Session(graph=tf.Graph()) +# tf.saved_model.loader.load最后一个参数代表saved_model的保存路径 +tf.saved_model.loader.load(sess, {}, "/mnt/saved_model") +graph = tf.get_default_graph() + +from tensorflow.python.framework import graph_util +# 固化模型函数 +# output_tensor_names: list,指定模型的输出tensor的name +# freeze_model_path: 模型导出的文件路径 +def freeze_model(sess, output_tensor_names, freeze_model_path): + out_graph = graph_util.convert_variables_to_constants( + sess, sess.graph.as_graph_def(), output_tensor_names) + with tf.gfile.GFile(freeze_model_path, 'wb') as f: + f.write(out_graph.SerializeToString()) + + print("freeze model saved in {}".format(freeze_model_path)) + +# 导出模型 +freeze_model(sess, ["logits"], "model.pb") +``` \ No newline at end of file diff --git a/pytorch_to_onnx.md b/docs/user_guides/pytorch2onnx.md similarity index 100% rename from pytorch_to_onnx.md rename to docs/user_guides/pytorch2onnx.md diff --git a/pytorch2paddle.md b/docs/user_guides/pytorch2paddle.md similarity index 98% rename from pytorch2paddle.md rename to docs/user_guides/pytorch2paddle.md index a751345..82f2d20 100644 --- a/pytorch2paddle.md +++ b/docs/user_guides/pytorch2paddle.md @@ -46,7 +46,7 @@ torch_module.load_state_dict(torch_state_dict) torch_module.eval() # 进行转换 from x2paddle.convert import pytorch2paddle -pytorch2paddle(torch_model, +pytorch2paddle(torch_module, save_dir="pd_model_trace", jit_type="trace", input_examples=[torch.tensor(input_data)]) diff --git a/export_tf_model.md b/export_tf_model.md deleted file mode 100644 index 7bed951..0000000 --- a/export_tf_model.md +++ /dev/null @@ -1,44 +0,0 @@ -## 如何导出TensorFlow模型 - -本文档介绍如何将TensorFlow模型导出为X2Paddle支持的模型格式。 - -TensorFlow提供了接口可将网络参数和网络结构同时保存到同一个文件中,并且只保存指定的前向计算子图,下面示例展示了如何导出tensorflow/models下的VGG16模型 - -步骤一 下载模型参数文件 -``` -wget http://download.tensorflow.org/models/vgg_16_2016_08_28.tar.gz -tar xzvf vgg_16_2016_08_28.tar.gz -``` - -步骤二 加载和导出模型 -``` -#coding: utf-8 -import tensorflow.contrib.slim as slim -from tensorflow.contrib.slim.nets import vgg -from tensorflow.python.framework import graph_util -import tensorflow as tf - -# 固化模型函数 -# output_tensor_names: list,指定模型的输出tensor的name -# freeze_model_path: 模型导出的文件路径 -def freeze_model(sess, output_tensor_names, freeze_model_path): - out_graph = graph_util.convert_variables_to_constants( - sess, sess.graph.as_graph_def(), output_tensor_names) - with tf.gfile.GFile(freeze_model_path, 'wb') as f: - f.write(out_graph.SerializeToString()) - - print("freeze model saved in {}".format(freeze_model_path)) - -# 加载模型参数 -sess = tf.Session() -inputs = tf.placeholder(dtype=tf.float32, - shape=[None, 224, 224, 3], - name="inputs") -logits, endpoint = vgg.vgg_16(inputs, num_classes=1000, is_training=False) -load_model = slim.assign_from_checkpoint_fn( - "vgg_16.ckpt", slim.get_model_variables("vgg_16")) -load_model(sess) - -# 导出模型 -freeze_model(sess, ["vgg_16/fc8/squeezed"], "vgg16.pb") -``` -- GitLab