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的主要内容,如果未能解决你的问题,请参考以下文章