由淺入深地帶你瞭解分析張量

神經網絡的輸入、輸出、權重都是張量,神經網絡中的各種計算和變換就是對張量操作,張量這種數據結構是神經網絡的基石,可以說沒有理解張量就沒有真正理解神經網絡和人工智能。本文由淺入深地詳細講解分析張量,望能給予讀者啟發——袁宵。

張量的定義

張量(tensor)是一個多維數組(multidimensional arrays),即一種存儲數字集合的數據結構,這些數字可通過索引(index)單獨訪問,並可通過多個索引進行索引。

張量是將向量和矩陣推廣到任意維數。如下圖所示,一個張量的維數與張量中用來表示標量值的索引的數量一致。

由淺入深地帶你瞭解分析張量

新張量 = 張量[索引]

張量的視圖與存儲

點擊張量的存儲.ipynb 深入學習,下面是該文件的主要內容:

  • 張量,PyTorch中的基本數據結構
  • 索引並在PyTorch張量上進行操作以探索和處理數據
  • 與NumPy多維數組互操作
  • 將計算移至GPU以提高速度

張量的視圖與存儲的定義

存儲(Storage)是一維的數字數據數組,例如包含給定類型的數字(可能是float或int32)的連續內存塊。張量是這樣一個存儲的視圖,它能夠通過使用偏移量(offset)和每一維度的步長(per-dimension strides)索引(index)到該存儲中。存儲的佈局總是一維的,而與可能涉及到它的任何張量的維數無關。

多個張量可以對相同的存儲進行索引,即使它們對數據的索引是不同的。但是,底層內存只分配一次,因此不管存儲實例管理的數據有多大,都可以快速地創建數據上的替代張量視圖。

由淺入深地帶你瞭解分析張量

張量視圖的多維性意義

張量的視圖就是我們理解張量的方式,比如 shape 為[2,4,4,3]的張量 A,我們從邏輯上可以理解 為 2 張圖片,每張圖片 4 行 4 列,每個位置有 RGB 3 個通道的數據;張量的存儲體現在張 量在內存上保存為一段連續的內存區域,對於同樣的存儲,我們可以有不同的理解方式, 比如上述 A,我們可以在不改變張量的存儲下,將張量 A 理解為 2 個樣本,每個樣本的特徵為長度 48 的向量。這就是存儲與視圖的關係。

張量存儲的一維性

在存儲數據時,內存並不支持這個維度層級概念,只能以平鋪方式按序寫入內存,因此這 種層級關係需要人為管理,也就是說,每個張量的存儲順序需要人為跟蹤。為了方便表達,我們把張量 shape 中相對靠左側的維度叫做大維度,shape 中相對靠右側的維度叫做小維度,比如[2,4,4,3]的張量中,圖片數量維度與通道數量相比,圖片數量叫做大維度,通道 數叫做小維度。在優先寫入小維度的設定下,形狀(2, 3)張量的內存佈局為:

<tensor>array([[1., 4.],
[2., 1.],
[3., 5.]], dtype=float32)>

[1., 4., 2., 1., 3., 5.]
/<tensor>

數據在創建時按著初始的維度順序寫入,改變張量的視圖僅僅是改變了張量的理解方 式,並不會改變張量的存儲順序,這在一定程度上是從計算效率考慮的,大量數據的寫入 操作會消耗較多的計算資源。

張量存儲的形狀(大小)、存儲偏移量和步長

為了索引到存儲中,張量依賴於一些信息,這些信息連同它們的存儲一起明確地定義了它們:大小、存儲偏移量和步長(下圖)。

中文英文意義形狀shape是一個元組,表示張量表示的每個維度上有多少個元素。注意張量的形狀(shape)與存儲的大小(size)等價。步長stride是一個元組,表示當索引在每個維度上增加1時,必須跳過的存儲中的元素數量。存儲偏移量storage offset存儲中對應於張量中第一個元素的index。

由淺入深地帶你瞭解分析張量

上圖例子中,在二維張量中訪問元素(i,j)(i,j)的結果是訪問存儲中的$storage_offset + stride[0] i + stride[1] j$元素。

更加廣義的:對於形狀為shape(d1,d2,..,dn)shape(d1,d2,..,dn)的張量的視圖中的元素E(e1,e2,…,en)E(e1,e2,…,en),如果該張量的存儲的步長為 stride(s1,s2,…,sn)stride(s1,s2,…,sn) 、存儲偏移量為 storage offsetstorage offset,那麼元素EE的存儲位置indexindex是:

由此我們得出了張量視圖的計算式子:

張量視圖 = 張量存儲 + 張量形狀 + 張量步長 + 張量偏移

張量存儲對張量操作的影響

這種張量和存儲之間的間接性導致了一些操作,比如轉置一個張量或者提取一個次張量,這些操作是便宜的,因為它們不會導致內存的重新分配;而是,它們包括分配一個新的張量對象,這個張量對象的形狀、存儲偏移量或步長有不同的值。

子張量的維數變少,而索引的存儲空間仍然和原來的點張量一樣。改變子張量會對原張量產生副作用(對子張量的修改會影響原張量)。但是這種效果可能並不總是存在,因為可以把子張量克隆成一個新的張量。

沒有分配新的內存:只有通過創建一個新的張量實例來獲得轉置(transpose),這個張量實例的步長與原來的張量不同。可以通過張量的重新佈局函數,比如PyTorch中的contiguous()函數,來強制拷貝一份張量,讓它的佈局和從新創建的張量一樣。

由淺入深地帶你瞭解分析張量

張量的視圖與存儲的區別與聯繫

聯繫

對於形狀 shape 為(d1, d2,.., dn)的張量的視圖中的元素E(e1, e2,…,en),如果該張量的存儲的步長為 stride 為 (s1, s2,…,sn) 、存儲偏移量storage offset 為 s_o,那麼元素E的存儲位置index是:

張量視圖 = 張量存儲 + 張量形狀 + 張量步長 + 張量偏移

區別

  • 相同存儲可以有不同的視圖:tensor_B.storage() 與 tensor_B_transpose.storage() 相同,但是 tensor_B 與 tensor_B_transpose 不同。
  • 相同的視圖可以有不同的存儲:tensor_A 與 tensor_B_transpose 相同,但是 tensor_A.storage() 與 tensor_B_transpose.storage() 不同。

總結:張量的視圖與存儲通過索引來建立關係,它們之間沒有必然性,即相同存儲可以有不同的視圖,相同的視圖可以有不同的存儲。

張量的操作

點擊 TensorFlow張量的常用操作.ipynb 深入學習,下面是該文件的主要內容:

dtype=int32, float32, string, bool
tf.convert_to_tensor, tf.constant, tf.zeros, tf.ones, tf.zeros_like, tf.fill, tf.random.normal, tf.random.uniform, tf.range
A[1][2][1], A[1, 2, 1], A[ :, :, 0:3:2], A[..., 0:3:2]
tf.reshape, tf.expand_dims, tf.squeeze, tf.transpose

tf.tile
+, -, *, /, //, %, **, tf.pow, tf.square, tf.sqrt, tf.math.log, tf.matmul, @
tf.concat, tf.stack, tf.split, tf.unstack
tf.norm, tf.reduce_max min mean sum, tf.argmax, tf.argmin
tf.equal
tf.pad, tf.keras.preprocessing.sequence.pad_sequences, tf.tile
tf.maximum, tf.minimum, tf.clip_by_value
tf.gather, tf.gather_nd
tf.boolean_mask
tf.where
tf.scatter_nd
tf.meshgrid


分享到:


相關文章: