1. 程式人生 > 實用技巧 >簡單粗暴 TensorFlow 2 之基礎

簡單粗暴 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)

其中:關於引數 ab 的偏導數為 ,

以上只是簡單函式的計算,複雜函式手動計算並不現實,這就需要使用已經做好的函式方法。

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] 拼裝在一起,就可以組合出所需的引數了。

官網學習資料

簡單粗暴 TensorFlow 2