教程 | TensorFlow 1.11 教程 —— 研究与实验 —— 自定义层(9.15 ver.)

更新至 2018-9-15 版本

译自:TensorFlow 官方教程


我们推荐使用高级 API tf.keras 构建神经网络,也就是说,大多数 TensorFlow API 都在 eager execution 下可用。

import tensorflow as tf
tfe = tf.contrib.eager

tf.enable_eager_execution()

层:常用的操作集

大多数时候,在为机器学习模型编写代码时,你希望在更高的抽象级别上操作,而不是单个操作以及对单个变量的操作。

许多机器学习模型都可以表示为相对简单的层的组合和堆叠,而 TensorFlow 既提供了一组常见的层,也提供了一种简单的方法,让你可以从头开始编写自己应用程序的层,或者对现有层进行组合。

TensorFlow 在 tf.keras 包中包含了完整的 Keras API,建立自己的模型时,Keras 层十分有用。

# 在 tf.keras.layers 包中, 层是对象。建立层就是建立对象。
# 大多数层的第一个参数是输出维度/通道的数量。
layer = tf.keras.layers.Dense(100)
# 通常不需要输入维度的数量,
# 因为它可以在层第一次使用时推断出来,
# 在一些复杂模型中也可以手动指定。
layer = tf.keras.layers.Dense(10, input_shape=(None, 5))

现有层的完整列表可以在文档中查看到。它包括密集层(全连接层)、Conv2D、LSTM、批归一化、Dropout 等。

# 要使用一个层,只需要调用它
layer(tf.zeros([10, 5]))
<tf.Tensor: id=30, shape=(10, 10), dtype=float32, numpy=
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]], dtype=float32)>
# 层有许多有用的方法。例如,你可以通过调用 layer.variables 检查层中的所有变量。
# 这种情况下,全连接层有权重和偏差变量。
layer.variables
[<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy=
 array([[ 0.5491831 , -0.5334953 , -0.33720493,  0.15916592,  0.02661264,
         -0.54477704, -0.10383385, -0.3822977 , -0.12068653,  0.07283705],
        [-0.6046467 , -0.3235745 , -0.3678471 ,  0.42985398, -0.32315367,
          0.01954406, -0.5670417 , -0.22975442,  0.410201  , -0.6054503 ],
        [-0.21158552,  0.16337538, -0.55872625, -0.4717918 , -0.14633152,
          0.4356466 , -0.29068574, -0.365243  , -0.13362256, -0.38409072],
        [ 0.33547556,  0.13281083, -0.04601526, -0.42355436, -0.0060991 ,
         -0.5032983 , -0.1809687 , -0.22183692,  0.08809811,  0.09949732],
        [-0.60682243, -0.3103218 ,  0.38421983, -0.5180574 , -0.42757726,
         -0.21736482,  0.07201356, -0.254764  , -0.28456295, -0.02735662]],
       dtype=float32)>,
 <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>]
# 变量也可以通过存取器访问
layer.kernel, layer.bias
(<tf.Variable 'dense_1/kernel:0' shape=(5, 10) dtype=float32, numpy=
 array([[ 0.5491831 , -0.5334953 , -0.33720493,  0.15916592,  0.02661264,
         -0.54477704, -0.10383385, -0.3822977 , -0.12068653,  0.07283705],
        [-0.6046467 , -0.3235745 , -0.3678471 ,  0.42985398, -0.32315367,
          0.01954406, -0.5670417 , -0.22975442,  0.410201  , -0.6054503 ],
        [-0.21158552,  0.16337538, -0.55872625, -0.4717918 , -0.14633152,
          0.4356466 , -0.29068574, -0.365243  , -0.13362256, -0.38409072],
        [ 0.33547556,  0.13281083, -0.04601526, -0.42355436, -0.0060991 ,
         -0.5032983 , -0.1809687 , -0.22183692,  0.08809811,  0.09949732],
        [-0.60682243, -0.3103218 ,  0.38421983, -0.5180574 , -0.42757726,
         -0.21736482,  0.07201356, -0.254764  , -0.28456295, -0.02735662]],
       dtype=float32)>,
 <tf.Variable 'dense_1/bias:0' shape=(10,) dtype=float32, numpy=array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], dtype=float32)>)

实现自定义层

实现自己的层的最佳方法是扩展 tf.keras.Layer 类并且实现:

  • __init__,执行所有独立于输入的初始化
  • build,在这里你知道输入张量的形状,并且进行剩余的初始化
  • 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_variable("kernel", 
                                    shape=[input_shape[-1].value, 
                                           self.num_outputs])
    
  def call(self, input):
    return tf.matmul(input, self.kernel)
  
layer = MyDenseLayer(10)
print(layer(tf.zeros([10, 5])))
print(layer.variables)
tf.Tensor(
[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]], shape=(10, 10), dtype=float32)
[<tf.Variable 'my_dense_layer/kernel:0' shape=(5, 10) dtype=float32, numpy=
array([[ 0.04571068, -0.4502709 ,  0.03811407, -0.39218163,  0.3836605 ,
        -0.06154126,  0.5084277 ,  0.5511847 , -0.01946509, -0.24898636],
       [ 0.55244833,  0.04234415,  0.367557  , -0.16904458,  0.5583802 ,
         0.5395512 ,  0.23943478,  0.26102585,  0.44694608, -0.17953226],
       [-0.27093476, -0.06255698, -0.36378002, -0.52204967, -0.4386331 ,
         0.04419768,  0.31893957,  0.5911831 , -0.16269788, -0.34716034],
       [ 0.1851421 ,  0.28576833,  0.32846957, -0.060004  , -0.4525128 ,
        -0.24665937,  0.22914791, -0.19578329, -0.33844247, -0.57873464],
       [-0.5436014 ,  0.27190197, -0.3022089 , -0.39169455, -0.62015975,
         0.48647386,  0.17486733,  0.21422738,  0.3566833 ,  0.12303311]],
      dtype=float32)>]

尽可能使用标准层,则整体代码更易于阅读和维护,因为其他读者将熟悉标准层的行为。如果你想使用 tf.keras.layerstf.contrib.layers 中不存在的层,考虑提交一个 github issue,或者,更好的是发送一个 pull request!


模型:组合层

机器学习模型中许多有趣的网络层都是通过组合现有层来实现的。例如,resnet 中的每个残差块都是卷积、批归一化和 shortcut 的组合。

创建包含其他层的层的主要类是 tf.keras.Model,可以通过继承 tf.keras.Model 实现。

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])
print(block(tf.zeros([1, 2, 3, 3])))
print([x.name for x in block.variables])
tf.Tensor(
[[[[0. 0. 0.]
   [0. 0. 0.]
   [0. 0. 0.]]

  [[0. 0. 0.]
   [0. 0. 0.]
   [0. 0. 0.]]]], shape=(1, 2, 3, 3), dtype=float32)
...

然而,大多数时候,组合许多层的模型只需要简单地调用一个又一个层。使用 tf.keras.Sequential 可以在非常少的代码中完成这一步。

 my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1)),
                               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]))
<tf.Tensor: id=508, shape=(1, 2, 3, 3), dtype=float32, numpy=
array([[[[0., 0., 0.],
         [0., 0., 0.],
         [0., 0., 0.]],

        [[0., 0., 0.],
         [0., 0., 0.],
         [0., 0., 0.]]]], dtype=float32)>

下一步

现在,你可以回到前面的教程中,使用层和模型修改线性回归示例,从而优化结构。

猜你喜欢

转载自blog.csdn.net/qq_20084101/article/details/82709744