使用数据增强层在Tensorflow 2.7.0上保存模型

12

当我尝试使用Tensorflow版本2.7.0保存带有数据增强层的模型时,出现了错误。

这是数据增强的代码:

input_shape_rgb = (img_height, img_width, 3)
data_augmentation_rgb = tf.keras.Sequential(
  [ 
    layers.RandomFlip("horizontal"),
    layers.RandomFlip("vertical"),
    layers.RandomRotation(0.5),
    layers.RandomZoom(0.5),
    layers.RandomContrast(0.5),
    RandomColorDistortion(name='random_contrast_brightness/none'),
  ]
)

现在我建立模型的方式如下:

# Build the model
input_shape = (img_height, img_width, 3)

model = Sequential([
  layers.Input(input_shape),
  data_augmentation_rgb,
  layers.Rescaling((1./255)),

  layers.Conv2D(16, kernel_size, padding=padding, activation='relu', strides=1, 
     data_format='channels_last'),
  layers.MaxPooling2D(),
  layers.BatchNormalization(),

  layers.Conv2D(32, kernel_size, padding=padding, activation='relu'), # best 4
  layers.MaxPooling2D(),
  layers.BatchNormalization(),

  layers.Conv2D(64, kernel_size, padding=padding, activation='relu'), # best 3
  layers.MaxPooling2D(),
  layers.BatchNormalization(),

  layers.Conv2D(128, kernel_size, padding=padding, activation='relu'), # best 3
  layers.MaxPooling2D(),
  layers.BatchNormalization(),

  layers.Flatten(),
  layers.Dense(128, activation='relu'), # best 1
  layers.Dropout(0.1),
  layers.Dense(128, activation='relu'), # best 1
  layers.Dropout(0.1),
  layers.Dense(64, activation='relu'), # best 1
  layers.Dropout(0.1),
  layers.Dense(num_classes, activation = 'softmax')
 ])

 model.compile(loss='categorical_crossentropy', optimizer='adam',metrics=metrics)
 model.summary()

完成训练后,我只需执行:

model.save("./")

我遇到了如下错误:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-84-87d3f09f8bee> in <module>()
----> 1 model.save("./")


/usr/local/lib/python3.7/dist-packages/keras/utils/traceback_utils.py in 
 error_handler(*args, **kwargs)
 65     except Exception as e:  # pylint: disable=broad-except
 66       filtered_tb = _process_traceback_frames(e.__traceback__)
 ---> 67       raise e.with_traceback(filtered_tb) from None
 68     finally:
 69       del filtered_tb

 /usr/local/lib/python3.7/dist- 
 packages/tensorflow/python/saved_model/function_serialization.py in 
 serialize_concrete_function(concrete_function, node_ids, coder)
 66   except KeyError:
 67     raise KeyError(
 ---> 68         f"Failed to add concrete function '{concrete_function.name}' to 
 object-"
 69         f"based SavedModel as it captures tensor {capture!r} which is 
 unsupported"
 70         " or not reachable from root. "

 KeyError: "Failed to add concrete function 
 'b'__inference_sequential_46_layer_call_fn_662953'' to object-based SavedModel as it 
 captures tensor <tf.Tensor: shape=(), dtype=resource, value=<Resource Tensor>> which 
 is unsupported or not reachable from root. One reason could be that a stateful 
 object or a variable that the function depends on is not assigned to an attribute of 
 the serialized trackable object (see SaveTest.test_captures_unreachable_variable)."

通过更改模型的结构,我检查了出现此错误的原因,并发现原因来自于数据增强层,因为RandomFlipRandomRotation等已从layers.experimental.prepocessing.RandomFlip更改为layers.RandomFlip,但仍然出现错误。

2个回答

11
这似乎是Tensorflow 2.7中使用model.save并将默认参数save_format="tf"与其组合时的一个错误。由于它们不可序列化,因此图层RandomFlipRandomRotationRandomZoomRandomContrast会导致问题。有趣的是,Rescaling层可以轻松保存而没有任何问题。解决方法是简单地使用更旧的Keras H5格式保存模型:model.save("test", save_format='h5')
import tensorflow as tf
import numpy as np

class RandomColorDistortion(tf.keras.layers.Layer):
    def __init__(self, contrast_range=[0.5, 1.5], 
                 brightness_delta=[-0.2, 0.2], **kwargs):
        super(RandomColorDistortion, self).__init__(**kwargs)
        self.contrast_range = contrast_range
        self.brightness_delta = brightness_delta
    
    def call(self, images, training=None):
        if not training:
            return images
        contrast = np.random.uniform(
            self.contrast_range[0], self.contrast_range[1])
        brightness = np.random.uniform(
            self.brightness_delta[0], self.brightness_delta[1])
        
        images = tf.image.adjust_contrast(images, contrast)
        images = tf.image.adjust_brightness(images, brightness)
        images = tf.clip_by_value(images, 0, 1)
        return images
    
    def get_config(self):
        config = super(RandomColorDistortion, self).get_config()
        config.update({"contrast_range": self.contrast_range, "brightness_delta": self.brightness_delta})
        return config
        
input_shape_rgb = (256, 256, 3)
data_augmentation_rgb = tf.keras.Sequential(
  [ 
    tf.keras.layers.RandomFlip("horizontal"),
    tf.keras.layers.RandomFlip("vertical"),
    tf.keras.layers.RandomRotation(0.5),
    tf.keras.layers.RandomZoom(0.5),
    tf.keras.layers.RandomContrast(0.5),
    RandomColorDistortion(name='random_contrast_brightness/none'),
  ]
)
input_shape = (256, 256, 3)
padding = 'same'
kernel_size = 3
model = tf.keras.Sequential([
  tf.keras.layers.Input(input_shape),
  data_augmentation_rgb,
  tf.keras.layers.Rescaling((1./255)),
  tf.keras.layers.Conv2D(16, kernel_size, padding=padding, activation='relu', strides=1, 
     data_format='channels_last'),
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.BatchNormalization(),

  tf.keras.layers.Conv2D(32, kernel_size, padding=padding, activation='relu'), # best 4
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.BatchNormalization(),

  tf.keras.layers.Conv2D(64, kernel_size, padding=padding, activation='relu'), # best 3
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.BatchNormalization(),

  tf.keras.layers.Conv2D(128, kernel_size, padding=padding, activation='relu'), # best 3
  tf.keras.layers.MaxPooling2D(),
  tf.keras.layers.BatchNormalization(),

  tf.keras.layers.Flatten(),
  tf.keras.layers.Dense(128, activation='relu'), # best 1
  tf.keras.layers.Dropout(0.1),
  tf.keras.layers.Dense(128, activation='relu'), # best 1
  tf.keras.layers.Dropout(0.1),
  tf.keras.layers.Dense(64, activation='relu'), # best 1
  tf.keras.layers.Dropout(0.1),
  tf.keras.layers.Dense(5, activation = 'softmax')
 ])

model.compile(loss='categorical_crossentropy', optimizer='adam')
model.summary()
model.save("test", save_format='h5')

使用您自定义层加载模型的代码示例如下:

model = tf.keras.models.load_model('test.h5', custom_objects={'RandomColorDistortion': RandomColorDistortion})

其中RandomColorDistortion是您自定义层的名称。


1
尝试使用h5格式时,我遇到了另一种类型的NotImplementedError错误。 - moumed
1
你所遇到的错误与你最初的问题无关。如在 GitHub 中已经提到的,如果你计划稍后保存自定义层,则必须向其添加配置。可以参考此帖子:https://dev59.com/ElIG5IYBdhLWcg3w6mR8 或者查看我的答案中自定义层的配置。 - AloneTogether
啊是的,当然,我忘记了这个细节。但是我还有一个问题,我正在使用模型保存来稍后加载模型并将其转换为.tflite,但在尝试加载时,我遇到了与自定义层有关的错误,我会在github上创建错误,并让我们讨论一下。谢谢@AloneTogether。 - moumed
1
更新了答案,它有效吗? - AloneTogether
是的,我现在可以加载和转换模型了,谢谢@AloneTogether。 - moumed
1
在这样做时,我遇到了CustomMaskWarning警告,它似乎可以工作,但我希望它不会破坏模型。 - SashimiDélicieux

0

你也可以将Keras和Tensorflow降级到2.6版本。


1
这是我目前正在使用的解决方案,但我想使用最新的TF版本。 - moumed

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