TensorFlow入門(二) TensorFlow資料模型——張量
1.2 TensorFlow資料模型——張量
1.2.1 張量的概念
在TensorFlow程式中,所有的資料都通過張量的形式來表示。從功能角度上看,張量可以簡單理解為多維陣列,其中零階張量表示為標量(scalar),也就是一個數(張量的型別也可以是字串);第一階張量為向量(vector),也就是一個一維陣列;第n階張量可以理解為一個n維陣列。但張量在TensorFlow中的實現並不是直接採用陣列的形式,它只是對TensorFlow中運算結果的引用。在張量中並沒有真正儲存數字,它儲存的是如何得到這些數字的計算過程。還是以向量加法為例,當執行如下程式碼時,並不會得到加法的結果,而會得到對結果的一個引用。
import tensorflow as tf
# tf.constant是一個計算,這個計算的結果為一個張量,儲存在變數a中
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = tf.add(a, b, name="add")
print(result)
"""
輸出:
Tensor("add:0", shape(2,), dtype=float32)
"""
從上面的程式碼可以看出TensorFlow中的張量和NumPy中的陣列不同,TensorFlow計算的結果不是一個具體的數字,而是一個張量的結構。從上面的程式碼的執行結果可以看出,一個張量中主要儲存了三個屬性:名字(name),維度(shape)和型別(type)。
張量的第一個屬性名字不僅是一個張量的唯一識別符號,它同樣也給出了這個張量是如何計算出來的。TensorFlow的計算都可以通過計算圖的模型來建立,而計算圖上的每一個節點代表了一個計算,計算結果就儲存在張量之中。所以張量和計算圖上節點所代表的計算結果是對應的。這樣張量的命名就可以通過“node:src_output”的形式來給出。其中node為節點的名稱,src_output表示當前張量來自節點的第幾個輸出。比如上面程式碼打出來的“add:0”就說明了result這個張量是計算節點“add”輸出的第一個結果(編號從0開始)。
張量的第二個屬性是張量的維度(shape)。這個屬性描述了一個張量的維度資訊。比如上面樣例中shape=(2,)說明了張量result是一個一維陣列,這個陣列的長度為2。維度是張量一個很重要的屬性,圍繞張量的維度TensorFlow也給出很多有用的運算。 張量的第三個屬性是型別(type),每一個張量會有一個唯一的型別。TensorFlow會對參與運算的所有張量進行型別的檢查,當發現型別不匹配時會報錯。比如執行下面這段程式就會得到型別不匹配的錯誤:
import tensorflow as tf
a = tf.constant([1, 2], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
這段程式和上面的樣例基本一模一樣,唯一不同的是把其中一個加數的小數點去掉了。這會使得加數a的型別為整數而加數b的型別為實數,這樣程式會報型別不匹配的錯誤:
ValueError: Tensor conversion requested dtype int32 for Tensor with dtype float32: 'Tensor("b:0", shape=(2,), dtype=float32)
如果將一個加數指定成實數型別“a = tf.constant([1, 2], name="a", dtype=tf.float32)”,那麼兩個加數的型別相同就不會報錯了。如果不指定型別,TensorFlow會給出預設的型別,比如不帶小數點的型別會被預設為int32,帶小數的會預設為float32。因為使用預設型別有可能會導致潛在的型別不匹配問題,所以一般建議通過指定dtype來明確指出變數或者常量的型別。TensorFlow支援14種不同的型別,主要包括了實數(tf.float32、tf.float64)、整數(tf.int8、tf.int16、tf.int32、tf.int64、tf.uint8)、布林型(tf.bool)和複數(tf.complex64、tf.complex128)。
1.2.2 張量的使用
和TensorFlow的計算模型相比,TensorFlow的資料模型相對比較簡單。張量使用主要可以總結為兩大類。
第一類用途是對中間結果的引用。當一個計算包含很多中間結果時,使用張量可以大大提高程式碼的可讀性。以下為使用張量和不使用張量記錄中間結果來完成向量加法的功能的程式碼對比。
# 使用張量記錄中間結果
a = tf.constant([1.0, 2.0], name="a")
b = tf.constant([2.0, 3.0], name="b")
result = a + b
# 直接計算向量的和,這樣可讀性會較差
result = tf.constant([1.0, 2.0], name="a") +
tf.constant([2.0, 3.0], name="b")
從上面的樣例程式可以看到a和b其實就是對常量生成這個運算結果的引用,這樣在做加法時(比如在構建深層神經網路時)通過張量來引用計算的中間結果可以使程式碼的可閱讀性大大提升。同樣通過張量來儲存中間結果,這樣可以方便獲取中間結果。比如在卷積神經網路中,卷積層或者池化層有可能改變張量的維度,通過result.get_shape函式來獲取結果張量的維度資訊可以免去人工計算的麻煩。
使用張量的第二類情況是當計算圖構造完成之後,張量可以用來獲得計算結果,也就是得到真實的數字。雖然張量本身沒有儲存具體的數字,但是通過會話(session),就可以得到這些具體的數字。比如在上述程式碼中,可以使用 tf.Session().run(result) 語句來得到計算結果。