web-dev-qa-db-fra.com

Keras: gouttes de précision lors du réglage initial

J'ai du mal à mettre au point un modèle Inception avec Keras. 

J'ai réussi à utiliser des tutoriels et de la documentation pour générer un modèle de couches supérieures entièrement connectées qui classe mon jeu de données dans les catégories appropriées avec une précision de plus de 99% à l'aide des fonctionnalités de goulot d'étranglement de Inception.

import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras import applications


# dimensions of our images.
img_width, img_height = 150, 150

#paths for saving weights and finding datasets
top_model_weights_path = 'Inception_fc_model_v0.h5'
train_data_dir = '../data/train2'
validation_data_dir = '../data/train2' 

#training related parameters?
inclusive_images = 1424
nb_train_samples = 1424
nb_validation_samples = 1424
epochs = 50
batch_size = 16


def save_bottlebeck_features():
    datagen = ImageDataGenerator(rescale=1. / 255)

    # build bottleneck features
    model = applications.inception_v3.InceptionV3(include_top=False, weights='imagenet', input_shape=(img_width,img_height,3))

    generator = datagen.flow_from_directory(
        train_data_dir,
        target_size=(img_width, img_height),
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    bottleneck_features_train = model.predict_generator(
        generator, nb_train_samples // batch_size)

    np.save('bottleneck_features_train', bottleneck_features_train)

    generator = datagen.flow_from_directory(
        validation_data_dir,
        target_size=(img_width, img_height),
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False)

    bottleneck_features_validation = model.predict_generator(
        generator, nb_validation_samples // batch_size)

    np.save('bottleneck_features_validation', bottleneck_features_validation)

def train_top_model():
    train_data = np.load('bottleneck_features_train.npy')
    train_labels = np.array(range(inclusive_images))

    validation_data = np.load('bottleneck_features_validation.npy')
    validation_labels = np.array(range(inclusive_images))

    print('base size ', train_data.shape[1:])

    model = Sequential()
    model.add(Flatten(input_shape=train_data.shape[1:]))
    model.add(Dense(1000, activation='relu'))
    model.add(Dense(inclusive_images, activation='softmax'))
    model.compile(loss='sparse_categorical_crossentropy',
             optimizer='Adam',
             metrics=['accuracy'])

    proceed = True

    #model.load_weights(top_model_weights_path)

    while proceed:
        history = model.fit(train_data, train_labels,
              epochs=epochs,
              batch_size=batch_size)#,
              #validation_data=(validation_data, validation_labels), verbose=1)
        if history.history['acc'][-1] > .99:
            proceed = False

    model.save_weights(top_model_weights_path)


save_bottlebeck_features()
train_top_model()

Epoque 50/50 1424/1424 [============================] - 17s 12ms/étape - perte: 0.0398 - acc: 0.9909

J'ai également pu empiler ce modèle sur le début pour créer mon modèle complet et utiliser ce modèle complet pour classifier avec succès mon ensemble d'entraînement.

from keras import Model
from keras import optimizers
from keras.callbacks import EarlyStopping

img_width, img_height = 150, 150

top_model_weights_path = 'Inception_fc_model_v0.h5'
train_data_dir = '../data/train2'
validation_data_dir = '../data/train2' 

#how many inclusive examples do we have?
inclusive_images = 1424
nb_train_samples = 1424
nb_validation_samples = 1424
epochs = 50
batch_size = 16

# build the complete network for evaluation
base_model = applications.inception_v3.InceptionV3(weights='imagenet', include_top=False, input_shape=(img_width,img_height,3))

top_model = Sequential()
top_model.add(Flatten(input_shape=base_model.output_shape[1:]))
top_model.add(Dense(1000, activation='relu'))
top_model.add(Dense(inclusive_images, activation='softmax'))

top_model.load_weights(top_model_weights_path)

#combine base and top model
fullModel = Model(input= base_model.input, output= top_model(base_model.output))

#predict with the full training dataset
results = fullModel.predict_generator(ImageDataGenerator(rescale=1. / 255).flow_from_directory(
        train_data_dir,
        target_size=(img_width, img_height),
        batch_size=batch_size,
        class_mode='categorical',
        shuffle=False))

l'inspection des résultats du traitement sur ce modèle complet correspond à la précision du modèle entièrement connecté généré par le goulot d'étranglement.

import matplotlib.pyplot as plt
import operator

#retrieve what the softmax based class assignments would be from results
resultMaxClassIDs = [ max(enumerate(result), key=operator.itemgetter(1))[0] for result in results]

#resultMaxClassIDs should be equal to range(inclusive_images) so we subtract the two and plot the log of the absolute value 
#looking for spikes that indicate the values aren't equal 
plt.plot([np.log(np.abs(x)+10) for x in (np.array(resultMaxClassIDs) - np.array(range(inclusive_images)))])

 results: spikes are misclassifications

Voici le problème: Lorsque je prends ce modèle complet pour tenter de le former, la précision tombe à 0 même si la validation reste supérieure à 99%. 

model2 = fullModel

for layer in model2.layers[:-2]:
    layer.trainable = False

# compile the model with a SGD/momentum optimizer
# and a very slow learning rate.
#model.compile(loss='binary_crossentropy', optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),  metrics=['accuracy'])

model2.compile(loss='categorical_crossentropy',
             optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), 
             metrics=['accuracy'])

train_datagen = ImageDataGenerator(rescale=1. / 255)

test_datagen = ImageDataGenerator(rescale=1. / 255)

train_generator = train_datagen.flow_from_directory(
    train_data_dir,
    target_size=(img_height, img_width),
    batch_size=batch_size,
    class_mode='categorical')

validation_generator = test_datagen.flow_from_directory(
    validation_data_dir,
    target_size=(img_height, img_width),
    batch_size=batch_size,
    class_mode='categorical')

callback = [EarlyStopping(monitor='acc', min_delta=0, patience=3, verbose=0, mode='auto', baseline=None)]
# fine-tune the model
model2.fit_generator(
    #train_generator,
    validation_generator,
    steps_per_Epoch=nb_train_samples//batch_size,
    validation_steps = nb_validation_samples//batch_size,
    epochs=epochs,
    validation_data=validation_generator)

Epoque 1/50 89/89 [============================] - 388s 4s/step - perte: 13.5787 - acc: 0.0000e + 00 - val_loss: 0.0353 - val_acc: 0.9937

et ça empire à mesure que les choses avancent

Epoque 21/50 89/89 [============================] - 372s 4s/step - perte: 7.3850 - acc: 0.0035 - val_loss : 0.5813 - val_acc: 0.8272

La seule chose à laquelle je pouvais penser, c’est que les étiquettes d’entraînement ont été mal assignées dans ce dernier train, mais j’ai réussi à le faire avec un code similaire en utilisant VGG16 auparavant.

J'ai parcouru le code en essayant de trouver une incohérence pour expliquer pourquoi un modèle qui fait des prédictions précises 99% du temps laisse tomber la précision de son entraînement tout en maintenant la précision de la validation lors du réglage, mais je ne peux pas le comprendre. Toute aide serait appréciée.

Informations sur le code et l'environnement:

Les choses qui vont paraître étranges, mais qui sont censées l'être:

  • Il n'y a qu'une image par classe. Ce NN est destiné à classer les objets Dont les conditions d’environnement et d’orientation sont contrôlées Il n’existe qu’une image acceptable pour chaque classe Correspondant à la bonne situation environnementale et de rotation.
  • Le test et la validation sont identiques. Ce NN n’est jamais conçu que pour être utilisé dans les cours sur lesquels il est formé. Les imagesil traitera seront des copies conformes des exemples de classe. C'est mon intention de sur-adapter le modèle à ces classes

J'utilise:

  • Windows 10 
  • Python 3.5.6 sous le client Anaconda 1.6.14 
  • Keras 2.2.2
  • Tensorflow 1.10.0 en tant que backend 
  • CUDA 9.0
  • CuDNN 8.0

J'ai vérifié:

  1. Différence de précision des keras dans un modèle ajusté
  2. Réglage fin des keras VGG16: faible précision
  3. Keras: la précision du modèle diminue après avoir atteint une précision de 99% et une perte de 0,01
  4. Keras inception v3, erreur de formation et d'optimisation
  5. Comment trouver quelle version de TensorFlow est installée sur mon système?

mais ils semblent sans rapport.

7

Remarque: Étant donné que votre problème est un peu étrange et difficile à déboguer sans votre modèle et votre jeu de données formés, cette réponse est juste une (meilleure) hypothèse, après avoir examiné de nombreux problèmes qui pourraient se produire. S'il vous plaît fournir vos commentaires et je vais supprimer cette réponse si cela ne fonctionne pas.

Puisque inception_V3 contient des couches BatchNormalization, le problème est peut-être dû au comportement (quelque peu ambigu ou inattendu) de cette couche lorsque vous définissez le paramètre trainable sur False ( 1 , 2 , 3 , 4 ).

Voyons maintenant s’il s’agit de la racine du problème: comme suggéré par @fchollet , définissez la phase d’apprentissage lors de la définition du modèle à ajuster:

from keras import backend as K

K.set_learning_phase(0)

base_model = applications.inception_v3.InceptionV3(weights='imagenet', include_top=False, input_shape=(img_width,img_height,3))

for layer in base_model.layers:
    layer.trainable = False

K.set_learning_phase(1)

top_model = Sequential()
top_model.add(Flatten(input_shape=base_model.output_shape[1:]))
top_model.add(Dense(1000, activation='relu'))
top_model.add(Dense(inclusive_images, activation='softmax'))

top_model.load_weights(top_model_weights_path)

#combine base and top model
fullModel = Model(input= base_model.input, output= top_model(base_model.output))

fullModel.compile(loss='categorical_crossentropy',
             optimizer=optimizers.SGD(lr=1e-4, momentum=0.9), 
             metrics=['accuracy'])


#####################################################################
# Here, define the generators and then fit the model same as before #
#####################################################################

Côté Remarque: Cela ne pose aucun problème dans votre cas, mais n'oubliez pas que lorsque vous utilisez top_model(base_model.output), le modèle séquentiel complet (c'est-à-dire top_model) est stocké sous la forme d'une couche de fullModel. Vous pouvez le vérifier en utilisant fullModel.summary() ou print(fullModel.layers[-1]). D'où quand vous avez utilisé:

for layer in model2.layers[:-2]:
    layer.trainable = False 

vous ne gèlez pas non plus la dernière couche de base_model. Toutefois, comme il s'agit d'une couche Concatenate et que, par conséquent, aucun paramètre ne peut être défini, aucun problème ne se produit et il se comporterait comme prévu.

5
today

Comme la réponse précédente, je vais essayer de partager quelques réflexions pour voir si cela aide.

Il y a un certain nombre de choses qui ont attiré mon attention (et qui méritent d’être passées en revue). Remarque: certains d’entre eux auraient également dû vous donner des problèmes avec les différents modèles.

  • Corrigez si je me trompe, mais il semble que vous ayez utilisé sparse_categorical_crossentropy pour le premier entraînement, alors que vous avez utilisé categorical_crossentropy pour le second. Est-ce correct? Parce que je pense qu'ils assument les étiquettes différemment (rares supposent des entiers et l'autre suppose un chaud).
  • Avez-vous essayé de définir les couches que vous avez finalement ajoutées à trainable = True? Je sais que vous avez déjà paramétré les autres sur trainable = False, mais c'est peut-être aussi une chose à vérifier.
  • Il semble que le générateur de données n'utilise pas la fonction de prétraitement par défaut utilisée dans Inception v3, qui utilise un canal par moyenne.
  • Avez-vous essayé des expériences utilisant l'API fonctionnel au lieu de l'API séquentielle?

J'espère que ça aide.

1
Thomas Paula