--- template: overrides/blogs.html tags: - deep learning - tensorflow --- # 使用tf.keras自定义模型 !!! info 作者:[Vincent](https://github.com/Realvincentyuan),发布于2021-06-06,阅读时间:约6分钟,微信公众号文章链接:[:fontawesome-solid-link:](https://mp.weixin.qq.com/s/z2uBxwe8UNDXWMDNS_k-Gg) ## 1 前言 tf.keras提供了许多方便调用的API构建深度学习模型,但有些情况需要自定义层和模型,因此在这篇文章里,我们将着眼自定义模型,使用TensorFlow 2.X里的自定义方法为解决方案提供更多灵活性。 ## 2 自定义层 ### 2.1 创建没有权重的层 当自定义层无需权重时,使用`tf.keras.layers.Lambda`会非常方便,示例如下: ```python exponential_layer = keras.layers.Lambda(lambda x: tf.exp(x)) ``` 然后这个自定义层可以像其他层一样在Sequential API和Functional API中使用以构建模型。甚至可以像调用Python函数一样调用它: ```Python print(exponential_layer(2.0).numpy()) ``` 输出为: ```Python 7.389056 ``` ### 2.2 创建具有权重的层 如需创建具有权重的层,通常是继承`tf.keras.layers.Layer`类,并重写`__init__`、`build`和`call`三个方法,示例如下: ```Python 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内置的层一样被调用: ```Python input_ = Input((1,)) output = Linear(units=1)(input_) model = Model(input_, output) model.compile(optimizer='Adam', loss="mse") model.summary() ``` 输出为: ```python 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__()中的父类,并将其包含在层配置中,[示例]('https://www.tensorflow.org/guide/keras/custom_layers_and_models#%E5%8F%AF%E9%80%89%E6%8B%A9%E5%9C%A8%E5%B1%82%E4%B8%8A%E5%90%AF%E7%94%A8%E5%BA%8F%E5%88%97%E5%8C%96' '可选择在层上启用序列化')如下: ```python 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`参数来区分模型不同运行状态下的行为,示例如下: ```Python 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的算子计算每个实例的损失。 ```python 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_state`和`result`三个方法,[示例](https://tf.wiki/zh_hans/basic/models.html#id26 '简单粗暴Tensorflow 2.0')如下: ```Python 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模型,能够为日常的工作流更添灵活性,实际工作中,还需反复推敲,确保正确无误。 希望这次的分享对你有帮助,欢迎在评论区留言讨论!