tensorflow 2.X中构建模型的三种方式:Sequential, Functional, Subclassing

Posted bitcarmanlee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tensorflow 2.X中构建模型的三种方式:Sequential, Functional, Subclassing相关的知识,希望对你有一定的参考价值。

0.前言

tf 2.x与1.x相比,API的变化较大,构建模型的方式也有所差异。我们先直接说结论,在2.x中,tf构建模型的方式有如下三种:
1.tf.keras.Sequential
2.keras Functional API
3.keras Model Subclassing

下面我们以一个简单的线性回归模型为例,说明一下这三种模型构建的使用方式。

1.通过Sequential构建

@keras_export('keras.Sequential', 'keras.models.Sequential')
class Sequential(functional.Functional):
  """`Sequential` groups a linear stack of layers into a `tf.keras.Model`.

  `Sequential` provides training and inference features on this model.

  Examples:

  >>> # Optionally, the first layer can receive an `input_shape` argument:
  >>> model = tf.keras.Sequential()
  >>> model.add(tf.keras.layers.Dense(8, input_shape=(16,)))
  >>> # Afterwards, we do automatic shape inference:
  >>> model.add(tf.keras.layers.Dense(4))

Sequential的精髓是将一些网络层线性堆叠在一起,最后组成tf.keras.Model的方式。因此可以认为Sequential是tf.keras.Model的一种特殊形式。

Sequential的组成方式是layer-by-layer,优点是实现起来最简明,自然缺点也很明显,根据上面源码中的说明不难看出,网络只能是线性结构,Sequential构建的模型,不能共享某一层,不能有多个网络分支,也不能有多个输入输出,所以适合构建结构比较简单的线性网络。

下面我们通过Sequential构建一个y=wx+b的线性回归网络。

import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, Input

def sequence_model():
    X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
    y = tf.constant([[10.0], [20.0]])

    model = Sequential()
    model.add(Dense(1, activation=None, kernel_initializer=tf.zeros_initializer(),
                    bias_initializer=tf.zeros_initializer()))
    model.compile(optimizer='sgd', loss='mse')
    model.fit(X, y, epochs=5000)

    print(model.variables)

因为是y=wx+b的线性回归,所以只需一个全连接层即可,而且该全连接层不需要使用激活函数,神经元直接输出结果即可。

model中可以通过add方法,将所有的层依次添加,这种方式比较清晰,推荐使用。
也可以将所有的层组成一个列表,传给model。这种表达方式不如使用add方法添加清晰,个人不推荐使用。

compile是配置学习的过程,主要的参数如下

1.优化器optimizer:
可以设置为tf中已经预定义的优化器名称,比如rmsprop, sgd, adam等,默认为rmsprop。还可以传一个Optimizer类的对象。

2.损失函数loss:
loss为模型最小化的目标函数。它可为预定义的损失函数名,比如我们示例中使用的mse,也可以为一个自定义损失函数。

3.指标列表metrics:
因为线性回归属于回归问题,所以本例中没有设置该参数。比如像分类问题,就可以设置metrics=[‘accuracy’]。

上述代码运行结果为

Epoch 4997/5000
1/1 [==============================] - 0s 625us/step - loss: 3.0923e-11
Epoch 4998/5000
1/1 [==============================] - 0s 299us/step - loss: 3.0923e-11
Epoch 4999/5000
1/1 [==============================] - 0s 262us/step - loss: 3.0923e-11
Epoch 5000/5000
1/1 [==============================] - 0s 321us/step - loss: 3.0923e-11
[<tf.Variable 'dense/kernel:0' shape=(3, 1) dtype=float32, numpy=
array([[4.8735565e-06],
       [1.1111153e+00],
       [2.2222164e+00]], dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([1.1111081], dtype=float32)>]

可以看出,最终训练出来的参数,w为[[4.8735565e-06],[1.1111153e+00],[2.2222164e+00]], b为1.1111081。

2.通过Functional API方式构建

Functional API方式通过使用keras.models中的Model类来构建模型。前面我们提到,Sequential是Model的一种特殊情况,相比于Sequential,Model可以设计更加复杂与任意拓扑结构的神经网络。

@keras_export('keras.Model', 'keras.models.Model')
class Model(base_layer.Layer, version_utils.ModelVersionSelector):
  """`Model` groups layers into an object with training and inference features.

  Arguments:
      inputs: The input(s) of the model: a `keras.Input` object or list of
          `keras.Input` objects.
      outputs: The output(s) of the model. See Functional API example below.
      name: String, the name of the model.

  There are two ways to instantiate a `Model`:

  1 - With the "Functional API", where you start from `Input`,
  you chain layer calls to specify the model's forward pass,
  and finally you create your model from inputs and outputs:

  python
  import tensorflow as tf

  inputs = tf.keras.Input(shape=(3,))
  x = tf.keras.layers.Dense(4, activation=tf.nn.relu)(inputs)
  outputs = tf.keras.layers.Dense(5, activation=tf.nn.softmax)(x)
  model = tf.keras.Model(inputs=inputs, outputs=outputs)
  ...

上面的注释,提到了Model构建的两种方式,一种就是我们现在说的Functional API方式,从Input开始,用户把所有层链接起来并指定模型的前向传播方式,最后会得到从inputs到outputs的模型。

相比Sequential方式,Functional API的方式,可以设计更复杂,任意拓扑结构的网络。相比于Sequential方式只能依次线性叠加层,Model方式可以实现
1.支持层共享。
2.支持多输入多输出。
3.可以定义模型分支,比如inception block , resnet block等。

同样以上面线性回归的例子,我们用Functional API的方式来实现。

def function_model():
    X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
    y = tf.constant([[10.0], [20.0]])

    inputs = Input(shape=(3, ))
    output = Dense(1, activation=None, kernel_initializer=tf.zeros_initializer(),
                    bias_initializer=tf.zeros_initializer())(inputs)
    model = Model(inputs=inputs, outputs=output)
    model.compile(optimizer='sgd', loss='mse')
    model.fit(X, y, epochs=5000)

    print(model.variables)

代码的输出结果:

Epoch 4996/5000
1/1 [==============================] - 0s 457us/step - loss: 3.0923e-11
Epoch 4997/5000
1/1 [==============================] - 0s 406us/step - loss: 3.0923e-11
Epoch 4998/5000
1/1 [==============================] - 0s 408us/step - loss: 3.0923e-11
Epoch 4999/5000
1/1 [==============================] - ETA: 0s - loss: 3.0923e-11
1/1 [==============================] - 0s 884us/step - loss: 3.0923e-11
Epoch 5000/5000
1/1 [==============================] - 0s 286us/step - loss: 3.0923e-11
[<tf.Variable 'dense/kernel:0' shape=(3, 1) dtype=float32, numpy=
array([[4.8735565e-06],
       [1.1111153e+00],
       [2.2222164e+00]], dtype=float32)>, <tf.Variable 'dense/bias:0' shape=(1,) dtype=float32, numpy=array([1.1111081], dtype=float32)>]

3.通过Subclassing的方式构建

keras.models中的Model类构建方法,除了上面提到的Functional API方式,还有第二种就是Subclassing方式。

2 - By subclassing the `Model` class: in that case, you should define your
  layers in `__init__` and you should implement the model's forward pass
  in `call`.

Model类中的注释,已经教我们怎么使用subclassing方式构建模型了。我们需要做的是两点:
1.在__init__方法中定义各网络层。
2.在call方法中实现网络的前向结构。

以线性回归的例子,我们来看下如何通过subclassing的方式实现。

class LinearRegression(tf.keras.Model):

    def __init__(self):
        super().__init__()
        self.dense = tf.keras.layers.Dense(
            units=1,
            activation=None,
            kernel_initializer=tf.zeros_initializer(),
            bias_initializer=tf.zeros_initializer()
        )


    def call(self, input):
        output = self.dense(input)
        return output

def subclass_model():
    X = tf.constant([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
    y = tf.constant([[10.0], [20.0]])

    model = LinearRegression()
    optimizer = tf.keras.optimizers.SGD(learning_rate=0.01)
    for epoch in range(5000):
        with tf.GradientTape() as tape:
            y_pred = model(X)
            loss = tf.reduce_mean(tf.square(y_pred - y))
        grads = tape.gradient(loss, model.variables)
        optimizer.apply_gradients(grads_and_vars=zip(grads, model.variables))
        if epoch % 1000 == 0:
            print(f"""epoch is: epoch, loss is: loss""")

    print(model.variables)

最后代码的输出为:

epoch is: 0, loss is: 250.0
epoch is: 1000, loss is: 1.895978130050935e-08
epoch is: 2000, loss is: 3.092281986027956e-11
epoch is: 3000, loss is: 3.092281986027956e-11
epoch is: 4000, loss is: 3.092281986027956e-11
[<tf.Variable 'Variable:0' shape=(3, 1) dtype=float32, numpy=
array([[4.8735565e-06],
       [1.1111153e+00],
       [2.2222164e+00]], dtype=float32)>, <tf.Variable 'Variable:0' shape=(1,) dtype=float32, numpy=array([1.1111081], dtype=float32)>]

当然因为线性回归的网络结构比较简单,如果不使用Dense全连接层,我们也可以自己简单实现:

class LinearRegressionV2(tf.keras.Model):
    def __init__(self):
        super().__init__()
        self.w = tf.Variable(shape=(3, 1), initial_value=([[0.], [0.], [0.]]), trainable=True)
        self.b = tf.Variable(shape=(1, ), initial_value=[0.], trainable=True)

    def call(self, inputs):
        return tf.matmul(inputs, self.w) + self.b

使用这个类的效果跟LinearRegression类的效果是一致的

4.小结

总结一下这三种构建模型的方式
Sequential模式使用起来最简单,如果我们需要的网络结构是纯线性结构,优先考虑使用Sequential模式。比如经典的Lenet5,AlexNet,VGGNet等结构都可以使用Sequential实现。
Functional API方式实现相对复杂一些,当Sequential模式实现不了的时候,可以考虑Functional API。ResNet,GoogleNet,Xception,SqueezeNet等网络结构都可以用Functional API的方式实现。
如果有较多定制化需求,Sequential模式与Functional API方式都实现不了,那么最后考虑使用Subclassing的方式来实现。

以上是关于tensorflow 2.X中构建模型的三种方式:Sequential, Functional, Subclassing的主要内容,如果未能解决你的问题,请参考以下文章

Keras的三种建模方式

Keras的三种建模方式

Keras的三种建模方式

5 PyTorch构建模型的三种方式

5 PyTorch构建模型的三种方式

Tensorflow : 读取数据的三种方式及tfrecord的使用