Commit 8590ec24 authored by yaoht's avatar yaoht
Browse files

init commit

parent 38a732e1
# Tencent is pleased to support the open source community by making TNN available.
#
# Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#
# Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# https://opensource.org/licenses/BSD-3-Clause
#
# 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 src.c2oObject as Node
import typing
def getTransposeAttri(layer) -> typing.Dict:
if layer.type == "ShuffleChannel":
# 超参数字典
perm_array = [0, 2, 1, 3, 4]
attributes = {"perm": perm_array}
return attributes
else:
orders = layer.permute_param.order
attributes = {"perm": orders}
return attributes
# 计算输出维度
def getTransposeOutShape(layer, input_shape, attributes):
if layer.type == "ShuffleChannel":
n, g, c, h, w = input_shape[0][0], input_shape[0][1], input_shape[0][2], input_shape[0][3], input_shape[0][4]
output_shape = [[n, c, g, h, w]]
return output_shape
else:
orders = attributes.get("perm")
shape = []
for order in orders:
shape.append(input_shape[0][order])
return [shape]
# 构建节点
def createTranspose(layer, node_name, input_name, output_name, input_shape) -> Node:
attributes = getTransposeAttri(layer)
output_shape = getTransposeOutShape(layer, input_shape, attributes)
node = Node.c2oNode(layer, node_name, "Transpose", input_name, output_name, input_shape, output_shape, attributes)
return node
import numpy as np
import src.c2oObject as Node
##-----------------------------------------------------UnPooling层--------------------------------------------------##
#获取超参数
def getUnPoolingAttri(layer):
# ##池化核尺寸
# kernel_shape = np.array([layer.pooling_param.kernel_size]*2).reshape(1,-1)[0].tolist()
# if layer.pooling_param.kernel_size == []:
# kernel_shape = [layer.pooling_param.kernel_h,layer.pooling_param.kernel_w]
# ##步长
# strides = [1, 1]#默认为1
# if layer.pooling_param.stride != []:
# strides = np.array([layer.pooling_param.stride]*2).reshape(1,-1)[0].tolist()
# ##填充
# pads = [0, 0, 0, 0]#默认为0
# # 这里与卷积时一样,有pad,就按其值设置
# if layer.pooling_param.pad != []:
# pads = np.array([layer.pooling_param.pad] * 4).reshape(1, -1)[0].tolist()
# elif layer.pooling_param.pad_h != 0 or layer.pooling_param.pad_w != 0:
# pads = [layer.pooling_param.pad_h,layer.pooling_param.pad_w,layer.pooling_param.pad_h,layer.pooling_param.pad_w]
#超参数字典
dict = {"kernel_shape": [2, 2],
"strides": [2, 2],
"pads": [0, 0, 0, 0]
}
return dict
#计算输出维度
def getUnPoolingOutShape(input_shape,layer,dict):
kernel_shape = dict["kernel_shape"]
pads = dict["pads"]
strides = dict["strides"]
#计算输出维度,与卷积一样,若为非整数则向上取整
# h = (input_shape[0][2] - kernel_shape[0] + 2 * pads[0])/strides[0] + 1
# if h > int(h):
# output_shape_h = int(h) + 1
# pads = [0,0,1,1]
# else:
# output_shape_h = int(h)
# output_shape = [[input_shape[0][0],input_shape[0][1],output_shape_h,output_shape_h]]
output_shape = [[input_shape[0][0], input_shape[0][1], input_shape[0][2]*2, input_shape[0][3]*2]]
return output_shape
#构建节点
def createUnPooling(layer,nodename,inname,outname,input_shape):
dict = getUnPoolingAttri(layer)
output_shape = getUnPoolingOutShape(input_shape,layer,dict)
node = Node.c2oNode(layer, nodename, "MaxUnpool", inname, outname, input_shape, output_shape, dict=dict)
return node
import src.c2oObject as Node
import numpy as np
# 获取超参数
def get_upsample_attri(layer):
# scale = layer.upsample_param.scale
# scales = [1.0,1.0,scale,scale]
# dict = {"scales":scales,"mode":"nearest"}#Upsample将scales放入参数里面了
# dict = {"width_scale": scale,"height_scale":scale, "mode": "nearest"}#在OpenVINO读onnx的时候要求用width_scale和height_scale
scale = layer.upsample_param.scale
scales = [1.0, 1.0, scale, scale]
attributes = {"mode": "linear",
'scales': scales}
return attributes
def get_upsample_outputshape(input_shape, layer):
scale = layer.upsample_param.scale
scales = [1.0, 1.0, scale, scale]
output_shape = [np.multiply(np.array(scales, dtype=np.int), np.array(input_shape[0])).tolist()]
return output_shape
def create_upsample_node(layer, node_name, input_name, output_name, input_shape):
attributes = get_upsample_attri(layer)
output_shape = get_upsample_outputshape(input_shape, layer)
# print(output_shape)
node = Node.c2oNode(layer, node_name, "Upsample", input_name, output_name, input_shape, output_shape, attributes)
return node
from src.OPs.BatchNorm import *
from src.OPs.Concat import *
from src.OPs.Conv import *
from src.OPs.Dropout import *
from src.OPs.Eltwise import *
from src.OPs.Gemm import *
from src.OPs.LRN import *
from src.OPs.Pooling import *
from src.OPs.PRelu import *
from src.OPs.ReLU import *
from src.OPs.Reshape import *
from src.OPs.Softmax import *
from src.OPs.Upsample import *
from src.OPs.UnPooling import *
from src.OPs.ConvTranspose import *
from src.OPs.Slice import *
from src.OPs.Transpose import *
from src.OPs.Sigmoid import *
from src.OPs.Min import *
from src.OPs.Clip import *
from src.OPs.Log import *
from src.OPs.Mul import *
from src.OPs.Interp import *
from src.OPs.Crop import *
from src.OPs.InstanceNorm import *
from src.OPs.PriroBox import create_priorbox_node
from src.OPs.DetectionOutput import create_detection_output
from src.OPs.Flatten import create_flatten_node
from src.OPs.Resize import create_resize_node
from src.OPs.Axpy import create_axpy_add_node, create_axpy_mul_node
from src.OPs.LpNormalization import create_Lp_Normalization
from src.OPs.Power import get_power_param, create_power_node
from src.OPs.Add import create_add_node
from src.OPs.Tanh import createTanh
# Tencent is pleased to support the open source community by making TNN available.
#
# Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#
# Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# https://opensource.org/licenses/BSD-3-Clause
#
# 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 argparse
def parse_args():
parser = argparse.ArgumentParser(description='convert caffe model to onnx')
parser.add_argument(dest='proto_file',
action='store',
help='the path for prototxt file, the file name must end with .prototxt')
parser.add_argument(dest='caffe_model_file',
action='store',
help='the path for caffe model file, the file name must end with .caffemodel!')
parser.add_argument('-o',
dest='onnx_file',
action='store',
help='the path for generate onnx file')
args = parser.parse_args()
return args
from onnx import helper
class c2oNode(object):
def __init__(self,layer,node_name,type,inputs_name,outputs_name,inputs_shape,outputs_shape,dict={}, Flag=False):
self.node = self.__createNode(type, inputs_name, outputs_name, node_name, dict)
self.bottom = layer.bottom
if Flag is True:
self.top = outputs_name
else:
self.top = layer.top
self.inputs_name = inputs_name
self.outputs_name = outputs_name
self.inputs_shape = inputs_shape
self.outputs_shape = outputs_shape
self.dict = dict
#创建节点
def __createNode(self, node_type, in_name, out_name, node_name, dict):
node_def = helper.make_node(
node_type,
in_name,
out_name,
node_name,
**dict,
)
return node_def
class c2oGraph():
def __init__(self,onnxname):
self.name = onnxname
self.in_tvi = []#存放输入信息,包括第一个输入和输入参数信息
self.out_tvi = []#存放输出信息
self.init_t = []#存放输入参数的值
self.hidden_out_tvi = []#存放中间输出信息
def addInputsTVI(self,in_tvi):
self.in_tvi.append(in_tvi)
def addOutputsTVI(self,out_tvi):
self.out_tvi.append(out_tvi)
def addInitTensor(self,init_t):
self.init_t.append(init_t)
def addValueInfoTVI(self,vi_tvi):
self.hidden_out_tvi.append(vi_tvi)
This diff is collapsed.
from google.protobuf import text_format
from proto import caffe_upsample_pb2
import onnx
from onnx import utils
def LoadCaffeModel(net_path, model_path):
# read prototxt
net = caffe_upsample_pb2.NetParameter()
text_format.Merge(open(net_path).read(), net)
# read caffemodel
model = caffe_upsample_pb2.NetParameter()
f = open(model_path, 'rb')
model.ParseFromString(f.read())
f.close()
return net, model
def LoadOnnxModel(onnx_path):
onnxmodel = onnx.load(onnx_path)
return onnxmodel
def SaveOnnxModel(onnx_model, onnx_save_path, need_polish=True):
try:
if need_polish:
polished_model = onnx.utils.polish_model(onnx_model)
onnx.save_model(polished_model, onnx_save_path)
else:
onnx.save_model(onnx_model, onnx_save_path)
print("模型保存成功,已保存至:" + onnx_save_path)
except Exception as e:
print("模型存在问题,未保存成功:", e)
from proto import caffe_upsample_pb2
from onnx import TensorProto
Layer_CONCAT = caffe_upsample_pb2.V1LayerParameter.CONCAT # 3
Layer_CONVOLUTION = caffe_upsample_pb2.V1LayerParameter.CONVOLUTION # 4
Layer_DROPOUT = caffe_upsample_pb2.V1LayerParameter.DROPOUT # 6
Layer_INNER_PRODUCT = caffe_upsample_pb2.V1LayerParameter.INNER_PRODUCT # 14
Layer_LRN = caffe_upsample_pb2.V1LayerParameter.LRN # 15
Layer_POOLING = caffe_upsample_pb2.V1LayerParameter.POOLING # 17
Layer_RELU = caffe_upsample_pb2.V1LayerParameter.RELU # 18
Layer_SOFTMAX = caffe_upsample_pb2.V1LayerParameter.SOFTMAX # 20
Layer_ELTWISE = caffe_upsample_pb2.V1LayerParameter.ELTWISE # 25
Layer_UPSAMPLE = caffe_upsample_pb2.V1LayerParameter.UPSAMPLE # 40
op_pname = {"Conv": ["_W", "_b"],
"BatchNorm": ["_mean", "_var"],
"Scale": ["_scale", "_b"],
"Reshape": ["_shape"],
"DouReshape": ["_Doureshape"],
"InnerProduct": ["_W", "_B"],
"Upsample": ["_roi_" ,"_Scale"],
"PRelu": ["_slope"],
"Transpose": ["_trans"],
"ConvTranspose": ["_W", "_b"],
"Slice": ['_starts', '_ends', '_axes', '_steps']
}
op_ptype = {"Conv": [TensorProto.FLOAT, TensorProto.FLOAT],
"BatchNorm": [TensorProto.FLOAT, TensorProto.FLOAT],
"Scale": [TensorProto.FLOAT, TensorProto.FLOAT],
"Reshape": [TensorProto.INT64],
"InnerProduct": [TensorProto.FLOAT, TensorProto.FLOAT],
"Upsample": [TensorProto.FLOAT, TensorProto.FLOAT],
"PRelu": [TensorProto.FLOAT],
"Transpose": [TensorProto.INT64],
"ConvTranspose": [TensorProto.FLOAT, TensorProto.FLOAT],
"DouReshape": [TensorProto.INT64],
"Slice": [TensorProto.INT64, TensorProto.INT64, TensorProto.INT64, TensorProto.INT64]
}
# Tencent is pleased to support the open source community by making TNN available.
#
# Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
#
# Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# https://opensource.org/licenses/BSD-3-Clause
#
# 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 is_ssd_model(proto_path):
proto_file = open(proto_path, 'r')
lines = proto_file.read()
proto_file.close()
if "PriorBox" in lines:
return True
elif "DetectionOutput" in lines:
return True
else:
return False
# Paddle 模型转换为 ONNX 模型
## 1. 环境搭建
- 安装PaddlePaddle
```shell script
pip install paddlepaddle
```
- 安装onnxruntime >= 1.10.0
```shell script
pip install onnxruntime
```
- 安装onnx
```
pip install onnx
```
- 安装paddle2onnx
```shell script
pip install paddle2onnx
```
## 2. caffe2onnx 工具使用
### 2.1获取PaddlePaddle部署模型
将paddle模型转换成onnx之前,请注意,所必需的模型文件:
- `model_name.pdmodel`: 表示模型结构
- `model_name.pdiparams`: 表示模型参数
[**注意**] 这里需要注意,两个文件其中参数文件后辍为 `.pdiparams`,如你的参数文件后辍是 `.pdparams` ,那说明你的参数是训练过程中保存的,当前还不是部署模型格式。
示例模型[下载地址](https://bj.bcebos.com/paddle2onnx/model_zoo/resnet50.tar.gz),下载模型后解压文件
```shell
tar -zxvf resnet50.tar.gz
```
## 命令行转换
```bash
paddle2onnx --model_dir resnet50 --model_filename inference.pdmodel --params_filename inference.pdiparams --save_file model.onnx
```
可调整的转换参数如下表,具体可参考[官网](https://github.com/PaddlePaddle/Paddle2ONNX):
| 参数 | 参数说明 |
| -------------------------- | ------------------------------------------------------------ |
| --model_dir | 配置包含 Paddle 模型的目录路径 |
| --model_filename | **[可选]** 配置位于 `--model_dir` 下存储网络结构的文件名 |
| --params_filename | **[可选]** 配置位于 `--model_dir` 下存储模型参数的文件名称 |
| --save_file | 指定转换后的模型保存目录路径 |
| --opset_version | **[可选]** 配置转换为 ONNX 的 OpSet 版本,目前支持 7~16 等多个版本,默认为 9 |
| --enable_onnx_checker | **[可选]** 配置是否检查导出为 ONNX 模型的正确性, 建议打开此开关, 默认为 False |
| --enable_auto_update_opset | **[可选]** 是否开启 opset version 自动升级功能,当低版本 opset 无法转换时,自动选择更高版本的 opset进行转换, 默认为 True |
| --deploy_backend | **[可选]** 量化模型部署的推理引擎,支持 onnxruntime、tensorrt 或 others,当选择 others 时,所有的量化信息存储于 max_range.txt 文件中,默认为 onnxruntime |
| --save_calibration_file | **[可选]** TensorRT 8.X版本部署量化模型需要读取的 cache 文件的保存路径,默认为 calibration.cache |
| --version | **[可选]** 查看 paddle2onnx 版本 |
| --external_filename | **[可选]** 当导出的 ONNX 模型大于 2G 时,需要设置 external data 的存储路径,推荐设置为:external_data |
| --export_fp16_model | **[可选]** 是否将导出的 ONNX 的模型转换为 FP16 格式,并用 ONNXRuntime-GPU 加速推理,默认为 False |
| --custom_ops | **[可选]** 将 Paddle OP 导出为 ONNX 的 Custom OP,例如:--custom_ops '{"paddle_op":"onnx_op"},默认为 {} |
## 修改模型shape
如果想要将输入name为image的模型model.onnx修改为shape[256, 3, 224, 224],可以用一下命令:
```bash
python -m paddle2onnx.optimize --input_model model.onnx --output_model new_model.onnx --input_shape_dict="{'inputs': [256, 3, 224, 224]}"
```
如果不知道模型的输入name,可以使用一下python脚本获知:
```shell
python get_model_input_output_info.py
```
其中get_model_input_output_info.py代码如下,替换model.onnx的路径即可查询自己的模型的输入输出信息。
```python
import onnx
def get_input_details(onnx_file):
# 加载ONNX模型
model = onnx.load(onnx_file)
# 获取输入信息
input_details = []
for input in model.graph.input:
input_name = input.name
input_shape = [dim.dim_value for dim in input.type.tensor_type.shape.dim]
input_details.append((input_name, input_shape))
return input_details
def get_output_details(onnx_file):
# 加载ONNX模型
model = onnx.load(onnx_file)
# 获取输出信息
output_details = []
for output in model.graph.output:
output_name = output.name
output_shape = [dim.dim_value for dim in output.type.tensor_type.shape.dim]
output_details.append((output_name, output_shape))
return output_details
# 调用函数并打印输入和输出信息
onnx_file = "model.onnx" # 将 "model.onnx" 替换为你的ONNX模型文件路径
input_details = get_input_details(onnx_file)
output_details = get_output_details(onnx_file)
print("输入信息:")
for name, shape in input_details:
print("Input Name:", name)
print("Input Shape:", shape)
print("输出信息:")
for name, shape in output_details:
print("Output Name:", name)
print("Output Shape:", shape)
```
\ No newline at end of file
import onnx
def get_input_details(onnx_file):
# 加载ONNX模型
model = onnx.load(onnx_file)
# 获取输入信息
input_details = []
for input in model.graph.input:
input_name = input.name
input_shape = [dim.dim_value for dim in input.type.tensor_type.shape.dim]
input_details.append((input_name, input_shape))
return input_details
def get_output_details(onnx_file):
# 加载ONNX模型
model = onnx.load(onnx_file)
# 获取输出信息
output_details = []
for output in model.graph.output:
output_name = output.name
output_shape = [dim.dim_value for dim in output.type.tensor_type.shape.dim]
output_details.append((output_name, output_shape))
return output_details
# 调用函数并打印输入和输出信息
onnx_file = "model.onnx" # 将 "your_model.onnx" 替换为你的ONNX模型文件路径
input_details = get_input_details(onnx_file)
output_details = get_output_details(onnx_file)
print("输入信息:")
for name, shape in input_details:
print("Input Name:", name)
print("Input Shape:", shape)
print("输出信息:")
for name, shape in output_details:
print("Output Name:", name)
print("Output Shape:", shape)
\ No newline at end of file
# tf转onnx
## 环境准备
- tensorflow安装
```bash
pip install tensorflow
```
- tf2onnx 安装(version>= 1.5.5)
```bash
pip install tf2onnx
```
## 模型格式确认
请先确认手里的模型文件的格式,一般情况下:
1. **SavedModel 文件结构**
- `saved_model.pb``saved_model.pbtxt`:这是SavedModel的核心文件,包含了模型的图(graph)和元数据(metadata)。
- `variables/`:这个文件夹包含两个文件,`variables.data-?????-of-?????``variables.index`,存储了模型的变量。
- `assets/`(可选):这个文件夹存储了任何附加的资源文件。
如果你的`.pb`文件位于一个包含上述结构的目录中,那么它很可能是一个SavedModel。
2. **Checkpoint 文件结构**
- Checkpoint 通常包含三个文件:一个`.index`文件,一个或多个`.data-?????-of-?????`文件,以及一个`checkpoint`文件,这个文件是保存模型变量的。
如果你的`.pb`文件位于一个包含上述结构的目录中,那么它很可能是一个Checkpoint。
3. **GraphDef 文件结构**
- 如果只有一个`.pb`文件,且没有与其相关联的其他文件或目录结构,那么它很可能是GraphDef。
可以使用一下代码对模型文件格式进行检查:
```python
import tensorflow as tf
from google.protobuf import text_format
from tensorflow.core.framework import graph_pb2
def is_saved_model(model_dir):
try:
model = tf.saved_model.load(model_dir)
return True
except Exception:
return False
def is_graph_def(pb_file):
try:
with tf.io.gfile.GFile(pb_file, "rb") as f:
graph_def = tf.compat.v1.GraphDef()
graph_def.ParseFromString(f.read())
return True
except Exception:
return False
def is_checkpoint(model_dir):
try:
checkpoint = tf.train.Checkpoint()
checkpoint.restore(model_dir).expect_partial()
return True
except Exception:
return False
model_path = "/path/to/model"
if is_saved_model(model_path):
print(f"{model_path} contains a SavedModel.")
elif is_graph_def(model_path):
print(f"{model_path} contains a GraphDef.")
elif is_checkpoint(model_path):
print(f"{model_path} contains a Checkpoint.")
else:
print(f"{model_path} format is unknown.")
```
## 模型输入输出的name和shape确认
使用下面代码对GraphDef格式的模型进行确认
```python
import tensorflow as tf
from tensorflow.python.framework import tensor_util
def load_graph_def(pb_file):
with tf.io.gfile.GFile(pb_file, "rb") as f:
graph_def = tf.compat.v1.GraphDef()
graph_def.ParseFromString(f.read())
return graph_def
def get_graph_inputs_outputs(graph_def):
inputs = []
outputs = []
for node in graph_def.node:
if node.op == 'Placeholder':
shape = None
for attr_value in node.attr.values():
if attr_value.HasField('shape'):
shape = [dim.size for dim in attr_value.shape.dim]
inputs.append({'name': node.name, 'shape': shape})
# Assuming outputs are nodes with no outputs themselves, usually not a strict rule
elif not any(node.name in input for input in [n.input for n in graph_def.node]):
shape = None
try:
tensor_shape = tensor_util.MakeNdarray(node.attr["shape"].shape)
shape = tensor_shape.shape
except:
pass
outputs.append({'name': node.name, 'shape': shape})
return inputs, outputs
def print_graph_info(inputs, outputs):
print("Inputs:")
for input_info in inputs:
print(f"Name: {input_info['name']}, Shape: {input_info['shape']}")
print("\nOutputs:")
for output_info in outputs:
print(f"Name: {output_info['name']}, Shape: {output_info['shape']}")
# Path to your .pb file
pb_file_path = "resnet50v15_tf.pb"
# Load GraphDef
graph_def = load_graph_def(pb_file_path)
# Get inputs and outputs
inputs, outputs = get_graph_inputs_outputs(graph_def)
# Print inputs and outputs
print_graph_info(inputs, outputs)
```
## 模型转换
使用tf2onnx工具进行模型转换,详细工具说明可以查看tf2onnx工具官网,tf2onnx项目地址:https://github.com/onnx/tensorflow-onnx 建议大家阅读 tf2onnx 的 README.md 文件,里面有详细的对该工具各个参数的说明。
```
options:
-h, --help show this help message and exit
--input INPUT input from graphdef
--graphdef GRAPHDEF input from graphdef
--saved-model SAVED_MODEL
input from saved model
--tag TAG tag to use for saved_model
--signature_def SIGNATURE_DEF
signature_def from saved_model to use
--concrete_function CONCRETE_FUNCTION
For TF2.x saved_model, index of func signature in __call__ (--signature_def is ignored)
--checkpoint CHECKPOINT
input from checkpoint
--keras KERAS input from keras model
--tflite TFLITE input from tflite model
--tfjs TFJS input from tfjs model
--large_model use the large model format (for models > 2GB)
--output OUTPUT output model file
--inputs INPUTS model input_names (optional for saved_model, keras, and tflite)
--outputs OUTPUTS model output_names (optional for saved_model, keras, and tflite)
--ignore_default IGNORE_DEFAULT
comma-separated list of names of PlaceholderWithDefault ops to change into Placeholder ops
--use_default USE_DEFAULT
comma-separated list of names of PlaceholderWithDefault ops to change into Identity ops using
their default value
--rename-inputs RENAME_INPUTS
input names to use in final model (optional)
--rename-outputs RENAME_OUTPUTS
output names to use in final model (optional)
--use-graph-names (saved model only) skip renaming io using signature names
--opset OPSET opset version to use for onnx domain
--dequantize remove quantization from model. Only supported for tflite currently.
--custom-ops CUSTOM_OPS
comma-separated map of custom ops to domains in format OpName:domain. Domain
'ai.onnx.converters.tensorflow' is used by default.
--extra_opset EXTRA_OPSET
extra opset with format like domain:version, e.g. com.microsoft:1
--load_op_libraries LOAD_OP_LIBRARIES
comma-separated list of tf op library paths to register before loading model
--target {rs4,rs5,rs6,caffe2,tensorrt,nhwc}
target platform
--continue_on_error continue_on_error
--verbose, -v verbose output, option is additive
--debug debug mode
--output_frozen_graph OUTPUT_FROZEN_GRAPH
output frozen tf graph to file
--inputs-as-nchw INPUTS_AS_NCHW
transpose inputs as from nhwc to nchw
--outputs-as-nchw OUTPUTS_AS_NCHW
transpose outputs as from nhwc to nchw
Usage Examples:
python -m tf2onnx.convert --saved-model saved_model_dir --output model.onnx
python -m tf2onnx.convert --input frozen_graph.pb --inputs X:0 --outputs output:0 --output model.onnx
python -m tf2onnx.convert --checkpoint checkpoint.meta --inputs X:0 --outputs output:0 --output model.onn
```
下面是将GraphDef格式的模型转换onnx的示例, resnet50v15_tf.pb模型[下载地址](https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/modelzoo/Official/cv/Resnet50v1.5_for_ACL/resnet50v15_tf.pb)
```bash
python -m tf2onnx.convert --graphdef resnet50v15_tf.pb --output model_nchw.onnx --inputs input_tensor:0 --outputs global_step:0,ArgMax:0,softmax_tensor:0 --inputs-as-nchw input_tensor:0
```
# tflite转onnx
同样使用tf2onnx工具,例如将ResNet50.tflite模型转为onnx模型,模型[下载地址](https://hf-mirror.com/qualcomm/ResNet50/resolve/main/ResNet50.tflite?download=true)
```bash
python -m tf2onnx.convert --opset 16 --tflite ResNet50.tflite --output model.onnx
```
\ No newline at end of file
import tensorflow as tf
from google.protobuf import text_format
from tensorflow.core.framework import graph_pb2
def is_saved_model(model_dir):
try:
model = tf.saved_model.load(model_dir)
return True
except Exception:
return False
def is_graph_def(pb_file):
try:
with tf.io.gfile.GFile(pb_file, "rb") as f:
graph_def = tf.compat.v1.GraphDef()
graph_def.ParseFromString(f.read())
return True
except Exception:
return False
def is_checkpoint(model_dir):
try:
checkpoint = tf.train.Checkpoint()
checkpoint.restore(model_dir).expect_partial()
return True
except Exception:
return False
model_path = "predict_net.pb"
if is_saved_model(model_path):
print(f"{model_path} contains a SavedModel.")
elif is_graph_def(model_path):
print(f"{model_path} contains a GraphDef.")
elif is_checkpoint(model_path):
print(f"{model_path} contains a Checkpoint.")
else:
print(f"{model_path} format is unknown.")
# import tensorflow as tf
# from tensorflow.python.framework import graph_util
# from tensorflow.python.framework import graph_io
# def load_graph_def(pb_file):
# with tf.io.gfile.GFile(pb_file, "rb") as f:
# graph_def = tf.compat.v1.GraphDef()
# graph_def.ParseFromString(f.read())
# return graph_def
# def get_input_output_names(graph_def):
# input_names = []
# output_names = []
# for node in graph_def.node:
# if node.op == 'Placeholder':
# input_names.append(node.name)
# # Identify output nodes as those not used as inputs to other nodes
# is_output = True
# for n in graph_def.node:
# if node.name in n.input:
# is_output = False
# break
# if is_output:
# output_names.append(node.name)
# return input_names, output_names
# # 指定GraphDef pb文件路径
# pb_file_path = "resnet50v15_tf.pb"
# # 加载GraphDef
# graph_def = load_graph_def(pb_file_path)
# # 获取输入和输出的节点名称
# input_names, output_names = get_input_output_names(graph_def)
# print("Input names:", input_names)
# print("Output names:", output_names)
import tensorflow as tf
from tensorflow.python.framework import tensor_util
def load_graph_def(pb_file):
with tf.io.gfile.GFile(pb_file, "rb") as f:
graph_def = tf.compat.v1.GraphDef()
graph_def.ParseFromString(f.read())
return graph_def
def get_graph_inputs_outputs(graph_def):
inputs = []
outputs = []
for node in graph_def.node:
if node.op == 'Placeholder':
shape = None
for attr_value in node.attr.values():
if attr_value.HasField('shape'):
shape = [dim.size for dim in attr_value.shape.dim]
inputs.append({'name': node.name, 'shape': shape})
# Assuming outputs are nodes with no outputs themselves, usually not a strict rule
elif not any(node.name in input for input in [n.input for n in graph_def.node]):
shape = None
try:
tensor_shape = tensor_util.MakeNdarray(node.attr["shape"].shape)
shape = tensor_shape.shape
except:
pass
outputs.append({'name': node.name, 'shape': shape})
return inputs, outputs
def print_graph_info(inputs, outputs):
print("Inputs:")
for input_info in inputs:
print(f"Name: {input_info['name']}, Shape: {input_info['shape']}")
print("\nOutputs:")
for output_info in outputs:
print(f"Name: {output_info['name']}, Shape: {output_info['shape']}")
# Path to your .pb file
pb_file_path = "resnet50v15_tf.pb"
# Load GraphDef
graph_def = load_graph_def(pb_file_path)
# Get inputs and outputs
inputs, outputs = get_graph_inputs_outputs(graph_def)
# Print inputs and outputs
print_graph_info(inputs, outputs)
# Pytorch模型转换onnx模型
## 环境准备
确保环境中有pytorch,可以用pip安装pytorch,具体可以参考PyTorch官网https://pytorch.org/get-started/locally/
```bash
pip install torch
```
## 模型确认
首先,请辨别手里的Pytorch模型是权重数据文件.pth还是包含模型结构以及权重数据的.pt文件,通常惯例是:
- **`.pth` 文件** 通常用于保存模型的权重(`state_dict`)。
- **`.pt` 文件** 通常用于保存整个模型(包括模型结构和权重)。
然而,这只是惯例,实际使用中两者可以互换,也取决于保存文件的人如何命名的,甚至可以是其他的后缀名。因此,为了准确加载模型,你需要知道该文件具体保存了什么。下面python脚本可以判断模型文件是否是完整模型文件。
```python
import torch
# 尝试加载 .pt 文件
try:
model_data = torch.load('resnet50.pt')
print(type(model_data)) # 打印数据类型
if isinstance(model_data, dict):
print(model_data.keys()) # 如果是字典,打印键
print("The .pt file is weights file)
else:
print("The .pt file contains the complete model.")
except Exception as e:
print(f"Error loading model: {e}")
```
## 导出模型
一般情况下,使用保存的权重文件,这里示例模型[下载地址](https://download.pytorch.org/models/resnet50-0676ba61.pth),转换模型用torch.onnx.export函数,具体操作如下。
其中,如果想导出动态模型,可以通过设置dynamic_axes,这里设置dynamic_axes={'input' : {0 : 'batch_size'}, 'output' : {0 : 'batch_size'}},即为将输入和输出tensor的名称为batch_size的索引为0的维度设置为动态模式,导出的模型输入shape将会变为[batch_size, 3, 224, 224]。同理,如果设置为dynamic_axes={'input' : {0 : 'batch_size', 2: 'height', 3: 'width'}, 'output' : {0 : 'batch_size', 2: 'height', 3: 'width'}},导出的模型输入输出shape将为变为[batch_size, 3, height, width]。
```python
import torch
import torchvision.models as models
# 定义模型结构(以 ResNet-50 为例)
model = models.resnet50() # 或者使用自己定义的模型实例
model.load_state_dict(torch.load('resnet50-0676ba61.pth'))
model.eval() # 设置为评估模式
# 示例输入
input_tensor = torch.randn(1, 3, 224, 224) # 修改为你的输入张量形状
# Export the model
torch.onnx.export(model, # 需要转换的pytorch模型变量
input_tensor, # 模型示例输入 (多输入情况为多变量tuple)
"resnet50.onnx", # 导出模型文件名
export_params=True, # 导出权重参数
opset_version=10, # 指定ONNX的opset版本(可选)
do_constant_folding=True, # 是否常量折叠(可选)
input_names = ['input'], # 模型输入的names
output_names = ['output'], # 模型输出的names
dynamic_axes={'input' : {0 : 'batch_size'}, # 动态维度指定
'output' : {0 : 'batch_size'}})
```
运行脚本即可得到onnx模型,如果想导出自己的onnx模型,自行做相应修改。
```bash
python export_onnx.py
```
import torch
import torchvision.models as models
# 定义模型结构(以 ResNet-50 为例)
model = models.resnet50() # 或者使用自己定义的模型实例
model.load_state_dict(torch.load('resnet50-0676ba61.pth'))
model.eval() # 设置为评估模式
# 示例输入
input_tensor = torch.randn(1, 3, 224, 224) # 修改为你的输入张量形状
# Export the model
torch.onnx.export(model, # 需要转换的pytorch模型变量
input_tensor, # 模型示例输入 (多输入情况为多变量tuple)
"resnet50.onnx", # 导出模型文件名
export_params=True, # 导出权重参数
opset_version=10, # 指定ONNX的opset版本(可选)
do_constant_folding=True, # 是否常量折叠(可选)
input_names = ['input'], # 模型输入的names
output_names = ['output'], # 模型输出的names
dynamic_axes={'input' : {0 : 'batch_size'}, # 动态维度指定
'output' : {0 : 'batch_size'}})
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment