Tensorflow 2.0 的這些新設計,你適應好了嗎?

      網友投稿 1005 2025-04-01

      編者按:幾天前,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 2.0 的開發(fā)初衷是制作一個更簡單易用的 Tensorflow。

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

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

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

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

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

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

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

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

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

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

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

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

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

      Keras(OOP)vs Tensorflow 1.x

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

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

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

      定義計算圖:定義參數初始化節(jié)點(你調用過幾次 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 個優(yōu)化器。

      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 個不同的列表中收集正確的變量并正確定義優(yōu)化器,從而計算梯度,對正確的子圖進行更新。

      # 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 步:定義計算圖”,最后是定義參數初始化節(jié)點:

      init_op?=?tf.global_variables_initializer()

      優(yōu) / 缺點

      只要正確定義了計算圖,且在訓練循環(huán)內和 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 )只是它的輸出張量,而不是內部所有圖的信息(盡管可以回溯輸出,但這么做很麻煩)。

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

      以上 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)) )

      最后,我們要做的是定義分別優(yōu)化 D 和 G 的 2 個優(yōu)化器。由于用的是 tf.keras,所以我們不用手動創(chuàng)建要更新的變量列表,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)

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

      init_op?=?tf.global_variables_initializer()

      優(yōu) / 缺點

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

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

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

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

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

      Eager Execution

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

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

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

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

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

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

      但在 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 語句和循環(huán)語句)進行流程控制區(qū)別于靜態(tài)圖的 tf.get_variable, tf.variable_scope, tf.layers。

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

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

      Tensorflow 2.0 的這些新設計,你適應好了嗎?

      一問一答

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

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

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

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

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

      問:我的項目在靜態(tài)圖上好好的,一放到 Eager Execution 上就不行了怎么辦?

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

      問:我發(fā)現 Tensorflow 2.x 里好像沒有某個 tf. 函數怎么辦?

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

      小結

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

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

      計算 TensorFlow 機器學習

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現本站中有涉嫌抄襲或描述失實的內容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:excel中如何在斜線上寫字的教程
      下一篇:怎么把標題8放在標題4的前面?(怎么增加標題4)
      相關文章
      亚洲人成影院在线观看| 亚洲精品成人区在线观看| 亚洲AV无码乱码国产麻豆| 亚洲国产成人精品女人久久久 | 久久精品国产亚洲av四虎| 亚洲精品你懂的在线观看| 国产精品亚洲精品日韩已方| 亚洲电影日韩精品| 亚洲午夜av影院| 2022中文字字幕久亚洲| 国产精品亚洲mnbav网站 | 亚洲一区二区三区高清| 亚洲乱亚洲乱淫久久| 亚洲AV无码国产在丝袜线观看 | 亚洲色精品三区二区一区| 久久久国产亚洲精品| 亚洲日韩AV一区二区三区中文| 亚洲 日韩经典 中文字幕| 亚洲影视自拍揄拍愉拍| jiz zz在亚洲| 亚洲AV噜噜一区二区三区| 日韩在线视精品在亚洲| 国产亚洲情侣久久精品| 亚洲男人在线无码视频| 国产精品亚洲综合专区片高清久久久 | 亚洲AV综合色区无码一区爱AV| 亚洲国产精品嫩草影院在线观看 | 亚洲综合精品成人| 久久久久久亚洲精品无码| 亚洲高清视频一视频二视频三| 久久精品亚洲男人的天堂| 亚洲色无码专区在线观看| 亚洲AV无码乱码在线观看富二代| 内射干少妇亚洲69XXX| 91亚洲视频在线观看| 成人区精品一区二区不卡亚洲| 亚洲av无码有乱码在线观看| 亚洲成a人无码av波多野按摩 | 亚洲色欲色欲www| 亚洲精品成a人在线观看☆| 亚洲成a人片在线观看久|