TensorFlow加载模型时出错:AttributeError:调用Flatten.call()时遇到异常,'list'对象没有'shape'属性
我想用自己的数据训练一个VGG19模型来进行二分类。我的数据包括一些.jpg格式的图片,还有一个pandas数据框,里面有两列:一列是图片的文件名(id),另一列是标签(T1),标签的值是0或1。不过,在我成功训练并保存模型后,想要加载模型时遇到了错误。我该怎么解决这个问题呢?在GitHub的讨论中,自2020年以来就有人在处理这个问题,但我没有找到适合我情况的解决方案。我也尝试过将模型保存为.json和.h5格式,但都没有成功。
Python: 3.10
keras: 3.1.1
tensorflow: 2.16.2
OS: Ubuntu 22.04
关于模型的训练和保存:
import os
import pandas as pd
import tensorflow as tf
from keras import losses
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.optimizers import Adam
from keras.metrics import Precision, Recall
from keras.applications.vgg19 import VGG19
IMG_HEIGHT = 224
IMG_WIDTH = 224
MODELS_INPUT_SHAPE = (IMG_HEIGHT, IMG_WIDTH, 3)
NUM_CLASSES = 2
EPOCHS = 1
BATCH_SIZE = 32
train_dataset = pd.read_csv('data/train-data.csv')
train_images_location = 'data/train-images/'
#Auxiliar function to prepare the images.
def preprocess_image(image):
image = tf.io.read_file(image)
image = tf.image.decode_jpeg(image, channels=3)
image = tf.image.resize(image, [224, 224])
image = tf.cast(image, tf.float32) / 255.0 # Normalize pixel values to [0, 1]
return image
train_image_paths = [os.path.join('data/train-images', img_path) for img_path in train_dataset['id']]
one_hot_labels = tf.one_hot(train_dataset['T1'], NUM_CLASSES)
train_dataset = tf.data.Dataset.from_tensor_slices((train_image_paths, one_hot_labels))
train_dataset = train_dataset.map(lambda x, y: (preprocess_image(x), y))
train_dataset = train_dataset.batch(BATCH_SIZE)
models = {
'vgg19_binary.keras': VGG19(weights='imagenet', include_top=False, input_shape=MODELS_INPUT_SHAPE)
}
for model_name in models.keys():
base_model = models[model_name]
for layer in base_model.layers:
layer.trainable = False
model = Sequential()
model.add(base_model)
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dense(128, activation='relu'))
model.add(Dense(64, activation='relu'))
model.add(Dense(32, activation='relu'))
model.add(Dense(16, activation='relu'))
model.add(Dense(NUM_CLASSES, activation='sigmoid'))
model.compile(loss=losses.BinaryCrossentropy(),
optimizer=Adam(learning_rate=0.0001),
metrics=
['accuracy',
Precision(),
Recall()])
model.fit(train_dataset, epochs=EPOCHS)
# Evaluate the model on the testing data
model_location = 'models/'+model_name
model.save(model_location)
关于模型的加载:
from keras.models import load_model
vgg19_model_location = 'models/vgg19_binary.keras'
vgg19_model = load_model(vgg19_model_location)
以及完整的错误信息:
Traceback (most recent call last):
File "/home/javier/PycharmProjects/thesis/thesis/binary_early_fusion.py", line 15, in <module>
loaded_model = model_from_json(loaded_model_json)
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/models/model.py", line 586, in model_from_json
return serialization_lib.deserialize_keras_object(
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/saving/serialization_lib.py", line 711, in deserialize_keras_object
instance = cls.from_config(inner_config)
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/models/sequential.py", line 335, in from_config
model.add(layer)
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/models/sequential.py", line 116, in add
self._maybe_rebuild()
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/models/sequential.py", line 135, in _maybe_rebuild
self.build(input_shape)
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/layers/layer.py", line 223, in build_wrapper
original_build_method(*args, **kwargs)
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/models/sequential.py", line 176, in build
x = layer(x)
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/utils/traceback_utils.py", line 122, in error_handler
raise e.with_traceback(filtered_tb) from None
File "/home/javier/PycharmProjects/thesis/venv/lib/python3.10/site-packages/keras/src/layers/reshaping/flatten.py", line 72, in compute_output_spec
output_shape = self.compute_output_shape(inputs.shape)
AttributeError: Exception encountered when calling Flatten.call().
'list' object has no attribute 'shape'
Arguments received by Flatten.call():
• args=(['<KerasTensor shape=(None, 7, 7, 512), dtype=float32, sparse=False, name=keras_tensor_70>'],)
• kwargs=<class 'inspect._empty'>
2 个回答
1
Flatten() 这个函数是用来处理单个张量的,而你似乎给它传了一个列表。这就是为什么它会说在列表上找不到 ".shape" 的原因。
要解决这个问题,你需要检查一下你模型的形状。
你现在有这个(但要加上最后两行):
for layer in base_model.layers:
layer.trainable = False
layer_output = layer.output
print(layer.name, layer_output.shape)
看看形状,确保层的输出符合你模型的输入要求。
0
为了修复这个问题,我不得不重写代码并使用其他方法:
我改用了image_dataset_from_directory这个方法来获取图片和它们的标签,并把它们放进一个数据集中。最后,我还改变了构建模型的方式。现在我一开始就指定了模型的类型,然后再添加各个层。
不幸的是,我不知道为什么这些改变有效(我承认我在这个话题上不太懂)。希望这个回答能对你有所帮助。
import os
import numpy as np
import pandas as pd
import tensorflow as tf
from keras import losses
from keras.models import Model
from keras.models import Sequential
from keras.utils import image_dataset_from_directory
from keras.layers import Dense, Dropout, Flatten, Rescaling, Input
from keras.optimizers import Adam
from keras.metrics import Precision, Recall
from keras.applications.vgg19 import VGG19
from keras.applications.inception_v3 import InceptionV3
from keras.applications.resnet50 import ResNet50
from keras.applications.efficientnet_v2 import EfficientNetV2S
IMG_HEIGHT = 224
IMG_WIDTH = 224
MODELS_INPUT_SHAPE = (IMG_HEIGHT, IMG_WIDTH, 3)
NUM_CLASSES = 2
EPOCHS = 1
BATCH_SIZE = 32
train_dataframe = pd.read_csv('data/train-data.csv')
train_images_location = 'data/train-images/'
labels = []
#image_dataset_from_directory uses os.walk(directory) to obtain the filenames. labels then have to be sorted according to that, otherwise they get scrambled.
for root, directories, images in os.walk(train_images_location):
for image in images:
labels.append(int(train_dataframe[train_dataframe['id'] == image]['T1']))
labels = list(np.array(labels, dtype=np.float32))
train_dataset = image_dataset_from_directory(
train_images_location,
labels=labels,
label_mode="binary",
class_names=None,
color_mode='rgb',
batch_size=BATCH_SIZE,
image_size=(IMG_HEIGHT, IMG_HEIGHT),
shuffle=True,
)
models = {
'vgg19_binary.keras': VGG19(weights='imagenet', include_top=False, input_shape=MODELS_INPUT_SHAPE)
#'inceptionv3_binary.keras': InceptionV3(include_top=False, input_shape=MODELS_INPUT_SHAPE),
#'resnet50_binary.keras': ResNet50(include_top=False, input_shape=MODELS_INPUT_SHAPE),
#'efficientnetv2s_binary.keras': EfficientNetV2S(include_top=False, input_shape=MODELS_INPUT_SHAPE)
}
for model_name in models.keys():
base_model = models[model_name]
for layer in base_model.layers:
layer.trainable = False
img_input = Input(shape=(IMG_HEIGHT, IMG_WIDTH, 3))
x = Rescaling(1. / 255)(img_input)
x = base_model(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
x = Dense(128, activation='relu')(x)
x = Dense(64, activation='relu')(x)
x = Dense(32, activation='relu')(x)
x = Dense(16, activation='relu')(x)
x = Dense(8, activation='relu')(x)
x = Dense(1, activation='sigmoid')(x)
model = Model(img_input, x, name='vgg19_new_trained')
model.compile(loss=losses.BinaryCrossentropy(),
optimizer=Adam(learning_rate=0.0001),
metrics=
['accuracy',
Precision(),
Recall()])
model.fit(train_dataset, epochs=EPOCHS, batch_size=BATCH_SIZE)
model_location = 'models/'+model_name
model.save(model_location)