Skip to content

使用tf.keras自定义模型

Info

作者:Vincent,发布于2021-10-04,阅读时间:约2分钟,微信公众号文章链接:

1 前言

tf.keras提供了许多方便调用的API构建深度学习模型,但有些情况需要自定义层和模型,因此在这篇文章里,我们将着眼自定义模型,使用TensorFlow 2.X里的自定义方法为解决方案提供更多灵活性。

2 自定义层

2.1 创建没有权重的层

当自定义层无需权重时,使用tf.keras.layers.Lambda会非常方便,示例如下:

exponential_layer = keras.layers.Lambda(lambda x: tf.exp(x))

然后这个自定义层可以像其他层一样在Sequential API和Functional API中使用以构建模型。甚至可以像调用Python函数一样调用它:

print(exponential_layer(2.0).numpy())

输出为:

7.389056

2.2 创建具有权重的层

如需创建具有权重的层,通常是继承tf.keras.layers.Layer类,并重写__init__buildcall三个方法,示例如下:

class Linear(keras.layers.Layer):
    def __init__(self, units=32):
        super(Linear, self).__init__()
        self.units = units

    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            shape=(self.units,), initializer="random_normal", trainable=True
        )

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

自定义的层可以像tf.keras内置的层一样被调用:

input_ = Input((1,))
output = Linear(units=1)(input_)
model = Model(input_, output)
model.compile(optimizer='Adam', loss="mse")
model.summary()

输出为:

Model: "model_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 1)]               0         
_________________________________________________________________
linear_1 (Linear)            (None, 1)                 2         
=================================================================
Total params: 2
Trainable params: 2
Non-trainable params: 0
_________________________________________________________________

请注意,如果要将自定义层作为Functional API模型的一部分进行序列化,需实现get_config()方法,基础Layer类的__init__()方法会接受一些关键字参数,尤其是name和dtype。最好将这些参数传递给__init__()中的父类,并将其包含在层配置中,示例如下:

class Linear(keras.layers.Layer):
    def __init__(self, units=32, **kwargs):
        super(Linear, self).__init__(**kwargs)
        self.units = units

    def build(self, input_shape):
        self.w = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="random_normal",
            trainable=True,
        )
        self.b = self.add_weight(
            shape=(self.units,), initializer="random_normal", trainable=True
        )

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

    def get_config(self):
        config = super(Linear, self).get_config()
        config.update({"units": self.units})
        return config

如果在训练和推理阶段,自定义层的行为不同,比如Dropout或者BatchNormalization层,则需要在call函数里加入training参数来区分模型不同运行状态下的行为,示例如下:

class CustomDropout(keras.layers.Layer):
    def __init__(self, rate, **kwargs):
        super(CustomDropout, self).__init__(**kwargs)
        self.rate = rate

    def call(self, inputs, training=None):
        if training:
            return tf.nn.dropout(inputs, rate=self.rate)
        return inputs

2.3 自定义loss和metrics

自定义loss时,使用标签和预测值作为参数,然后用TensorFlow的算子计算每个实例的损失。

def huber_fn(y_true,y_pred):
  error=y_true-y_pred
  is_small_error=tf.abs(error)<1
  squared_loss=tf.square(error)/2
  linear_loss=tf.abs(error)-0.5

return tf.where(is_small_error,squared_loss,linear_loss)

自定义评估指标时,可以继承tf.keras.metrics.Metric类,并重写__init__update_stateresult三个方法,示例如下:

class SparseCategoricalAccuracy(tf.keras.metrics.Metric):
    def __init__(self):
        super().__init__()
        self.total = self.add_weight(name='total', dtype=tf.int32, initializer=tf.zeros_initializer())
        self.count = self.add_weight(name='count', dtype=tf.int32, initializer=tf.zeros_initializer())

    def update_state(self, y_true, y_pred, sample_weight=None):
        values = tf.cast(tf.equal(y_true, tf.argmax(y_pred, axis=-1, output_type=tf.int32)), tf.int32)
        self.total.assign_add(tf.shape(y_true)[0])
        self.count.assign_add(tf.reduce_sum(values))

    def result(self):
        return self.count / self.total

update_state()方法在调用自定义评价指标类的实例时起作用,它接收一个批次实例中标签、预测值和其他自定义的参数来更新定义的变量。result()方法则计算并返回最终的结果,它会在update_state()方法之后执行。

3 总结

上述例子介绍了如何自定义keras模型,能够为日常的工作流更添灵活性,实际工作中,还需反复推敲,确保正确无误。

希望这次的分享对你有帮助,欢迎在评论区留言讨论!


Viewed times

Comments