Précision de validation Keras ImageDataGenerator faible
J'essaie de préformer l'apprentissage par transfert avec mobilenetv2 pour classer 196 classes de voitures à partir de l'ensemble de données cars-196 de Stanford.
Mon environnement de travail est le notebook google colab. J'utilise ImageDataGenerator de keras pour charger les images pour le train et la validation. Sur les images d'entraînement, j'effectue également une augmentation des données.
Le code suivant explique comment je l'exécute:
# To load the dataset from the drive
from google.colab import drive
drive.mount('/content/drive')
import math
from keras.models import Sequential, Model
from keras.layers import Dense, Conv2D, MaxPooling2D, Flatten, Dropout, ReLU, GlobalAveragePooling2D
from keras.preprocessing.image import ImageDataGenerator
from keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input
BATCH_SIZE = 196
train_datagen = ImageDataGenerator(
rotation_range=20, # Rotate the augmented image by 20 degrees
zoom_range=0.2, # Zoom by 20% more or less
horizontal_flip=True, # Allow for horizontal flips of augmented images
brightness_range=[0.8, 1.2], # Lighter and darker images by 20%
width_shift_range=0.1,
height_shift_range=0.1,
preprocessing_function=preprocess_input
)
img_data_iterator = train_datagen.flow_from_directory(
# Where to take the data from, the classes are the sub folder names
'/content/drive/My Drive/Datasets/cars-196/car_data/train',
class_mode="categorical", # classes are in 2D one hot encoded way, default is true but just to point it out
shuffle=True, # shuffle the data, default is true but just to point it out
batch_size=BATCH_SIZE,
target_size=(224, 224) # This size is the default of mobilenet NN
)
validation_img_data_iterator = ImageDataGenerator().flow_from_directory(
'/content/drive/My Drive/Datasets/cars-196/car_data/test',
class_mode="categorical",
shuffle=True,
batch_size=BATCH_SIZE,
target_size=(224, 224)
)
base_model = MobileNetV2(weights='imagenet', include_top=False)
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x)
preds = Dense(196, activation='softmax')(x)
model = Model(inputs=base_model.input, outputs=preds)
# Disable training of already trained layer
for layer in model.layers[:-3]:
layer.trainable = False
model.compile(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy'])
# define the checkpoint
from keras.callbacks import ModelCheckpoint
filepath = "/content/drive/My Drive/Datasets/cars-196/model.h5"
checkpoint = ModelCheckpoint(filepath, monitor='loss', verbose=1, save_best_only=True, mode='min')
callbacks_list = [checkpoint]
history = model.fit(
img_data_iterator,
steps_per_epoch=math.ceil(8144/BATCH_SIZE), # 8144 is the number of training images
validation_steps=math.ceil(8062/BATCH_SIZE), # 8062 is the number of validation images
validation_data=validation_img_data_iterator,
epochs=100,
callbacks=callbacks_list
)
À propos de la taille du lot, à partir de cette question de stackoverflow, j'ai décidé de définir la taille du lot comme le nombre d'étiquettes disponibles, mais cela n'a rien changé en termes de val_accuracy.
J'ai ajouté un abandon de 0,5 entre les couches entièrement connectées que j'ai ajoutées, mais encore une fois, aucun changement dans la précision de la validation.
Ma précision sur l'ensemble de formation arrive à environ 92% tandis que la précision de validation reste à environ 0,7%.
Je suppose que ImageDataGenerator agit de manière étrange et fausse la précision, mais je n'ai trouvé aucune solution au problème, donc ATM je n'ai pas d'écoute sur la raison derrière cela.
Quelqu'un a-t-il des suppositions sur ce qui pourrait être le problème?
----- ÉDITER
Le dossier train et test ont tous des sous-dossiers avec le nom des étiquettes (les différentes voitures que je veux identifier) et chaque sous-dossier contient des images de cette voiture. C'est ainsi que se présente le jeu de données cars-196. ImageDataGenerator attache la bonne étiquette à l'image, en fonction du sous-dossier dans lequel se trouvait l'image.
Réponses
Le problème était que je n'appliquais pas la preprocess_inputfonction au générateur d'images de données de validation.
À la place de
validation_img_data_iterator = ImageDataGenerator().flow_from_directory(
'/content/drive/My Drive/Datasets/cars-196/car_data/test',
class_mode="categorical",
shuffle=True,
batch_size=BATCH_SIZE,
target_size=(224, 224)
)
Je l'ai changé en
validation_img_data_iterator = ImageDataGenerator(
preprocessing_function=preprocess_input
).flow_from_directory(
'/content/drive/My Drive/Datasets/cars-196/car_data/test',
class_mode="categorical",
shuffle=True,
batch_size=BATCH_SIZE,
target_size=(224, 224)
)