簡單粗暴 TensorFlow 2 之基礎
簡單粗暴 TensorFlow 2 之基礎
TensorFlow 安裝與環境配置
推薦使用Anaconda,Python 3.7以上
開啟Anacond Prompt
anacoda 用的常用命令
- conda -V #常看conda版本
- conda env list #列出已有環境
- conda env remove -n env_name #移除環境
- conda create --name [env-name] # 建立名為[env-name]的Conda虛擬環境
- conda activate [env-name] # 進入名為[env-name]的Conda虛擬環境
- conda activate [env-name] # 進入名為[env-name]的Conda虛擬環境
# “tf2”是你建立的conda虛擬環境的名字,--name與-n都可以,預設會在ananconda/env/下安裝,檔名為tf2
conda create --name tf2 python=3.7
# 進入名為“tf2”的conda虛擬環境
conda activate tf2
在啟用這個虛擬環境時如果報告錯誤
CommandNotFoundError: Your shell has not been properly configured to use 'conda activate'. If using 'conda activate' from a batch script, change your
invocation to 'CALL conda.bat activate'.
解決方法:
先進入預設的虛擬環境,退出後在重新啟用tf2環境
CALL conda.bat activate
deactivate
###啟用tf2虛擬環境
conda activate tf2
###安裝TensorFlow,使用conda install tensorflow安裝,conda源更新慢。
pip install tensorflow
CUDA Toolkit 和 cuDNN 的安裝
若支援GPU可以安裝
#conda search cudatoolkit 和 conda search cudnn 搜尋 conda 源中可用的版本號 conda install cudatoolkit=10.1 conda install cudnn=7.6.5
TensorFlow基礎
TensorFlow 使用 張量 (Tensor)作為資料的基本單位。TensorFlow 的張量在概念上等同於多維陣列,我們可以使用它來描述數學中的標量(0 維陣列)、向量(1 維陣列)、矩陣(2 維陣列)等各種量。這裡要注意0維陣列和1維陣列的區別。
張量的屬性包括:形狀(shape),型別(dtype,預設為tf.float32,可使用dtype指定)和值(numpy(),將張量的值轉為numpy陣列),可使用物件.屬性的方式進行訪問。
# 定義一個隨機數(標量)
random_float = tf.random.uniform(shape=())
# 定義一個有2個元素的零向量
zero_vector = tf.zeros(shape=(2))
# 定義兩個2×2的常量矩陣
A = tf.constant([[1., 2.], [3., 4.]])
B = tf.constant([[5., 6.], [7., 8.]])
# 檢視矩陣A的形狀、型別和值
print(A.shape) # 輸出(2, 2),即矩陣的長和寬均為2
print(A.dtype) # 輸出<dtype: 'float32'>
print(A.numpy()) # 輸出[[1. 2.]
# [3. 4.]]
自動求導機制
TensorFlow 提供了強大的 自動求導機制 來計算導數。在即時執行模式(eager)下,TensorFlow 引入了 tf.GradientTape()
這個 “求導記錄器” 來實現自動求導。
import tensorflow as tf
x = tf.Variable(initial_value=3.)
with tf.GradientTape() as tape: # 在 tf.GradientTape() 的上下文內,所有計算步驟都會被記錄以用於求導
y = tf.square(x)
y_grad = tape.gradient(y, x) # 計算y關於x的導數
print(y, y_grad)
###輸出結果
tf.Tensor(9.0, shape=(), dtype=float32)
tf.Tensor(6.0, shape=(), dtype=float32)
-
tf.Variable()宣告變數,需要有一個初始化的過程,其引數initial_value(經常會誤寫成initial_values)指定初始值。該變數與普通張量一樣具有上面說的三種屬性,同時,它還能夠被TF的自動求導機制所求導。
-
with tf.GradientTape() as tape 上下文管理器(context manager),來連線需要計算梯度的函式和變數,方便求解同時也提升效率。
-
tf.GradientTape() 是一個自動求導的記錄器。只要進入了
with tf.GradientTape() as tape
的上下文環境,則在該環境中計算步驟都會被自動記錄,主要進行正向傳播和計算損失loss。Tape翻譯過來就是膠帶、磁帶的意思。GradientTape預設只監控由tf.Variable建立的traiable=True屬性(預設)的變數。預設情況下GradientTape的資源在呼叫gradient函式後就被釋放,再次呼叫就無法計算了。所以如果需要多次計算梯度,需要開啟persistent=True屬性。
with tf.GradientTape(persistent=True) as tape
x = tf.constant(3.0)
with tf.GradientTape(persistent=True) as tape:
tape.watch(x) #確保某個tensor被tape追蹤,現在預設設定裡,一般不需要指定了,GradientTape會監控可訓練變數
y = x * x
z = y * y
dz_dx = g.gradient(z, x) # z = y^2 = x^4, z’ = 4*x^3 = 4*3^3
dy_dx = g.gradient(y, x) # y’ = 2*x = 2*3 = 6
del tape # 刪除這個上下文膠帶
使用 tf.GradientTape() 計算函式 L(w, b) = ||Xw + b - y||^2 在 w = (1, 2)^T, b = 1 時分別對 w, b 的偏導數。
X = tf.constant([[1., 2.], [3., 4.]])
y = tf.constant([[1.], [2.]])
w = tf.Variable(initial_value=[[1.], [2.]])
b = tf.Variable(initial_value=1.)
with tf.GradientTape() as tape:
L = tf.reduce_sum(tf.square(tf.matmul(X, w) + b - y))
w_grad, b_grad = tape.gradient(L, [w, b]) # 計算L(w, b)關於w, b的偏導數
print(L, w_grad, b_grad)
###輸出
tf.Tensor(125.0, shape=(), dtype=float32)
tf.Tensor(
[[ 70.]
[100.]], shape=(2, 1), dtype=float32)
tf.Tensor(30.0, shape=(), dtype=float32)
L(w, b)表示的就是損失函式,均方誤差,也可看成是L2範數的平方。但是這裡並沒有乘以取均值的係數1/N(N為樣本數量,或者批次大小),對損失函式乘以常數等價於調整學習率,所有在具體實現時通常不寫在損失函式中。
線性迴歸示例
使用numpy手動實現梯度下降法求損失函式的引數
a, b = 0, 0
num_epoch = 10000
learning_rate = 5e-4
for e in range(num_epoch):
# 手動計算損失函式關於自變數(模型引數)的梯度
y_pred = a * X + b
grad_a, grad_b = 2 * (y_pred - y).dot(X), 2 * (y_pred - y).sum()
# 更新引數
a, b = a - learning_rate * grad_a, b - learning_rate * grad_b
print(a, b)
其中:關於引數 a
和 b
的偏導數為 ,
以上只是簡單函式的計算,複雜函式手動計算並不現實,這就需要使用已經做好的函式方法。
TensorFlow 下的線性迴歸
X = tf.constant(X)
y = tf.constant(y)
a = tf.Variable(initial_value=0.)
b = tf.Variable(initial_value=0.)
variables = [a, b]
num_epoch = 10000
optimizer = tf.keras.optimizers.SGD(learning_rate=5e-4)
for e in range(num_epoch):
# 使用tf.GradientTape()記錄損失函式的梯度資訊
with tf.GradientTape() as tape:
y_pred = a * X + b
loss = tf.reduce_sum(tf.square(y_pred - y))
# TensorFlow自動計算損失函式關於自變數(模型引數)的梯度
grads = tape.gradient(loss, variables)
# TensorFlow自動根據梯度更新引數
optimizer.apply_gradients(grads_and_vars=zip(grads, variables))
-
tf.keras.optimizers.SGD(learning_rate=5e-4)
聲明瞭一個梯度下降 優化器 (Optimizer)。優化器可以幫助我們根據計算出的求導結果更新模型引數,從而最小化某個特定的損失函式,具體使用方式是呼叫其apply_gradients()
方法。 -
更新模型引數的方法
optimizer.apply_gradients()
需要提供引數grads_and_vars
,即待更新的變數(如上述程式碼中的variables
)及損失函式關於這些變數的偏導數(如上述程式碼中的grads
)。具體而言,這裡需要傳入一個 Python 列表(List),列表中的每個元素是一個(變數的偏導數,變數)
對。比如上例中需要傳入的引數是[(grad_a, a), (grad_b, b)]
。我們通過grads = tape.gradient(loss, variables)
求出 tape 中記錄的loss
關於variables = [a, b]
中每個變數的偏導數,也就是grads = [grad_a, grad_b]
,再使用 Python 的zip()
函式將grads = [grad_a, grad_b]
和variables = [a, b]
拼裝在一起,就可以組合出所需的引數了。
官網學習資料