iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 15
0
AI & Data

深度學習裡的冰與火之歌 : Tensorflow vs PyTorch系列 第 15

Day 15: Tensorflow 2.0 再造訪 keras

  • 分享至 

  • xImage
  •  

給讀者: 9/21 11:45 pm 是搶先版,陸續還會有更新。

這個禮拜我們將要離開 PyTorch 一陣子,再回到我們尚未探險完全的 Tensorflow 2.0。我們在這個禮拜中將會把重心放在 Tensorflow, 領著大家來看看 Tensorflow 官方提供的指南們。

今天則是著重在 High-level 的 tensorflow.keras,一開始會先快速介紹 keras,然後以 keras 的 callback 的例子來說明如何建立模型和完成訓練。在進入主題之前,讓我先介紹一下%tensorflow_version這個可在 colab 中使用的 line magic。無須另外安裝任何套件,就可以在 colab 的 cell 裡使用,用法大概如下:

  1. 列印出目前 tensorflow 的版本
%tensorflow_version
#=>Currently selected TF version: 1.x
#Available versions:
#* 1.x
#* 2.x`
  1. 選擇 tensorflow 2.x 或 1.x,則在 %tensorflow_version 後增加引數,目前接受 1.x 或 2.x,作為在 1.x 或 2.x 版本之間的互換。
%tensorflow_version 2.x
#=>%tensorflow_version` only switches the major version: '1.x' or '2.x'.
#You set: '2.0.0a0'. This will be interpreted as: `2.x`.
#TensorFlow 2.x selected.
  1. 若在 %tensorflow_version 後特別註明版本,如。那麼則會開始安裝該版本。

tf.keras

tensorflow 2.0 mini-intro中,我們提到了,為了要 keras 能夠無縫與 Tensorflow 相接,一定要從 tensorflow import keras 模組。倘若在自己的環境中安裝了 keras 和 tensorflow,可以透過 tf.keras.version 來查看包入 tensorflow 內的是哪一版本的 keras。

使用 tf.keras 來建立模型,可以使用下列兩個 API:

  1. Sequential model
  2. Functional API

Sequential model

在這個最簡單的 API 來建立深度學習模型,則是利用一個“容器”的概念,在 tf.keras 則是 tf.keras.Sequential()。大家可以把 tf.keras.Sequential() 當作是一個 layer-wise 地可直線放入許多類神經網路的層級實踐類別:如,fully-connected layer 的 tf.keras.layers.Dense
以下的原始碼可以建造出一個


# Create a sigmoid layer:
from tensorflow.keras import layers

model = tf.keras.Sequential()
#加入第一層,該層的 output neurons 數目=64,啟動函式為 sigmoid
model.add(layers.Dense(64, activation='sigmoid'))

#加入第二層,該層的 output neurons 數目=64,啟動函式為預設 relu
#並對該層參數做 l1-regularization,其係數為 0.01
model.add(layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01)))

#加入第三層,該層的 output neurons 數目=64,啟動函式為預設 relu
#並對該層參數做 l2-regularization,其係數為 0.01
model.add(layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01)))

#加入第四層,該層的 output neurons 數目=64,啟動函式為預設 relu
#該層參數初始化是用正交策略
model.add(layers.Dense(64, kernel_initializer='orthogonal'))

#加入第五層,該層的 output neurons 數目=64,啟動函式為預設 relu
#該層偏移初始化是用常數策略(皆設為2.0)
model.add(layers.Dense(64, bias_initializer=tf.keras.initializers.Constant(2.0)))

上面的原始碼,有一個問題,就是缺少指定輸入的 neurons。我們可以在加入第一層的全聯階層,把 input 的 dimensions 傳入(layers.Dense(64, activation='relu', input_shape=(32,)),),或者我們可以用 tf.keras.Input來建構輸入。

inputs = tf.keras.Input(shape=(32,))  # Returns an input placeholder
y = model(inputs)

Functional API

Sequential API 有一個問題,就是建立的模型在連結上變化較少,幾乎是將類神經網路的實踐層,如排隊般一直線的執行。由於缺乏彈性,所以像 Inception 或是 ResNet 有額外的分支以及連結。所以,為了能夠滿足建立模型的靈活性,keras 還有一個 Functional API,來滿足研究者們的需求。
如果說 Sequential API 是一個 layer-wise 的容器,那麼這個 Functional API,則像拿著積木,由底層開始拼湊起模型的全貌。而這裡所說的底層,就是從 input 開始。Functional API 提供一個建構模型的類別 tf.keras.Model,這個類別在實例化的時候需要使用者提供最初 input 和最後 output,而 input 到 output 之間的相依關係或計算圖建置則完全交付與使用者,tf.keras.Model 對計算圖如何連結一無所知。
以下的原始碼是一個簡單的例子,如何用 Functional API 完成 Sequential API 的任務。

inputs = keras.Input(shape=(784,), name='img')
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
outputs = layers.Dense(10, activation='softmax')(x)

model = keras.Model(inputs=inputs, outputs=outputs, name='mnist_model')

什麼是 keras callback

keras 實作了一個 tf.keras.callbacks.Callback 的介面。這個介面上實作了多個方法,每一個方法都會有對應的事件,一旦事件發生就會 trigger tf.keras.callbacks.Callback 類別物件的相對應方法。

tf.keras.callbacks.Callback提供了以下事件方法的介面:

  1. on_(train|test|predict)_begin(self, logs=None):在模型的fit/evaluate/predict 開始之前
  2. on_(train|test|predict)_end(self, logs=None):在模型的fit/evaluate/predict 結束之前
  3. on_(train|test|predict)_batch_begin(self, batch, logs=None):在模型的 fit/evaluate/predict 每一個 batch 開始之前
  4. on_(train|test|predict)_batch_end(self, batch, logs=None):在模型的 fit/evaluate/predict 每一個 batch 結束之前

針對 training 的事件則還有下列兩項:

  1. on_epoch_begin(self, epoch, logs=None):在每一個 training epoch 開始前
  2. on_epoch_end(self, epoch, logs=None):在每一個 training epoch 結束前

哪些 functions 可以接受 callbakcs?tf.keras.Model 物件的下列三個方法都可以使用 keyword callback 來指定傳入的 callback

  • fit(), fit_generator()
  • evaluate(), evaluate_generator()
  • predict(), predict_generator()

callback 實例操作

Learning rate scheduling

Learning rate scheduling 是一種 adaptive learning rate 的方法,主要的觀察是當 training epochs 增加時,相對應的 learning rate 應該減緩。Learning rate scheduling 則是手動提供 learning rate 減少的時程表。
首先我們來看一下我們的 Callback 物件,LearningRateScheduler 類別的實例。類別 LearningRateScheduler 要先繼承tf.keras.callbacks.Callback 才能使用不同事件的介面。在實例化時,__init__需要傳入一個 schedule function,該 function 接受一個 epoch 的 index,回傳該 epoch 對應的 learning rate。
因為這是一個 epoch 等級的事件,在每次 epoch 開始前,LearningRateScheduler 必須就現在的 epoch 數目,利用自身的 scedule 物件,傳回新的 learning rate,因此,需實做的部分為on_epoch_begin

class LearningRateScheduler(tf.keras.callbacks.Callback):
  """Learning rate scheduler which sets the learning rate according to schedule.

  Arguments:
      schedule: a function that takes an epoch index
          (integer, indexed from 0) and current learning rate
          as inputs and returns a new learning rate as output (float).
  """

  def __init__(self, schedule):
    super(LearningRateScheduler, self).__init__()
    self.schedule = schedule

  def on_epoch_begin(self, epoch, logs=None):
    if not hasattr(self.model.optimizer, 'lr'):
      raise ValueError('Optimizer must have a "lr" attribute.')
    # Get the current learning rate from model's optimizer.
    lr = float(tf.keras.backend.get_value(self.model.optimizer.lr))
    # Call schedule function to get the scheduled learning rate.
    scheduled_lr = self.schedule(epoch, lr)
    # Set the value back to the optimizer before this epoch starts
    tf.keras.backend.set_value(self.model.optimizer.lr, scheduled_lr)
    print('\nEpoch %05d: Learning rate is %6.4f.' % (epoch, scheduled_lr))

這裏則是利用 keras 的 backend API 中的 get_value()來取得 optimizer 的 learning rate 參數。使用的方法為 tf.keras.backend.get_value(self.model.optimizer.lr)
而關於 schedule 的實踐方法,下面是一種可能,主要是用 lookup 的方式來完成。在下面的程式碼,首先會建立一個 LR_SCHEDULE 的 list 全域物件,該物件內的每一個元素都是一個 tuple。tuple 內的第一個元素是 epoch 的數字,而第二個元素則是對應的 learning rate。
小幫手函式 lr_schedule,會將傳入的 epoch 參數,也是目前即將開始的 epoch 數目,如果在 LR_SCHEDULE 內(透過迴圈檢查),則回傳相對應的 learning rate,否則就原封不動地回傳原本的 learning rate。

LR_SCHEDULE = [
    # (epoch to start, learning rate) tuples
    (3, 0.05), (6, 0.01), (9, 0.005), (12, 0.001)
]

def lr_schedule(epoch, lr):
  """Helper function to retrieve the scheduled learning rate based on epoch."""
  if epoch < LR_SCHEDULE[0][0] or epoch > LR_SCHEDULE[-1][0]:
    return lr
  for i in range(len(LR_SCHEDULE)):
    if epoch == LR_SCHEDULE[i][0]:
      return LR_SCHEDULE[i][1]
  return lr

下面我們來看如何應用 callback 在模型中。首先,先建立一個模型,在下面我們用的是 Sequential API 來建構沒有 hidden layer 的類神經網路。建構完模型後,要呼叫模型的 compile 方法,將 optimizer 等等與訓練相關的參數設定好。

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, activation = 'linear', input_dim = 784)) model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=0.1), loss='mean_squared_error', metrics=['mae'])
_ = model.fit(x_train, y_train,
          batch_size=64,
          steps_per_epoch=5,
          epochs=15,
          verbose=0,
          callbacks=[LearningRateScheduler(lr_schedule)])

這樣就可以在 training loop 中,看到列印出的 learning rate 變化。


上一篇
Day 14: 使用 TorchScript 來擴充 PyTorch
下一篇
Day 16: Tensorflow 2.0 再造訪 eager-execution
系列文
深度學習裡的冰與火之歌 : Tensorflow vs PyTorch31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言