天天看點

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

對于最流行的機器學習架構來說,TensorFlow 2.0 将是一個重要的裡程碑:大量的更改即将到來,所有的一切都以人人可以使用 ML 為目标。但是,這些更改要求老使用者完全重新學習如何使用架構:本文介紹了 1.x 和 2.x 版本之間的所有(已知的)差異,主要是思維方式的改變,并着重介紹了新實作的優缺點。

對于新手來說,本文也是一個很好的起點:現在就開始以 TensorFlow 2.0 的方式思考,這樣你就不必重新學習一個新的架構(除非 TensorFlow 3.0 釋出)。

TensorFlow 2.0:為什麼?何時?

TensorFlow 2.0 的核心思想是使 TensorFlow 更易于學習和應用。

在公告郵件清單 (https://groups.google.com/a/tensorflow.org/forum/#!forum/announce)中,谷歌大腦工程師 Martin Wicke 對 TensorFlow 2.0 做了初步介紹。簡而言之:

 ●  Eager Execution 将是 2.0 的核心特性。它将使用者對程式設計模型的期望與 TensorFlow 實踐更好地結合起來,使 TensorFlow 更易于學習和應用。

 ●  支援更多的平台和語言,通過交換格式的标準化和 API 的對齊,改進這些元件之間的相容性和對等性。

 ●  删除棄用的 API 并減少重複,避免給使用者帶來混亂。

 ●  公開的 2.0 設計過程:社群現在可以與 TensorFlow 開發人員合作,使用 TensorFlow 讨論組讨論新特性。

 ●  相容性和延續性:提供一個與 TensorFlow 1.x 相容的子產品,這意味着 TensorFlow 2.0 将有一個包含所有 TensorFlow 1.x API 的子產品。

 ●  On-disk 相容性:TensorFlow 1.x 中導出的模型(檢查點和當機模型)将與 TensorFlow 2.0 相容,隻需要重命名某些變量。

 ●  tf.contrib:完全删除。大型的維護中的子產品将移動到獨立的存儲庫;未使用和未維護的子產品将被删除。

實際上,如果你是一個 TensorFlow 新手,那麼你很幸運。如果像我一樣,從 0.x 版本開始使用 TensorFlow,那麼必須重寫所有的代碼庫(與從 0.x 向 1.x 轉換不同,更改的地方特别多);不過,TensorFlow 的作者聲稱,将會釋出一個轉換工具來幫助轉換。然而,轉換工具并不完美,需要人工幹預。

此外,你必須改變你的思維方式;這很有挑戰性,但每個人都喜歡挑戰,不是嗎?

讓我們面對這個挑戰,從第一個巨大的差異開始詳細檢視這次新版本設計的更改:删除 tf.get_variable、f.variable_scope、tf.layers 和強制轉換為基于 Keras 的方法,使用 tf.keras。

請注意,釋出日期還沒有确定。但是,從 TensorFlow 讨論組中,我們知道,2018 年底釋出可能會釋出一個預覽版本,2.0 的正式版本可能會在 2019 年春天釋出。

是以,最好是在 RFC 被接受後立即更新所有現有的代碼庫,以便順利過渡到這個新的 TensorFlow 版本。

Keras(OOP)與 TensorFlow 1.x 比較

“RFC:TensorFlow 2.0 中的變量”已經被接受。這個 RFC 可能是對現有代碼庫影響最大的一個,而且,TensorFlow 的老使用者需要換一種新的思維方式。

正如文章“使用 Go 來了解TensorFlow”中描述的那樣,每個變量在計算圖中都有一個唯一的名稱。

作為一個早期的 TensorFlow 使用者,我習慣于按照以下模式設計我的計算圖:

 ●  哪些操作連接配接了變量節點?将圖定義為多個連接配接的子圖。為了定義不同圖的變量,在單獨的 tf.variable_scope 中定義每個子圖。在不同的範圍内定義子圖,可以在 Tensorboard 中得到一個清晰的圖表示。

 ●  在相同的執行步驟中,我是否需要多次使用子圖?為了避免建立一個以 _n 為字首的新圖,一定要利用 tf.variable_scope 的 reuse 參數。

 ●  圖已經定義了?建立變量初始化 op(看看 tf.global_variables_initializer() 調用了多少次?)

 ●  把圖加載到 Session 中并運作。

在我看來,示例“如何在 TensorFlow 中實作簡單的 GAN”可以更好地說明這些步驟的合理性。

通過 GAN 了解 TensorFlow 1.x

GAN 判别器 D 必須使用 tf.variable_scope reuse 參數定義,因為,我們希望首先給 D 提供真樣本,然後提供假樣本,最後計算 D 相關參數的梯度。

相反,生成網絡 G 在一次疊代中從未使用兩次,是以,不需要擔心其變量重用。

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

當調用這兩個函數時,在預設圖中定義了兩個不同的子圖,每個子圖都有自己的作用域(生成器或判别器)。請注意,這個函數傳回的是定義子圖的輸出張量,而不是圖本身。

為了共用 D 圖,我們定義了 2 個輸出(真和假),并定義了訓練 G 和 D 所需的損失函數。

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事
最後要做的隻是定義訓練 D 和 G 所需的 2 個損失函數和 2 個優化器。
關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

損失函數很容易定義。對抗式訓練的特點是首先要用真樣本和由 G 生成的樣本對 D 進行訓練,然後用 D 評估的結果作為輸入信号對對抗性的 G 進行訓練。

對抗性訓練的這兩個訓練步驟需要單獨運作,但是,我們在同一個圖中定義了模型,我們不想在訓練 D 時更新 G 變量,反之亦然。

這樣,由于我們在預設圖中定義了每個變量,是以每個變量都是全局的,我們必須使用兩個不同的清單獲得正确的變量,并確定定義了優化器,以便計算梯度,并僅對恰當的子圖應用更新。

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事
好了,我們到了第三步,圖定義最後要做的是定義變量初始化 op:
關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

優點 / 缺點

圖已經正确定義,當在訓練循環和會話中使用時,它可以正常工作。然而,從軟體工程的角度來看,有一些特性值得注意:

 ●  使用上下文管理器 tf.variable_scope 更改由 tf.layers 定義的變量的(完整)名稱:在不同的變量作用域内,對 tf.layers.* 方法的相同調用會在不同的變量作用域内定義一組新的變量。

 ●  布爾辨別 reuse 可以完全改變 tf.layers.* 方法任何調用的行為(定義或重用)。

 ●  每個變量都是全局的: tf.layers 調用 tf.get_variable(在 tf.layers 内部使用)定義的變量可以從任何地方通路:上面使用 tf.trainable_variables(prefix) 來獲得兩個變量清單就是對這種情況的一個很好說明。

 ●  定義子圖并不簡單:隻是調用 discriminator 并不能獲得一個新的、獨立的判别器。有點違反直覺。

 ●  子圖定義的傳回值不是其唯一的輸出向量,其中也沒有包含圖的所有資訊(雖然可以追溯到輸入,但并不簡單)。

 ●  定義變量初始化 op 太無趣(但這剛剛通過 tf.train.MonitoredSession 和 tf.train.MonitoredTrainingSession 得到了解決)。

這 6 條大概全是缺點。

我們使用 TensorFlow 1.x 的方式定義了 GAN:下面讓我們遷移到 TensorFlow 2.0。

通過 GAN 了解 TensorFlow 2.x

如前一節所述,在 TensorFlow 2.x 中,思維方式改變了。tf.get_variable、tf.variable_scope、tf.layers 被移除并強制轉換為基于 Keras 的方法,使用 tf.keras 會迫使 TensorFlow 開發人員改變其思維方式。

我們必須使用 tf.keras 定義生成器 G 和判别器 D:這将為我們提供變量共享特性,我們曾經使用該特性來定義 D,但是底層實作的方式不同。

請注意:tf.layers 将被移除,是以,請現在就開始使用 tf.keras 定義你的模型,這是為 2.x 做準備所必須的。

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

看下該方法的不同:生成器和判别器都傳回一個 tf.keras.Model,而不僅僅是一個輸出張量。

這意味着,使用 Keras,我們可以執行個體化我們的模型,并在源代碼的不同部分使用相同的模型,我們可以有效地使用模型變量,而無需定義以 _n 為字首的新子圖。實際上,和 1.x 版本不同,我們隻定義一個 D 模型,但使用了兩次。

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

同樣:不需要像前面那樣定義 D_fake,也不需要在定義圖時提前考慮變量共享問題。

現在,我們可以繼續定義 G 和 D 的損失函數了。

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事
到目前為止還不錯。最後要做的是定義兩個分别優化 D 和 G 的優化器。因為我們用的是 tf.keras,是以不需要手動建立需要更新的變量清單,因為 tf.keras.Models 對象本身具有這個屬性:
關于即将釋出的TensorFlow 2.0,你需要知道這幾件事
我們已經準備好了:我們到達了第 3 步,由于我們仍然在使用靜态圖模式,我們必須定義變量初始化 op:
關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

 ●  從 tf.layers 轉換到 tf.keras 很簡單:所有的 tf.layers 方法都有對等的 tf.keras.layers 方法。

 ●  f.keras.Model 完全解決了變量重用以及圖重定義的困擾。

 ●  tf.keras.Model 不是一個輸出張量,但是一個包含自有變量的完整模型。

 ●  我們還是必須初始化所有變量,但就像我們前面說過的那樣,tf.train.MonitoredSession 可以幫我們完成。

不管是在 TensorFlow 1.x 中,還是在 2.x 中,GAN 示例都是首先使用“舊”的圖定義範式,然後在會話中執行(不管是現在還是将來,這都是一個很好且有效的範式,并且是——個人觀點——最好的)。

然而,TensorFlow 2.x 的另一個大變化是讓 Eager 模式成為預設執行模式。在 TensorFlow 1.x 中,我們必須顯式地啟用 Eager Execution,而在 TensorFlow 2.x 中,我們要做相反的事情。

Eager 模式優先

以下是 Eager Execution 指南的解釋:

TensorFlow 的 Eager Execution 是一種必要的程式設計環境,它可以立即評估操作,而不需要建構圖:操作傳回具體的值,而不是建構一個計算圖并稍後運作。這使得開始 TensorFlow 入門和模型調試變得很容易,同時也減少了模闆檔案。請按照本指南在互動式 Python 解釋器中運作下面的代碼示例。

Eager Execution 是一個靈活的機器學習研究和試驗平台,提供以下特性:

 ●  直覺的接口——自然地構造代碼并使用 Python 資料結構。快速疊代小模型和小樣本。

 ●  更易于調試——直接調用 ops 檢查正在運作的模型和測試更改。使用标準的 Python 調試工具進行即時錯誤報告。

 ●  自然的控制流——使用 Python 控制流而不是圖控制流,簡化了動态模型的規範。

簡而言之:不需要首先定義圖,然後在會話中計算它。在 Eager 模式中使用 TensorFlow 可以混合定義和執行,就像标準的 Python 程式一樣。

這與靜态圖版本并不是一一對應的,因為有些在圖中很自然的東西并不存在于這樣一個指令式環境中。

這裡,最重要的例子是 tf.GradientTape 上下文管理器,這隻存在于 Eager 模式下。

當我們有一個圖,我們知道節點是如何連接配接的,當我們要計算某個函數的梯度時,我們可以從輸出回溯到圖的輸入,計算梯度并得到結果。

在 Eager 模式下,我們不能這樣。使用自動微分法計算函數梯度的唯一方法是建構一個圖。建構在 tf.GradientTape 上下文管理器中執行的、對一些可觀察的元素(比如變量)進行操作的圖,然後,可以由 tf.GradientTape 來計算我們需要的梯度。

在 tf.GradientTape 文檔頁上,我們可以找到例子,清楚地說明如何使用 tf.GradientTape 以及為什麼需要它:

關于即将釋出的TensorFlow 2.0,你需要知道這幾件事

此外,控制流操作就是 Python 的控制流操作(比如 for loop、if 語句……),與 tf.while_loop、tf.map_fn、tf.cond 不同,那些方法我們必須在靜态圖版本中使用。

有一個工具,叫做 Autograph(),它可以幫助你使用普通的 Python 編寫複雜的圖代碼。在背景,AutoGraph 自動将代碼轉換為等效的 TensorFlow 圖代碼。

不過,你需要編寫的 Python 代碼不是純 Python(例如,如果你要聲明一個函數傳回一個指定 TensorFlow 資料類型的元素清單,那會用到在标準 Python 函數中不會使用的操作),而其功能至少在本文寫作時是有限的。

之是以建立這個工具,是因為圖版本有一個很大的優勢,即一旦導出,它就成為“單個檔案”,而在生産環境中傳遞經過訓練的機器學習模型,使用靜态圖模式要容易得多。另外,靜态圖模式更快。

就我個人而言,我不太喜歡 Eager 模式。可能是因為我已經習慣了靜态圖版本,并且我發現,Eager 模式是 PyTorch 的粗糙模仿。另外,嘗試将 GAN 從 PyTorch 實作轉成 TensorFlow 2.x 版本,同時使用靜态圖和 Eager 模式時,我無法讓 Eager 模式發揮作用,我還不知道為什麼(雖然靜态圖實作工作得很好)。我在 GitHub 上送出了一個 Bug 報告(當然,這個錯誤可能是我自己的):TensorFlow Eager 版本失敗了,而 TensorFlow 靜态圖可以正常運作(https://github.com/tensorflow/tensorflow/issues/23407)。

轉換到 TensorFlow 2.x 還需要做其他的修改,我将在下一節“該怎麼辦?”中總體介紹。

該怎麼辦?

關于轉換到 TensorFlow 2.x,下面是我根據自己的了解整理的 F.A.Q 清單。

如果我的項目使用了 tf.contrib,該怎麼辦?

所有關于 tf.contrib 内部項目命運的資訊可以在這裡找到:tf.contrib日落(https://github.com/tensorflow/community/blob/rfc-contrib/rfcs/20180907-contrib-sunset.md)。

你可能隻需要安裝一個新的 Python 包,或者将 tf. instrument .something 重命名為 tf.something。

如果我的項目在 TensorFlow 1.x 中可以運作,而在 2.x 中無法運作了,該怎麼辦?

不應該出現這種情況:請再次檢查轉換實作是否正确,如果是,則在 GitHub 上送出一個 Bug 報告。

如果項目在靜态圖模式下可以運作,而在 Eager 模式下無法運作,該怎麼辦?

這是我目前遇到的問題,我已經送出了報告:TensorFlow Eager 版本失敗了,而 TensorFlow 靜态圖可以正常運作。

現在我還不知道這是我自己的 Bug,還是實際的 TensorFlow Eager 版本有什麼問題。但是,由于我習慣于靜态圖的思考方式,是以我将避免使用 Eager 版本。

如果某個 tf. 方法在 2.x 中被删除了,該怎麼辦?

這個方法很可能隻是被移動了。在 TensorFlow 1.x 中,有很多方法的别名。而在 TensorFlow 2.x 中,我們的目标是(如果 RFC: TensorFlow 名稱空間 如我所願被接受的話)删除許多别名,并将方法移動到更好的位置,以提高整體的一緻性。

在 RFC 中,你可以找到新提議的名稱空間、要删除的名稱空間清單以及所有其他為增強架構的一緻性(可能)要進行的更改。

另外,即将釋出的轉換工具可能可以正确地為你應用所有這些更新(這隻是我對該轉換工具的猜測,但由于這是一項簡單的任務,那是很可能會出現的一個特性)。

小 結

本文的寫作目的是闡明 TensorFlow 2.0 将給架構使用者帶來的變化和挑戰。

TensorFlow 1 中的 GAN 實作以及到 TensorFlow 2.x 的轉換應該可以清楚地說明使用新版本所需要的心态改變。

總的來說,我認為,TensorFlow 2.x 将改進架構的品質,标準化并簡化它的用法。從未見過靜态圖方法且習慣于使用指令式語言的新使用者可能會發現,Eager 模式是進入 TensorFlow 世界的一個很好的切入點。

不過,更新中有些部分我不喜歡(這隻是我個人的觀點):

 ●  把重點放在 Eager Execution 上,并使之成為預設模式:在我看來,這似乎是一種營銷手段。TensorFlow 似乎是想要追趕 PyTorch(預設是 Eager);

 ●  靜态圖和 Eager(以及混合它們的可能性)不是 1:1 相容性的,在我看來,這可能會在大型項目中造成混亂,使得項目難以維護;

 ●  切換到基于 Keras 的方法是一項很好的舉措,但它使圖在 Tensorboard 中的可視化變得非常難看。事實上,變量和圖的定義是全局的,在 TensorFlow 圖中建立新“塊”的 tf.named_scope(為了共享變量更容易,每次調用 Keras Model 時都會調用)被圖隔開,它是内部使用的,它的輸入節點清單中包含所有的模型變量——這使得 Tensorboard 中圖的可視化變得幾乎沒有用處,對于這樣一個好工具,這真是個遺憾。

如果你喜歡這篇文章,請分享;如果文章中有什麼問題 / 可以改進的地方,請随時告訴我。

原文釋出時間為:2018-11-15

本文作者:AI前線小組

本文來自雲栖社群合作夥伴“

磐創AI

”,了解相關資訊可以關注“

”。