使用 TensorFlow 進(jìn)行微分操作
在之前的學(xué)習(xí)之中,我們所使用的模型,包括 Keras 模型與 Estimator 模型,都是使用已經(jīng)定義好的模型進(jìn)行 “組裝”,從而達(dá)到我們的目的。那么我們能不能自定義一個網(wǎng)絡(luò)模型,從而進(jìn)行自定義的訓(xùn)練呢?答案是肯定的。
因此從這節(jié)課開始,我們會幫助大家學(xué)習(xí)如何進(jìn)行自定義的一些操作,這節(jié)課我們便來學(xué)習(xí)所有機(jī)器學(xué)習(xí)自定義的基礎(chǔ) —— 自定義梯度。
1. 什么是梯度
微分是所有目前幾乎所有機(jī)器學(xué)習(xí)的基礎(chǔ),也是 TensorFlow 與 Pytorch 等框架的基礎(chǔ)。我們對模型進(jìn)行優(yōu)化的過程大致可以分為以下三個步驟:
- 數(shù)據(jù)通過模型得到輸出;
- 我們通過計算得到模型中每個參數(shù)的梯度;
- 確定學(xué)習(xí)的步長(學(xué)習(xí)率);
- 按照梯度的方向和學(xué)習(xí)率對每個參數(shù)進(jìn)行優(yōu)化。
我們可以很清楚的看到,最后的兩步是關(guān)鍵的優(yōu)化部分,而第二步 —— 求得梯度的一步就是這兩個關(guān)鍵步驟的前提和基礎(chǔ)。因此我們要首先了解什么是 “梯度”。
梯度的本意是一個向量(矢量),表示某一函數(shù)在該點(diǎn)處的方向?qū)?shù)沿著該方向取得最大值 —— 百度百科。
簡單來說,就是梯度是一個方向,它會指明某一個參數(shù)在哪個方向上面變化地更快。或者不恰當(dāng)?shù)卣f(但是卻非常容易理解),梯度可以理解為一個參數(shù)的導(dǎo)數(shù)。
因此我們可以通過梯度就可以得到模型的參數(shù)在哪個方向上面變化,會使得最終的結(jié)果的 Loss 變??;進(jìn)而我們就可以進(jìn)行模型的優(yōu)化工作。
舉個例子:
y = x**2 + 4
這是一個很簡單的賦值公式:我們將 x 的平方加上 4 ,然后將其賦給 y 。
那么 y 對于 x 求導(dǎo)數(shù),便得到:
dy_dx = 2*x
因此我們在 x 取任意一個值的時候便可以得到 y 對于 x 的梯度。比如當(dāng) x 為 5 的時候,那么 y 對于 x 的梯度便為 2 * 5 = 10 。
2. TensorFlow 之中的梯度帶
既然我們能了解了什么是微分,那么在 TensorFlow 是如何進(jìn)行梯度的計算的呢?
答案就是 “梯度帶”:
tf.GradientTape()
我們先來看一個簡單的例子:
import tensorflow as tf
x = tf.constant(5.0)
with tf.GradientTape(persistent=True) as t:
t.watch(x)
y = x * x
z = y * y
dz_dx = t.gradient(z, x)
dy_dx = t.gradient(y, x)
在這個例子之中,我們定義了兩個操作:
y = x * x
z = y * y
我們令 y 為 x 的平方,讓 z 為 y 的平方,然后我們來追蹤 x 的梯度。
那么先讓我們將 z 也改寫為 x 的形式:
y = x * x
z = x * x * x * x
分別對于 x 求導(dǎo),我們可以得到 y 和 z 的導(dǎo)數(shù):
dy_dx = 2 * x
dz_dx = 4 * x * x * x
通過手動計算,我們可以將 x 的數(shù)值帶入其中,從而得到 y 與 z 關(guān)于 x 的梯度值:分別為 10 和 500 。
在上面的代碼之中,我們可以輸出 dz_dx 和 dy_dx :
print(dz_dx)
print(dy_dx)
我們得到結(jié)果:
10.0
500.0
可以看到,程序已經(jīng)自動求得了梯度,并且與我們計算的完全一致。
具體對于 tf.GradientTape () 這個 API 來說,它會在它自身的上下文之中,將所有執(zhí)行與發(fā)生的操作都記錄在一個 tape 上。 當(dāng)我們計算結(jié)束的時候,這個 tape 上面便記錄了一個完整的計算過程,那么它便從后向前,使用反向微分算法的方式來進(jìn)行梯度的計算,從而得到每個變量的梯度。
3. 使用梯度帶來進(jìn)行自動微分
通過上面小節(jié)的例子,想必大家已經(jīng)對使用 TensorFlow 進(jìn)行梯度求解有了一個大體的了解。
具體來說,我們大致需要經(jīng)過幾個步驟進(jìn)行自動微分的操作:
- 定義梯度帶 tf.GradientTape () 的上下文;
- 指定我們要跟蹤梯度的數(shù)據(jù),tape.watch();
- 在梯度帶上下文之中進(jìn)行我們想要的操作(一般是讓數(shù)據(jù)通過網(wǎng)絡(luò));
- 通過得到的結(jié)果來使用梯度帶求得各個參數(shù)的梯度;
- 后續(xù)的操作,比如根據(jù)梯度進(jìn)行優(yōu)化等。
那么讓我們來看一個更加實(shí)際一些的例子,使用矩陣進(jìn)行運(yùn)算。
import tensorflow as tf
x = tf.ones((5, 5))
with tf.GradientTape() as t:
t.watch(x)
y = tf.reduce_sum(x)
y = tf.reduce_sum(y+x)
z = tf.multiply(y, y)
dz_dx = t.gradient(z, x)
print(dz_dx)
我們可以得到輸出,值得注意的是,因為我們的輸入都是矩陣,因此得到的梯度也是一個矩陣:
tf.Tensor(
[[33800. 33800. 33800. 33800. 33800.]
[33800. 33800. 33800. 33800. 33800.]
[33800. 33800. 33800. 33800. 33800.]
[33800. 33800. 33800. 33800. 33800.]
[33800. 33800. 33800. 33800. 33800.]], shape=(5, 5), dtype=float32)
如此,我們便求得了梯度,這是我們進(jìn)行自定義網(wǎng)絡(luò)的第一步,下一步,我們要將整個網(wǎng)絡(luò)的計算放在梯度帶的上下文之中進(jìn)行計算。
4. 小結(jié)
在這節(jié)課之中,我們學(xué)習(xí)了什么是梯度,然后又了解了 TensorFlow 之中對于梯度求解的 API:tf.GradientTape (),最后我們又通過矩陣的梯度求解了解了該 tf.GradientTape () API 的具體用法。