AdaptiveAvgPool2D 不支持 onnx 导出,导出过程会告诉你,onnx不支持那个动态操作巴拉巴拉
我用的是 pp_liteseg 导出为 onnx 模型,都一样,paddle和Torch的 Adaptive Pool2D 都是动态的,onnx暂时都不支持,我根据下述公式,将 AdaptiveAvgPool2D 改了,可以用onnx导出了,但是需要指定一下原图的大小,和输出图的大小
h s t a r t = f l o o r ( i ∗ H i n / H o u t ) h e n d = c e i l ( ( i + 1 ) ∗ H i n / H o u t ) w s t a r t = f l o o r ( j ∗ W i n / W o u t ) w e n d = c e i l ( ( j + 1 ) ∗ W i n / W o u t ) O u t p u t ( i , j ) = ∑ I n p u t [ h s t a r t : h e n d , w s t a r t : w e n d ] ( h e n d − h s t a r t ) ∗ ( w e n d − w s t a r t ) \begin{aligned} hstart &= floor(i * H_{in} / H_{out})\\ hend &= ceil((i + 1) * H_{in} / H_{out})\\ wstart &= floor(j * W_{in} / W_{out}) \\ wend &= ceil((j + 1) * W_{in} / W_{out}) \\ Output(i ,j) &= \frac{\sum Input[hstart:hend, wstart:wend]}{(hend - hstart) * (wend - wstart)} \end{aligned} hstarthendwstartwendOutput(i,j)=floor(i∗Hin/Hout)=ceil((i+1)∗Hin/Hout)=floor(j∗Win/Wout)=ceil((j+1)∗Win/Wout)=(hend−hstart)∗(wend−wstart)∑Input[hstart:hend,wstart:wend]
参考自:
AdaptiveAvgPool2D
class CostomAdaptiveAvgPool2D(nn.Layer):def __init__(self, output_size, input_size):super(CostomAdaptiveAvgPool2D, self).__init__()self.output_size = output_sizeself.input_size = input_sizedef forward(self, x):H_in, W_in = self.input_sizeH_out, W_out = [self.output_size, self.output_size] \if isinstance(self.output_size, int) \else self.output_sizeout_i = []for i in range(H_out):out_j = []for j in range(W_out):hs = int(np.floor(i * H_in / H_out))he = int(np.ceil((i+1) * H_in / H_out))ws = int(np.floor(j * W_in / W_out))we = int(np.ceil((j+1) * W_in / W_out))# print(hs, he, ws, we)kernel_size = [he-hs, we-ws]out = F.avg_pool2d(x[:, :, hs:he, ws:we], kernel_size) out_j.append(out)out_j = paddle.concat(out_j, -1)out_i.append(out_j)out_i = paddle.concat(out_i, -2)return out_i
用 CostomAdaptiveAvgPool2D 调换 AdaptiveAvgPool2D, 并且指定原图和输出图的大小即可
[14,14]
就是输入图的大小,我这个pp_liteseg 输入shape是 224 × 224 224\times 224 224×224,所以上一层的shape是
[-1, 512, 14, 14]
然后一次过三个AdaptiveAvgPool2D 变成
[-1, 512, 1, 1]
[-1, 512, 2, 2]
[-1, 512, 4, 4]
然后 paddle2onnx 导出即可
paddle2onnx --model_dir . \--model_filename model.pdmodel \--params_filename model.pdiparams \--opset_version 11 \--save_file output.onnx
最后测试一下:
import paddle
import numpy as np
import paddle.nn as nn
import paddle.nn.functional as Fclass CostomAdaptiveAvgPool2D(nn.Layer):def __init__(self, output_size, input_size):super(CostomAdaptiveAvgPool2D, self).__init__()self.output_size = output_sizeself.input_size = input_sizedef forward(self, x):H_in, W_in = self.input_sizeH_out, W_out = [self.output_size, self.output_size] \if isinstance(self.output_size, int) \else self.output_sizeout_i = []for i in range(H_out):out_j = []for j in range(W_out):hs = int(np.floor(i * H_in / H_out))he = int(np.ceil((i+1) * H_in / H_out))ws = int(np.floor(j * W_in / W_out))we = int(np.ceil((j+1) * W_in / W_out))# print(hs, he, ws, we)kernel_size = [he-hs, we-ws]out = F.avg_pool2d(x[:, :, hs:he, ws:we], kernel_size) out_j.append(out)out_j = paddle.concat(out_j, -1)out_i.append(out_j)out_i = paddle.concat(out_i, -2)return out_i# 输入
input_data = np.random.rand(1, 3, 128, 128)
x = paddle.to_tensor(input_data)adaptive_avg_pool = paddle.nn.AdaptiveAvgPool2D(output_size=1)
pool_out = adaptive_avg_pool(x = x)
costom_avg_pool = CostomAdaptiveAvgPool2D(output_size=(1, 1), input_size=(128, 128))
costom_pool_out = costom_avg_pool(x = x)
>>> costom_pool_out
Tensor(shape=[1, 3, 1, 1], dtype=float64, place=Place(gpu:0), stop_gradient=True,[[[[0.49809119]],[[0.49674173]],[[0.50111742]]]])>>> pool_out
Tensor(shape=[1, 3, 1, 1], dtype=float64, place=Place(gpu:0), stop_gradient=True,[[[[0.49809119]],[[0.49674173]],[[0.50111742]]]])