NotImplementedError:在 `__init__` 中有参数的层必须覆盖 `get_config`

Posted

技术标签:

【中文标题】NotImplementedError:在 `__init__` 中有参数的层必须覆盖 `get_config`【英文标题】:NotImplementedError: Layers with arguments in `__init__` must override `get_config` 【发布时间】:2020-02-28 21:47:51 【问题描述】:

我正在尝试使用 model.save() 保存我的 TensorFlow 模型,但是 - 我收到了这个错误。

此处提供模型摘要: Model Summary

变压器模型的代码:

def transformer(vocab_size, num_layers, units, d_model, num_heads, dropout, name="transformer"):
    inputs = tf.keras.Input(shape=(None,), name="inputs")
    dec_inputs = tf.keras.Input(shape=(None,), name="dec_inputs")

    enc_padding_mask = tf.keras.layers.Lambda(
        create_padding_mask, output_shape=(1, 1, None),
        name='enc_padding_mask')(inputs)
    # mask the future tokens for decoder inputs at the 1st attention block
    look_ahead_mask = tf.keras.layers.Lambda(
        create_look_ahead_mask,
        output_shape=(1, None, None),
        name='look_ahead_mask')(dec_inputs)
    # mask the encoder outputs for the 2nd attention block
    dec_padding_mask = tf.keras.layers.Lambda(
        create_padding_mask, output_shape=(1, 1, None),
        name='dec_padding_mask')(inputs)

    enc_outputs = encoder(
        vocab_size=vocab_size,
        num_layers=num_layers,
        units=units,
        d_model=d_model,
        num_heads=num_heads,
        dropout=dropout,
    )(inputs=[inputs, enc_padding_mask])

    dec_outputs = decoder(
        vocab_size=vocab_size,
        num_layers=num_layers,
        units=units,
        d_model=d_model,
        num_heads=num_heads,
        dropout=dropout,
    )(inputs=[dec_inputs, enc_outputs, look_ahead_mask, dec_padding_mask])

    outputs = tf.keras.layers.Dense(units=vocab_size, name="outputs")(dec_outputs)

    return tf.keras.Model(inputs=[inputs, dec_inputs], outputs=outputs, name=name)

我不明白为什么它会给出这个错误,因为模型训练得非常好。 任何帮助将不胜感激。

我的保存代码供参考:

print("Saving the model.")
saveloc = "C:/tmp/solar.h5"
model.save(saveloc)
print("Model saved to: " + saveloc + " succesfully.")

【问题讨论】:

我认为这已经随着 tensorflow 2.2.0 改变了......所以现在你不会遇到这个错误 【参考方案1】:

这不是错误,这是一项功能。

此错误让您知道 TF 无法保存您的模型,因为它无法加载它。 具体来说,它将无法重新实例化您的自定义 Layer 类:encoderdecoder

要解决这个问题,只需根据您添加的新参数覆盖他们的 get_config 方法即可。

层配置是包含层配置的 Python 字典(可序列化)。稍后可以从此配置中重新实例化同一层(没有经过训练的权重)。


例如,如果您的 encoder 类看起来像这样:

class encoder(tf.keras.layers.Layer):

    def __init__(
        self,
        vocab_size, num_layers, units, d_model, num_heads, dropout,
        **kwargs,
    ):
        super().__init__(**kwargs)
        self.vocab_size = vocab_size
        self.num_layers = num_layers
        self.units = units
        self.d_model = d_model
        self.num_heads = num_heads
        self.dropout = dropout

    # Other methods etc.

那么你只需要重写这个方法:

    def get_config(self):

        config = super().get_config().copy()
        config.update(
            'vocab_size': self.vocab_size,
            'num_layers': self.num_layers,
            'units': self.units,
            'd_model': self.d_model,
            'num_heads': self.num_heads,
            'dropout': self.dropout,
        )
        return config

当 TF 看到这一点时(对于两个类),您将能够保存模型。

因为现在加载模型时,TF 将能够从配置中重新实例化同一层。


Layer.from_config 的source code 可能会更好地了解它的工作原理:

@classmethod
def from_config(cls, config):
  return cls(**config)

【讨论】:

我会试试这个。这不应该默认使用类构造中的参数来完成吗? 这个可以已经完成了,但是考虑不是所有参数都需要保存的情况,只需要它们的导数就足够了。例如x_trainmeanstd。在这种情况下,该层可以接受x_train(mean, std) 来构造。在构建模型时,您将使用x_train 并保存self.mean, self.std,然后仅保存mean, stdconfig 以重新实例化,而不使用整个x_train 我没有很好地理解它。有什么资料可以阅读更多有关它的信息吗?另外,为什么我们要重写编码器层而不是编码器本身?另外,如果编码器层是一个函数而不是一个类呢? @Mee 1. 是的:tensorflow.org/guide/keras/… 2. 因为序列化模型的标准部件已经开箱即用。但是对于像 Encoder 和 Decoder 这样的自定义层,你需要定义它。 3. 我不确定,您可能想将此作为一个单独的问题提出。但是为什么不把你函数的逻辑放到一个 Layer 子类中呢? 感谢您的回复。我确实把它放在了一个单独的问题中。请你检查一下好吗? ***.com/questions/59796343/…【参考方案2】:

我认为简单的解决方案是为 gpu tensorflow-gpu==2.4.2 安装 tensorflow==2.4.2 ,我遇到了这个问题并调试了一整天,但没有解决。最后我安装了旧的稳定版本,错误消失了

【讨论】:

【参考方案3】:

这个问题是由于在 keras 和 tf.keras 库之间混合导入导致的,不支持。

在任何地方使用 tf.keras.models 或 usr keras.models

您不应该在这些库之间混合导入,因为它不起作用并会产生各种奇怪的错误消息。这些错误会随着 keras 和 tensorflow 的版本而变化。

【讨论】:

即使我的问题与 OP 不完全相同,我也得到了完全相同的错误。约翰的回答对我来说很神奇,当我写的时候让我的 VSCode 自动导入 Dropout:model.add(Dropout())。它导入它: from tensorflow.python.keras.layers.core import Dropout 并因此产生上述错误。正确的导入是: from tensorflow.keras.layers import Dropout【参考方案4】:

我建议您尝试以下方法:

model = tf.keras.Model(...)
model.save_weights("some_path")
...
model.load_weights("some_path")

【讨论】:

以上是关于NotImplementedError:在 `__init__` 中有参数的层必须覆盖 `get_config`的主要内容,如果未能解决你的问题,请参考以下文章

PyTorch NotImplementedError 转发

NotImplementedError:无法将符号张量 (lstm_4/strided_slice:0) 转换为 numpy 数组

Python之美[从菜鸟到高手]--NotImplemented小析

无法将符号张量 (lstm_15/strided_slice:0) 转换为 numpy 数组

Python NotImplementedError:无法在进程之间传递池对象

Python开发运维之路day8