原文來(lái)源:Google Brain
作者:Asim Shankar 、 Wolff Dobson
「雷克世界」編譯:嗯~阿童木呀
今天,我們?cè)赥ensorFlow中引入了Eager Execution。Eager Execution是一個(gè)命令式、運(yùn)行定義式的接口,其中,操作一旦從Python中調(diào)用便立刻得以執(zhí)行。這樣TensorFlow的入門使用就變得相對(duì)簡(jiǎn)單,并可以使研究和開發(fā)過(guò)程更為直觀。
使用Eager Execution的益處包括以下幾點(diǎn):
具有面對(duì)即時(shí)運(yùn)行錯(cuò)誤的快速調(diào)試和Python工具的集成。
支持使用易用的Python控制流的動(dòng)態(tài)模型。
大力支持自定義的、高階梯度。
適用幾乎所有可用的TensorFlow操作。
現(xiàn)在,eager可作為一個(gè)實(shí)驗(yàn)性特征,所以我們正在尋求來(lái)自社區(qū)的反饋意見,以指導(dǎo)我們的研究方向。
為了更好地理解這一點(diǎn),我們可以看一些相關(guān)代碼,這就非常具有技術(shù)性了,而且如果熟悉TensorFlow的話也將會(huì)有所幫助。
使用Eager Execution
一旦你啟用Eager Execution,操作將立即得以執(zhí)行,并將其值返回給Python,而不需要調(diào)用Session.run()。例如,將兩個(gè)矩陣相乘,代碼為:
importtensorflow as tf
importtensorflow.contrib.eager as tfe
tfe.enable_eager_execution()
x = [[2.]]
m = tf.matmul(x, x)
使用print或Python調(diào)試器來(lái)檢查中間結(jié)果其實(shí)是很簡(jiǎn)單的。
print(m)
# The 1x1 matrix [[4.]]
可以使用Python控制流來(lái)構(gòu)建動(dòng)態(tài)模型。下面是使用TensorFlow算術(shù)運(yùn)算構(gòu)建考拉茲猜想(Collatz conjecture)的一個(gè)示例:
a = tf.constant(12)
counter = 0
while not tf.equal(a, 1):
iftf.equal(a % 2, 0):
a = a / 2
else:
a = 3 * a + 1
print(a)
其中,tf.constant(12)張量對(duì)象的使用可以促進(jìn)將所有的數(shù)學(xué)運(yùn)算提升到張量運(yùn)算中,因此所有返回值都將是張量。
梯度
大多數(shù)TensorFlow用戶都對(duì)自動(dòng)微分法(automatic differentiation)感興趣。因?yàn)樵诿總€(gè)調(diào)用期間可能會(huì)出現(xiàn)不同的操作,所以我們將所有的前向操作記錄到一個(gè)磁帶上,然后在計(jì)算梯度時(shí)回放。一旦我們計(jì)算好梯度之后,就可以將磁帶丟棄。
如果你對(duì)autograd包非常熟悉的話,那就很簡(jiǎn)單了,因?yàn)锳PI與它是很相似的。例如:
def square(x):
returntf.multiply(x, x)
grad = tfe.gradients_function(square)
print(square(3.)) # [9.]
print(grad(3.)) # [6.]
gradients_function調(diào)用使用Python函數(shù)square()作為參數(shù),并返回一個(gè)Python可調(diào)用的函數(shù),用以計(jì)算函數(shù)square()相對(duì)于其輸入的偏導(dǎo)數(shù)。所以,為了得到輸入為3.0時(shí)的square()的導(dǎo)數(shù),調(diào)用grad(3.0),即結(jié)果為6。
可以使用相同的gradients_function調(diào)用以得到square的二階導(dǎo)數(shù):
gradgrad = tfe.gradients_function(lambda x: gra
d(x)[0])
print(gradgrad(3.)) # [2.]
正如我們所說(shuō),控制流可以導(dǎo)致不同的操作得以運(yùn)行,正如在這個(gè)例子中顯示的那樣:
def abs(x):
return x if x > 0. else -x
grad = tfe.gradients_function(abs)
print(grad(2.0)) # [1.]
print(grad(-2.0)) # [-1.]
自定義梯度
用戶可能希望為操作或函數(shù)自定義梯度。如果從多方面考慮的話,這可能會(huì)是有益的,包括為一系列操作提供一個(gè)更有效或數(shù)值更穩(wěn)定的梯度。
下面是一個(gè)示例,說(shuō)明了自定義梯度的使用。我們先看看函數(shù)log(1 + ex),它通常出現(xiàn)在交叉熵和對(duì)數(shù)似然值(log likelihoods)的計(jì)算中。
def log1pexp(x):
return tf.log(1 + tf.exp(x))
grad_log1pexp = tfe.gradients_function(log1pexp
)
# The gradient computation works fine at x = 0.
print(grad_log1pexp(0.))
# [0.5]
# However it returns a `nan` at x = 100 due to
numerical instability.
print(grad_log1pexp(100.))
# [nan]
我們可以使用上述函數(shù)的自定義梯度來(lái)分析簡(jiǎn)化梯度表達(dá)式。需要注意的是,下面的梯度函數(shù)實(shí)現(xiàn)是如何重用在正向傳遞期間得以計(jì)算的表達(dá)式(tf.exp(x))的,通過(guò)避免冗余計(jì)算使得梯度計(jì)算更加高效。
@tfe.custom_gradient
e = tf.exp(x)
def grad(dy):
returndy * (1 - 1 / (1 + e))
return tf.log(1 + e), grad
# Gradient at x = 0 works as before.
# And now gradient computation at x=100 works a
s well.
# [1.0]
構(gòu)建模型
模型可以在類中進(jìn)行組織。下面是一個(gè)模型類,它創(chuàng)建一個(gè)(簡(jiǎn)單的)雙層網(wǎng)絡(luò),可以對(duì)標(biāo)準(zhǔn)的MNIST手寫數(shù)字進(jìn)行分類。
classMNISTModel(tfe.Network):
def __init__(self):
super(MNISTModel, self).__init__()
self.layer1 = self.track_layer(tf.layers.Dens
e(units=10))
self.layer2 = self.track_layer(tf.layers.Dens
def call(self, input):
"""Actually runs the model."""
result = self.layer1(input)
result = self.layer2(result)
return result
我們建議在tf.layers中使用類(而不是函數(shù)),因?yàn)樗鼈儎?chuàng)建并包含了模型參數(shù)(變量)。變量的生命周期是與層對(duì)象的生命周期綁定在一起的,因此要確保對(duì)其進(jìn)行追蹤。
為什么要使用tfe.Network?網(wǎng)絡(luò)就是層的容器,也就是tf.layer.Layer本身,使得Network對(duì)象能夠嵌入到其他Network對(duì)象中。它還包含幫助檢查、保存和恢復(fù)的實(shí)用程序。
即使不對(duì)模型進(jìn)行訓(xùn)練,我們也可以強(qiáng)制調(diào)用它并檢查輸出結(jié)果:
# Let's make up a blank input image
model = MNISTModel()
batch = tf.zeros([1, 1, 784])
print(batch.shape)
# (1, 1, 784)
result = model(batch)
print(result)
# tf.Tensor([[[ 0. 0., ...., 0.]]], shape=(1,
1, 10), dtype=float32)
請(qǐng)注意,我們不需要任何占位符或會(huì)話(sessions)。在我們第一次將輸入傳遞進(jìn)去時(shí),層參數(shù)的大小就已經(jīng)得以設(shè)置。
為了能夠?qū)θ魏文P瓦M(jìn)行訓(xùn)練,我們定義了一個(gè)損失函數(shù)進(jìn)行優(yōu)化、計(jì)算梯度,并使用優(yōu)化器來(lái)更新變量。首先,定義一個(gè)損失函數(shù):
defloss_function(model, x, y):
y_ = model(x)
returntf.nn.softmax_cross_entropy_with_logits
(labels=y, logits=y_)
然后,訓(xùn)練的循環(huán)過(guò)程如下:
optimizer = tf.train.GradientDescentOptimizer(l
earning_rate=0.001)
for (x, y) in tfe.Iterator(dataset):
grads = tfe.implicit_gradients(loss_function)
(model, x, y)
optimizer.apply_gradients(grads)
對(duì)于在其計(jì)算過(guò)程中使用的所有TensorFlow變量,implicit_gradients()將計(jì)算loss_function的導(dǎo)數(shù)。
我們可以將計(jì)算移動(dòng)到GPU上,就像我們一直使用TensorFlow所做的那樣:
withtf.device("/gpu:0"):
optimizer.minimize(lambda: loss_function(mo
del, x, y))
(注意:我們正在減少存儲(chǔ)損失,并直接調(diào)用optimizer.minimize,當(dāng)然,你也可以使用上面提到的apply_gradients()方法,它們是等效的。)
使用具有g(shù)raph的eager
Eager Execution使開發(fā)和調(diào)試過(guò)程更加具有互動(dòng)性,但是涉及在分布式訓(xùn)練、性能優(yōu)化和生產(chǎn)部署方面,TensorFlow graphs具有很多優(yōu)勢(shì)。
啟用Eager Execution時(shí),執(zhí)行操作的相同代碼將構(gòu)建一個(gè)描述Eager Execution未啟動(dòng)時(shí)的計(jì)算圖。要將模型轉(zhuǎn)換為圖,只需在新的Python會(huì)話中運(yùn)行相同的代碼即可,其中,Eager Execution還沒有得以啟用,正如在MNIST示例中所看到的那樣。可以從檢查點(diǎn)保存和恢復(fù)模型變量的值,從而使我們能夠更容易地在eager(命令式)和graph(聲明式)之間移動(dòng)編程。通過(guò)這種方式,啟用Eager Execution編程的模型可以輕松導(dǎo)出以進(jìn)行生產(chǎn)部署。
在不久的將來(lái),我們將提供實(shí)用程序,以便選擇性地將模型的部分轉(zhuǎn)換為圖。通過(guò)這種方式,你可以將計(jì)算的各個(gè)部分(例如一個(gè)自定義RNN單位的內(nèi)部)融合在一起,以便實(shí)現(xiàn)高性能,還可以保持Eager Execution的靈活性和易讀性。
代碼如何更改?
對(duì)于當(dāng)前的TensorFlow用戶來(lái)說(shuō),使用Eager Execution應(yīng)該是很直觀、容易的。只有少數(shù)專門針對(duì)eager 的API可能會(huì)有些難度。大多數(shù)現(xiàn)有的API和操作需要與已啟動(dòng)的eager一起運(yùn)行。下面是一些注意事項(xiàng):
與TensorFlow一樣,我們的建議是,如果你還沒有從隊(duì)列切換到使用tf.data進(jìn)行輸入處理,那你該將其提上日程了。它更容易使用,且通常較快。更多詳情,可點(diǎn)擊鏈接查閱:博客文章(https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html),相關(guān)資料文檔(https://www.tensorflow.org/programmers_guide/datasets)。
使用面向?qū)ο髮?,如tf.layer.Conv2D()或Keras層;它們可以對(duì)變量進(jìn)行顯式存儲(chǔ)。
對(duì)于大多數(shù)模型來(lái)說(shuō),你可以編寫一些代碼,以便它們?cè)趫?zhí)行Eager Execution和圖形構(gòu)建時(shí)都可以使用。當(dāng)然也有一些例外,例如使用Python控制流基于輸入來(lái)改變計(jì)算的動(dòng)態(tài)模型。
一旦你調(diào)用了tfe.enable_eager_execution(),它就不能關(guān)閉。要獲取圖行為,請(qǐng)啟動(dòng)一個(gè)新的Python會(huì)話。
入門和未來(lái)
這只是一個(gè)預(yù)覽版,所以你可能會(huì)碰到一些不足之處。入門須知:
安裝TensorFlow的nightly版本:https://github.com/tensorflow/tensorflow#installation
查看README文件(包括已知問(wèn)題):https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/README.md
在Eager Execution的用戶指南中獲取詳細(xì)的說(shuō)明:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/g3doc/guide.md
查看GitHub中的有關(guān)Eager示例:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/examples
查閱變更日志:https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/README.md#changelog