自訂層

在 TensorFlow.org 上檢視 在 Google Colab 中執行 在 GitHub 上檢視原始碼 下載筆記本

我們建議使用 tf.keras 作為高階 API 來建構神經網路。也就是說,大多數 TensorFlow API 都可與 eager execution 搭配使用。

import tensorflow as tf
print(tf.config.list_physical_devices('GPU'))

層:常用的實用運算集合

大部分時候,在編寫機器學習模型的程式碼時,您都希望在比個別運算和個別變數操作更高的抽象層級上運作。

許多機器學習模型可以表示為相對簡單的層的組合和堆疊,而 TensorFlow 既提供許多常見層的集合,也讓您可以輕鬆地從頭開始或作為現有層的組合來編寫自己的應用程式特定層。

TensorFlow 在 tf.keras 套件中包含完整的 Keras API,而 Keras 層在建構您自己的模型時非常有用。

# In the tf.keras.layers package, layers are objects. To construct a layer,
# simply construct the object. Most layers take as a first argument the number
# of output dimensions / channels.
layer = tf.keras.layers.Dense(100)
# The number of input dimensions is often unnecessary, as it can be inferred
# the first time the layer is used, but it can be provided if you want to
# specify it manually, which is useful in some complex models.
layer = tf.keras.layers.Dense(10, input_shape=(None, 5))

預先存在的層的完整清單可以在 文件中找到。它包括 Dense (全連接層)、Conv2D、LSTM、BatchNormalization、Dropout 和許多其他層。

# To use a layer, simply call it.
layer(tf.zeros([10, 5]))
# Layers have many useful methods. For example, you can inspect all variables
# in a layer using `layer.variables` and trainable variables using
# `layer.trainable_variables`. In this case a fully-connected layer
# will have variables for weights and biases.
layer.variables
# The variables are also accessible through nice accessors
layer.kernel, layer.bias

實作自訂層

實作您自己的層的最佳方式是擴充 tf.keras.Layer 類別並實作

  1. __init__,您可以在其中執行所有與輸入無關的初始化
  2. build,您可以在其中得知輸入張量的形狀並完成其餘的初始化
  3. call,您可以在其中執行前向運算

請注意,您不必等到呼叫 build 才建立變數,您也可以在 __init__ 中建立它們。但是,在 build 中建立它們的優點是,它可以根據層將運作的輸入形狀延遲建立變數。另一方面,在 __init__ 中建立變數表示建立變數所需的形狀需要明確指定。

class MyDenseLayer(tf.keras.layers.Layer):
  def __init__(self, num_outputs):
    super(MyDenseLayer, self).__init__()
    self.num_outputs = num_outputs

  def build(self, input_shape):
    self.kernel = self.add_weight("kernel",
                                  shape=[int(input_shape[-1]),
                                         self.num_outputs])

  def call(self, inputs):
    return tf.matmul(inputs, self.kernel)

layer = MyDenseLayer(10)
_ = layer(tf.zeros([10, 5])) # Calling the layer `.builds` it.
print([var.name for var in layer.trainable_variables])

如果盡可能使用標準層,整體程式碼會更容易閱讀和維護,因為其他讀者會熟悉標準層的行為。如果您想使用 tf.keras.layers 中不存在的層,請考慮提交 github issue,甚至更好的是,向我們發送 pull request!

模型:組合層

機器學習模型中許多有趣的類層事物是透過組合現有層來實作的。例如,resnet 中的每個殘差區塊都是卷積、批次正規化和捷徑的組合。層可以巢狀在其他層內。

通常,當您需要模型方法 (例如:Model.fitModel.evaluateModel.save) 時,您會從 keras.Model 繼承 (詳情請參閱自訂 Keras 層和模型)。

keras.Model (而不是 keras.layers.Layer) 提供的另一個功能是,除了追蹤變數外,keras.Model 還追蹤其內部層,使其更易於檢查。

例如,這是一個 ResNet 區塊

class ResnetIdentityBlock(tf.keras.Model):
  def __init__(self, kernel_size, filters):
    super(ResnetIdentityBlock, self).__init__(name='')
    filters1, filters2, filters3 = filters

    self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))
    self.bn2a = tf.keras.layers.BatchNormalization()

    self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')
    self.bn2b = tf.keras.layers.BatchNormalization()

    self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))
    self.bn2c = tf.keras.layers.BatchNormalization()

  def call(self, input_tensor, training=False):
    x = self.conv2a(input_tensor)
    x = self.bn2a(x, training=training)
    x = tf.nn.relu(x)

    x = self.conv2b(x)
    x = self.bn2b(x, training=training)
    x = tf.nn.relu(x)

    x = self.conv2c(x)
    x = self.bn2c(x, training=training)

    x += input_tensor
    return tf.nn.relu(x)


block = ResnetIdentityBlock(1, [1, 2, 3])
_ = block(tf.zeros([1, 2, 3, 3]))
block.layers
len(block.variables)
block.summary()

但是,在大多數情況下,組合許多層的模型只是依序呼叫一個層。這可以使用 tf.keras.Sequential 以非常少的程式碼完成

my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1),
                                                    input_shape=(
                                                        None, None, 3)),
                             tf.keras.layers.BatchNormalization(),
                             tf.keras.layers.Conv2D(2, 1,
                                                    padding='same'),
                             tf.keras.layers.BatchNormalization(),
                             tf.keras.layers.Conv2D(3, (1, 1)),
                             tf.keras.layers.BatchNormalization()])
my_seq(tf.zeros([1, 2, 3, 3]))
my_seq.summary()

後續步驟

現在您可以回到先前的筆記本,並調整線性迴歸範例以使用層和模型來更好地組織結構。