揭祕 NumPy 的形

使用 NumPy, TensorFlow, Pytorch ,我們經常會使用數組的 reshape 操作,變化數組為各種 shape.

一個一維數組,長度為 12,為什麼能變化為二維 (12,1) 或 (2,6) 等,三維 (12,1,1) 或 (2,3,2) 等,四維 (12,1,1,1) 或 (2,3,1,2) 等,總之,可以變化為任意多維度。

reshape 是如何做到的?使用了什麼魔法數據結構和算法嗎?

NumPy 作為數據分析和深度學習領域的必備基礎庫,數值計算效率666得飛起。今天我們就以 NumPy 數組的 reshape 方法為例,一探究竟數據的這種 reshape 變化及背後的實現原理。

這篇文章對於 reshape 方法的原理解釋,會很獨到,儘可能讓朋友們弄明白數組 reshape 的魔法。

如同往常一樣,導入 NumPy 包:

<code>import numpy as np
/<code>

創建一個一維數組 a ,從 0 開始,間隔為 2 ,含有 12 個元素的數組:

<code>a = np.arange(0,24,2)
/<code>

打印數組 a

<code>In [48]: a
Out[48]: array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22])
/<code>

如上數組 a, NumPy 會將其解讀成兩個結構,一個 buffer ,還有一個 view 。

buffer 的示意圖如下所示:

揭秘 NumPy 的形

view 是解釋 buffer 的一個結構,比如數據類型, flags 信息等:

<code>In [50]: a.dtype
Out[50]: dtype('int32')

In [51]: a.flags
Out[51]:
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
WRITEBACKIFCOPY : False
UPDATEIFCOPY : False
/<code>

使用 a[6] 訪問數組 a 中 index 為 6 的元素。從背後實現看, NumPy 會輔助一個軸,軸的取值為 0 到 11 。

從概念上看,它的示意圖如下所示:

揭秘 NumPy 的形

所以,藉助這個軸 i , a[6] 就會被索引到元素 12,如下所示:

揭秘 NumPy 的形

至此,大家要建立一個軸的概念。

接下來,做一次 reshape 變化,變化數組 a 的 shape 為 (2,6):

<code>b = a.reshape(2,6)
/<code>

打印 b:

<code>In [53]: b
Out[53]:

array([[ 0, 2, 4, 6, 8, 10],
[12, 14, 16, 18, 20, 22]])
/<code>

此時,NumPy 會建立兩個軸,假設為 i , j , i 的取值為 0 到 1, j 的取值為 0 到 5,示意圖如下:

揭秘 NumPy 的形

使用 b[1][2] 獲取元素到 16

<code>In [54]: b[1][2]
Out[54]: 16
/<code>

兩個軸的取值分為 1,2,如下圖所示,定位到元素 16

揭秘 NumPy 的形

平時,有些讀者朋友可能會混淆兩個 shape,(12,) 和 (12,1) ,其實前者一個軸,後者兩個軸,示意圖分別如下:

一個軸,取值從 0 到 11

揭秘 NumPy 的形

兩個軸,i 軸取值從 0 到 11, j 軸取值從 0 到 0

揭秘 NumPy 的形

至此,大家要建立兩個軸的概念。

並且,通過上面幾幅圖看到,無論 shape 如何變化,變化的是軸或稱作 view,底下的 buffer 始終未變。

接下來,上升到三個軸,變化數組 a 的 shape 為 (2,3,2) :

<code>c = a.reshape(2,3,2)
/<code>

打印 c:

<code>In [55]: c = a.reshape(2,3,2)

In [56]: c
Out[56]:
array([[[ 0, 2],
[ 4, 6],
[ 8, 10]],

[[12, 14],
[16, 18],
[20, 22]]])
/<code>

數組 c 有三個軸,取值分別為 0 到 1, 0 到 2, 0 到 1,示意圖如下所示:

揭秘 NumPy 的形

讀者們注意體會, i , j , k 三個軸,其值的分佈規律。如果去掉 i 軸取值為 1 的單元格後,

揭秘 NumPy 的形

實際就對應到數組 c 的前半部分元素:

<code>array([[[ 0,  2],
[ 4, 6],
[ 8, 10]],
/<code>

也就是如下的索引組合 :

揭秘 NumPy 的形

至此,三個軸的 reshape 已經講完。

最後,說一個有意思的問題。

還記得,原始的一維數組 a 嗎?它一共有 12 個元素,後來,我們變化它為數組 c ,shape 為 (2,3,2),那麼如何升級為 4 維或 任意維呢?

4 維可以為:(1,2,3,2),示意圖如下:

揭秘 NumPy 的形

看到,軸 i 索引取值只有 0,它被稱為自由維度,可以任意插入到原數組的任意軸間。

比如,5 維可以為:(1,2,1,3,2):

揭秘 NumPy 的形

至此,你應該完全理解 reshape 操作後的魔法:

  • buffer 是個一維數組,永遠不變;
  • 變化的 shape 通過 view 傳達;
  • 取值僅有 0 的自由軸,能變化出任意維度。

最後補充一點:reshape 操作返回的對象僅僅是原數組的視圖,是一個引用,並未發生複製操作,因此 reshape 一個高效的操作。

要想學到別人可能沒掌握的本領,只有靜下心來,踏實下來。通過日復一日的訓練,才能到達理想的彼岸,遇見更好的自己。


分享到:


相關文章: