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的方式来实现。

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TensorFlow 2.x,Slim库已经被整合到了TensorFlow的核心API,因此不再需要单独调用Slim函数。下面是一些常见的TensorFlow 2.x操作,用于替代Slim函数的使用: 1. 定义模型: 在TensorFlow 2.x,可以使用`tf.keras`模块定义模型。例如,可以使用`tf.keras.layers`来定义各种层,如卷积层、全连接层等。以下是一个示例: ```python import tensorflow as tf model = tf.keras.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(10, activation='softmax') ]) ``` 2. 加载预训练模型: 在TensorFlow 2.x,可以使用`tf.keras.applications`模块加载和使用预训练模型。例如,可以使用`tf.keras.applications.ResNet50`加载ResNet50模型。以下是一个示例: ```python import tensorflow as tf pretrained_model = tf.keras.applications.ResNet50(weights='imagenet') ``` 3. 定义损失函数和优化器: 在TensorFlow 2.x,可以使用`tf.losses`模块定义损失函数,使用`tf.optimizers`模块定义优化器。以下是一个示例: ```python import tensorflow as tf loss_object = tf.losses.SparseCategoricalCrossentropy() optimizer = tf.optimizers.Adam() ``` 需要注意的是,TensorFlow 2.x的许多操作和函数都直接使用了Eager Execution模式,可以即时执行,无需构建图。这使得TensorFlow的使用更加直观和方便。 希望这些信息对你有帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值