【小白學PyTorch】20 TF2的eager模式與求導

【新聞】:機器學習煉丹術的粉絲的人工智能交流羣已經建立,目前有目標檢測、醫學圖像、時間序列等多個目標爲技術學習的分羣和水羣嘮嗑的總羣,歡迎大家加煉丹兄爲好友,加入煉丹協會。微信:cyx645016617.

參考目錄:

之前講解了如何構建數據集,如何創建TFREC文件,如何構建模型,如何存儲模型。這一篇文章主要講解,TF2中提出的一個eager模式,這個模式大大簡化了TF的複雜程度。

1 什麼是eager模式

Eager模式(積極模式),我認爲是TensorFlow2.0最大的更新,沒有之一。

Tensorflow1.0的時候還是靜態計算圖,在《小白學PyTorch》系列的第一篇內容,就講解了Tensorflow的靜態特徵圖和PyTorch的動態特徵圖的區別。Tensorflow2.0提出了eager模式,在這個模式下,也支持了動態特徵圖的構建

不得不說,改的和PyTorch越來越像了,但是人類的工具總是向着簡單易用的方向發展,這肯定是無可厚非的。

2 TF1.0 vs TF2.0

TF1.0中加入要計算梯度,是隻能構建靜態計算圖的。

  1. 是先構建計算流程;
  2. 然後開始起一個會話對象;
  3. 把數據放到這個靜態的數據圖中。

整個流程非常的繁瑣。

# 這個是tensorflow1.0的代碼
import tensorflow as tf
a = tf.constant(3.0)
b = tf.placeholder(dtype = tf.float32)
c = tf.add(a,b)
sess = tf.Session() #創建會話對象
init = tf.global_variables_ini                            tializer()
sess.run(init) #初始化會話對象
feed = {
    b: 2.0
} #對變量b賦值
c_res = sess.run(c, feed) #通過會話驅動計算圖獲取計算結果
print(c_res)

代碼中,我們需要用palceholder先開闢一個內存空間,然後構建好靜態計算圖後,在把數據賦值到這個被開闢的內存中,然後再運行整個計算流程。

下面我們來看在eager模式下運行上面的代碼

import tensorflow as tf
a = tf.Variable(2)
b = tf.Variable(20)
c = a + b

沒錯,這樣的話,就已經完成一個動態計算圖的構建,TF2是默認開啓eager模式的,所以不需要要額外的設置了。這樣的構建方法,和PyTorch是非常類似的。

3 獲取導數/梯度

假如我們使用的是PyTorch,那麼我們如何得到\(w\times x + b\)的導數呢?

import torch
# Create tensors.
x = torch.tensor(10., requires_grad=True)
w = torch.tensor(2., requires_grad=True)
b = torch.tensor(3., requires_grad=True)
# Build a computational graph.
y = w * x + b    # y = 2 * x + 3
# Compute gradients.
y.backward()
# Print out the gradients.
print(x.grad)    # tensor(2.)
print(w.grad)    # tensor(10.)
print(b.grad)    # tensor(1.)

都沒問題吧,下面用Tensorflow2.0來重寫一下上面的內容:

import tensorflow as tf
x = tf.convert_to_tensor(10.)
w = tf.Variable(2.)
b = tf.Variable(3.)
with tf.GradientTape() as tape:
    z = w * x + b
dz_dw = tape.gradient(z,w)
print(dz_dw)
>>> tf.Tensor(10.0, shape=(), dtype=float32)

我們需要注意這幾點:

  • 首先結果來看,沒問題,w的梯度就是10;
  • 對於參與計算梯度、也就是參與梯度下降的變量,是需要用tf.Varaible來定義的;
  • 不管是變量還是輸入數據,都要求是浮點數float,如果是整數的話會報錯,並且梯度計算輸出None;
  • tensorflow提供tf.GradientTape來實現自動求導,所以在tf.GradientTape內進行的操作,都會記錄在tape當中,這個就是tape的概念。一個攝影帶,把計算的過程錄下來,然後進行求導操作

現在我們不僅要輸出w的梯度,還要輸出b的梯度,我們把上面的代碼改成:

import tensorflow as tf
x = tf.convert_to_tensor(10.)
w = tf.Variable(2.)
b = tf.Variable(3.)
with tf.GradientTape() as tape:
    z = w * x + b
dz_dw = tape.gradient(z,w)
dz_db = tape.gradient(z,b)
print(dz_dw)
print(dz_db)

運行結果爲:

這個錯誤翻譯過來就是一個non-persistent的錄像帶,只能被要求計算一次梯度。 我們用tape計算了w的梯度,然後這個tape清空了數據,所有我們不能再計算b的梯度。

解決方法也很簡單,我們只要設置這個tape是persistent就行了:

import tensorflow as tf
x = tf.convert_to_tensor(10.)
w = tf.Variable(2.)
b = tf.Variable(3.)
with tf.GradientTape(persistent=True) as tape:
    z = w * x + b
dz_dw = tape.gradient(z,w)
dz_db = tape.gradient(z,b)
print(dz_dw)
print(dz_db)

運行結果爲:

4 獲取高階導數

import tensorflow  as tf
x = tf.Variable(1.0)
with tf.GradientTape() as t1:
    with tf.GradientTape() as t2:
        y = x * x * x
    dy_dx = t2.gradient(y, x)
    print(dy_dx)
d2y_d2x = t1.gradient(dy_dx, x)
print(d2y_d2x)
>>> tf.Tensor(3.0, shape=(), dtype=float32)
>>> tf.Tensor(6.0, shape=(), dtype=float32)

想要得到二階導數,就要使用兩個tape,然後對一階導數再求導就行了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章