值错误:未知层:Functional

24

我在Colab中创建了一个CNN,并在每个epoch保存了模型。 我导出了h5文件,现在正在尝试对一些测试图像运行该模型。 这是主要错误:

ValueError: Unknown layer: Functional

这是我用来运行模型并在每个时期保存的代码:

epochs = 50

callbacks = [
    tf.keras.callbacks.TensorBoard(log_dir='./logs'),
    keras.callbacks.ModelCheckpoint("save_at_{epoch}.h5"),
]
model.compile(
    optimizer=keras.optimizers.Adam(1e-3),
    loss="binary_crossentropy",
    metrics=["accuracy"],
)
model.fit(
    train_ds, epochs=epochs, callbacks=callbacks, validation_data=val_ds,
)

在模型运行完后,我只是在Colab边栏中将h5文件下载到本地。然后我从本地磁盘重新上传了该文件,并尝试着如下加载模型:

在模型运行完后,我只需从Colab侧边栏上将h5文件下载到本地。然后我将文件从本地磁盘重新上传,这里是我尝试加载模型的方法:

# load and evaluate a saved model
from tensorflow.keras.models import load_model

# load model#
loaded_model = load_model('save_at_47.h5')
loaded_model.layers[0].input_shape

以下是完整的跟踪信息:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-4-6af7396280fa> in <module>()
      3 
      4 # load model#
----> 5 loaded_model = load_model('save_at_47.h5')
      6 loaded_model.layers[0].input_shape

5 frames
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/saving/save.py in load_model(filepath, custom_objects, compile)
    182     if (h5py is not None and (
    183         isinstance(filepath, h5py.File) or h5py.is_hdf5(filepath))):
--> 184       return hdf5_format.load_model_from_hdf5(filepath, custom_objects, compile)
    185 
    186     if sys.version_info >= (3, 4) and isinstance(filepath, pathlib.Path):

/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/saving/hdf5_format.py in load_model_from_hdf5(filepath, custom_objects, compile)
    176     model_config = json.loads(model_config.decode('utf-8'))
    177     model = model_config_lib.model_from_config(model_config,
--> 178                                                custom_objects=custom_objects)
    179 
    180     # set weights

/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/saving/model_config.py in model_from_config(config, custom_objects)
     53                     '`Sequential.from_config(config)`?')
     54   from tensorflow.python.keras.layers import deserialize  # pylint: disable=g-import-not-at-top
---> 55   return deserialize(config, custom_objects=custom_objects)
     56 
     57 

/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/layers/serialization.py in deserialize(config, custom_objects)
    107       module_objects=globs,
    108       custom_objects=custom_objects,
--> 109       printable_module_name='layer')

/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/utils/generic_utils.py in deserialize_keras_object(identifier, module_objects, custom_objects, printable_module_name)
    360     config = identifier
    361     (cls, cls_config) = class_and_config_for_serialized_keras_object(
--> 362         config, module_objects, custom_objects, printable_module_name)
    363 
    364     if hasattr(cls, 'from_config'):

/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/utils/generic_utils.py in class_and_config_for_serialized_keras_object(config, module_objects, custom_objects, printable_module_name)
    319   cls = get_registered_object(class_name, custom_objects, module_objects)
    320   if cls is None:
--> 321     raise ValueError('Unknown ' + printable_module_name + ': ' + class_name)
    322 
    323   cls_config = config['config']

ValueError: Unknown layer: Functional

看起来这里有几个类似的问题,以及这里。更改导入方法尚未产生帮助,尝试制作某种自定义对象也没有起效。


请问您能提供用于保存模型的代码吗? - Reza Behzadpour
你的错误中所提到的“Functional”层是什么?它是你自己创建的自定义层吗? - Dr. Snoopy
嗨 @Dr. Snoopy,我试图弄清楚同样的事情,我从Keras Image classification from scratch文档中得到了大部分代码。 - LobstaBoy
2
简单易懂。只需要运行包含模型定义和编译的代码单元,然后按照我之前告诉你的方式将权重加载到这个空白模型中即可。 - Reza Behzadpour
太简单了,轻松搞定!感谢 @Reza Behzadpour! - LobstaBoy
显示剩余3条评论
7个回答

21

这个错误的解决方案非常简单,例如原因是你在版本为“2.3.0”的TensorFlow和“2.4.3”的Keras上训练了模型(在Colab或本地)。现在你正在使用另一个版本的Keras和TensorFlow访问保存的模型(.h5),会导致错误。解决方案是使用升级后的版本重新训练模型,或者将TF和Keras降级到与模型训练时相同的版本。


2
请问一下,您是如何知道错误发生在特定的“2.3.0”版本上的? - Van Teo Le
1
“functional”层在2.3.0版本中是新的吗? - Van Teo Le

9

从零开始重新构建网络:

image_size = (212, 212)
batch_size = 32

data_augmentation = keras.Sequential(
    [
        layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
        layers.experimental.preprocessing.RandomRotation(0.8),
    ]
)


def make_model(input_shape, num_classes):
    inputs = keras.Input(shape=input_shape)
    # Image augmentation block
    x = data_augmentation(inputs)

    # Entry block
    x = layers.experimental.preprocessing.Rescaling(1.0 / 255)(x)
    x = layers.Conv2D(32, 3, strides=2, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    x = layers.Conv2D(64, 3, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    previous_block_activation = x  # Set aside residual

    for size in [128, 256, 512, 728]:
        x = layers.Activation("relu")(x)
        x = layers.SeparableConv2D(size, 3, padding="same")(x)
        x = layers.BatchNormalization()(x)

        x = layers.Activation("relu")(x)
        x = layers.SeparableConv2D(size, 3, padding="same")(x)
        x = layers.BatchNormalization()(x)

        x = layers.MaxPooling2D(3, strides=2, padding="same")(x)

        # Project residual
        residual = layers.Conv2D(size, 1, strides=2, padding="same")(
            previous_block_activation
        )
        x = layers.add([x, residual])  # Add back residual
        previous_block_activation = x  # Set aside next residual

    x = layers.SeparableConv2D(1024, 3, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    x = layers.GlobalAveragePooling2D()(x)
    if num_classes == 2:
        activation = "sigmoid"
        units = 1
    else:
        activation = "softmax"
        units = num_classes

    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(units, activation=activation)(x)
    return keras.Model(inputs, outputs)


model = make_model(input_shape=image_size + (3,), num_classes=2)
keras.utils.plot_model(model, show_shapes=False)

已加载权重:

model.load_weights('save_at_47.h5')

并对一张图片进行了预测:

# Running inference on new data
img = keras.preprocessing.image.load_img(
    "le_image.jpg", target_size=image_size
)
img_array = keras.preprocessing.image.img_to_array(img)
img_array = tf.expand_dims(img_array, 0)  # Create batch axis

predictions = model.predict(img_array)
score = predictions[0]
print(
    "This image is %.2f percent negative and %.2f percent positive."
    % (100 * (1 - score), 100 * score)
)

1
这是Colab特有的问题吗?我也遇到了同样的错误。 - Abhishek Singh
你救了我的命!;P - diegodsp
1
@LobstaBoy 这个解决方案并不完全正确。你抱怨load_model('save_at_47.h5')无法正常工作,而在答案中你建议使用load_weights代替。那么,如果我想让 load_model 正常工作,该怎么办呢? - VM_AI
@VM_AI,看起来存在某种版本兼容性问题,需要不断切换版本才能加载模型,这似乎比将权重加载到模型中更加麻烦。我甚至不确定为什么会出现错误,因为我是在同一Colab环境中保存和加载模型的。 - LobstaBoy
@LobstaBoy 我认为加载权重也有缺陷,因为当你使用 model.load_weights(filename) 加载权重并重新训练模型时,错误应该从最近的检查点开始。但实际上,它从第一个 epoch 开始的错误开始。我仍在寻找如何正确保存和加载权重/模型的解决方案。 - VM_AI

5
以下技巧可以在您使用较旧的 TF(在我的情况下是2.1.0)并尝试解压由新版本TF(例如2.4.1)打包的h5文件时,有助于应对此错误。
your_model = tf.keras.models.load_model(
    path_to_h5,
    custom_objects={'Functional':tf.keras.models.Model})

4

我在使用 TensorFlow 2.3.0 时遇到了同样的问题,后来我降级到了 TensorFlow 2.2.0,问题得以解决。


我正在使用带有tf的CoLab,因此每次打开工作区时都可以降级版本,但是过一段时间后可能会变得繁琐。不过还是很好知道,谢谢! - LobstaBoy

3

我在colab上使用tf2.3训练模型,但在本地机器上使用tf2.2加载时遇到了相同的问题。解决方法是使用以下命令升级TensorFlow:

pip3 install --upgrade tensorflow

3

这里的讨论为我省去了些麻烦!我分享一个类似情况的不同解决方案,在该解决方案中,模型序列化是手动完成的(而不是通过fit方法),使用模型结构(yaml)和h5权重文件。在这种情况下,简单地编辑yaml文件就足以使序列化模型适应旧版本的tensorflow。

在我的情况下,我是这样生成模型的

    model_config = { "class_name": "Model",
                     "config":model.get_config()
                     }

    with open(os.path.join(dump_model_dir, "model_config.yaml"), "w") as file:
        file.write(model.to_yaml())

    model.save_weights(os.path.join(dump_model_dir, "model_weights.hdf5"))

使用TF2.2和2.3处理此问题会发现yaml文件有微小的更改。以下是仅具有Conv2D层的模型结构的两个版本之间的差异。它可以手动或使用sed轻松修复:

需要将class_name: Functional更改为class_name: Model,并删除在Conv2D和其他Conv层中新出现的groups: 1配置项。为了保持一致性,我还更改了最后的keras版本。显然,这仅适用于问题模型的组参数具有默认值1的情况。如果您没有使用yaml文件而是使用json文件,则应使用类似的解决方案。

*** model_config_tf22.yaml  2021-01-07 15:00:03.042791215 +0100
--- model_config_tf23.yaml  2021-01-07 14:59:56.426791386 +0100
***************
*** 1,5 ****
  backend: tensorflow
! class_name: Model
  config:
    input_layers:
    - - input_1
--- 1,5 ----
  backend: tensorflow
! class_name: Functional
  config:
    input_layers:
    - - input_1
***************
*** 34,39 ****
--- 34,40 ----
        - 1
        dtype: float32
        filters: 128
+       groups: 1
        kernel_constraint: null
        kernel_initializer:
          class_name: RandomUniform
***************
*** 343,346 ****
    - - Conv2D_5_37
      - 0
      - 0
! keras_version: 2.3.0-tf
--- 351,354 ----
    - - Conv2D_5_37
      - 0
      - 0
! keras_version: 2.4.0

你在哪里进行了这些更改?是在定义Keras模型的Python代码中吗? - stackoverflowuser2010
不好意思,如果之前表述不够清晰。这个解决方案解决的是与原问题相同的问题,即在使用yaml或json格式的模型结构文件和权重文件进行序列化的情况下。我编辑了解决方案,希望能澄清这一点。 - A Roebel
非常感谢,你帮了我很多。 - Vadim Plăcintă

0

你应该尝试安装和升级:

!pip install --upgrade tensorflow
!pip install keras_efficientnet_v2
!pip install efficientnet

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