使用 Python3.x Tensorflow2 加载数据(尺寸错误)后使用预训练的 MobileNet 时出错
Error when using a pretrained MobileNet after loading datas (dimension error) with Python3.x Tensorflow2
我的数据集是一个训练数据文件夹和一个验证数据文件夹。
在每个文件夹(train 和 valid)中都有 .npy 文件。每个文件是(s,256,256,3)(卷中的s帧,3个通道,256个高宽相同)。每个文件都有一个带有标签的 CSV 文件。
这是我加载数据的代码:
train_df = pd.read_csv(os.path.join(WORK_DIR,
"train.csv"), names=['case', 'abnormal'],
dtype = {'case' : str, 'abnormal' : np.int64})
train_image = []
for i in tqdm(range(train_df.shape[0])):
vol = np.load(os.path.join(WORK_DIR, train_folder, train_df[img_col][i]))
train_image.append(vol)
X_train = np.array(train_image)
y_train= np.array(train_df.drop([img_col],axis=1))
train_loader = tf.data.Dataset.from_tensor_slices((X_train, y_train))
train_dataset = (train_loader.shuffle(X_train.shape[0]))
第一个问题:可以吗?
想法是使用迁移学习对数据进行分类(正常还是异常?)我使用的是Tensorflow 2.4。所以我决定使用预训练模型来提取特征,然后添加一个 2D 全局平均池化层、dropout,然后是密集层。这是我的代码:
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
base_model = tf.keras.applications.MobileNetV2(include_top=False, input_tensor=tf.keras.Input(batch_input_shape=(32,256,256, 3)),
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
max_pool_layer = tf.keras.layers.GlobalMaxPool2D()
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')
inputs = tf.keras.Input((256,256,3), batch_size=32)
x = base_model(inputs, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
history = model.fit(train_dataset,
epochs=initial_epochs,
validation_data=validation_dataset, batch_size=32)
我的错误在哪里?
这是我的问题:
ValueError: Input 0 is incompatible with layer model: expected shape=(None, 256, 256, 3),
found shape=(None, 32, 256, 256, 3)
尝试更改行中的 batch_input_shape
:
base_model = tf.keras.applications.MobileNetV2(include_top=False, input_tensor=tf.keras.Input(batch_input_shape=(32,256,256, 3)),
weights='imagenet')
通过这样做:
base_model._layers[0].batch_input_shape = (None, 256, 256, 3)
有关详细信息:请查看此 link
以下是一些可能对您有所帮助的提示。当您构建数据集时,对于图像数据,您应该通过绘制其中的一些样本来进行检查。例如:
train_loader = tf.data.Dataset.from_tensor_slices((X_train, y_train))
for i, (x,y) in train_loader:
if i == 1: break
print(x.shape, y.shape)
plt.imshow(x[i])
plt.title(y[i])
在构建模型时,绘制模型以查看其构建是否正确实际上是一个很好的做法。如果可能的话,传递一些虚拟样本以确保一切正常。
# define layer
base_model = tf.keras.applications.MobileNetV2(include_top=False,
input_shape=(256,256,3),
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')
# adding up
inputs = tf.keras.Input((256,256,3), batch_size=32)
x = base_model(inputs, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
# build all
model = tf.keras.Model(inputs, outputs)
现在,您可以传递一些虚拟样本并绘制您的模型。
model(tf.ones((1, 256, 256, 3))) # pass one dummy samples
tf.keras.utils.plot_model(model) # to plot
是的,在您想要使用预训练模型(已经在 imagenet 等大型数据集上训练过)的情况下,迁移学习的想法是可行的。但是,应该注意它是特定于您的任务的。使用预训练模型(使用 imagenet 权重)对医学图像(X 射线、断层扫描等)进行分类并不是一个好主意,因为它确实是特定的分类任务。但是,您可以开始添加一个带有 n_outputs 的分类层(取决于 类 的数量)并且只训练额外的层。
如果你想让模型更适合你的任务(你的数据集),你可以使用微调,这也是迁移学习,但你也在训练你的基础模型的一些层(看到你正在设置 base_model.trainable = False 并且只更新分类层的权重)。但是,在这种情况下,您还需要将 trainable = True 设置为 base_model 层的一部分(通常是最后一层),因为 CNN 模型的第一层通常学习图像的一般特征,而且可以肯定的是base_model 学习的权重适用于您的图像。
您还需要先训练您的分类层(就像您拥有的那样)。然后解冻 base_model 最后一层的一部分并重新训练。从 tensorflow here 查看本指南,您可以在其中找到相关示例。
试试这个:
我假设 X_train 的形式是:[[0.2,0.3,0.001,0.3,..],[0.02,0.4,...],[...]..]
而 y_train 的形式是:[[0],[1]..[]]
对吗?并且还根据preprocess_input函数归一化。
让我们放弃这两个 numpy 数组,这是您的训练数据。
base_model = tf.keras.applications.MobileNetV2(include_top=False, input_shape=(256,256,3),
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
max_pool_layer = tf.keras.layers.GlobalMaxPool2D() # I dont see where this is used
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')
x = base_model.output
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
classification_layer = prediction_layer(x)
model = tf.keras.Model(inputs=base_model.input, outputs=classification_layer)
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
# I added ImageDataGenerator instead
train_generator = ImageDataGenerator(validation_split = 0.2).flow(X_train,
y_train,
batch_size = 32,
subset = 'training')
val_generator = ImageDataGenerator(validation_split=0.2).flow(X_train,
y_train,
batch_size = 32,
subset = 'validation')
history = model.fit(train_generator, epochs = initial_epochs, validation_data= val_generator)
我的数据集是一个训练数据文件夹和一个验证数据文件夹。 在每个文件夹(train 和 valid)中都有 .npy 文件。每个文件是(s,256,256,3)(卷中的s帧,3个通道,256个高宽相同)。每个文件都有一个带有标签的 CSV 文件。
这是我加载数据的代码:
train_df = pd.read_csv(os.path.join(WORK_DIR,
"train.csv"), names=['case', 'abnormal'],
dtype = {'case' : str, 'abnormal' : np.int64})
train_image = []
for i in tqdm(range(train_df.shape[0])):
vol = np.load(os.path.join(WORK_DIR, train_folder, train_df[img_col][i]))
train_image.append(vol)
X_train = np.array(train_image)
y_train= np.array(train_df.drop([img_col],axis=1))
train_loader = tf.data.Dataset.from_tensor_slices((X_train, y_train))
train_dataset = (train_loader.shuffle(X_train.shape[0]))
第一个问题:可以吗?
想法是使用迁移学习对数据进行分类(正常还是异常?)我使用的是Tensorflow 2.4。所以我决定使用预训练模型来提取特征,然后添加一个 2D 全局平均池化层、dropout,然后是密集层。这是我的代码:
preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input
base_model = tf.keras.applications.MobileNetV2(include_top=False, input_tensor=tf.keras.Input(batch_input_shape=(32,256,256, 3)),
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
max_pool_layer = tf.keras.layers.GlobalMaxPool2D()
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')
inputs = tf.keras.Input((256,256,3), batch_size=32)
x = base_model(inputs, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
history = model.fit(train_dataset,
epochs=initial_epochs,
validation_data=validation_dataset, batch_size=32)
我的错误在哪里?
这是我的问题:
ValueError: Input 0 is incompatible with layer model: expected shape=(None, 256, 256, 3),
found shape=(None, 32, 256, 256, 3)
尝试更改行中的 batch_input_shape
:
base_model = tf.keras.applications.MobileNetV2(include_top=False, input_tensor=tf.keras.Input(batch_input_shape=(32,256,256, 3)),
weights='imagenet')
通过这样做:
base_model._layers[0].batch_input_shape = (None, 256, 256, 3)
有关详细信息:请查看此 link
以下是一些可能对您有所帮助的提示。当您构建数据集时,对于图像数据,您应该通过绘制其中的一些样本来进行检查。例如:
train_loader = tf.data.Dataset.from_tensor_slices((X_train, y_train))
for i, (x,y) in train_loader:
if i == 1: break
print(x.shape, y.shape)
plt.imshow(x[i])
plt.title(y[i])
在构建模型时,绘制模型以查看其构建是否正确实际上是一个很好的做法。如果可能的话,传递一些虚拟样本以确保一切正常。
# define layer
base_model = tf.keras.applications.MobileNetV2(include_top=False,
input_shape=(256,256,3),
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')
# adding up
inputs = tf.keras.Input((256,256,3), batch_size=32)
x = base_model(inputs, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
# build all
model = tf.keras.Model(inputs, outputs)
现在,您可以传递一些虚拟样本并绘制您的模型。
model(tf.ones((1, 256, 256, 3))) # pass one dummy samples
tf.keras.utils.plot_model(model) # to plot
是的,在您想要使用预训练模型(已经在 imagenet 等大型数据集上训练过)的情况下,迁移学习的想法是可行的。但是,应该注意它是特定于您的任务的。使用预训练模型(使用 imagenet 权重)对医学图像(X 射线、断层扫描等)进行分类并不是一个好主意,因为它确实是特定的分类任务。但是,您可以开始添加一个带有 n_outputs 的分类层(取决于 类 的数量)并且只训练额外的层。
如果你想让模型更适合你的任务(你的数据集),你可以使用微调,这也是迁移学习,但你也在训练你的基础模型的一些层(看到你正在设置 base_model.trainable = False 并且只更新分类层的权重)。但是,在这种情况下,您还需要将 trainable = True 设置为 base_model 层的一部分(通常是最后一层),因为 CNN 模型的第一层通常学习图像的一般特征,而且可以肯定的是base_model 学习的权重适用于您的图像。
您还需要先训练您的分类层(就像您拥有的那样)。然后解冻 base_model 最后一层的一部分并重新训练。从 tensorflow here 查看本指南,您可以在其中找到相关示例。
试试这个:
我假设 X_train 的形式是:[[0.2,0.3,0.001,0.3,..],[0.02,0.4,...],[...]..]
而 y_train 的形式是:[[0],[1]..[]]
对吗?并且还根据preprocess_input函数归一化。
让我们放弃这两个 numpy 数组,这是您的训练数据。
base_model = tf.keras.applications.MobileNetV2(include_top=False, input_shape=(256,256,3),
weights='imagenet')
base_model.trainable = False
global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
max_pool_layer = tf.keras.layers.GlobalMaxPool2D() # I dont see where this is used
prediction_layer = tf.keras.layers.Dense(1, activation='sigmoid')
x = base_model.output
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
classification_layer = prediction_layer(x)
model = tf.keras.Model(inputs=base_model.input, outputs=classification_layer)
base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(lr=base_learning_rate),
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
# I added ImageDataGenerator instead
train_generator = ImageDataGenerator(validation_split = 0.2).flow(X_train,
y_train,
batch_size = 32,
subset = 'training')
val_generator = ImageDataGenerator(validation_split=0.2).flow(X_train,
y_train,
batch_size = 32,
subset = 'validation')
history = model.fit(train_generator, epochs = initial_epochs, validation_data= val_generator)