Yet-another-efficientdet-pytorch: How to change the model to onnx?

Created on 12 Apr 2020  ·  20Comments  ·  Source: zylo117/Yet-Another-EfficientDet-Pytorch

Thank you for your contribution!I want to change the model to onnx , but I get many errors.could you tell me when will you update the code to support onnx format?

Most helpful comment

@vicwer 我也转成功,但是我用onnxruntime加载的时候报错onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Node () Op (Conv) [ShapeInferenceError] Attribute strides has incorrect size,你有遇到过这错误吗?

modify efficientnet/model.py line 50:

Depthwise convolution phase

    k = self._block_args.kernel_size
    s = self._block_args.stride
    if isinstance(s, list):
        s = s[0]
    # print(s)
    self._depthwise_conv = Conv2d(
        in_channels=oup, out_channels=oup, groups=oup,  # groups makes it depthwise
        kernel_size=k, stride=s, bias=False)

strides should be int for converting to onnx. in author's code sometimes it's list

All 20 comments

I haven't worked on that, so I can't provide any more infomation. you can share you work or error info and let the community give you some help.

I haven't worked on that, so I can't provide any more infomation. you can share you work or error info and let the community give you some help.
I get this error when I am trying to change the model to onnx:
File "/home/hy/anaconda3/lib/python3.7/site-packages/torch/onnx/symbolic_helper.py", line 82, in _parse_arg "', since it's not constant, please try to make " RuntimeError: Failed to export an ONNX attribute 'onnx::Div', since it's not constant, please try to make things (e.g., kernel size) static if possible
And my code is here:
model = EfficientDetBackbone(compound_coef=compound_coef, num_classes=len(obj_list)) model.load_state_dict(torch.load(f'weights/efficientdet-d{compound_coef}.pth')) model = model.cuda() image_input = torch.rand(1,3,512,512) image_input = image_input.cuda() model.backbone_net.model.set_swish(memory_efficient=False) torch.onnx.export(model, image_input, 'weights/efficientdet_D4.onnx', verbose=False) model.backbone_net.model.set_swish(memory_efficient=True)
it's running well on the other depository

I have resolved it,just set flag onnx_export to true,it will work.
file backbone.py,add onnx_export :
`class EfficientDetBackbone(nn.Module):
def __init__(self, num_classes=80, compound_coef=0, load_weights=False,onnx_export = False, **kwargs):
super(EfficientDetBackbone, self).__init__()
self.compound_coef = compound_coef

    self.backbone_compound_coef = [0, 1, 2, 3, 4, 5, 6, 6]
    self.fpn_num_filters = [64, 88, 112, 160, 224, 288, 384, 384]
    self.fpn_cell_repeats = [3, 4, 5, 6, 7, 7, 8, 8]
    self.input_sizes = [512, 640, 768, 896, 1024, 1280, 1280, 1536]
    self.box_class_repeats = [3, 3, 3, 4, 4, 4, 5, 5]
    self.anchor_scale = [4., 4., 4., 4., 4., 4., 4., 5.]
    self.aspect_ratios = kwargs.get('ratios', [(1.0, 1.0), (1.4, 0.7), (0.7, 1.4)])
    self.num_scales = len(kwargs.get('scales', [2 ** 0, 2 ** (1.0 / 3.0), 2 ** (2.0 / 3.0)]))
    conv_channel_coef = {
        # the channels of P3/P4/P5.
        0: [40, 112, 320],
        1: [40, 112, 320],
        2: [48, 120, 352],
        3: [48, 136, 384],
        4: [56, 160, 448],
        5: [64, 176, 512],
        6: [72, 200, 576],
        7: [72, 200, 576],
    }

    num_anchors = len(self.aspect_ratios) * self.num_scales

    self.bifpn = nn.Sequential(
        *[BiFPN(self.fpn_num_filters[self.compound_coef],
                conv_channel_coef[compound_coef],
                True if _ == 0 else False,
                attention=True if compound_coef < 6 else False,onnx_export = onnx_export)  # modify by alex , add onnx_export
          for _ in range(self.fpn_cell_repeats[compound_coef])])

    self.num_classes = num_classes
    self.regressor = Regressor(in_channels=self.fpn_num_filters[self.compound_coef], num_anchors=num_anchors,
                               num_layers=self.box_class_repeats[self.compound_coef],onnx_export = onnx_export) # modify by alex , add onnx_export
    self.classifier = Classifier(in_channels=self.fpn_num_filters[self.compound_coef], num_anchors=num_anchors,
                                 num_classes=num_classes,
                                 num_layers=self.box_class_repeats[self.compound_coef],onnx_export = onnx_export) # modify by alex , add onnx_export

    self.anchors = Anchors(anchor_scale=self.anchor_scale[compound_coef], **kwargs)  # anchor_scale

`

@zhui-ying Would you share all of your modification more precisely?
I tried to reproduce but I am still getting errors.

@zhui-ying @ordogfioka I modify the code as what you did, but I still got the same problem as you post before.

@zhui-ying Same question, could you provide a complete code to convert onnx? Thanks a lot.

change opset_version=11 works though:
torch.onnx.export(model, image_input, 'weights/efficientdet_D4.onnx', verbose=True, opset_version=11)`
But it causes errors when converting to trt using tensorrt 7.0

我根据你改的试了试还是出现了‘Failed to export an ONNX attribute, since it's not constant, please try to make things (e.g., kernel size) static if possible’这个错误,这个改咋整啊,我的pytorch版本是1.2.0

我根据你改的试了试还是出现了‘Failed to export an ONNX attribute, since it's not constant, please try to make things (e.g., kernel size) static if possible’这个错误,这个改咋整啊,我的pytorch版本是1.2.0

我用的1.4

我根据你改的试了试还是出现了‘Failed to export an ONNX attribute, since it's not constant, please try to make things (e.g., kernel size) static if possible’这个错误,这个改咋整啊,我的pytorch版本是1.2.0

我用的1.4

你好,我也用的1.4,但是转onnx时,报错RuntimeError: ONNX export failed: Could't export Python operator SwishImplementation,你有遇到这样的问题吗?

onnx_export = true
opset_version=11

onnx_export = true
opset_version=11

作者在utils_extra.py 里自己定义的两个类(staticsamepadding和pooling)里面的forward函数里包含了python,numpy的代码,并且是根据x动态确定shape的,这些是不能直接导出成onnx的。解决方法是把image_shape 当作参数传入init里。比如:

class Conv2dStaticSamePadding(nn.Module):
    """
    created by Zylo117
    The real keras/tensorflow conv2d with same padding
    """

    def __init__(self, in_channels, out_channels, kernel_size, stride=1, bias=True, groups=1, dilation=1, image_size=None, **kwargs):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride,
                              bias=bias, groups=groups)
        self.stride = self.conv.stride
        self.kernel_size = self.conv.kernel_size
        self.dilation = self.conv.dilation

        if isinstance(self.stride, int):
            self.stride = [self.stride] * 2
        elif len(self.stride) == 1:
            self.stride = [self.stride[0]] * 2

        if isinstance(self.kernel_size, int):
            self.kernel_size = [self.kernel_size] * 2
        elif len(self.kernel_size) == 1:
            self.kernel_size = [self.kernel_size[0]] * 2

        assert image_size is not None
        h, w = image_size if type(image_size) == list else [image_size, image_size]

        h_step = math.ceil(w / self.stride[1])
        v_step = math.ceil(h / self.stride[0])
        h_cover_len = self.stride[1] * (h_step - 1) + 1 + (self.kernel_size[1] - 1)
        v_cover_len = self.stride[0] * (v_step - 1) + 1 + (self.kernel_size[0] - 1)

        extra_h = h_cover_len - w
        extra_v = v_cover_len - h

        left = extra_h // 2
        right = extra_h - left
        top = extra_v // 2
        bottom = extra_v - top

        self.static_padding = nn.ZeroPad2d((left, right, top, bottom))

    def forward(self, x):
        x = self.static_padding(x)
        x = self.conv(x)

        return x

同理Anchor class也要修改。

@neverrop 我用torch1.4,添加onnx_export=true, opset_version=true之后其他没改动,倒是转换成功了,很神奇。我目的是onnx2ncnn,但是失败了,里面不支持的op太多了,用onnxsim精简op时报错,很无力。

@vicwer 我也转成功,但是我用onnxruntime加载的时候报错onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Node () Op (Conv) [ShapeInferenceError] Attribute strides has incorrect size,你有遇到过这错误吗?

onnx_export = true
opset_version=11

作者在utils_extra.py 里自己定义的两个类(staticsamepadding和pooling)里面的forward函数里包含了python,numpy的代码,并且是根据x动态确定shape的,这些是不能直接导出成onnx的。解决方法是把image_shape 当作参数传入init里。比如:

class Conv2dStaticSamePadding(nn.Module):
    """
    created by Zylo117
    The real keras/tensorflow conv2d with same padding
    """

    def __init__(self, in_channels, out_channels, kernel_size, stride=1, bias=True, groups=1, dilation=1, image_size=None, **kwargs):
        super().__init__()
        self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride,
                              bias=bias, groups=groups)
        self.stride = self.conv.stride
        self.kernel_size = self.conv.kernel_size
        self.dilation = self.conv.dilation

        if isinstance(self.stride, int):
            self.stride = [self.stride] * 2
        elif len(self.stride) == 1:
            self.stride = [self.stride[0]] * 2

        if isinstance(self.kernel_size, int):
            self.kernel_size = [self.kernel_size] * 2
        elif len(self.kernel_size) == 1:
            self.kernel_size = [self.kernel_size[0]] * 2

        assert image_size is not None
        h, w = image_size if type(image_size) == list else [image_size, image_size]

        h_step = math.ceil(w / self.stride[1])
        v_step = math.ceil(h / self.stride[0])
        h_cover_len = self.stride[1] * (h_step - 1) + 1 + (self.kernel_size[1] - 1)
        v_cover_len = self.stride[0] * (v_step - 1) + 1 + (self.kernel_size[0] - 1)

        extra_h = h_cover_len - w
        extra_v = v_cover_len - h

        left = extra_h // 2
        right = extra_h - left
        top = extra_v // 2
        bottom = extra_v - top

        self.static_padding = nn.ZeroPad2d((left, right, top, bottom))

    def forward(self, x):
        x = self.static_padding(x)
        x = self.conv(x)

        return x

同理Anchor class也要修改。

能否提供修改好的类文件? staticsamepadding(已经有了)和pooling, 以及Anchor class

@neverrop 或者能否百度网盘共享下一转换好的模型呢?

@vicwer 我也转成功,但是我用onnxruntime加载的时候报错onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Node () Op (Conv) [ShapeInferenceError] Attribute strides has incorrect size,你有遇到过这错误吗?

modify efficientnet/model.py line 50:

Depthwise convolution phase

    k = self._block_args.kernel_size
    s = self._block_args.stride
    if isinstance(s, list):
        s = s[0]
    # print(s)
    self._depthwise_conv = Conv2d(
        in_channels=oup, out_channels=oup, groups=oup,  # groups makes it depthwise
        kernel_size=k, stride=s, bias=False)

strides should be int for converting to onnx. in author's code sometimes it's list

@RENCHEN90 thanks man, this solved the issue for me. However, I had to modify line 415 in model.py as well:
if block._depthwise_conv.stride == [2, 2]: change to
if block._depthwise_conv.stride == (2, 2):

since if you pass a single int as stride to Conv2dStaticSamePadding the type of depthwise_conv.stride will be tuple instead of list. At least with pytorch 1.5 and python 3.7 this is the case.

`class Conv2dStaticSamePadding(nn.Module):
"""
created by Zylo117
The real keras/tensorflow conv2d with same padding
"""

def __init__(self, in_channels, out_channels, kernel_size, stride=1, bias=True, groups=1, dilation=1, image_size=None, **kwargs):
    super().__init__()
    self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride=stride,
                          bias=bias, groups=groups)
    self.stride = self.conv.stride
    self.kernel_size = self.conv.kernel_size
    self.dilation = self.conv.dilation

    if isinstance(self.stride, int):
        self.stride = [self.stride] * 2
    elif len(self.stride) == 1:
        self.stride = [self.stride[0]] * 2

    if isinstance(self.kernel_size, int):
        self.kernel_size = [self.kernel_size] * 2
    elif len(self.kernel_size) == 1:
        self.kernel_size = [self.kernel_size[0]] * 2

    assert image_size is not None
    h, w = image_size if type(image_size) == list else [image_size, image_size]

    h_step = math.ceil(w / self.stride[1])
    v_step = math.ceil(h / self.stride[0])
    h_cover_len = self.stride[1] * (h_step - 1) + 1 + (self.kernel_size[1] - 1)
    v_cover_len = self.stride[0] * (v_step - 1) + 1 + (self.kernel_size[0] - 1)

    extra_h = h_cover_len - w
    extra_v = v_cover_len - h

    left = extra_h // 2
    right = extra_h - left
    top = extra_v // 2
    bottom = extra_v - top

    self.static_padding = nn.ZeroPad2d((left, right, top, bottom))

def forward(self, x):
    x = self.static_padding(x)
    x = self.conv(x)

    return x`

I had a error: assert image_size is not None
How should i set image_size for Conv2dStaticSamePadding(conv_channels[2], num_channels, 1)

@neverrop 我用torch1.4,添加onnx_export=true, opset_version=true之后其他没改动,倒是转换成功了,很神奇。我目的是onnx2ncnn,但是失败了,里面不支持的op太多了,用onnxsim精简op时报错,很无力。

@

@vicwer 我也转成功,但是我用onnxruntime加载的时候报错onnxruntime.capi.onnxruntime_pybind11_state.Fail: [ONNXRuntimeError] : 1 : FAIL : Node () Op (Conv) [ShapeInferenceError] Attribute strides has incorrect size,你有遇到过这错误吗?

modify efficientnet/model.py line 50:

Depthwise convolution phase

    k = self._block_args.kernel_size
    s = self._block_args.stride
    if isinstance(s, list):
        s = s[0]
    # print(s)
    self._depthwise_conv = Conv2d(
        in_channels=oup, out_channels=oup, groups=oup,  # groups makes it depthwise
        kernel_size=k, stride=s, bias=False)

strides should be int for converting to onnx. in author's code sometimes it's list

@RENCHEN90 your code can send me ([email protected]), the new code can not succes

Was this page helpful?
0 / 5 - 0 ratings

Related issues

HaolyShiit picture HaolyShiit  ·  5Comments

LiangTinting picture LiangTinting  ·  6Comments

AlexeyAB picture AlexeyAB  ·  9Comments

CuiZhiying picture CuiZhiying  ·  8Comments

mnslarcher picture mnslarcher  ·  5Comments