无法保存自定义子类化模型

时间:2018-08-12 07:53:41

标签: python-3.x tensorflow keras

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

此处的最小完整代码示例:

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")

错误消息:

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

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

有人处理这个问题吗?

谢谢!

更新1:
在keras 2.2.2上(不是tf.keras)
找到评论(用于模型保存)
文件:keras / engine / network.py
函数:get_config

  

#子类网络不可序列化
  #(除非由
实现序列化   #子网络的作者)。

因此,显然它不起作用...
我想知道,他们为什么不在documentation中指出(例如:“使用子类却无法保存!”)

更新2:
keras documentation中找到:

  

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

     

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

因此,无法使用子类保存模型。
只能使用Model.save_weights()

7 个答案:

答案 0 :(得分:7)

此答案适用于Tensorflow 2.0

TL; DR:

  1. 请勿将model.save()用于自定义子类keras模型;
  2. 改为使用save_weights()load_weights()

在Tensorflow团队的帮助下,保存自定义子类Keras模型的最佳实践是保存其权重并在需要时将其加载回去。

我们之所以不能简单地保存Keras自定义子类模型,是因为它包含自定义代码,无法安全序列化。但是,当我们具有相同的模型结构和自定义代码时,可以保存/加载权重。

Keras的作者Francois Chollet撰写了一篇很棒的教程,内容涉及如何在here的Colab中的Tensorflow 2.0中保存/加载顺序/功能/ Keras /自定义子类模型。在保存子类模型部分中,它表示:

  

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

     

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

答案 1 :(得分:4)

此问题将根据1.13 pre-release patch notes在即将发布的版本中解决:

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

编辑: 看来这还不像笔记所建议的那样完成。 docs for that function for v1.13状态:

  

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

答案 2 :(得分:4)

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

从开始使用Tensorflow以来,我一直是Model Subclass的粉丝,我觉得这种建立模型的方式更加Pythonic和协作友好。但是,使用这种方法保存模型始终是一个难题。

最近,我开始更新我的知识,并了解以下information似乎对 Tensorflow 2.1 来说是正确的:

子类模型

我发现了this

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

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

最后是confirmation

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

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

对于所有对模型子分类感兴趣的人,这应该是这条痛苦道路的尽头。

答案 3 :(得分:1)

其实tensorflow文档说:

<块引用>

为了保存/加载具有自定义层的模型或子类模型,您应该覆盖 get_config 和可选的 from_config 方法。此外,您应该使用 register 自定义对象,以便 Keras 知道它。

例如:

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


layer = Linear(64)
config = layer.get_config()
print(config)
new_layer = Linear.from_config(config)

输出为:

{'name': 'linear_8', 'trainable': True, 'dtype': 'float32', 'units': 64}

你可以玩这个简单的代码。例如,在函数“get_config()”中,删除config.update(),看看发生了什么。有关详细信息,请参阅 thisthis。这些是 tensorflow 网站上的 Keras 指南。

答案 4 :(得分:1)

我找到了解决它的方法。创建一个新模型并从保存的 .h5 模型加载权重。这种方式不是首选,但它适用于 keras 2.2.4 和 tensorflow 1.12。

class MyModel(keras.Model):  
     def __init__(self, inputs, *args, **kwargs):
          outputs = func(inputs)
     super(MyModel, self).__init__( inputs=inputs, outputs=outputs, *args, **kwargs)

def get_model():  
    return MyModel(inputs, *args, **kwargs)

model = get_model()
model.save(‘file_path.h5’)

model_new = get_model()  
model_new.compile(optimizer=optimizer, loss=loss, metrics=metrics) 
model_new.load_weights(‘file_path.h5’)  
model_new.evaluate(x_test, y_test, **kwargs)

答案 5 :(得分:0)

model.predict之前使用tf.saved_model.save

答案 6 :(得分:0)

实际上是通过以下方式重新创建模型

keras.models.load_model('path_to_my_model')

不是为我工作

首先,我们必须从构建的模型中保存权重

model.save_weights('model_weights', save_format='tf')

然后 我们必须为子类Model初始化一个新实例,然后使用已建模型的一条记录和load_weights进行编译和train_on_batch

loaded_model = ThreeLayerMLP(name='3_layer_mlp')
loaded_model.compile(optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"])
loaded_model.train_on_batch(x_train[:1], y_train[:1])
loaded_model.load_weights('model_weights')

这在TensorFlow == 2.2.0中完美工作