无法保存自定义子类型

Red*_*yed 12 python-3.x keras tensorflow

tf.keras.Model子类化的启发,我创建了自定义模型.
我可以训练它并获得成功的结果,但我无法保存它.
我使用python3.6和tensorflow v1.10(或v1.9)

这里的最小完整代码示例:

import tensorflow as tf
from tensorflow.keras.datasets import mnist


class Classifier(tf.keras.Model):
    def __init__(self):
        super().__init__(name="custom_model")

        self.batch_norm1 = tf.layers.BatchNormalization()
        self.conv1 = tf.layers.Conv2D(32, (7, 7))
        self.pool1 = tf.layers.MaxPooling2D((2, 2), (2, 2))

        self.batch_norm2 = tf.layers.BatchNormalization()
        self.conv2 = tf.layers.Conv2D(64, (5, 5))
        self.pool2 = tf.layers.MaxPooling2D((2, 2), (2, 2))

    def call(self, inputs, training=None, mask=None):
        x = self.batch_norm1(inputs)
        x = self.conv1(x)
        x = tf.nn.relu(x)
        x = self.pool1(x)

        x = self.batch_norm2(x)
        x = self.conv2(x)
        x = tf.nn.relu(x)
        x = self.pool2(x)

        return x


if __name__ == '__main__':
    (x_train, y_train), (x_test, y_test) = mnist.load_data()

    x_train = x_train.reshape(*x_train.shape, 1)[:1000]
    y_train = y_train.reshape(*y_train.shape, 1)[:1000]

    x_test = x_test.reshape(*x_test.shape, 1)
    y_test = y_test.reshape(*y_test.shape, 1)

    y_train = tf.keras.utils.to_categorical(y_train)
    y_test = tf.keras.utils.to_categorical(y_test)

    model = Classifier()

    inputs = tf.keras.Input((28, 28, 1))

    x = model(inputs)
    x = tf.keras.layers.Flatten()(x)
    x = tf.keras.layers.Dense(10, activation="sigmoid")(x)

    model = tf.keras.Model(inputs=inputs, outputs=x)
    model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
    model.fit(x_train, y_train, epochs=1, shuffle=True)

    model.save("./my_model")
Run Code Online (Sandbox Code Playgroud)

错误信息:

1000/1000 [==============================] - 1s 1ms/step - loss: 4.6037 - acc: 0.7025
Traceback (most recent call last):
  File "/home/user/Data/test/python/mnist/mnist_run.py", line 62, in <module>
    model.save("./my_model")
  File "/home/user/miniconda3/envs/ml3.6/lib/python3.6/site-packages/tensorflow/python/keras/engine/network.py", line 1278, in save
    save_model(self, filepath, overwrite, include_optimizer)
  File "/home/user/miniconda3/envs/ml3.6/lib/python3.6/site-packages/tensorflow/python/keras/engine/saving.py", line 101, in save_model
    'config': model.get_config()
  File "/home/user/miniconda3/envs/ml3.6/lib/python3.6/site-packages/tensorflow/python/keras/engine/network.py", line 1049, in get_config
    layer_config = layer.get_config()
  File "/home/user/miniconda3/envs/ml3.6/lib/python3.6/site-packages/tensorflow/python/keras/engine/network.py", line 1028, in get_config
    raise NotImplementedError
NotImplementedError

Process finished with exit code 1
Run Code Online (Sandbox Code Playgroud)

我查看了错误行,发现get_config方法检查self._is_graph_network

有人处理这个问题吗?

谢谢!

更新1:
在keras 2.2.2(不是tf.keras)
找到注释(用于模型保存)
文件:keras/engine/network.py
功能:get_config

#Subclassed networks不是可序列化的
#(除非序列化由
#subsedsed network的作者实现).

所以,显然它不会起作用......
我想知道,为什么他们不在文档中指出它(比如:"使用没有能力保存的子类化!")

更新2:
keras文档中找到:

在子类模型中,模型的拓扑被定义为Python代码
(而不是图层的静态图).这意味着
无法检查或序列化模型的拓扑.因此,以下
方法和属性不适用于子类型模型:

model.inputs和model.outputs.
model.to_yaml()和model.to_json()
model.get_config()和model.save().

因此,没有办法通过使用子类来保存模型.
它可以只使用Model.save_weights()

Hua*_*uan 16

This answer is for Tensorflow 2.0

TL;DR:

  1. do not use model.save() for custom subclass keras model;
  2. use save_weights() and load_weights() instead.

With the help of the Tensorflow Team, it turns out the best practice of saving a Custom Sub-Class Keras Model is to save its weights and load it back when needed.

The reason that we can not simply save a Keras custom subclass model is that it contains custom codes, which can not be serialized safely. However, the weights can be saved/loaded when we have the same model structure and custom codes without any problem.

There has a great tutorial written by Francois Chollet who is the author of Keras, for how to save/load Sequential/Functional/Keras/Custom Sub-Class Models in Tensorflow 2.0 in Colab at here. In Saving Subclassed Models section, it said that:

顺序模型和功能模型是表示层DAG的数据结构。这样,它们可以安全地序列化和反序列化。

子类化模型的不同之处在于,它不是数据结构,而是一段代码。通过调用方法的主体定义模型的体系结构。这意味着无法安全地序列化模型的体系结构。要加载模型,您需要有权访问创建模型的代码(模型子类的代码)。或者,您可以将此代码序列化为字节码(例如,通过腌制),但这是不安全的,并且通常不可移植。

  • 请注意,通过将权重和架构分开保存,您将丢失使用 model.save() 保存的编译信息(优化器和损失) (2认同)
  • 看来您现在可以根据[tensorflow的文档](https://www.tensorflow.org/guide/keras/save_and_serialize#part_ii_ saving_and_loading_of_subclassed_models)使用自定义代码保存完整模型。您的答案可以编辑:) (2认同)

qwi*_*twa 6

根据1.13预发行补丁说明,此问题将在即将发布的版本中修复

  • Keras和Python API:
    • 现在可以通过保存子类的Keras模型tf.contrib.saved_model.save_keras_model

编辑:看来这还不像注释所建议的那样完成。该函数针对v1.13文档状态如下:

模型限制:-顺序模型和功能模型始终可以保存。-只有当serve_only = True时才能保存分类模型。这是由于当前的实现会复制模型以导出训练图和评估图。由于无法确定子类模型的拓扑,因此无法克隆子类模型。子类别的模型将来将完全可导出。


ElP*_*i42 5

Tensorflow 2.1 允许使用 SavedModel 格式保存子类模型

从我开始使用 Tensorflow 开始,我一直是 Model Subclass 的粉丝,我觉得这种构建模型的方式更加 Pythonic 和协作友好。但是使用这种方法保存模型总是一个痛点。

最近,我开始更新我的知识并获得以下信息,这些信息对于Tensorflow 2.1似乎是正确的:

子类模型

我发现了这个

第二种方法是使用 model.save 保存整个模型,并使用 load_model 恢复以前存储的子类模型。

最后将模型、重量和其他内容保存到 SavedModel 文件中

最后确认

保存自定义对象:如果您使用的是 SavedModel 格式,则可以跳过此部分。HDF5 和 SavedModel 的主要区别在于 HDF5 使用对象配置来保存模型架构,而 SavedModel 保存执行图。因此,SavedModels 能够 保存自定义对象,如子类模型和自定义层,而 无需原始代码。

我亲自对此进行了测试,并且有效地,子类模型的 model.save() 生成了 SavedModel 保存。不再需要使用 model.save_weights() 或相关函数,它们现在更多用于特定用例。

对于我们所有对模型子类感兴趣的人来说,这应该是这条痛苦道路的结束。