溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

發(fā)布時間:2020-08-09 02:13:51 來源:ITPUB博客 閱讀:215 作者:京東云技術新知 欄目:互聯(lián)網科技

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試 是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

作者:京東AI研究院 張建浩

煉丹師在轉換模型的時候,經常會發(fā)現(xiàn)給轉換前后的模型輸入同樣的圖片,模型結果有微小的差別。其中的原因有數(shù)值算法的誤差、不同 jpeg 解碼庫產生的結果不同等等,也有不同框架內部對某些算子的實現(xiàn)差異。

在給 ONNX 貢獻 Resize 算子的 spec 的時候,我發(fā)現(xiàn) Resize 是一個突出體現(xiàn)了框架實現(xiàn)差異的算子——多種 Resize 類型、不統(tǒng)一的超參數(shù)、將錯就錯的歷史遺留 bug 和其它極易被忽略的問題集中在一起,導致幾乎每個框架的 Resize 操作的結果都有差異,而 ONNX 是一個神經網絡模型的中間格式,它應該盡量保留原始框架的算子的語義。經過查看相關論文和各種框架的源代碼,我分析和總結了 Resize 操作眾多的實現(xiàn)方式。最終為 ONNX 貢獻了一個較為完善的、標準化的 Resize 算子的 spec,它包含多個(基本)正交的參數(shù),TensorFlow 1.x、TensorFlow 2.x、PyTorch、OpenCV 的 resize/interpolation 方法都可以用這個算子 100% 無損的表達。 本文將簡單介紹各種 resize 操作的共同流程,并分析是哪些因素引起了不同框架 resize 操作的不同。

多維 tensor (例如二維圖像)的 resize 操作是用多個在一維 tensor 上進行的 resize 操作組合出來的,所以我們只討論一維 tensor 上的 resize 操作,經過分析各個框架的源代碼,我發(fā)現(xiàn)它的流程可以總結如下:

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

1

先討論w和f,w(i)是第 i 個像素點的坐標,乍一看, w(i)完全可以等于i本身,其實沒有這么簡單。例如一個長度為 3 的 tensor,如果第i個像素點的坐標等于i本身,那么三個像素點在tensor 中的位置就如下圖中最左邊的樣子,橫線的長度代表一維 tensor 的長度,圓圈代表像素點:

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

三個像素點沒有對稱地分布在 tensor 上,而是往左偏了。出于直覺,我們覺得這不是一件特別好的事情。在各種框架中,有兩種常見的方法來解決這個問題:

一個是選取w(i)=i+0.5,以一個長度為 3 的一維 tensor 為例,它第 0 個像素點在 0.5 位置,第 1 個像素點在 1.5 位置,第 2 個像素點在 2.5 位置,這稱為 half_pixel,也就是上圖中中間的方法。這種方法中,

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

(這很符合直覺)。另一個是仍讓w(i)=i,但改變函數(shù)f,使

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

仍以長度為 3 的一維 tensor 為例,這種方法相當于在 resize 時砍掉了最右邊長度為 1 的部分,使像素點的分布“被”對稱了。這稱為 align_corner,也就是上圖中最右邊的方法,在各種框架的 resize 方法的參數(shù)里常見的 align_corner=True/False 就是它了,它的名字來源于它可以讓 tensor 中第一個和最后一個像素(即 corner)在縮放后保持不變。


那如果我們不采用這兩種方法,一定要使用“直覺不好”的 asymmetric 方法,究竟會發(fā)生什么呢?TensorFlow 1.x 就給我們提供了這樣一個反面典型,它在 align_corner=False 時的實現(xiàn)是錯的,原因就是使用了上圖中錯誤的 asymmetric 方法,這會導致奇怪的縮放結果,這篇博客中???? https://hackernoon.com/how-tensorflows-tf-image-resize-stole-60-days-of-my-life-aba5eb093f35,

作者用 TensorFlow 1.x 訓練的超分辨率神經網絡總是出現(xiàn)奇怪的問題,最終他發(fā)現(xiàn)問題根源是 TensorFlow 錯誤的 resize 實現(xiàn),他還給了一個形象的例子:把 16x16 的下圖左側圖像縮小到 4x4,本應得到如下圖右側所示的圖像,而 TensorFlow 1.x 卻給出了下圖中間的奇怪結果,圖像的對稱性被完全破壞了,其中的原因就如上文所述。TensorFlow 1.x 的 resize 結果和其它框架不同的一大原因就是它錯誤的 resize 實現(xiàn),好在 TensorFlow 2.x 已經修復了這個問題。

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

2

接下來討論另外兩個函數(shù)g和h,nearest, linear, cubic 這三種常見的 resize 的不同方式,是在g和h上有所不同。如上文所述,函數(shù)  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  得到離  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  最近的像素點,nearest 只需要找最近的一個像素點,linear 要找最近的兩個(左右各一個),cubic 要找最近的四個(左右各兩個);函數(shù)h(a,r)是計算這一個/兩個/四個像素點的加權平均值,其中權值是由r確定的(如上文所述,r是  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試 距左側像素點的距離)。對 nearest/linear/cubic 的每一種來說,如何從r得到各個像素點的權值都有各自標準的實現(xiàn),nearest resize 不必說,對于 linear resize,兩個像素點的權值是  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  。對 cubic 來說,四個像素點的權值是

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試


[1]其中A是一個固定的參數(shù),它的取值卻是每個框架不同,兩個常見的選擇是 -0.5 (TensorFlow 部分版本的實現(xiàn))和 -0.75(PyTorch)。因為A沒有統(tǒng)一的標準取值,所以各個框架的 cubic resize 結果不同是常見的事情。

補充一句題外話:cubic resize 的權值計算起來比 linear resize 復雜的多,所以它的耗時肯定會長一些,但產生的圖像性質更好(這篇 paper ???? https://arxiv.org/abs/1812.0118 7 發(fā)現(xiàn)圖片預處理使用 cubic resize 可以提升分類網絡準確率)。

還有一個會引起 cubic resize 結果差異的細節(jié)是,cubic resize 需要找到  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  的左右各兩個最相鄰的像素點,但  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  左右兩側不一定能保證各有兩個像素點(假設某種情況下計算得到  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  ,那么它左邊只有一個像素點),此時也有兩種現(xiàn)存的不同方法,一種是對圖像做 edge padding,即認為仍從左邊找到了兩個像素點,并且這兩個像素點的值都是第一個像素點的值;另一種是認為找到了三個而不是四個像素點,并對三個像素點的權值做歸一化。

3

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

小結

總結一下,各個框架 Resize 操作的結果不同的原因是多種多樣的,例如 TensorFlow 用了自己發(fā)明的錯誤實現(xiàn)、cubic resize 中參數(shù) A 沒有固定的取值、非整數(shù)的

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

是否自動取整等等。

ONNX Resize 算子的 spec 就是基于上面的分析寫出來的,具體的描述在???? https://github.com/onnx/onnx/bl ob/master/docs/Operators.md#Resize ,

Python 版的參考實現(xiàn)在 ???? https://github.com/onnx/onnx/bl ob/master/onnx/backend/test/case/node/resize.py

其中比較核心的屬性 coordinate_transformation_mode 是把w、f和  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試  復合得到的單個函數(shù)  是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試 ,即

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

在這里沒有用獨立的函數(shù)w和f的原因除了看起來更簡單之外,也有解決現(xiàn)實問題的考慮——有一些框架的某些 resize 實現(xiàn)沒有使用

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

的形式,而是直接讓

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

雖然這顯然是不合理的(coordinate_transformation_mode=tf_half_pixel_for_nn 就描述了這樣一個不合理的實現(xiàn)),但也只能承認它們的存在。相比起來,上一個版本的 ONNX Resize 算子 spec 的制定者沒有意識到 Resize 算子的復雜性,完全模仿了 TensorFlow 的實現(xiàn),不僅和其它框架的結果不一致,而且連 TensorFlow 的 bug 也一并模仿了。

現(xiàn)在 TensorFlow、PyTorch 都支持了導出這一版本的 Resize 算子,TensorRT 等部署框架也支持導入和運行這個 Resize 算子。自己創(chuàng)造的東西能被眾多知名的框架跟進,我感到非常大的成就感。

參考: https://ieeexplore.ieee.org/doc ument/1163711

歡迎點擊“ 京東智聯(lián)云 ”了解更多精彩內容!

是什么引起了各個框架 Resize 操作的結果不同?——來自 ONNX 的標準化嘗試

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI