NumPy的as_strided是一個操弄data的小技巧,常用於避免使用for迴圈 來加速運算,最典型的例子是Convolution。

Convolution在影像處理上是很常利用到的計算。直覺上要實現它非常簡單,用for迴圈 幾行程式即可輕易完成。但是當資料量很龐大的時候,例如幾萬張圖,這個時候在Python上 跑for迴圈就是一場惡夢了。

如同Matlab,如果我們可以用矩陣運算取代for迴圈的話,那麼計算效率將會有驚人的提升。 以下圖的一維矩陣做例子,要得到綠色的5個值,我們需要經過5次的迴圈;但如果先把convolution 要用到的data展開成一個二維矩陣,這時候只要一個簡單的矩陣乘法就可以得到我們要的結果了。 簡單的說,就是以空間換取時間!

在了解如何加速之後,我們來看看在Python上怎麼用as_strided來實作。 首先我們建立一張簡單的2D圖,然後定義好filter的大小 (HH, WW)以及stride的距離。根據以上 資訊,我們可以計算出經過convolution後的圖的大小 (OH, OW)。

再來就是用as_strided把data展開,它需要兩個參數: shapestridesshape就是data展開後的矩陣的dimension,由於在每個要進行 convolution的位置都要複製好一組(HH, WW)的data,我們可以推得shape = (OH, OW, HH, WW)。

strides指的是memory offset。x這個二維的matrix實際上是存放在一維的記憶體中, 存放的順序是先由左到右再由上到下,在我們的範例中每個pixel的值恰好就是它存放的order。 我們要透過strides告訴as_strided要怎麼在存放x的這一大塊memory之間尋找data。 stridesshape有對應的關係,在這個範例,strides必須要有4個值, 讓我們一個一個看該怎麼填值。

  • stride of WW: 在這個convolution block之中,下一個W方向的data,填1 (addr+=1)。
  • stride of HH: 在這個convolution block之中,下一個H方向的data,填W (addr+=1*w)。
  • stride of OW: 在這張圖之中,下一個W方向convolution的位置,填stride (addr+=stride)。
  • stride of OH: 在這張圖之中,下一個H方向convolution的位置,填stride*W (addr+=stride*W)。

從上面的規則,我們可以得到strides = (stride*W, stride, 1*W, 1)。不過,這個只是順序的offset, 不是實際記憶體位址的offset。我們必須再根據x實際的data type來計算記憶體位址, 因此最後再將strides乘上x.itemsize。