本系列筆記記錄了學習TensorFlow2的過程,主要依據
https://github.com/dragen1860/Deep-Learning-with-TensorFlow-book
進行學習
首先需要明確TensorFlow 是一個面向於深度學習算法的科學計算庫,內部數據保存在張量(Tensor)對象上,所有的運算操作(Operation, OP)也都是基於張量對象進行。
數據類型
Tensorflow中的基本數據類型有三種,包括數值型、字符串型和布爾型。
【數值型】又包括:(在 TensorFlow 中間,為了表達方便,一般把標量、向量、矩陣也統稱為張量,不作區分,需要根據張量的維度數和形狀自行判斷。 )
1.Scalar(標量)
維度數(Dimension,也叫秩)為 0,shape 為[]
a = tf.constant(1.2) # 創建標量
2.Vector(向量)
維度數為 1,長度不定,shape 為[𝑛]
x = tf.constant([1,2.,3.3])
3.Matrix(矩陣)
維度數為 2,每個維度上的長度不定,shape 為[𝑛,𝑚] (n 行 m 列)
b = tf.constant([[1,2],[3,4]])
4.Tensor(張量)
維度數dim > 2的數組統稱為張量。
c = tf.constant([[[1,2],[3,4]],[[5,6],[7,8]]]) (三維張量定義)
【字符串】類型:
a = tf.constant('Hello, Deep Learning.')
在 tf.strings 模塊中,提供了常見的字符串型的工具函數,如拼接 join(),長度 length(),切分 split()等等,如:tf.strings.lower(a)
【布爾】類型:
a = tf.constant(True)
數值精度
常用的精度類型有 tf.int16, tf.int32, tf.int64, tf.float16, tf.float32, tf.float64,其中 tf.float64 即為 tf.double
對於大部分深度學習算法,一般使用 tf.int32, tf.float32 可滿足運算精度要求,部分對精度要求較高的算法,如強化學習,可以選擇使用 tf.int64, tf.float64 精度保存張量。
通過訪問張量的 dtype 成員屬性可以判斷張量的保存精度: a = tf.constant(np.pi, dtype=tf.float16) ,print(a.dtype)
轉換精度 :a = tf.cast(a,tf.float32)
布爾型與整形之間相互轉換也是合法的,是比較常見的操作,一般默認 0 表示 False,1 表示 True,在 TensorFlow 中,將非 0 數字都視為 True
待優化張量
為了區分需要計算梯度信息的張量與不需要計算梯度信息的張量,TensorFlow 增加了 一種專門的數據類型來支持梯度信息的記錄:tf.Variable。tf.Variable 類型在普通的張量類 型基礎上添加了 name,trainable 等屬性來支持計算圖的構建。由於梯度運算會消耗大量的 計算資源,而且會自動更新相關參數,對於不需要的優化的張量,如神經網絡的輸入 X, 不需要通過 tf.Variable 封裝;相反,對於需要計算梯度並優化的張量,如神經網絡層的W 和𝒃,需要通過 tf.Variable 包裹以便 TensorFlow 跟蹤相關梯度信息。 通過 tf.Variable()函數可以將普通張量轉換為待優化張量。
a = tf.constant([-1, 0, 1, 2])
aa = tf.Variable(a)
aa.name, aa.trainable
'''''''''''''''''''''''''''''''
Out[20]:
('Variable:0', True)
'''''''''''''''''''''''''''''''
創建 Variable 對象是默認啟用優化標志,可以設置 trainable=False 來設置張量不需要優化。
除了通過普通張量方式創建 Variable,也可以直接創建: a = tf.Variable([[1,2],[3,4]])
待優化張量可看做普通張量的特殊類型,普通張量也可以通過 GradientTape.watch()方法臨 時加入跟蹤梯度信息的列表。
創建張量
1.從 Numpy, List 對象創建
通過 tf.convert_to_tensor 可以創建新 Tensor,並將保存在 Python List 對象或者 Numpy Array 對象中的數據導入到新 Tensor 中: tf.convert_to_tensor([1,2.]) | tf.convert_to_tensor(np.array([[1,2.],[3,4]]))
需要注意的是,Numpy 中浮點數數組默認使用 64-Bit 精度保存數據,轉換到 Tensor 類型時 精度為 tf.float64,可以在需要的時候轉換為 tf.float32 類型。
tf.constant()和 tf.convert_to_tensor()都能夠自動的把 Numpy 數組或者 Python List 數據類型轉化為 Tensor 類型
2. 創建全 0,全 1 張量
考慮線性變換 𝒚 = 𝑊𝒙 +𝒃,將權值矩陣 W 初始化為全 1 矩陣,偏置 b 初始化為全 0 向量,此時線性變 化層輸出𝒚 = 𝒙,是一種比較好的層初始化狀態。通過 tf.zeros()和 tf.ones()即可創建任意形 狀全 0 或全 1 的張量。例如,創建為 0 和為 1 的標量張量:
tf.zeros([2,2]) | tf.ones([3,2])
通過 tf.zeros_like, tf.ones_like 可以方便地新建與某個張量 shape 一致,內容全 0 或全 1 的張量 : tf.zeros_like(a)
tf.*_like 是一個便捷函數,可以通過 tf.zeros(a.shape)等方式實現
3.創建自定義數值張量
tf.fill([2,2], 99)
4.創建已知分布的張量
正態分布(Normal Distribution,或 Gaussian Distribution)和均勻分布(Uniform Distribution)是最常見的分布之一,創建采樣自這 2 種分布的張量非常有用,比如在卷積神經網絡中,卷積核張量 W 初始化為正態分布有利於網絡的訓練;在對抗生成網絡中,隱藏變量 z 一般采樣自均勻分布。
通過 tf.random.normal(shape, mean=0.0, stddev=1.0)可以創建形狀為 shape,均值為 mean,標准差為 stddev 的正態分布𝒩(𝑚𝑒𝑎𝑛,𝑠𝑡𝑑𝑑𝑒𝑣2)。
通過 tf.random.uniform(shape, minval=0, maxval=None, dtype=tf.float32)可以創建采樣自 [𝑚𝑖𝑛𝑣𝑎𝑙,𝑚𝑎𝑥𝑣𝑎𝑙]區間的均勻分布的張量。
5. 創建序列
tf.range(start, limit, delta=1)可以創建[𝑠𝑡𝑎𝑟𝑡,𝑙𝑖𝑚𝑖𝑡),步長為 delta 的序列,不包含 limit 本身:tf.range(1,10,delta=2)
張量的典型應用
1.標量
在 TensorFlow 中,標量最容易理解,它就是一個簡單的數字,維度數為 0,shape 為 []。標量的典型用途之一是誤差值的表示、各種測量指標的表示,比如准確度(Accuracy, acc),精度(Precision)和召回率(Recall)等。
out = tf.random.uniform([4,10]) #隨機模擬網絡輸出
y = tf.constant([2,3,2,0]) # 隨機構造樣本真實標簽
y = tf.one_hot(y, depth=10) # one-hot 編碼
loss = tf.keras.losses.mse(y, out) # 計算每個樣本的 MSE
loss = tf.reduce_mean(loss) # 平均 MSE
print(loss)
2.向量
向量是一種非常常見的數據載體,如在全連接層和卷積神經網絡層中,偏置張量𝒃就 使用向量來表示。如圖 所示,每個全連接層的輸出節點都添加了一個偏置值,把所有 輸出節點的偏置表示成向量形式:𝒃 = [𝑏1,𝑏2]𝑇。
# z=wx,模擬獲得激活函數的輸入 z
z = tf.random.normal([4,2])
b = tf.zeros([2]) # 模擬偏置向量
z = z + b # 累加偏置(到這里 shape 為[4,2]的𝒛和 shape 為[2]的𝒃張量可以直接相加,這是為什么呢?讓我們在 Broadcasting 一節為大家揭秘)
通過高層接口類 Dense()方式創建的網絡層,張量 W 和𝒃存儲在類的內部,由類自動創 建並管理。可以通過全連接層的 bias 成員變量查看偏置變量𝒃
fc = tf.keras.layers.Dense(3)# 創建一層 Wx+b,輸出節點為 3 (原書表述為:fc = layers.Dense(3) # 創建一層 Wx+b,輸出節點為 3,此處前提是:)
fc.build(input_shape=(2,4))# 通過 build 函數創建 W,b 張量,輸入節點為 4
fc.bias # 查看偏置
3.矩陣
矩陣也是非常常見的張量類型,比如全連接層的批量輸入𝑋 = [𝑏,𝑑𝑖𝑛],其中𝑏表示輸入樣本的個數,即 batch size,𝑑𝑖𝑛表示輸入特征的長度。比如特征長度為 4,一共包含 2 個樣本的輸入可以表示為矩陣: x = tf.random.normal([2,4])
可以通過全連接層的 kernel 成員名查看其權值矩陣 W:
fc =tf.keras.layers.Dense(3) # 定義全連接層的輸出節點為 3(原書表述為layers.Dense(3))
fc.build(input_shape=(2,4)) # 定義全連接層的輸入節點為 4
fc.kernel
4.三維張量
三維的張量一個典型應用是表示序列信號,它的格式是 𝑋 = [𝑏,𝑠𝑒𝑞𝑢𝑒𝑛𝑐𝑒 𝑙𝑒𝑛,𝑓𝑒𝑎𝑡𝑢𝑟𝑒 𝑙𝑒𝑛] 其中𝑏表示序列信號的數量,sequence len 表示序列信號在時間維度上的采樣點數,feature len 表示每個點的特征長度。
為了能夠方便字符串被神經網絡處理,一般將單詞通過嵌入層(Embedding Layer)編碼為固定長度的向量,比如“a”編碼為某個長度 3 的向量,那么 2 個 等長(單詞數為 5)的句子序列可以表示為 shape 為[2,5,3]的 3 維張量,其中 2 表示句子個數,5 表示單詞數量,3 表示單詞向量的長度。
5.四維張量
我們這里只討論 3/4 維張量,大於 4 維的張量一般應用的比較少,如在元學習(meta learning)中會采用 5 維的張量表示方法,理解方法與 3/4 維張量類似。
4 維張量在卷積神經網絡中應用的非常廣泛,它用於保存特征圖(Feature maps)數據, 格式一般定義為
[𝑏,ℎ,w,𝑐]
其中𝑏表示輸入的數量,h/w分布表示特征圖的高寬,𝑐表示特征圖的通道數,部分深度學習框架也會使用[𝑏,𝑐,ℎ, ]格式的特征圖張量,例如 PyTorch。圖片數據是特征圖的一種, 對於含有 RGB 3 個通道的彩色圖片,每張圖片包含了 h 行 w 列像素點,每個點需要 3 個數 值表示 RGB 通道的顏色強度,因此一張圖片可以表示為[h,w,3]。
維度變換
基本的維度變換包含了改變視圖 reshape,插入新維度 expand_dims,刪除維度squeeze,交換維度 transpose,復制數據 tile 等
1.Reshape
tf.reshape(x,[2,-1]) #其中的參數-1 表示當前軸上長度需要根據視圖總元素不變的法則自動推導,從而方便用戶書寫。
2.增刪維度
增加維度
一張 28x28 灰度圖片的數據保存為 shape 為[28,28]的張量,在末尾給張量增加一新維度,定義為為通道數維度,此時張量的 shape 變為[28,28,1]:
x = tf.random.uniform([28,28],maxval=10,dtype=tf.int32)
x = tf.expand_dims(x,axis=0)
刪除維度
x = tf.squeeze(x, axis=0)
如果不指定維度參數 axis,即 tf.squeeze(x),那么他會默認刪除所有長度為 1 的維度
3.交換維度
x = tf.random.normal([2,32,32,3])
tf.transpose(x,perm=[0,3,1,2])
'''
shape=(2, 3, 32, 32)
'''
4.數據復制
通過 tf.tile(b, multiples=[2,1])即可在 axis=0 維度復制 1 次,在 axis=1 維度不復制。
Broadcasting
Broadcasting 也叫廣播機制(自動擴展也許更合適),它是一種輕量級張量復制的手段,在邏輯上擴展張量數據的形狀,但是只要在需要時才會執行實際存儲復制操作。對於大部分場景,Broadcasting 機制都能通過優化手段避免實際復制數據而完成邏輯運算,從而相對於 tf.tile 函數,減少了大量計算代價。
A = tf.random.normal([32,1])
tf.broadcast_to(A, [2,32,32,3])
'''out
<tf.Tensor: id=13, shape=(2, 32, 32, 3), .....>
數學運算
1.加減乘除
加減乘除是最基本的數學運算,分別通過 tf.add, tf.subtract, tf.multiply, tf.divide 函數實現,TensorFlow 已經重載了+ −∗ /運算符,一般推薦直接使用運算符來完成加減乘除運算。整除和余除也是常見的運算之一,分別通過//和%運算符實現。
2.乘方
通過 tf.pow(x, a)可以方便地完成𝑦 = 𝑥𝑎乘方運算,也可以通過運算符**實現𝑥 ∗∗ 𝑎運算
對於常見的平方和平方根運算,可以使用 tf.square(x)和 tf.sqrt(x)實現。
3.指數、對數
通過 tf.pow(a, x)或者**運算符可以方便實現指數運算𝑎𝑥,特別地,對於自然指數𝑒𝑥,可以通過 tf.exp(x)實現
自然對數 log𝑒 𝑥可以通過 tf.math.log(x)實現, 如果希望計算其他底數的對數,可以根據對數的換底公式
4.矩陣相乘
通過@運算符可以方便的實現矩陣相乘,還可以通過 tf.matmul(a, b)實現
TensorFlow 中的矩陣相乘可以使用批量方式,也就是張量 a,b 的維度數可以大於 2。當張量 a,b 維度數大於 2時,TensorFlow 會選擇 a,b 的最后兩個維度進行矩陣相乘,前面所有的維度都視作 Batch維度。
矩陣相乘函數支持自動 Broadcasting 機制
a = tf.random.normal([4,28,32])
b = tf.random.normal([32,16])
tf.matmul(a,b)
Out:
<tf.Tensor: id=264, shape=(4, 28, 16)........>