1. 張量的索引與切片操作
通過索引與切片操作可以提取張量的部分數(shù)據(jù),它們的使用頻率非常高集惋。
1.1 索引操作
在Tensorflow中,支持基本的[i][j]···標準索引方式踩娘,也支持通過逗號分隔索引號的索引方式刮刑。例如:
x = tf.random.normal([4,32,32,3])
x[0]#取第一張圖片的數(shù)據(jù)
x[0][1]#取第一張圖片的第二行
x[0][1][2]#取第一張圖片,第二行养渴,第三列的數(shù)據(jù)
x[2][1][0][1]#取第三張圖片雷绢,第二行,第一列理卑,B通道顏色強度值
當張量的維度數(shù)較高時翘紊,使用[i][j]...[k]的方式書寫不方便,可以采用[i,j...k]的方式索引藐唠,它們是等價的帆疟。
x[1,9,2]#取第二張圖片孵滞,第十行,第三列的數(shù)據(jù)
1.2 切片操作
通過start:end:step切片方式可以方便地提取一段數(shù)據(jù)鸯匹,其中start為開始讀取位置的索引坊饶,end 為結(jié)束讀取位置的索引(不包含end 位),step為采樣步長殴蓬。
以下為切片操作的示例:
x[1:3]#讀取第2,3張圖片
start: end: step切片方式有很多簡寫方式匿级,其中start、end染厅、step 3個參數(shù)可以根據(jù)需要選擇性地省略痘绎,全部省略時即為::,表示從最開始讀取到最末尾肖粮,步長為1孤页,即不跳過任何元素。如x[0,::]表示讀取第1張圖片的所有行涩馆,其中::表示在行維度上讀取所有行行施,它等價于x[0]的寫法:
x[0,::]#讀取第一張圖片
為了更加簡潔,::可以簡寫為單個冒號:魂那,例如:
x[:,0:28:2,0:28:2,:]#表示讀取所有圖片蛾号,隔行采樣,隔列采樣涯雅,讀取所有通道數(shù)據(jù)
總結(jié)一下start: end: step切片的簡寫方式鲜结,其中從第一個元素讀取時start可以省略,即start=0 是可以省略的活逆。取到最后一個元素時end 可以省略精刷,步長為1 時step 可以省略。
特別地蔗候,step可以為負數(shù)怒允,考慮最特殊的一種例子,當step = -1時琴庵,start: end: -1表示從start開始误算,逆序讀取至end 結(jié)束(不包含end)仰美,索引號 end <= start迷殿。考慮一個0~9的簡單序列向量咖杂,逆序取到第1 號元素庆寺,不包含第1 號:
x = tf.range(9) #創(chuàng)建0~9向量
x[8:0:-1]# 從8取到0,逆序,不含0
x[::-1]#逆序取全部元素
x[::-2]#逆序間隔取樣
x = tf.random.normal([4,32,32,3])
x[0,::-2,::-2,:]#取第一張圖片的所有通道诉字,行按逆序隔行取樣懦尝,列按逆序隔行取樣
當采樣的維度數(shù)較多時知纷,不需要采樣的維度一般用單冒號: 表示采樣所有元素,此時有可能出現(xiàn)大量的:冒號陵霉,例如:
x = tf.random.normal([4,32,32,3])
x[:,:,:,1]#只需要采樣G通道上的數(shù)據(jù)
為了避免出現(xiàn)像[:,:,:,1]這樣過多冒號的情況琅轧,可以使用···表示多個維度上所有的數(shù)據(jù),其中維度的數(shù)量需根據(jù)規(guī)則自動推斷:當切片方式出現(xiàn)···符號時踊挠,···符號左邊的維度將自動對齊到最左邊乍桂,···符號右邊的維度將自動對齊到最右邊,而系統(tǒng)將自動推斷···符號代表的維度數(shù)量效床。下面是一些示例:
x = tf.random.normal([4,32,32,3])
x[0:2,...,1:]#取第1,2張圖片的G/B通道數(shù)據(jù)
x[2:,...]#讀取最后2張圖片
x[...,:2]#讀取 R/G 通道數(shù)據(jù)
小結(jié):
張量的索引與切片方式多種多樣睹酌,尤其是切片操作,剛開始學習時很容易犯迷糊剩檀。但本質(zhì)上切片操作只有start: end: step這一種基本形式憋沿,通過這種基本形式有目的地省略掉默認參數(shù),從而衍生出多種簡寫方法沪猴,這也是很好理解的辐啄。另外,由于深度學習一般處理的維度在4維以下运嗜,···操作符完全可以用: 符號代替则披,因此理解了這些就會發(fā)現(xiàn)張量切片操作并不復雜。
2. 張量的維度變換操作
在神經(jīng)網(wǎng)絡運算過程中洗出,維度變換是最核心的張量操作士复,通過維度變換可以將數(shù)據(jù)任意地切換形式,滿足不同場合的運算需求翩活。
基本的維度變換操作函數(shù)包括:
- reshape():改變視圖函數(shù)
- expand_dims():插入新維度
- squeeze():刪除維度
- transpose():交換維度
- tile():復制數(shù)據(jù)
2.1 reshape操作
在TensorFlow中阱洪,可以通過張量的 ndim 和 shape 成員屬性獲得張量的維度數(shù)和形狀。
x = tf.random.normal([4,32,32,3])
print(x.ndim,x.shape)
4 (4, 32, 32, 3)
通過tf.reshape(x, new_shape)菠镇,可以將張量的視圖任意地合法改變冗荸。例如:
x = tf.range(96)
tf.reshape(x,[2,-1])
<tf.Tensor: shape=(2, 48), dtype=int32, numpy=
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47],
[48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95]],
dtype=int32)>
其中的參數(shù)-1表示當前軸上長度需要根據(jù)張量總元素不變的法則自動推導,從而方便用戶書寫利耍。
tf.reshape(x,[2,4,12])#改變張量數(shù)據(jù)的視圖
tf.reshape(x,[2,-1,3])#再次改變張量數(shù)據(jù)的視圖
2.2 張量增蚌本、刪維度
增加一個長度為1的維度相當于給原有的數(shù)據(jù)添加一個新維度的概念,維度長度為1隘梨,故數(shù)據(jù)并不需要改變程癌,僅僅是改變數(shù)據(jù)的理解方式,因此可以理解為改變視圖的一種特殊方式轴猎。
下面是示例:
x = tf.random.uniform([2,2],maxval=10,dtype=tf.int32)
x = tf.expand_dims(x,axis=2)
print(x)
tf.Tensor(
[[[3]
[5]]
[[6]
[2]]], shape=(2, 2, 1), dtype=int32)
通過 tf.expand_dims(x,axis) 可在指定的 axis 軸前插入一個新的維度嵌莉。
同樣的方法,我們可以在最前面插入一個維度捻脖。
x = tf.expand_dims(x, axis=0)
print(x)
tf.Tensor(
[[[[6]
[9]]
[[1]
[1]]]], shape=(1, 2, 2, 1), dtype=int32)
需要注意的是锐峭,tf.expand_dims 的 axis 為正時中鼠,表示在當前維度之前插入一個新維度;為負時沿癞,表示當前維度之后插入一個新的維度援雇。
通過 tf.squeeze(x, axis)函數(shù)可以刪除張量的維度,axis 參數(shù)為待刪除的維度的索引號椎扬。下面看一下用法:
x = tf.random.normal([1,32,32,1])
print('brfore: ',x.shape)
x = tf.squeeze(x,axis=0)#刪除圖片數(shù)量維度
print('after: ',x.shape)
brfore: (1, 32, 32, 1)
after: (32, 32, 1)
#繼續(xù)刪除通道數(shù)維度
x = tf.squeeze(x, axis=2)
print(x.shape)
(32, 32)
提示:如果不指定維度參數(shù) axis熊杨,即 tf.squeeze(x),那么它會默認刪除所有長度為1的維度盗舰。
x = tf.random.normal([1,28,28,1])
print('before:', x.shape)
x = tf.squeeze(x)
print('after:',x.shape)
before: (1, 28, 28, 1)
after: (28, 28)
建議使用tf.squeeze()時逐一指定需要刪除的維度參數(shù)晶府,防止Tensorflow意外刪除某些長度為1的維度。
2.3 交換維度操作
通過交換維度操作钻趋,改變了張量的存儲順序川陆,同時也改變了張量的視圖。
通過使用 tf.transpose(x, perm) 函數(shù)完成維度交換操作蛮位,其中perm參數(shù)表示新維度的順序List较沪。
下面看操作示例:
x = tf.random.normal([4,32,32,3])
x = tf.transpose(x, [0,3,1,2])#把圖片的通道維度移動到圖片數(shù)量維度后
print(x.shape)
(4, 3, 32, 32)
注意:通過 tf.transpose 完成維度交換后,張量的存儲順序已經(jīng)改變失仁,視圖也隨之改變尸曼,后續(xù)的所有操作必須基于新的存儲順序和視圖進行。
2.4 復制數(shù)據(jù)操作
通過 tf.tile(x, multiples) 函數(shù)完成數(shù)據(jù)在指定維度上的復制操作萄焦,multiples參數(shù)分別指定了每個維度上面的復制倍數(shù)控轿,對應位置為1表明不復制,為2表明新長度為原來長度的2倍拂封,即數(shù)據(jù)復制一份茬射,經(jīng)此類推。
下面看操作示例:
b = tf.constant([1,2])
b = tf.expand_dims(b, axis=0)#插入一個新的維度
print(b.shape)
(1, 2)
#在 Batch 維度上復制數(shù)據(jù)1份冒签,實現(xiàn)如下:
b = tf.tile(b, multiples=[2,1])
print(b.shape)
(2, 2)
再看另一個例子:
x = tf.range(4)
x = tf.reshape(x,[2,2])
print(x.shape)
(2, 2)
# 然后在列維度上復制1份數(shù)據(jù)
x = tf.tile(x,multiples=[1,2])#在列維度復制數(shù)據(jù)
print(x.shape)
(2, 4)
# 然后在行維度復制1份數(shù)據(jù)
x = tf.tile(x, multiples=[2,1])#在行維度復制數(shù)據(jù)
print(x.shape)
(4, 4)
注意:tf.tile 會創(chuàng)建一個新的張量來保存復制后的數(shù)據(jù)在抛,由于復制操作涉及大量數(shù)據(jù)的讀寫IO操作,計算代價相對較高萧恕。然而神經(jīng)網(wǎng)絡中不同shape之間的張量運算操作十分頻繁刚梭,那么有沒有輕量級的復制操作呢?這就要看接下來的Broadcasting操作了票唆。
3. Broadcasting操作
Broadcasting稱為廣播機制(或自動擴展機制)朴读,它是一種輕量級的張量復制操作,它在邏輯上擴展張量數(shù)據(jù)的形狀惰说,但是只會在需要時才會執(zhí)行實際存儲復制操作磨德。對于大部分場景缘回,Broadcasting機制都能通過優(yōu)化手段避免實際復制數(shù)據(jù)而完成邏輯運算吆视,從面相對于 tf.tile 函數(shù)典挑,減少了計算代價。
下面看使用示例:
a = tf.random.normal([2,32,32,1])
b = tf.random.normal([32,32])
# 張量相加
c = a+b
print(c.shape)
(2, 32, 32, 32)
# 張量相減
c = a-b
print(c.shape)
(2, 32, 32, 32)
# 張量相乘
c = a*b
print(c.shape)
(2, 32, 32, 32)
# 張量相除
c = a/b
print(c.shape)
(2, 32, 32, 32)
可以看到啦吧,上面這些運算都能Broadcasting成[2,32,32,32]的公共shape您觉,再進行運算。
4. 數(shù)學運算
4.1 加授滓、減琳水、乘、除運算
加般堆、減在孝、乘、除是最基本的數(shù)學運算淮摔,分別通過tf.add私沮、tf.subtract、tf.multiply和橙、tf.divide函數(shù)實現(xiàn)仔燕,Tensorflow已經(jīng)重載了+、-魔招、*晰搀、/運算符,可以直接使用運算符來進行運算办斑。
整除和余除也是常見的運算之一外恕,分別使用//和%運算符實現(xiàn)。
下面看使用示例:
a = tf.range(5)
b = tf.constant(2)
a//b#整除運算
<tf.Tensor: shape=(5,), dtype=int32, numpy=array([0, 0, 1, 1, 2], dtype=int32)>
a%b#余除運算
<tf.Tensor: shape=(5,), dtype=int32, numpy=array([0, 1, 0, 1, 0], dtype=int32)>
4.2 乘方運算
通過 tf.pow(x,a) 可以方便的完成乘方運算乡翅,也可以通過運算符實現(xiàn)xa運算吁讨。示例如下:
x = tf.range(4)
tf.pow(x,3)
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([ 0, 1, 8, 27], dtype=int32)>
x**2
<tf.Tensor: shape=(4,), dtype=int32, numpy=array([0, 1, 4, 9], dtype=int32)>
x = tf.constant([1,4,9,16])
x = tf.cast(x, dtype=tf.float32)#求平方根
x**0.5
<tf.Tensor: shape=(4,), dtype=float32, numpy=array([1., 2., 3., 4.], dtype=float32)>
對于常見的求平方、求平方根運算峦朗,可以使用 tf.square(x) 和 tf.sqrt(x)實現(xiàn)建丧。
示例如下:
x = tf.range(5)
x = tf.cast(x, dtype=tf.float32)
x = tf.square(x)#求平方
print(x)
tf.Tensor([ 0. 1. 4. 9. 16.], shape=(5,), dtype=float32)
x = tf.sqrt(x)# 求平方根
print(x)
tf.Tensor([0. 1. 2. 3. 4.], shape=(5,), dtype=float32)
4.3 指數(shù)和對數(shù)運算
通過tf.pow(a,x)或者**運算符也可以方便的實現(xiàn)指數(shù)運算。
對于自然指數(shù)e??波势,可以通過 tf.exp(x)實現(xiàn)翎朱。
示例如下:
x = tf.constant([1,2,3])
2**x #指數(shù)運算
<tf.Tensor: shape=(3,), dtype=int32, numpy=array([2, 4, 8], dtype=int32)>
tf.exp(1.0)
<tf.Tensor: shape=(), dtype=float32, numpy=2.7182817>
在 TensorFlow 中,自然對數(shù)可以通過 tf.math.log(x)實現(xiàn)尺铣。
x = tf.exp(3.)
tf.math.log(x)
<tf.Tensor: shape=(), dtype=float32, numpy=3.0>
Tensorflow還沒有推出任意底數(shù)的log函數(shù)拴曲,但我們可以換一種方法實現(xiàn)它,示例如下:
x = tf.constant([1.,2.])
x = 10 ** x
print(x)
tf.Tensor([ 10. 100.], shape=(2,), dtype=float32)
tf.math.log(x) / tf.math.log(10.)#計算以10為底數(shù)的對數(shù)
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([1., 2.], dtype=float32)>