2018/12/21

TensorFlow 基本運算 1/2

18 TensorFlow 基本運算 1/2

上一單元討論過 tensor 是 tf 的基本單元,而constant(), Variable() 及placeholder()是三種常用的tensor。tf tensor 的運算功能非常強大,文末所附的參考文獻書中提供了有關 tensor 各項運算函數詳盡的整理。因函數項目繁多,我們依文獻表格所列討論其中較常用的項目。

🔸 產生已知值的 tensor
下列是幾個產生已知值 tensor的函數。
建立已知值的 tensor
表中列出7種常用於建立已知值的tensor 的函數,括號內的參數設定中若有名稱及設定值的項目表示是預設值,建立時若不設定就取用預設值,若只有參數的名稱表示建立時要設定,否則會報錯。例如在constant()中的第一個參數 value 一定要給值,而這個值可用多維陣列,或是一個python 的 array,而其他的因都有預設值,所以建立時可以忽略,如
import tensorflow as tf
sess = tf.InteractiveSession()
const = tf.constant([[1,2,3],[4,5,6]])
print(const.eval())
sess.close() 
#[[1 2 3]
# [4 5 6]]
也可先建立一個np array,再傳給const,如
import tensorflow as tf
import numpy as np
sess = tf.InteractiveSession()
a = np.array([[1,2,3],[4,5,6]])
const = tf.constant(a)
print(const.eval())
sess.close()
#[[1 2 3]
#[4 5 6]]
例子中若把第6行改成print(a.eval()),因 a不是 tf 的 tensor,所以沒有.eval() 屬性,執行時會報錯。constant()函數其他的參數用法如下
import tensorflow as tf
import numpy as np
sess = tf.InteractiveSession()
const = tf.constant(3, shape = (2,3),name = 'Const1', verify_shape=True)
print(const.eval())
sess.close()
上列程式把value 設成 3, name則由預設值改成 'const1',這是設定要在 graph 中顯示的名稱。此例中設 verify_shape=True,這樣會報錯, 因3的shape 是(),和 給定的 (2,3) 不符合,但又要檢查以會出錯。但若把verify設成False,就不會報錯,且tensor 會設成元素值都是 3 的 (2,3) array。
再來看zeros函數的參數第一個是shape,以我們要建立時要給個一定的形外,
import tensorflow as tf
sess = tf.InteractiveSession()
zero = tf.zeros((2,3))
print(zero.eval())
print(zero.dtype)
sess.close()
[[0. 0. 0.]
 [0. 0. 0.]]
<dtype: 'float32'>
data type 預設是float32。fill()函數
import tensorflow as tf
sess = tf.InteractiveSession()
fill = tf.fill((2,3),3.0)
print(fill.eval())
print(fill.dtype)
sess.close()
[[0. 0. 0.]
 [0. 0. 0.]]
<dtype: 'float32'>
函數的第一個參數dims,應該是shape才對。且此函數會依輸入的值來自動判定data type。其他幾個函數就如Python裡的一樣。

🔸 Tensor Data Types
下表是tensor 的資料形態
Tensor Data Types
其中比較常用的有bool, int16,int32, float32, string等。一般在使用時會加上一個tf.形態前面,如tf.float32。

🔸 產生隨機值 tensor
隨機值在機器學習裡很重要,常用於產生隨機資料用以模擬樣本,另外在ANN中的權重變數也常要用隨機值開始進行調整。tf的隨機函數如下表
隨機值 tensor
表中列了幾種常用的機率分佈的隨機變數,參數的設定語法明和前面討論過的類似,其中seed是指隨機的種子,可給定一個固定值如101則每次依序取出的機值會相同。另 truncated_normal是放棄二倍stddev 以外竹取樣點。除表列之外,tf亦提供其他如gamma, poisson等分佈。


🔸 轉換 tensor
在陣列的處理中最常用的轉換功能可能就是reshape (整形)和slice (切分),tf 亦提供其他功能如下表
轉換 tensor
如下列程式
import tensorflow as tf
sess = tf.InteractiveSession()
vec = tf.constant([1., 2., 3., 4.])
mat = tf.reshape(vec, [2, 2])
# mat = tf.reshape(vec, (2, 2))  也ok
print(mat.eval())
#[[1. 2.]
#[3. 4.]]
reshape的參數,新的shape也可以有一維的大小 (size) 指定為$-1$,tf 會自動調整為正確的大小,如
import tensorflow as tf
sess = tf.InteractiveSession()
vec = tf.constant([1., 2., 3., 4.,5.,6,7,8])
mat1 = tf.reshape(vec, [2, -1])
mat2 = tf.reshape(vec, [-1, 2])
print(mat1.eval())
print(mat2.eval())
sess.close()
[[1. 2. 3. 4.]
 [5. 6. 7. 8.]]
[[1. 2.]
 [3. 4.]
 [5. 6.]
 [7. 8.]]
可見mat1的$-1$被調整為4。另外也可以把某些大小為1的維消去,如
import tensorflow as tf
sess = tf.InteractiveSession()
mat = tf.constant([[[1,2,3]]])
mat1 = tf.squeeze(mat, axis=None)
mat2 = tf.squeeze(mat, axis=0)
print(mat.shape)
print(mat1.shape)
print(mat2.shape)
sess.close()
#(1, 1, 3)
#(3,)
#(1, 3)
上列程式中的axis是指要去第幾個軸,沒有指定就是大小為1的維都消去。另外也可以將矩陣依不同的維反轉,如
import tensorflow as tf
sess = tf.InteractiveSession()
mat = tf.constant([[1., 2., 3.], [4., 5., 6.]])
rev_mat0 = tf.reverse(mat, [0])
rev_mat1 = tf.reverse(mat, [1])
print(rev_mat0.eval())
print(rev_mat1.eval())
sess.close()
[[4. 5. 6.]
 [1. 2. 3.]]
[[3. 2. 1.]
 [6. 5. 4.]]
例中參數若選軸0,則是列反轉,若選1則是行反轉。幾個名詞需要釐清,在tf中軸(axis)和維(dim)是同義,常常互用。另對多維矩陣,因軸是從0算起,往後我們講第幾維時也都要從0算起。

🔸 基本數學操作
在機器學習中常需執行大量的數學運算,tensor 常用的運算如下表
基本數學操作
表中前四個是元素對元素運算,如
import tensorflow as tf
sess = tf.InteractiveSession()
a = tf.constant([5., 5., 5.])
b = tf.constant([2., 2., 2.])
sum = tf.add(a, b) 
sub = tf.subtract(a, b) 
multi = tf.multiply(a, b) 
div = tf.divide(a, b)
print(sum.eval())
print(sub.eval())
print(multi.eval())
print(quot.eval())
sess.close()
[7. 7. 7.]
[3. 3. 3.]
[10. 10. 10.]
[1.5 1.5 1.5]
a表中的divide 和 div 當對float 操作結果是一樣,但若對整數操作 ,divide 傳回float而div傳回整數。有些也和用python常規的數學操作如$+,-,*,/$一樣。add_n可把多個tensors 相加,但是要用方括號包起來,如 [a,b,c]。其他幾項函數由字面應可了解其功能。

下表是常用的指數 (Exponents) 和對數 (logarithms)運算
指數和對數
上表的操作由凶數名稱應該可以理解其功能。下表是向量和矩陣的運算,為tensor最重要的操作
向量和矩陣的運算
第一個函數tensordot求內積,如
import tensorflow as tf
sess = tf.InteractiveSession()
a1 = tf.constant([4., 3., 2.])
a2 = tf.constant([3., 2., 1.])
dot0 = tf.tensordot(a1, a2,0)
dot1 = tf.tensordot(a1, a2,1)
print(dot0.eval())
print(dot1.eval())
sess.close()
[[12.  8.  4.]
 [ 9.  6.  3.]
 [ 6.  4.  2.]]
20.0
可見後面的axes參數若1是純點積,若0就很奇怪,a2被複製3個列且每一列分別乘上a1的元素。表中第二個函數 cross 是求向量的cross product (叉乘)  ,依照官網的解釋, a and b must be the same shape; they can either be simple 3-element vectors, or any shape where the innermost dimension is 3. In the latter case, each pair of corresponding 3-element vectors is cross-multiplied independently. 這一句 'the innermost dimension is 3' 是說最內層的維度是3,這很奇怪,應該是指最內層維的大小為3吧!應該是要說 the size of the innermost dimension is 3!依說明應是指 a,b 必需是相同的外形,且最內層維的大小(元素)必需是3,這樣才能執行叉乘  ,若只有一維那就是平常的三度空間的向量叉乘,若有多維則是 a,b tensor 各自維對維的向量叉乘,執行結果的外形和a, b一樣。(多維tensor 的最內兩層是相當二維的列和行,所以最內層是指行。如
import tensorflow as tf
sess = tf.InteractiveSession()
a1 = tf.constant([[1,2,3],[4,5,6]])
a2 = tf.constant([[4,5,6],[1,2,3]])
dot0 = tf.cross(a1, a2)
print(dot0.eval())
sess.close()
#[[-3  6 -3]
# [ 3 -6  3]]
表中的diag()建議不要用,用matrix_diag()替代,功能是傳回由給定的向量的值當對角元素的對角陣,這功能在線性代數相關的運算常用到。例子如
import tensorflow as tf
sess = tf.InteractiveSession()
a1 = tf.constant([1, 2, 3, 4])
dial = tf.matrix_diag(a1)
print(dial.shape)
print(dial.eval())
sess.close()
(4, 4)
[[1 0 0 0]
 [0 2 0 0]
 [0 0 3 0]
 [0 0 0 4]]
傳回的是個$4\times 4$的對角矩陣。表中其他如trace(), transpose(), eye()都很常用。特別提一下最重要的矩陣相乘,如
import tensorflow as tf
sess = tf.InteractiveSession()
a = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3])
b = tf.constant([7, 8, 9, 10, 11, 12], shape=[3, 2])
c = tf.matmul(a, b)
print(c.eval())
sess.close()
#[[ 58  64]
# [139 154]]
這個函數有很多選項,預設都是False, 包括要不要先轉置、adjointed(伴隨化)就是取共軛並轉置、是不是稀疏矩陣宣告True時可對rank-2 的乘最佳化。另外 norm (範數)也很重要,例子如下Google
import tensorflow as tf
sess = tf.InteractiveSession()
a1 = tf.constant([1., 2., 3., 4.])
norm = tf.norm(a1)
print(norm.eval())
sess.close()
#5.477226
函數參數ord 是指norm 的階次(orde)  ,支援的有 'fro', 'euclidean', 1, 2, np.inf  和任何正實數的所謂p-norm,預設是 'euclidean' 歐氏距離。 特別注意的是函數不接受 int32 的資料形態,必需用float。另一個好用的是解決$Ax=b$的函數 ,matrix_solve(),用法如下
import tensorflow as tf
sess = tf.InteractiveSession()
mat = tf.constant([5., 3., 3., 7., 1., 7.,7.,4.,9.], shape=[3, 3])
rhs = tf.constant([2., 3., 4., 4., 6., 5.], shape=[3, 2])
sol = tf.matrix_solve(mat, rhs)
print(sol .eval())
sess.close()
[[-0.13513511  0.3648649 ]
 [ 0.2162162   0.2162162 ]
 [ 0.6756757   0.17567569]]
函數參數matrix的外形是  [..., M, M] 表示最內二層是方矩陣,. Rhs 的外形是 [..., M, K],也就是有要求K組解。
最後我們來看看tf 提供的兩種矩陣分解的函數,首先看看qr() 分解函數 ,例
import tensorflow as tf
sess = tf.InteractiveSession()
mat = tf.constant([1., 1., 0., -1., 0., 1.,0.,1.,1.,0., 0., 1.], shape=[4, 3])
q_full, r_full = tf.qr(mat, full_matrices = False)
# q_full, r_full = tf.qr(mat, full_matrices = True)
print(q_full.eval())
print(r_full.eval())
sess.close()
[[-0.7071068  -0.4082482   0.        ]
 [ 0.70710677 -0.40824822  0.        ]
 [ 0.         -0.8164965   0.        ]
 [ 0.          0.         -1.        ]]
[[-1.4142135  -0.7071068   0.70710677]
 [ 0.         -1.2247449  -1.2247447 ]
 [ 0.          0.         -1.        ]]
QR分解可將一個$m\times n$的矩陣$A$分解成$m\times n$的單位正交矩陣和一個$n\times n$的上三角矩陣相乘,此種分解是為經濟大小(economy size) ,因$A$的行向量必需相互獨立,所以要求$m\geq n$。當分解出的$Q$為 $m\times m$,$R$為 $m\times n$時稱為full size 分解,但因在$R$中的$m-n$列全為0,所以較大的$Q$也沒有意義,所以一般只求economy size的分解。函數的 args中,full_matrices預設就是False。若改成True,結果如下
[[-0.7071068  -0.4082482   0.          0.5773502 ]
 [ 0.70710677 -0.40824822  0.          0.5773502 ]
 [ 0.         -0.8164965   0.         -0.5773503 ]
 [ 0.          0.         -1.          0.        ]]
[[-1.4142135  -0.7071068   0.70710677]
 [ 0.         -1.2247449  -1.2247447 ]
 [ 0.          0.         -1.        ]
 [ 0.          0.          0.        ]].
可見$R$的最後一列全為0。最後來看看奇異直分
import tensorflow as tf
sess = tf.InteractiveSession()
mat = tf.constant([1., 1., 0., -1., 0., 1.,0.,1.,1.,0., 0., 1.,
                  2.1, 3.3, 4.4, 5.5, 6.6, 7.7], shape=[6,3])
#s, u, v = tf.svd(mat,full_matrices=False )
s, u, v = tf.svd(mat, full_matrices = True)
print(s.eval())
print(u.eval())
print(v.eval())
sess.close()
[13.051314   1.9917123  0.6755219]
[[ 0.07834956  0.4660151   0.45112786  0.17048556 -0.28911805 -0.67861664]
 [ 0.01809015 -0.69608074  0.22042486 -0.37633687 -0.5353177  -0.19586454]
 [ 0.09643961 -0.23006608  0.6715511   0.5532434   0.03498919  0.4236576 ]
 [ 0.05249707 -0.32215646 -0.51035357  0.71507007 -0.18367222 -0.29654327]
 [ 0.44825205 -0.32834914  0.1200952  -0.08659246  0.7045751  -0.41582423]
 [ 0.88348633  0.18377547 -0.14843252 -0.06635968 -0.3137831   0.24654233]]
[[ 0.4490554   0.74474967 -0.49365684]
 [ 0.57350785  0.18341754  0.79840255]
 [ 0.68515545 -0.641643   -0.34475514]]
和qr分解相反,在svd分解中,full_matrics 我們常設為True,預設值False。有關svd分解請參考前面縮減維度的單元,就不再詳述 。

參考文獻
Matthew Scarpino, TensorFlow For Dummies, John Wiley & Sons, Inc, 2018

沒有留言:

張貼留言