重磅!Tensorflow 2.0 即將入場

2021-01-14 CVer

編者按:幾天前,Tensorflow剛度過自己的3歲生日,作為當前最受歡迎的機器學習框架,Tensorflow在這個寶座上已經盤踞了近三年。無論是成熟的Keras,還是風頭正盛的pytorch,它的地位似乎總是無法被撼動。而就在即將到來的2019年,Tensorflow 2.0將正式入場,給暗流湧動的框架之爭再燃一把火。

如果說兩代Tensorflow有什麼根本不同,那應該就是Tensorflow 2.0更注重使用的低門檻,旨在讓每個人都能應用機器學習技術。考慮到它可能會成為機器學習框架的又一個重要裡程碑,本文會介紹1.x和2.x版本之間的所有(已知)差異,重點關注它們之間的思維模式變化和利弊關係。

通過閱讀這篇文章,熟悉Tensorflow的老用戶可以儘早轉變思維,適應新版本的變化。而新手也可以直接以Tensorflow 2.0的方式思考,至少目前沒有必要急著去學習別的框架。

Tensorflow 2.0的開發初衷是製作一個更簡單易用的Tensorflow。

第一個向公眾透露項目具體開發內容的人是Google Brain的工程師Martin Wicke,我們可以在他的公告郵件列表裡找到Tensorflow 2.0的蛛絲馬跡。在這裡,我們對它做一些簡單提要:

Tensorflow 2.0的核心功能是動態圖機制Eager execution。它允許用戶像正常程序一樣去編寫、調試模型,使TensorFlow更易於學習和應用。

支持更多平臺、更多語言,通過標準化API的交換格式和提供準線改善這些組件之間的兼容性。

刪除已棄用的API並減少重複的API數,避免給用戶造成混淆。

2.0版的設計對公眾開放:社區可以和Tensorflow開發人員一起工作,共同探討新功能。

兼容性和連續性:Tensorflow 2.0會提供Tensorflow 1.x的兼容性模塊,也就是它會內置所有Tensorflow 1.x API的模塊。

硬碟兼容性:只需修改一些變量名稱,Tensorflow 1.x中導出的模型(checkpoints和模型freeze)就能和Tensorflow 2.0兼容。

tf.contrib退出歷史舞臺。其中有維護價值的模塊會被移動到別的地方,剩餘的都將被刪除。

換言之,如果你在這之前從沒接觸過Tensorflow,你是幸運的。但是,如果你和我們一樣是從0.x版本用起的,那麼你就可能得重寫所有代碼庫——雖然官方說會發布轉換工具方便老用戶,但這種工具肯定有很多bug,需要一定的手動幹預。

而且,你也必須開始轉變思維模式。這做起來不容易,但真的猛士不就應該喜歡挑戰嗎?

所以為了應對挑戰,我們先來適應第一個巨大差異:移除tf.get_variable,tf.variable_scope,tf.layers,強制轉型到基於Keras的方法,也就是用tf.keras。

關於Tensorflow 2.0的發布日期,官方並沒有給出明確時間。但根據開發小組成員透露的消息,我們可以確定它的預覽版會在今年年底發布,官方正式版可能會在2019年春季發布。

所以留給老用戶的時間已經不多了。

Keras(OOP)vs Tensorflow 1.x在GitHub上,RFC:TensorFlow 2.0中的變量這份意見稿已經被官方接受,它可能是對現有代碼庫影響最大的RFC,值得參考。

我們都知道,在Tensorflow裡,每個變量在計算圖中都有一個唯一的名稱,我們也已經習慣按照這種模式設計計算圖:

哪些操作連接我的變量節點:把計算圖定義為連接的多個子圖,並用tf.variable_scope在內部定義每個子圖,以便定義不同計算圖的變量,並在Tensorboard中獲得清晰的圖形表示。

需要在執行同一步驟時多次使用子圖:一定要用tf.variable_scope裡的reuse參數,不然Tensorflow會生成一個前綴為_n的新計算圖。

定義計算圖:定義參數初始化節點(你調用過幾次tf.global_variables_initializer()?)。

把計算圖加載到Session,運行。

下面,我們就以在Tensorflow中實現簡單的GAN為例,更生動地展現上述步驟。

Tensorflow 1.x的GAN

要定義GAN的判別器D,我們一定會用到tf.variable_scope裡的reuse參數。因為首先我們會把真實圖像輸入判別器,之後把生成的假樣本再輸進去,在且僅在最後計算D的梯度。相反地,生成器G裡的參數不會在一次迭代中被用到兩次,所以沒有擔心的必要。

def generator(inputs):

   """generator network.

   Args:

       inputs: a (None, latent_space_size) tf.float32 tensor

   Returns:

       G: the generator output node

   """

   with tf.variable_scope("generator"):

       fc1 = tf.layers.dense(inputs, units=64, activation=tf.nn.elu, name="fc1")

       fc2 = tf.layers.dense(fc1, units=64, activation=tf.nn.elu, name="fc2")

       G = tf.layers.dense(fc1, units=1, name="G")

   return G


def discriminator(inputs, reuse=False):

   """discriminator network

   Args:

       inputs: a (None, 1) tf.float32 tensor

       reuse: python boolean, if we expect to reuse (True) or declare (False) the variables

   Returns:

       D: the discriminator output node

   """

   with tf.variable_scope("discriminator", reuse=reuse):

       fc1 = tf.layers.dense(inputs, units=32, activation=tf.nn.elu, name="fc1")

       D = tf.layers.dense(fc1, units=1, name="D")

   return D

當這兩個函數被調用時,Tensorflow會默認在計算圖內部定義兩個不同的子圖,每個子圖都有自己的scope(生成器/判別器)。請注意,這個函數返回的是定義好的子圖的張量,而不是子圖本身。

為了共享D這個子圖,我們需要定義兩個輸入(真實圖像/生成樣本),並定義訓練G和D所需的損失函數。

# Define the real input, a batch of values sampled from the real data

real_input = tf.placeholder(tf.float32, shape=(None,1))

# Define the discriminator network and its parameters

D_real = discriminator(real_input)


# Arbitrary size of the noise prior vector

latent_space_size = 100

# Define the input noise shape and define the generator

input_noise = tf.placeholder(tf.float32, shape=(None,latent_space_size))

G = generator(input_noise)


# now that we have defined the generator output G, we can give it in input to

# D, this call of `discriminator` will not define a new graph, but it will

# **reuse** the variables previously defined

D_fake = discriminator(G, True)

最後要做的是分別定義訓練D和G所需的2個損失函數和2個優化器。

D_loss_real = tf.reduce_mean(

   tf.nn.sigmoid_cross_entropy_with_logits(logits=D_real, labels=tf.ones_like(D_real))

)


D_loss_fake = tf.reduce_mean(

   tf.nn.sigmoid_cross_entropy_with_logits(logits=D_fake, labels=tf.zeros_like(D_fake))

)


# D_loss, when invoked it first does a forward pass using the D_loss_real

# then another forward pass using D_loss_fake, sharing the same D parameters.

D_loss = D_loss_real + D_loss_fake


G_loss = tf.reduce_mean(

   tf.nn.sigmoid_cross_entropy_with_logits(logits=D_fake, labels=tf.ones_like(D_fake))

)

定義損失函數不難,對抗訓練的一個特點是把真實圖像和由G生成的圖像輸入判別器D,由後者輸出評估結果,並把結果饋送給生成器G做參考。這意味著對抗訓練其實是分兩步走,G和D同在一個計算圖內,但在訓練D時,我們不希望更新G中的參數;同理,訓練G時,我們也不希望更新D裡的參數。

因此,由於我們在默認計算圖中定義了每個變量,而且它們都是全局變量,我們必須在2個不同的列表中收集正確的變量並正確定義優化器,從而計算梯度,對正確的子圖進行更新。

# Gather D and G variables

D_vars = tf.trainable_variables(scope="discriminator")

G_vars = tf.trainable_variables(scope="generator")


# Define the optimizers and the train operations

train_D = tf.train.AdamOptimizer(1e-5).minimize(D_loss, var_list=D_vars)

train_G = tf.train.AdamOptimizer(1e-5).minimize(G_loss, var_list=G_vars)

到這裡,我們已經完成了上面提到的「第3步:定義計算圖」,最後是定義參數初始化節點:

init_op = tf.global_variables_initializer()

優/缺點

只要正確定義了計算圖,且在訓練循環內和session內使用,上述GAN就能正常訓練了。但是,從軟體工程角度看,它有一些值得注意的點:

用tf.variable_scope修改由tf.layers定義的(完整)變量名稱:這其實是對不同scope的變量重新用了一次tf.layers.*,導致的結果是定義了新scope下的一組新變量。

布爾標誌reuse可以完全改變調用tf.layers.*後的所有行為(定義/reuse)。

每個變量都是全局變量:tf.layers調用tf.get_variable(也就是在tf.layers下面調用)定義的變量可以隨處訪問。

定義子圖很麻煩:你沒法通過調用discriminator獲得一個新的、完全獨立的判別器,這有點違背常理。

子圖定義的輸出值(調用generator/discriminator)只是它的輸出張量,而不是內部所有圖的信息(儘管可以回溯輸出,但這麼做很麻煩)。

定義參數初始化節點很麻煩(不過這個可以用tf.train.MonitoredSession和tf.train.MonitoredTrainingSession規避)。

以上6點都可能是用Tensorflow構建GAN的缺點。

Tensorflow 2.x的GAN

前面提到了,Tensorflow 2.x移除了tf.get_variable,tf.variable_scope,tf.layers,強制轉型到了基於Keras的方法。明年,如果我們想用它構建GAN,我們就必須用tf.keras定義生成器G和判別器的:這其實意味著我們憑空多了一個可以用來定義D的共享變量函數。

註:明年tf.layers就沒有了,所以你最好從現在就開始適應用tf.keras來定義自己的模型,這是過渡到2.x版本的必要準備。

def generator(input_shape):

   """generator network.

   Args:

       input_shape: the desired input shape (e.g.: (latent_space_size))

   Returns:

       G: The generator model

   """

   inputs = tf.keras.layers.Input(input_shape)

   net = tf.keras.layers.Dense(units=64, activation=tf.nn.elu, name="fc1")(inputs)

   net = tf.keras.layers.Dense(units=64, activation=tf.nn.elu, name="fc2")(net)

   net = tf.keras.layers.Dense(units=1, name="G")(net)

   G = tf.keras.Model(inputs=inputs, outputs=net)

   return G


def discriminator(input_shape):

   """discriminator network.

   Args:

       input_shape: the desired input shape (e.g.: (latent_space_size))

   Returns:

       D: the discriminator model

   """

   inputs = tf.keras.layers.Input(input_shape)

   net = tf.keras.layers.Dense(units=32, activation=tf.nn.elu, name="fc1")(inputs)

   net = tf.keras.layers.Dense(units=1, name="D")(net)

   D = tf.keras.Model(inputs=inputs, outputs=net)

   return D

看到和Tensorflow的不同了嗎?在這裡,generator和discriminator都返回了一個tf.keras.Model,而不僅僅是輸出張量。

在Keras裡,變量共享可以通過多次調用同樣的Keras層或模型來實現,而不用像TensorFlow那樣需要考慮變量的scope。所以我們在這裡只需定義一個判別器D,然後調用它兩次。

# Define the real input, a batch of values sampled from the real data

real_input = tf.placeholder(tf.float32, shape=(None,1))


# Define the discriminator model

D = discriminator(real_input.shape[1:])


# Arbitrary set the shape of the noise prior vector

latent_space_size = 100

# Define the input noise shape and define the generator

input_noise = tf.placeholder(tf.float32, shape=(None,latent_space_size))

G = generator(input_noise.shape[1:])

再重申一遍,這裡我們不需要像原來那樣定義D_fake,在定義計算圖時也不用提前擔心變量共享。

之後就是定義G和D的損失函數:

D_real = D(real_input)

D_loss_real = tf.reduce_mean(

   tf.nn.sigmoid_cross_entropy_with_logits(logits=D_real, labels=tf.ones_like(D_real))

)


G_z = G(input_noise)


D_fake = D(G_z)

D_loss_fake = tf.reduce_mean(

   tf.nn.sigmoid_cross_entropy_with_logits(logits=D_fake, labels=tf.zeros_like(D_fake))

)


D_loss = D_loss_real + D_loss_fake


G_loss = tf.reduce_mean(

   tf.nn.sigmoid_cross_entropy_with_logits(logits=D_fake, labels=tf.ones_like(D_fake))

)

最後,我們要做的是定義分別優化D和G的2個優化器。由於用的是tf.keras,所以我們不用手動創建要更新的變量列表,tf.keras.Models的對象本身就是我們要的東西。

# Define the optimizers and the train operations

train_D = tf.train.AdamOptimizer(1e-5).minimize(D_loss, var_list=D.trainable_variables)

train_G = tf.train.AdamOptimizer(1e-5).minimize(G_loss, var_list=G.trainable_variables)

截至目前,因為我們用的還是靜態圖,所以還要定義變量初始化節點:

init_op = tf.global_variables_initializer()

優/缺點

從tf.layers到過渡tf.keras:Keras裡有所有tf.layers的對應操作。

tf.keras.Model幫我們完全省去了變量共享和計算圖重新定義的煩惱。

tf.keras.Model不是一個張量,而是一個自帶變量的完整模型。

定義變量初始化節點還是很麻煩,但之前也提到了,我們可以用tf.train.MonitoredSession規避。

以上是Tensorflow 1.x和2.x版本的第一個巨大差異,在下文中,我們再來看看第二個差異——Eager模式。

Eager Execution(動態圖機制)是TensorFlow的一個命令式編程環境,它無需構建計算圖,可以直接評估你的操作:直接返回具體值,而不是構建完計算圖後再返回。它的優點主要有以下幾點:

直觀的界面。更自然地構建代碼和使用Python數據結構,可完成小型模型和小型數據集的快速迭代。

更容易調試。直接調用ops來檢查運行模型和測試更改,用標準Python調試工具獲取即時錯誤報告。

更自然的流程控制。直接用Python流程控制而不是用計算圖。

簡而言之,有了Eager Execution,我們不再需要事先定義計算圖,然後再在session裡評估它。它允許用python語句控制模型的結構。

這裡我們舉個典型例子:Eager Execution獨有的tf.GradientTape。在計算圖模式下,如果我們要計算某個函數的梯度,首先我們得定義一個計算圖,從中知道各個節點是怎麼連接的,然後從輸出回溯到計算圖的輸入,層層計算並得到最終結果。

但在Eager Execution下,用自動微分計算函數梯度的唯一方法是構建圖。我們得先用tf.GradientTape根據可觀察元素(如變量)構建操作圖,然後再計算梯度。下面是tf.GradientTape文檔中的一個原因和示例:

x = tf.constant(3.0)

with tf.GradientTape() as g:

 g.watch(x)

 y = x * x

dy_dx = g.gradient(y, x) # Will compute to 6.0

此外,用python語句(如if語句和循環語句)進行流程控制區別於靜態圖的tf.get_variable,tf.variable_scope,tf.layers。

之前官方發布了一個名為Autograph的工具,它的作用是把普通Python代碼轉換成複雜的計算圖代碼,也就是允許用戶用Python直接編寫計算圖。但它指的Python事實上並不是真正意義上的Python(比如必須定義一個函數,讓它返回一個具有指定Tensorflow數據類型的元素列表),也沒法發揮程式語言的強大功能。

就個人而言,我不太喜歡Eager Execution,因為我已經習慣靜態圖了,而這個新改變有點像是對PyTorch的拙劣模仿。至於其他變化,我會在下面以問答方式做簡單介紹。

下面是我認為從TensorFlow過渡到TensorFlow 2.0會出現的一些常見問題。

問:如果我的項目要用到tf.contrib怎麼辦?

你可以用pip安裝一個新的Python包,或者把tf.contrib.something重命名為tf.something。

問:如果在Tensorflow 1.x裡能正常工作的東西到2.x沒法運行了怎麼辦?

不應該存在這種錯誤,建議你仔細檢查一下代碼轉換得對不對,閱讀GitHub上的錯誤報告。

問:我的項目在靜態圖上好好的,一放到Eager Execution上就不行了怎麼辦?

我也遇到了這個問題,而且目前還不知道具體原因。所以建議先不要用Eager Execution。

問:我發現Tensorflow 2.x裡好像沒有某個tf.函數怎麼辦?

這個函數很有可能只被移到別的地方去了。在Tensorflow 1.x中,很多函數會有重複、有別名,Tensorflow 2.x對這些函數做了統一刪減整理,也移動了部分函數的位置。你可以在RFC:TensorFlow命名空間裡找到將要新增、刪除、移動的所有函數。官方即將發布的工具也能幫你適應這個更新。

看了這麼多,相信讀者現在已經對Tensorflow 2.x有了大致了解,也有了心理準備。總的來說,正如大部分產品都要經歷更新迭代,我認為Tensorflow 2.x相比Tensorflow 1.x會是有明顯改進的一個版本。最後,我們再來看一下Tensorflow的發展時間軸,回憶過去三年來它帶給我們的記憶和知識。

原文地址:pgaleone.eu/tensorflow/gan/2018/11/04/tensorflow-2-models-migration-and-new-design/

若喜歡此文,歡迎給CVer點讚和轉發


▲長按關注我們

歡迎點讚!

相關焦點

  • TensorFlow 攜手 NVIDIA,使用 TensorRT 優化 TensorFlow Serving...
    目前,TensorFlow Serving 1.13 已實現對 TF-TRT 的支持,而不久後  TensorFlow 2.0 也將支持 TF-TRT 的實現。 TensorFlow 在官方博客中對這項成果進行了發布,雷鋒網 AI 科技評論編譯如下。
  • 資源| TensorFlow版本號升至1.0,正式版即將到來
    發布地址官網:https://www.tensorflow.org/versions/r1.0/GitHub:https://github.com/tensorflow/tensorflow/releases主要特性和提升TensorFlow Debugger (tfdbg):命令行接口和 API增加新的 python 3 docker 鏡像使
  • TensorFlow極速入門
    最後給出了在 tensorflow 中建立一個機器學習模型步驟,並用一個手寫數字識別的例子進行演示。1、tensorflow是什麼?tensorflow 是 google 開源的機器學習工具,在2015年11月其實現正式開源,開源協議Apache 2.0。
  • TensorFlow 1.9.0-rc0 升級 tf.keras 至 Keras 2.1.6 API
    TensorFlow 1.9.0-rc0 已發布。該版本帶來了不少改進和新特性:Update tf.keras to the Keras 2.1.6 API.
  • Tensorflow基礎教程15天之創建Tensor
    在將Tensor定義為Variable之後,Tensorflow才會將其傳入計算圖。如何操作我們將在這裡介紹創建Tensor的主要方法。序列TensorTensorflow允許我們定義數組Tensor。
  • 步履不停:TensorFlow 2.4新功能一覽!
    集合運算        https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/ops/collective_ops.py
  • TensorFlow 2.0開源工具書,30天「無痛」上手
    有人在 GitHub 開源了一個名為《30 天吃掉那隻 TensorFlow2.0》的入門工具書,或許可以讓你無痛上手 TensorFlow 2.0。作者 lyhue1991 表示,這本書對「人類用戶極其友善」,以「Don't let me think」為最高追求,「如果說通過學習 TensorFlow 官方文檔掌握 TensorFlow2.0 的難度大概是 9 的話,那麼通過學習本書掌握 TensorFlow2.0 的難度應該大概是 3。」
  • TensorFlow 2.0 新鮮出爐!新版本,新架構,新特性
    當然,TensorFlow 2.0 Alpha版本從安裝上便十分的簡單,僅需一句話:pip install -U --pre tensorflow而Eager execution 與 「@tf.function」作為核心升級重點,接下將會對其如何協同工作進行做詳細介紹。
  • 這裡有一份TensorFlow2.0中文教程
    今年 3 月份,谷歌在 Tensorflow Developer Summit 2019 大會上發布 TensorFlow 2.0 Alpha 版。作為當前最為流行的深度學習框架,2.0 Alpha 版的正式發布引人關注。近兩個月,網上已經出現了大量 TensorFlow 2.0 英文教程。在此文章中,機器之心為大家推薦一個持續更新的中文教程,以便大家學習。
  • TensorFlow2.0(8):誤差計算——損失函數總結
    import tensorflow as tfy = tf.random.uniform((5,),maxval=5,dtype=tf.int32) # 假設這是真實值print(y)y = tf.one_hot(y,depth=5) # 轉為熱獨編碼print(y)tf.Tensor([2 4 4 0 2],
  • 終於來了,TensorFlow 新增官方 Windows 支持
    隨著 TensorFlow r0.12 版的發布,這一開源軟體庫現已支持 Windows7、10 和 Server 2016。同時,這一新版本可以讓用戶在 TensorFlow 訓練中得到 CUDA 8 的 GPU 加速。我們已將新版本在 PyPI 中封裝成型。現在你可以使用命令 C:\> pip install tensorflow 安裝 TensorFlow 了。
  • 機器學習中的embedding原理及tensorflow 相關API的理解
    # 概述本文主要講解tensorflow中涉及embedding的API。之前看了一些文章,寫的雲山霧繞,花了好長時間才搞懂,太笨了。embedding 算法主要用於處理稀疏特徵,應用於NLP、推薦、廣告等領域。所以word2vec 只是embbeding 思想的一個應用,而不是全部。
  • 5個簡單的步驟掌握Tensorflow的Tensor
    在這篇文章中,我們將深入研究Tensorflow Tensor的細節。我們將在以下五個簡單步驟中介紹與Tensorflow的Tensor中相關的所有主題:第一步:張量的定義→什麼是張量?我們經常將NumPy與TensorFlow一起使用,因此我們還可以使用以下行導入NumPy:import tensorflow as tfimport numpy as np張量的創建:創建張量對象有幾種方法可以創建tf.Tensor對象。讓我們從幾個例子開始。
  • TensorFlow 2入門指南,初學者必備!
    from sklearn.model_selection import train_test_split #to split dataimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport tensorflow as tffrom te
  • 基於TensorFlow2.0的中文深度學習開源書來了!GitHub趨勢日榜第一
    TensorFlow 2.0 實戰案例項目作者還介紹了TensorFlow 2.0的實戰案例。這個repo也是PoweredByTF 2.0 Challenge的獲勝項目。CPU安裝pip install tensorflow -UGPU安裝先自己安裝CUDA 10.0和cudnn,然後設置LD_LIBRARY_PATH。
  • TensorFlow2.1正式版上線:最後一次支持Python2,進一步支持TPU
    去年 10 月,谷歌才發布了 TensorFlow 2.0 正式版。時隔三個月後,昨日官方發布了 TensorFlow 2.1,本次版本更新帶了了多項新特性、功能改進和 bug 修復。也就是說,如果使用 pip install tensorflow,則版本默認為是 gpu 版本(原始的 tensorflow-gpu 版本依然存在)。當然,不管有沒有英偉達版本的 GPU,tensorflow 依然能夠運行。如果需要使用 CPU 版本,用戶的安裝命令應該為:pip install tensorflow-cpu。
  • TensorFlow 2.1指南:keras模式、渴望模式和圖形模式(附代碼)
    如你所知,在Tensorflow中,存在這樣的範例:首先定義計算圖,然後進行編譯(或將其移至GPU),然後運行它。這種範例非常好,從技術上來講很有意義,但是,一旦在GPU中擁有了模型,幾乎就不可能對其進行調試。這就是為什麼,自從TensorFlow 2.0以其Alpha版本發布以來已經過去了大約一年,我決定在TensorFlow 2.1與大家分享使用的體驗。
  • 基於RTX2060構建TensorFlow-gpu(keras)學習平臺
    開始菜單運行anaconda navigator檢查是否安裝了notebook(默認有安裝)三、安裝tensorflow/keras在激活的環境中安裝:1. 如果機器上有gpu,則安裝gpu版本,沒有GPU就安裝cpu版。
  • 如何使用TensorFlow Hub的ESRGAN模型來在安卓app中生成超分圖片
    如果你想更多的了解 ESRGAN 模型,我們還有另外一個教程可供參考: lr = cv2.imread(test_img_path) lr = cv2.cvtColor(lr, cv2.COLOR_BGR2RGB) lr = tf.expand_dims(lr, axis=0) lr = tf.cast(lr, tf.float32) # Load TFLite model and allocate
  • Tensorflow 2.0的這些新設計,你適應好了嗎?
    而就在即將到來的2019年,Tensorflow 2.0將正式入場,給暗流湧動的框架之爭再燃一把火。如果說兩代Tensorflow有什麼根本不同,那應該就是Tensorflow 2.0更注重使用的低門檻,旨在讓每個人都能應用機器學習技術。