TensorFlow 2.0:保存并加载包含LSTM层的模型,而加载通用失败并出现ValueError

时间:2019-10-11 10:17:47

标签: tensorflow2.0

当我尝试保存和加载包含LSTM层的模型时,加载通用失败,并出现 ValueError:无法找到匹配的函数来调用从SavedModel加载的函数

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\model_selection\_validation.py:528: FutureWarning: From version 0.22, errors during fit will result in a cross validation score of NaN by default. Use error_score='raise' if you want an exception raised or error_score=np.nan to adopt the behavior from version 0.22.
  FutureWarning)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-162-9c9a4d6ea766> in <module>
     14 y = y_train1
     15 model_cv_result = model_cv.fit(X, y
---> 16                               , callbacks = [time_callback]
     17                               )
     18 

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\model_selection\_search.py in fit(self, X, y, groups, **fit_params)
    685                 return results
    686 
--> 687             self._run_search(evaluate_candidates)
    688 
    689         # For multi-metric evaluation, store the best_index_, best_params_ and

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\model_selection\_search.py in _run_search(self, evaluate_candidates)
   1146     def _run_search(self, evaluate_candidates):
   1147         """Search all candidates in param_grid"""
-> 1148         evaluate_candidates(ParameterGrid(self.param_grid))
   1149 
   1150 

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\model_selection\_search.py in evaluate_candidates(candidate_params)
    664                                for parameters, (train, test)
    665                                in product(candidate_params,
--> 666                                           cv.split(X, y, groups)))
    667 
    668                 if len(out) < 1:

C:\ProgramData\Anaconda3\lib\site-packages\joblib\parallel.py in __call__(self, iterable)
    919             # remaining jobs.
    920             self._iterating = False
--> 921             if self.dispatch_one_batch(iterator):
    922                 self._iterating = self._original_iterator is not None
    923 

C:\ProgramData\Anaconda3\lib\site-packages\joblib\parallel.py in dispatch_one_batch(self, iterator)
    757                 return False
    758             else:
--> 759                 self._dispatch(tasks)
    760                 return True
    761 

C:\ProgramData\Anaconda3\lib\site-packages\joblib\parallel.py in _dispatch(self, batch)
    714         with self._lock:
    715             job_idx = len(self._jobs)
--> 716             job = self._backend.apply_async(batch, callback=cb)
    717             # A job can complete so quickly than its callback is
    718             # called before we get here, causing self._jobs to

C:\ProgramData\Anaconda3\lib\site-packages\joblib\_parallel_backends.py in apply_async(self, func, callback)
    180     def apply_async(self, func, callback=None):
    181         """Schedule a func to be run"""
--> 182         result = ImmediateResult(func)
    183         if callback:
    184             callback(result)

C:\ProgramData\Anaconda3\lib\site-packages\joblib\_parallel_backends.py in __init__(self, batch)
    547         # Don't delay the application, to avoid keeping the input
    548         # arguments in memory
--> 549         self.results = batch()
    550 
    551     def get(self):

C:\ProgramData\Anaconda3\lib\site-packages\joblib\parallel.py in __call__(self)
    223         with parallel_backend(self._backend, n_jobs=self._n_jobs):
    224             return [func(*args, **kwargs)
--> 225                     for func, args, kwargs in self.items]
    226 
    227     def __len__(self):

C:\ProgramData\Anaconda3\lib\site-packages\joblib\parallel.py in <listcomp>(.0)
    223         with parallel_backend(self._backend, n_jobs=self._n_jobs):
    224             return [func(*args, **kwargs)
--> 225                     for func, args, kwargs in self.items]
    226 
    227     def __len__(self):

C:\ProgramData\Anaconda3\lib\site-packages\sklearn\model_selection\_validation.py in _fit_and_score(estimator, X, y, scorer, train, test, verbose, parameters, fit_params, return_train_score, return_parameters, return_n_test_samples, return_times, return_estimator, error_score)
    512             estimator.fit(X_train, **fit_params)
    513         else:
--> 514             estimator.fit(X_train, y_train, **fit_params)
    515 
    516     except Exception as e:

C:\ProgramData\Anaconda3\lib\site-packages\keras\wrappers\scikit_learn.py in fit(self, x, y, **kwargs)
    149         fit_args.update(kwargs)
    150 
--> 151         history = self.model.fit(x, y, **fit_args)
    152 
    153         return history

C:\ProgramData\Anaconda3\lib\site-packages\keras\engine\training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_freq, max_queue_size, workers, use_multiprocessing, **kwargs)
   1237                                         steps_per_epoch=steps_per_epoch,
   1238                                         validation_steps=validation_steps,
-> 1239                                         validation_freq=validation_freq)
   1240 
   1241     def evaluate(self,

C:\ProgramData\Anaconda3\lib\site-packages\keras\engine\training_arrays.py in fit_loop(model, fit_function, fit_inputs, out_labels, batch_size, epochs, verbose, callbacks, val_function, val_inputs, shuffle, initial_epoch, steps_per_epoch, validation_steps, validation_freq)
    144     for epoch in range(initial_epoch, epochs):
    145         model.reset_metrics()
--> 146         callbacks.on_epoch_begin(epoch)
    147         epoch_logs = {}
    148         if steps_per_epoch is not None:

C:\ProgramData\Anaconda3\lib\site-packages\keras\callbacks\callbacks.py in on_epoch_begin(self, epoch, logs)
    134         logs = logs or {}
    135         for callback in self.callbacks:
--> 136             callback.on_epoch_begin(epoch, logs)
    137         self._reset_batch_timing()
    138 

<ipython-input-121-c4f8115d9239> in on_epoch_begin(self, epoch, logs)
     19         self.times = []
     20     def on_epoch_begin(self, epoch, logs={}):
---> 21         self.epoch_time_start = time.time()
     22     def on_epoch_end(self, epoch, logs={}):
     23         self.times.append(time.time() - self.epoch_time_start)

AttributeError: 'float' object has no attribute 'time'

错误报告:

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)

当我评论LSTM层时,加载命令将成功。问题出在哪里?我们不能在TensorFlow 2.0中保存和加载具有LSTM层的模型吗?

3 个答案:

答案 0 :(得分:1)

万一其他人偶然发现此解决方案,那么该解决方案对我有用:

# 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,但是我遇到了同样的问题。

答案 1 :(得分:1)

TL; DR 尝试提供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有关的事实来看,我想它与我们将call类子类化时所需的Model函数有关,因为inputstraining是该函数的两个参数名称。 解决我的问题的是:

代替

def call(self, inputs):

提供training的默认值,即

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

答案 2 :(得分:0)

子类模型与其他keras模型类型(顺序模型和功能模型)有所不同。由于here中列出的那些差异,子类模型的保存和加载与其他Keras模型不同。

重要要点

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

在谈到子类模型与其他keras模型(顺序模型和功能模型)之间的差异时,可以使用三种不同的方法来保存子分类模型。

让我们创建一个模型

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。这等效于tf中的model.save格式。您可以再次调用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'保存的模型)能够保存自定义对象,例如子类模型和自定义层,而无需原始代码。

希望这会有所帮助。