當前位置:
首頁 > 科技 > TensorFlow發布「eager」模式,更易操作!

TensorFlow發布「eager」模式,更易操作!

圖:pixabay

原文來源:Google Brain

作者:Asim Shankar 、 Wolff Dobson

「雷克世界」編譯:嗯~阿童木呀

今天,我們在TensorFlow中引入了Eager Execution。Eager Execution是一個命令式、運行定義式的介面,其中,操作一旦從Python中調用便立刻得以執行。這樣TensorFlow的入門使用就變得相對簡單,並可以使研究和開發過程更為直觀。

使用Eager Execution的益處包括以下幾點:

?具有面對即時運行錯誤的快速調試和Python工具的集成。

?支持使用易用的Python控制流的動態模型。

?大力支持自定義的、高階梯度。

?適用幾乎所有可用的TensorFlow操作。

現在,eager可作為一個實驗性特徵,所以我們正在尋求來自社區的反饋意見,以指導我們的研究方向。

為了更好地理解這一點,我們可以看一些相關代碼,這就非常具有技術性了,而且如果熟悉TensorFlow的話也將會有所幫助。

使用Eager Execution

一旦你啟用Eager Execution,操作將立即得以執行,並將其值返回給Python,而不需要調用Session.run()。例如,將兩個矩陣相乘,代碼為:

importtensorflow as tf

tfe.enable_eager_execution()

x = [[2.]]

m = tf.matmul(x, x)

使用print或Python調試器來檢查中間結果其實是很簡單的。

print(m)

# The 1x1 matrix [[4.]]

可以使用Python控制流來構建動態模型。下面是使用TensorFlow算術運算構建考拉茲猜想(Collatz conjecture)的一個示例:

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)張量對象的使用可以促進將所有的數學運算提升到張量運算中,因此所有返回值都將是張量。

梯度

大多數TensorFlow用戶都對自動微分法(automatic differentiation)感興趣。因為在每個調用期間可能會出現不同的操作,所以我們將所有的前向操作記錄到一個磁帶上,然後在計算梯度時回放。一旦我們計算好梯度之後,就可以將磁帶丟棄。

如果你對autograd包非常熟悉的話,那就很簡單了,因為API與它是很相似的。例如:

def square(x):

returntf.multiply(x, x)

grad = tfe.gradients_function(square)

print(square(3.)) # [9.]

print(grad(3.)) # [6.]

gradients_function調用使用Python函數square()作為參數,並返回一個Python可調用的函數,用以計算函數square()相對於其輸入的偏導數。所以,為了得到輸入為3.0時的square()的導數,調用grad(3.0),即結果為6。

可以使用相同的gradients_function調用以得到square的二階導數:

gradgrad = tfe.gradients_function(lambda x: gra

d(x)[0])

print(gradgrad(3.)) # [2.]

正如我們所說,控制流可以導致不同的操作得以運行,正如在這個例子中顯示的那樣:

def abs(x):

return x if x > 0. else -x

grad = tfe.gradients_function(abs)

print(grad(2.0)) # [1.]

自定義梯度

用戶可能希望為操作或函數自定義梯度。如果從多方面考慮的話,這可能會是有益的,包括為一系列操作提供一個更有效或數值更穩定的梯度。

下面是一個示例,說明了自定義梯度的使用。我們先看看函數log(1 + ex),它通常出現在交叉熵和對數似然值(log likelihoods)的計算中。

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]

我們可以使用上述函數的自定義梯度來分析簡化梯度表達式。需要注意的是,下面的梯度函數實現是如何重用在正向傳遞期間得以計算的表達式(tf.exp(x))的,通過避免冗餘計算使得梯度計算更加高效。

@tfe.custom_gradient

def log1pexp(x):

e = tf.exp(x)

def grad(dy):

returndy * (1 - 1 / (1 + e))

return tf.log(1 + e), grad

grad_log1pexp = tfe.gradients_function(log1pexp

)

# Gradient at x = 0 works as before.

print(grad_log1pexp(0.))

# [0.5]

# And now gradient computation at x=100 works a

s well.

print(grad_log1pexp(100.))

# [1.0]

構建模型

模型可以在類中進行組織。下面是一個模型類,它創建一個(簡單的)雙層網路,可以對標準的MNIST手寫數字進行分類。

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中使用類(而不是函數),因為它們創建並包含了模型參數(變數)。變數的生命周期是與層對象的生命周期綁定在一起的,因此要確保對其進行追蹤。

為什麼要使用tfe.Network?網路就是層的容器,也就是tf.layer.Layer本身,使得Network對象能夠嵌入到其他Network對象中。它還包含幫助檢查、保存和恢復的實用程序。

即使不對模型進行訓練,我們也可以強制調用它並檢查輸出結果:

# 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)

請注意,我們不需要任何佔位符或會話(sessions)。在我們第一次將輸入傳遞進去時,層參數的大小就已經得以設置。

為了能夠對任何模型進行訓練,我們定義了一個損失函數進行優化、計算梯度,並使用優化器來更新變數。首先,定義一個損失函數:

defloss_function(model, x, y):

y_ = model(x)

(labels=y, logits=y_)

然後,訓練的循環過程如下:

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)

對於在其計算過程中使用的所有TensorFlow變數,implicit_gradients()將計算loss_function的導數。

我們可以將計算移動到GPU上,就像我們一直使用TensorFlow所做的那樣:

withtf.device("/gpu:0"):

for (x, y) in tfe.Iterator(dataset):

optimizer.minimize(lambda: loss_function(mo

del, x, y))

(注意:我們正在減少存儲損失,並直接調用optimizer.minimize,當然,你也可以使用上面提到的apply_gradients()方法,它們是等效的。)

使用具有graph的eager

Eager Execution使開發和調試過程更加具有互動性,但是涉及在分散式訓練、性能優化和生產部署方面,TensorFlow graphs具有很多優勢。

啟用Eager Execution時,執行操作的相同代碼將構建一個描述Eager Execution未啟動時的計算圖。要將模型轉換為圖,只需在新的Python會話中運行相同的代碼即可,其中,Eager Execution還沒有得以啟用,正如在MNIST示例中所看到的那樣。可以從檢查點保存和恢復模型變數的值,從而使我們能夠更容易地在eager(命令式)和graph(聲明式)之間移動編程。通過這種方式,啟用Eager Execution編程的模型可以輕鬆導出以進行生產部署。

在不久的將來,我們將提供實用程序,以便選擇性地將模型的部分轉換為圖。通過這種方式,你可以將計算的各個部分(例如一個自定義RNN單位的內部)融合在一起,以便實現高性能,還可以保持Eager Execution的靈活性和易讀性。

代碼如何更改?

對於當前的TensorFlow用戶來說,使用Eager Execution應該是很直觀、容易的。只有少數專門針對eager 的API可能會有些難度。大多數現有的API和操作需要與已啟動的eager一起運行。下面是一些注意事項:

?與TensorFlow一樣,我們的建議是,如果你還沒有從隊列切換到使用tf.data進行輸入處理,那你該將其提上日程了。它更容易使用,且通常較快。更多詳情,可點擊鏈接查閱:博客文章(https://developers.googleblog.com/2017/09/introducing-tensorflow-datasets.html),相關資料文檔(https://www.tensorflow.org/programmers_guide/datasets)。

?使用面向對象層,如tf.layer.Conv2D()或Keras層;它們可以對變數進行顯式存儲。

?對於大多數模型來說,你可以編寫一些代碼,以便它們在執行Eager Execution和圖形構建時都可以使用。當然也有一些例外,例如使用Python控制流基於輸入來改變計算的動態模型。

?一旦你調用了tfe.enable_eager_execution(),它就不能關閉。要獲取圖行為,請啟動一個新的Python會話。

入門和未來

這只是一個預覽版,所以你可能會碰到一些不足之處。入門須知:

?安裝TensorFlow的nightly版本:https://github.com/tensorflow/tensorflow#installation

?查看README文件(包括已知問題):https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/README.md

?在Eager Execution的用戶指南中獲取詳細的說明:https://github.com/tensorflow/tensorflow/tree/master/tensorflow/contrib/eager/python/g3doc/guide.md

?查看GitHub中的有關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


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雷克世界 的精彩文章:

MIT與FAIR提出「mixup」,用數據和標籤的隨機線性插值提高神經網路健壯性
如何一文讀懂「進化策略」?這裡有幾組動圖!
對抗攻擊最新研究:僅修改「一個像素」即可騙過神經網路!
如何使用Keras函數式API進行深度學習?
李國傑院士:AI創業公司如何擺脫被收購的命運「附雷克世界專訪」

TAG:雷克世界 |