keras通过模型子类(SubClassing)化实现自定义模型

keras通过模型子类(SubClassing)化实现自定义模型_第1张图片

前言:前面有文章专门讲过使用keras如何实现自定义层,本文将继续介绍使用keras如何实现一个自定义模型,其实实现自定义层和自定义的方式与pytorch极度类似,注意体会他们的联系和区别,前面的文章参见:

keras实现自定义层的关键步骤解析

另外,本文基于tensorflow2.0的高层API——tf.keras,使用的案例是手写字识别

一、函数式 API(Functional API)

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 SubClass)——实现自定义模型

其实所谓的“模型子类化”就是自己实现一个类来继承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的处理方法也是相似,仔细体会他们之间的相似点和不同点,可以编写更人性化的代码,更加理解这些高层框架之间的共性。

你可能感兴趣的:(深度学习,keras)