PyTorch轉ONNX之F.interpolate

PyTorch轉ONNX之F.interpolate



一、環境說明

  • Conda 4.7.11
  • Python 3.6.9
  • PyTorch 1.4.0
  • ONNX 1.6.0
  • protobuf 3.9.2

二、ONNX安裝問題

如果使用conda install onnx或者conda install -c conda-forge onnx,大有可能會在import onnx時,出現ImportError: libprotobuf.so.20: cannot open shared object file: No such file or directory,這是因爲在現在的python版本下,conda默認安裝的protobuf版本較低。可參考這條issue#2434,按下列命令進行安裝:

conda install protobuf=3.9
conda install -c conda-forge onnx

三、F.interpolate

PyTorch轉ONNX目前遇到的最難受的地方,是對F.interpolate的差強人意的支持,在模型裏面一旦有使用F.interpolate的上採樣方法,就會出問題。問題如下:

1. ONNX的op版本opset_version

在轉換過程中,我們一般會使用命令torch.onnx.export(model, input, "onnx_name.onnx")。那麼默認採用的opset_version=9,當切換爲opset_version=10opset_version=11後,用Netro可視化下進行對比,對比如下。

在這裏插入圖片描述

可以看出,對於同一個節點(node),當F.interpolate(mode='bilinear', align_corners=False)時,op9會將F.interpolate替換爲onnx.Upsampleop10會將其替換爲onnx.Resize,而op11會提供一個onnx.Constant,裏面是一個tensor,而且onnx.Resize內部會出現其它屬性。

那麼這三者有什麼具體區別呢。我可能提供不了準確的區別,下面是我的看法。

對於op9op10,應該是比較近似的,除了方法從onnx.Upsample變成了onnx.Resize,因此需要看看ONNX的源碼,兩者有什麼區別,另外,注意INPUT的scales,它們都使用了同樣的scales,這個scales,是一個onnx.Constant的node,在可視化中是看不到的,它的格式是float32,這就是op9op10op11的重要區別,也是後續坑的來源。接着,對於op11而言,它使用了onnx.Constant作爲一個node,而且在點開看onnx.Resize後,可以看見出現了coordinate_transformation_modecubic_coeff_anearest_mode屬性,這是op11完全支持F.interpolate所產生的屬性,coordinate_transformation_mode是對應align_cornerscubic_coeff_a對應mode=bicubicnearest_mode是對應mode=nearest,而查看INPUTS欄,它的sizes內容的格式是int64op9/op10float32op11int64的不同,造成了坑點,接下來是說明這裏的問題。

2. 插值方法與op版本

首先,在op9/op10下,F.interpolate(mode=nearest)是沒問題的,也不會出現什麼警告,當對於F.interpolate(mode=bilinear, align_corners=False)時,能轉換成功,但會出現如下警告,爲什麼會出現這個警告,我感覺是與下面的計算輸出大小的問題有關,不知道大家對此有什麼看法,麻煩大家賜教。

You are trying to export the model with onnx:Upsample for ONNX opset version 9. 
UserWarning: You are trying to export the model with onnx:Resize for ONNX opset version 10.
This operator might cause results to not match the expected results by PyTorch.
ONNX's Upsample/Resize operator did not match Pytorch's Interpolation until opset 11. Attributes to determine how to transform the input were added in onnx:Resize in opset 11 to support Pytorch's behavior (like coordinate_transformation_mode and nearest_mode).
We recommend using opset 11 and above for models using this operator. 

接着,當F.interpolate(mode=bilinear, align_corners=True)時,轉換就會失敗,報錯如下:

UserWarning: ONNX export failed on upsample_bilinear2d because align_corners == True not supported

而在op10下,上訴警告、報錯就不會出現,那麼總結一下,對於ONNX1.6而言,目前支持如下操作:

F.interpolate nearest bilinear, align_corners=False bilinear, align_corners=True bicubic
op-9 Y Y N N
op-10 Y Y N N
op-11 Y Y Y Y

那麼我們需要知道,ONNX是怎麼確認上採樣後輸出的大小的呢,之前提到,op9/op10的scales的格式爲float32op11的sizes的格式爲int64,爲什麼一直在提scales與sizes呢,因爲它們與計算輸出大小有緊密聯繫。

例如,大小爲input_size=[1, 3, 5, 5]的tensor作爲輸入,我們希望將tensor插值到output_size=[1, 3, 9, 9]。對於op9/op10而言,INPUTS中的X爲輸入tensor,scales爲input_size * scales = output_size,這就是scales的作用,因爲scales的格式爲float32,因此這個output_size竟然就是float32的,而scales=[1., 1., 1.799, 1.799],所以得到的這個output_size=[1, 3, 8.999, 8.999],所以預計的output_size與ONNX計算出來的output_size在精度上就會出問題,導致前後不相等,這樣的結果很神奇吧。對於op11而言,它提供了額外的onnx.Constant的node,INPUTS的sizes直接就是output_size,而sizes的格式爲int64,所以op11output_size與ONNX計算出來的output_size一致。我猜這就是ONNX's Upsample/Resize operator did not match Pytorch's Interpolation until opset 11.警告的來源之一。

好了,今天就寫到這麼多,接下來會開openvion的新坑,也就是pytorch->onnx->openvino這個過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章