yolov8-seg output transpose (0,3,1,2)
ChenLiangChong opened this issue · comments
Issue Type
Feature Request, Others
OS
Linux
onnx2tf version number
1.16.2
onnx version number
1.12.0
onnxruntime version number
1.16.3
onnxsim (onnx_simplifier) version number
0.4.33
tensorflow version number
2.10.0
Download URL for ONNX
https://drive.google.com/file/d/1QWHtwhQleps0rpy3HDt-E27Rqoev0nr2/view?usp=sharing
Parameter Replacement JSON
onnx2tf -i "yolov8n-seg.onnx" -o "yolov8n-seg_saved_model" --verbosity info -oiqt -ioqd "uint8"
Description
-
Deploying efficiently on the Android edge can save at least 200ms if this problem is solved.
-
After model conversion, the output dimensions also change. If the output was originally 4-dimensional, it becomes (0,2,3,1). I believe this is due to applying a dimension transformation to the entire model. However, in Android, we cannot directly use methods like transpose as in Python, and other approaches are too time-consuming.
-
I have already tried to find a way to add a transpose layer before the model's output in a similar conversion tool, but I cannot find the right place to make adjustments in this project.
-
Solving this problem would greatly benefit deployment on the edge.
-
I am using another conversion tool, https://github.com/MPolaris/onnx2tflite, and in my modified code in utils/builder.py line 88, I have added the following code.
input_nodes = [tf_tensor[x.name] for x in model_graph.input]
outputs_nodes = [tf_tensor[x.name] for x in model_graph.output]
//add transpose to get correct output
transposed_outputs = [tf.transpose(node, perm=[0, 3, 1, 2]) for node in outputs_nodes]
keras_model = keras.Model(inputs=input_nodes, outputs=transposed_outputs)
keras_model.trainable = False
All you have to do is break up the model before the final layer, Transpose
.
Read the README so thoroughly that it has holes in it.
https://github.com/PINTO0309/onnx2tf#2-run-test
# Split the model at the middle position for debugging
# Specify the output name of the OP
$ onnx2tf -i resnet18-v1-7.onnx -onimc resnetv15_stage2_conv1_fwd resnetv15_stage2_conv2_fwd
Hello, it seems like my previous response did not address my issue.
In the context of YOLOv8, the final output tensor shape is (1, 84, 8400) for my project. In YOLOv8-seg, the original output shape is (1, 116, 8400) and (1, 32, 160, 160).
The YOLOv8 official implementation also handles this special case in ultralytics/ultralytics/nn/autobackend.py, line 444.
# TF segment fixes: export is reversed vs ONNX export and protos are transposed
if len(y) == 2: # segment with (det, proto) output order reversed
if len(y[1].shape) != 4:
y = list(reversed(y)) # should be y = (1, 116, 8400), (1, 160, 160, 32)
y[1] = np.transpose(y[1], (0, 3, 1, 2)) # should be y = (1, 116, 8400), (1, 32, 160, 160)
y = [x if isinstance(x, np.ndarray) else x.numpy() for x in y]
In the model_zoo tflite provided by me, the shapes are (1, 116, 8400) and (1, 160, 160, 32), but in ONNX, it's (1, 116, 8400) and (1, 32, 160, 160). This means that the mask part is automatically transposed during conversion.
I am wondering if it's possible to add a transpose operation just before the last layer to handle this special case. When deploying, I want to output the original shapes (1, 116, 8400) and (1, 32, 160, 160) for efficiency. I have ten outputs for reg, cls, ms, and seg, respectively:
(1, 80, 80, 80) (1, 80, 40, 40) (1, 80, 20, 20)
(1, 1, 4, 6400) (1, 1, 4, 1600) (1, 1, 4, 400)
(1, 32, 80, 80) (1, 32, 40, 40) (1, 32, 20, 20)
(1, 32, 160, 160)
However, after converting to TFLite, the shapes become:
(1, 80, 80, 80) (1, 40, 40, 80) (1, 20, 20, 80)
(1, 6400, 4, 1) (1, 1600, 4, 1) (1, 400, 4, 1)
(1, 80, 80, 32) (1, 40, 40, 32) (1, 20, 20, 32)
(1, 160, 160, 32)
Simply extrapolate Transpose to the back of the final layer.
Hello, I have already attempted to extrapolate the Transpose operation to the back of the final layer.
However I encountered some problems, and the output of the onnx model compared to the TFLite output doesn't seem to indicate that the operation was successful.
I think the reg3 in onnx and Identity_4296 in tflite is the same output.
but i still got (1,1,4,400) and (1,4,400,1)
I suspect there might be an issue with how I wrote my JSON file. The JSON file I used is shown below.
{
"format_version": 1,
"operations": [
{
"op_name": "reg1",
"param_target": "outputs",
"param_name": "reg1",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "cls1",
"param_target": "outputs",
"param_name": "cls1",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "reg2",
"param_target": "outputs",
"param_name": "reg2",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "cls2",
"param_target": "outputs",
"param_name": "cls2",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "reg3",
"param_target": "outputs",
"param_name": "reg3",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "cls3",
"param_target": "outputs",
"param_name": "cls3",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "mc1",
"param_target": "outputs",
"param_name": "mc1",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "mc2",
"param_target": "outputs",
"param_name": "mc2",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "mc3",
"param_target": "outputs",
"param_name": "mc3",
"post_process_transpose_perm": [0, 3, 2, 1]
},
{
"op_name": "seg",
"param_target": "outputs",
"param_name": "seg",
"post_process_transpose_perm": [0, 3, 2, 1]
}
]
}
Can you help me confirm what adjustments are needed? Thank you.