前言:前面有文章专门讲过使用keras如何实现自定义层,本文将继续介绍使用keras如何实现一个自定义模型,其实实现自定义层和自定义的方式与pytorch极度类似,注意体会他们的联系和区别,前面的文章参见:
keras实现自定义层的关键步骤解析
另外,本文基于tensorflow2.0的高层API——tf.keras,使用的案例是手写字识别
tf.keras.Sequential 模型是层的简单堆叠,无法表示任意模型。使用 Keras 函数式 API 可以构建复杂的模型拓扑,例如:
- 多输入模型
- 多输出模型
- 具有共享层的模型(同一层被调用多次)
- 具有非序列数据流的模型(例如,剩余连接)
使用函数式 API 构建的模型具有以下特征:
- 层实例可调用并返回张量
- 输入张量和输出张量用于定义 tf.keras.Model 实例
- 此模型的训练方式和 Sequential 模型一样
以下示例使用函数式 API 构建一个简单的全连接网络:
inputs = tf.keras.Input(shape=(32,)) # 构建一个输入张量
# 层layer的实例对象是callable的,他接受一个tensor,并返回一个处理之后的tensor
x = layers.Dense(64, activation='relu')(inputs)
x = layers.Dense(64, activation='relu')(x)
predictions = layers.Dense(10, activation='softmax')(x)
# 在给定输入和输出的情况下实例化模型。
model = tf.keras.Model(inputs=inputs, outputs=predictions)
# 编译模型
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
训练 5 个 epochs
model.fit(data, labels, batch_size=32, epochs=5)
Epoch 1/5
1000/1000 [==============================] - 0s 260us/step - loss: 11.7190 - acc: 0.1080
Epoch 2/5
1000/1000 [==============================] - 0s 75us/step - loss: 11.5347 - acc: 0.1010
Epoch 3/5
1000/1000 [==============================] - 0s 74us/step - loss: 11.5020 - acc: 0.1100
Epoch 4/5
1000/1000 [==============================] - 0s 75us/step - loss: 11.4908 - acc: 0.1090
Epoch 5/5
1000/1000 [==============================] - 0s 74us/step - loss: 11.4809 - acc: 0.1330
其实所谓的“模型子类化”就是自己实现一个类来继承Model类,构建一个Model类的子类,需要实现三个方法,即:
- __init__()
- call()
- compute_output_shape()
通过对 tf.keras.Model 进行子类化并定义您自己的前向传播来构建完全可自定义的模型。在 __init__ 方法中创建层并将它们设置为类实例的属性;在 call 方法中定义前向传播;在compute_output_shape计算模型输出的形状。
注意:这里其实和自定义实现一个层很类似,自定义层也是实现三个方法,分别是:
- build(input_shape):
- call(x):
- compute_output_shape(input_shape):
在启用 Eager Execution 时,模型子类化特别有用,因为可以命令式地编写前向传播。
要点:针对作业使用正确的 API。虽然模型子类化较为灵活,但代价是复杂性更高且用户出错率更高。如果可能,请首选函数式 API。
以下示例展示了使用自定义前向传播进行子类化的 tf.keras.Model:
class MyModel(tf.keras.Model): # 自定义模型继承Model
def __init__(self, num_classes=10):
super(MyModel, self).__init__(name='my_model')
self.num_classes = num_classes
# 定义这个模型中的层 ,这跟pytorch是类似的
self.dense_1 = layers.Dense(32, activation='relu')
self.dense_2 = layers.Dense(num_classes, activation='sigmoid')
def call(self, inputs):
# 定义数据的前向传播,这个和pytorch也是类似的,只不过pytorch是在forward函数中实现的
x = self.dense_1(inputs)
x = self.dense_2(x)
return x
def compute_output_shape(self, input_shape):
# 如果想要使用定义的子类,则需要重载此方法,计算模型输出的形状
shape = tf.TensorShape(input_shape).as_list()
shape[-1] = self.num_classes
return tf.TensorShape(shape)
下面来实例化自己定义的新模型类:
model = MyModel(num_classes=10)
# 编译模型
model.compile(optimizer=tf.train.RMSPropOptimizer(0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
# 训练 5 epochs.
model.fit(data, labels, batch_size=32, epochs=5)
'''
Epoch 1/5
1000/1000 [==============================] - 0s 224us/step - loss: 11.5206 - acc: 0.0990
Epoch 2/5
1000/1000 [==============================] - 0s 62us/step - loss: 11.5128 - acc: 0.1070
Epoch 3/5
1000/1000 [==============================] - 0s 64us/step - loss: 11.5023 - acc: 0.0980
Epoch 4/5
1000/1000 [==============================] - 0s 65us/step - loss: 11.4941 - acc: 0.0980
Epoch 5/5
1000/1000 [==============================] - 0s 66us/step - loss: 11.4879 - acc: 0.0990
'''
总结:keras实现自定义的层和模型是多么的相似,不仅如此,和pytorch的处理方法也是相似,仔细体会他们之间的相似点和不同点,可以编写更人性化的代码,更加理解这些高层框架之间的共性。