TensorFlow 2.0: 如何保存和加载包含LSTM层的模型,遇到ValueError错误的解决方法

3

在我试图保存和加载包含LSTM层的模型时,使用load命令会出现值错误:无法找到与从SavedModel中加载的函数相匹配的函数

class RegNet(Model):
    def __init__(self,
             intermediate_dim=50,
             state_dim=9,
             name='RegNet',
             **kwargs):
        super(RegNet, self).__init__()
        self.d1 = Dense(intermediate_dim, activation='relu')
        self.d2 = Dense(state_dim, activation='relu')
        self.h = LSTM(state_dim, activation='sigmoid', return_sequences=True)
        self.o = Dense(state_dim, activation='softmax')

    def call(self, x):
        x = self.d1(x)
        x = self.d2(x)
        x = self.h(x)
        y = self.o(x)
        return y

regNet = RegNet()
...
# Export the model to a SavedModel
regNet.save(regNet_ckpt_dir, save_format='tf')
# Recreate the exact same model
tf.keras.models.load_model(regNet_ckpt_dir)

错误报告:
> ValueError: Could not find matching function to call loaded from the SavedModel. Got:
  Positional arguments (2 total):
    * Tensor("x:0", shape=(None, 1, 20), dtype=float32)
    * Tensor("training:0", shape=(), dtype=bool)
  Keyword arguments: {}

Expected these arguments to match one of the following 4 option(s):

Option 1:
  Positional arguments (2 total):
    * TensorSpec(shape=(None, 1, 20), dtype=tf.float32, name='input_1')
    * False
  Keyword arguments: {}

Option 2:
  Positional arguments (2 total):
    * TensorSpec(shape=(None, 1, 20), dtype=tf.float32, name='x')
    * False
  Keyword arguments: {}

Option 3:
  Positional arguments (2 total):
    * TensorSpec(shape=(None, 1, 20), dtype=tf.float32, name='x')
    * True
  Keyword arguments: {}

Option 4:
  Positional arguments (2 total):
    * TensorSpec(shape=(None, 1, 20), dtype=tf.float32, name='input_1')
    * True
  Keyword arguments: {}

当我注释掉LSTM层时,load命令会成功。问题出在哪里?我们无法在TensorFlow 2.0中保存和加载带有LSTM层的模型吗?


当我使用“功能API”构建模型而不是子类化“Model”类时,可以成功保存和加载包含LSTM层的模型。 - Long
你是如何使用函数式API保存它的?请将解决方案作为答案添加给其他人。谢谢。 - Mohammad
4个回答

7

如果其他人也遇到了同样的问题,这个解决方案对我起作用:

# Save model
tf.keras.models.save_model(model, "saved_model.hp5", save_format="h5")

# Load model
loaded_model = tf.keras.models.load_model("saved_model.hp5")

不确定为什么“model.save(filename)”语法在LSTM中不起作用,但我遇到了同样的问题。


这个答案对我也起作用了。注意:我的模型中根本没有LSTM层。当我使用h5格式的保存文件以及所示的save_model和load_model函数时,模型加载最终才能正常工作。因此,似乎tf 2.0.0存在一个损坏的tf格式保存文件或者一个损坏的model.save()函数(或两者都有)。 - Geoffrey Anderson
这个解决方案对我没用。我得到了一个不同的错误:ValueError: Unable to create group (name already exists)。实际上,我完全不能在TF2中保存和恢复模型。 - Chris Fonnesbeck

2

简而言之 尝试为training提供一个默认值,例如:

def call(self, inputs, training=None):

我遇到了与tensorflow 2.1.0类似的错误:

"ValueError: Could not find matching function to call loaded from the SavedModel. Got:
  Positional arguments (2 total):
    * Tensor("inputs:0", shape=(None, 128, 128, 3), dtype=float32)
    * Tensor("training:0", shape=(), dtype=bool)

我的模型没有LSTM层。 从错误信息中的投诉与inputstraining有关,我猜这与我们在子类化Model类时所需的call函数有关,因为inputstraining是该函数的两个参数名称。 解决我的问题的方法是:

不要使用

def call(self, inputs):

提供training的默认值,即:

def call(self, inputs, training=None):

哇!这就是正确的方法。只需在调用方法中给出training=None即可。对于任何卡在这里的人,请参考https://dev59.com/yLbna4cB1Zd3GeqPUAXQ。 - HopeKing

2

子类模型和其他Keras模型类型(Sequential和Functional)有所不同。由于列出的这里的差异,保存和加载子类模型与其他Keras模型不同。

重要点

  1. 子类化模型的不同之处在于它不是一个数据结构,而是一段代码。模型的架构是通过call方法的主体定义的。这意味着模型的架构无法安全地序列化。
  2. 从未使用过的子类化模型无法保存。这是因为需要在某些数据上调用子类化模型以创建其权重。在调用模型之前,它不知道应该期望哪种输入数据的形状和dtype,因此无法创建其权重变量。

提到了子类和其他Keras模型(Sequential和Functional)之间的差异后,有三种不同的方式可以保存子类化模型。

让我们创建一个模型

class ThreeLayerMLP(keras.Model):

  def __init__(self, name=None):
    super(ThreeLayerMLP, self).__init__(name=name)
    self.dense_1 = layers.Dense(64, activation='relu', name='dense_1')
    self.dense_2 = layers.Dense(64, activation='relu', name='dense_2')
    self.pred_layer = layers.Dense(10, name='predictions')

  def call(self, inputs):
    x = self.dense_1(inputs)
    x = self.dense_2(x)
    return self.pred_layer(x)

def get_model():
  return ThreeLayerMLP(name='3_layer_mlp')

model = get_model()

训练模型

(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
x_train = x_train.reshape(60000, 784).astype('float32') / 255
x_test = x_test.reshape(10000, 784).astype('float32') / 255

model.compile(loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
              optimizer=keras.optimizers.RMSprop())
history = model.fit(x_train, y_train,
                    batch_size=64,
                    epochs=1)
# Reset metrics before saving so that loaded model has same state,
# since metric states are not preserved by Model.save_weights
model.reset_metrics()

方法1:最简单的方法

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

# Save the model
model.save('path_to_my_model',save_format='tf')

# Recreate the exact same model purely from the file
new_model = keras.models.load_model('path_to_my_model')

方法2:与方法1相同

第二种方法是使用tf.saved_model.save。这等同于model.save中的tf格式。您可以再次调用load_model来恢复先前保存的子类模型。

方法3:

第三种方法是使用save_weights创建TensorFlow SavedModel检查点。要恢复一个子类模型,(1)您需要访问创建模型对象的代码并重新创建模型,(2)编译模型以恢复优化器状态和任何有状态指标,(3)在调用load_weights之前对一些数据进行调用。

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

# Recreate the model
new_model = get_model()
new_model.compile(loss=keras.losses.SparseCategoricalCrossentropy(from_logits=True),
                  optimizer=keras.optimizers.RMSprop())

# This initializes the variables used by the optimizers,
# as well as any stateful metric variables
new_model.train_on_batch(x_train[:1], y_train[:1])

# Load the state of the old model
new_model.load_weights('path_to_my_weights')

在保存模型时需要注意以下几点。

整个模型可以以两种不同的文件格式(SavedModelHDF5)保存。需要注意的是 TensorFlow SavedModel('tf')格式是 TF2.x 中默认的文件格式。但是,模型也可以以 HDF5 格式保存。HDF5 和 SavedModel 的主要区别在于 HDF5 使用对象配置来保存模型架构,而 SavedModel 则保存执行图。因此,SavedModels(使用 save_format='tf' 保存的模型)能够保存子类化模型和自定义层等自定义对象,而无需原始代码。

希望这可以帮到您。


0

看起来,添加一个compute_output_shape可以解决这个问题。

regNet.compute_output_shape(input_shape=(1,10,3))

这里是参考的代码片段(链接)

我使用了 TF2.7,并且模型已经成功保存和加载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接