Классификация изображений Keras: показана высокая точность, но на тестовых изображениях низкая - PullRequest
3 голосов
/ 11 октября 2019

Я пытаюсь сделать некоторую классификацию изображений в наборе данных Caltech101. Я использовал несколько предварительно обученных моделей в Керасе. Я использовал некоторое увеличение в тренировочном наборе:

train_datagen = keras.preprocessing.image.ImageDataGenerator(
                    rescale=1./255, rotation_range=15,
                                   width_shift_range=0.1,
                                   height_shift_range=0.1,
                                   shear_range=0.01,
                                   zoom_range=[0.9, 1.25],
                                   horizontal_flip=False,
                                   vertical_flip=False,
                                   fill_mode='reflect',
                                   data_format='channels_last',
                                   brightness_range=[0.5, 1.5])
    validation_datagen = keras.preprocessing.image.ImageDataGenerator(rescale=1./255)

    train_generator = train_datagen.flow_from_directory(
                    train1_dir,  # Source directory for the training images
                    target_size=(image_size, image_size),
                    batch_size=batch_size)

    validation_generator = validation_datagen.flow_from_directory(
                    validation_dir, # Source directory for the validation images
                    target_size=(image_size, image_size),
                    batch_size=batch_size)

Я также использовал некоторую раннюю остановку (остановка после 100 эпох):

es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=100)
    mc = ModelCheckpoint('best_model_%s_%s.h5' % (dataset_name, model_name), monitor='val_acc', mode='max', verbose=1, save_best_only=True)
    callbacks = [es, mc]

Сначала я тренирую последний слой:

base_model.trainable = False
    model = tf.keras.Sequential([
      base_model,
      keras.layers.GlobalAveragePooling2D(),
      keras.layers.Dense(num_classes, activation='softmax')
    ])
    model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=0.0001),
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    epochs = 10000
    steps_per_epoch = train_generator.n // batch_size
    validation_steps = validation_generator.n // batch_size
    history = model.fit_generator(train_generator,
                                  steps_per_epoch = steps_per_epoch,
                                  epochs=epochs,
                                  workers=4,
                                  validation_data=validation_generator,
                                  validation_steps=validation_steps, 
                                  callbacks=callbacks)

Затем я тренирую предыдущие слои, следуя обучающей программе Keras:

# After top classifier is trained, we finetune the layers of the network
base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))
# Fine tune from this layer onwards
fine_tune_at = 1
# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
    layer.trainable =  False

model.compile(optimizer = tf.keras.optimizers.RMSprop(lr=2e-5),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
epochs = 10000
history_fine = model.fit_generator(train_generator,
                                   steps_per_epoch = steps_per_epoch,
                                   epochs=epochs,
                                   workers=4,
                                   validation_data=validation_generator,
                                   validation_steps=validation_steps, 
                                   callbacks=callbacks
                                   )

Наконец, после того, как модель закончила обучение, я проверяю ее вручную на отдельном тестовом наборе

label_list = train_generator.class_indices
    numeric_to_class = {}
    for key, val in label_list.items():
        numeric_to_class[val] = key
    total_num_images = 0
    acc_num_images = 0
    with open("%s_prediction_%s.txt" % (dataset_name, model_name), "wt") as fid:
        fid.write("Label list:\n")
        for label in label_list:
            fid.write("%s," % label)
        fid.write("\n")
        fid.write("true_class,predicted_class\n")
        fid.write("--------------------------\n")
        for label in label_list:
            testing_dir = os.path.join(test_dir, label)
            for img_file in os.listdir(testing_dir):
                img = cv2.imread(os.path.join(testing_dir, img_file))
                img_resized = cv2.resize(img, (image_size, image_size), interpolation = cv2.INTER_AREA)                
                img1 = np.reshape(img_resized, (1, img_resized.shape[0], img_resized.shape[1], img_resized.shape[2]))
                pred_class_num = model.predict_classes(img1)
                pred_class_num = pred_class_num[0]
                true_class_num = label_list[label]                
                predicted_label = numeric_to_class[pred_class_num]               
                fid.write("%s,%s\n" % (label, predicted_label))
                if predicted_label == label:
                    acc_num_images += 1
                total_num_images += 1

    acc = acc_num_images / (total_num_images * 1.0)

Я должен был сделать это, потому что библиотека не выводит счет F1. Однако я обнаружил, что val_acc поднимается очень высоко (около 0,8), но во время фазы тестирования после тренировки точность очень низкая (я думаю, около 0,1). Я не понимаю, почему это так. Пожалуйста, помогите мне, большое спасибо.

ОБНОВЛЕНИЕ 15/10/2019: Я попытался просто обучить линейный SVM поверх сети, ничего не настраивая, и я получил 70% точности на Caltech101, используя VGG16 (сОптимизатор RMSProp). Однако я не уверен, что это лучший выбор.

ОБНОВЛЕНИЕ 2: Я использовал часть предварительной обработки, предложенную Дэниелом Моллером, в моем пользовательском наборе данных (около 450 изображений, 283 класса «открыто», 203 класса «закрыто»", и получил эту точность и потери при использовании ранней остановки с терпением = 100, просто тренируя последний слой с:

model = tf.keras.Sequential([
  base_model,
  keras.layers.GlobalAveragePooling2D(),
  keras.layers.Dense(num_classes, activation='softmax')
])

Accuracy

Loss

ОБНОВЛЕНИЕ 3: Я также попытался использовать последние полностью подключенные слои в VGG16, и добавил после каждого из них слой выпадения с частотой выпадения (скорость, которая установлена ​​на 0) 60% и терпение = 10 (для ранней остановки):

base_model = tf.keras.applications.VGG16(input_shape=IMG_SHAPE, \
                                               include_top=True, \
                                               weights='imagenet')
base_model.layers[-3].Trainable = True
base_model.layers[-2].Trainable = True
fc1 = base_model.layers[-3]
fc2 = base_model.layers[-2]
predictions = keras.layers.Dense(num_classes, activation='softmax')
dropout1 = Dropout(0.6)
dropout2 = Dropout(0.6)
x = dropout1(fc1.output)
x = fc2(x)
x = dropout2(x)
predictors = predictions(x)
model = Model(inputs=base_model.input, outputs=predictors)

И я получил наивысшую точность проверки 0,93750, точность теста: 0,966216. Графики: enter image description hereAccuracy

1 Ответ

1 голос
/ 15 октября 2019

Основная проблема, кажется, здесь:

Вы забыли изменить масштаб 1/255., когда открываете изображения для прогноза в:

.....
img = cv2.imread(os.path.join(testing_dir, img_file))
img_resized = cv2.resize(img, (image_size, image_size), interpolation = cv2.INTER_AREA)
img1 = np.reshape(img_resized, 
                  (1, img_resized.shape[0], img_resized.shape[1], img_resized.shape[2]))

#you will probably need:
img1 = img1/255.

pred_class_num = model.predict_classes(img1)
...........

Также обратите внимание, что откроется cv2изображения в формате BGR, в то время как Keras может открывать их в RGB.

  • Получить изображения из генератора Keras
  • Получить открытые вами изображения
  • Постройте эти изображения, чтобы проверить, выглядят ли они нормально (или, по крайней мере, одинаково, если все BGR, это не проблема, хотя все графики будут выглядеть смешно)

Пример:

keras_train = train_generator[0][0] #first image from first batch
keras_val = validation_generator[0][0]

img = cv2.imread(os.path.join(testing_dir, img_file))
img_resized = cv2.resize(img, (image_size, image_size), interpolation = cv2.INTER_AREA) 
img1 = np.reshape(img_resized, 
                  (1, img_resized.shape[0], img_resized.shape[1], img_resized.shape[2]))    
your_image = img1[0]/255. #first image from your batch rescaled 

Отобразите эти изображения с помощью matplotlib.
Также убедитесь, что они имеют одинаковый диапазон:

plt.imshow(keras_train)
plt.plot()
plt.imshow(keras_val)
plt.plot()
plt.imshow(your_image)
plt.plot()
print(keras_train.max(), keras_val.max(), img1.max())

Вам может понадобиться использовать np.flip(images, axis=-1) для преобразования BGR в RGB.

Советы по импорту моделей Keras

Если вы импортировали базовую модель из Keras, вы должны импортировать предварительную обработку из того же модуля, а также использовать средство открытия изображений Keras. Это устранит возможные несоответствия:

from keras.applications.resnet50 import ResNet50
from keras.preprocessing import image #use this instead of cv2   
from keras.applications.resnet50 import preprocess_input #use this in the generators    

В обоих генераторах используйте функцию предварительной обработки:

#no rescale, only preprocessing function
train_datagen = keras.preprocessing.image.ImageDataGenerator(
                               rotation_range=15,
                               width_shift_range=0.1,
                               height_shift_range=0.1,
                               shear_range=0.01,
                               zoom_range=[0.9, 1.25],
                               horizontal_flip=False,
                               vertical_flip=False,
                               fill_mode='reflect',
                               data_format='channels_last',
                               brightness_range=[0.5, 1.5],
                               preprocessing_function=preprocess_input)
validation_datagen = keras.preprocessing.image.ImageDataGenerator(
                               preprocessing_function=preprocess_input)

Загрузка и предварительная обработка изображений для прогнозирования:

img = image.load_img(img_path, target_size=(image_size,image_size))
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)

Больше в: https://keras.io/applications/

Другие возможности

Но может быть несколько других вещей, таких как:

  • Очень сильное переоснащение, ваше терпение к ранней пробке составляет 100! Обычно это очень большое число. Но единственный способ подтвердить это - проверить, не растет ли val_acc слишком высоко по сравнению с лучшим.
    • Предложение: перезагрузите лучшую модель, сохраненную с помощью ModelCheckpoint, прежде чем вы начнете прогнозировать
    • Предложение: опубликуйте историю акк, чтобы мы могли видеть, плохо ли это
  • Имеет ли базовая модель BatchNormalization слоев? - Когда вы замораживаете слой нормализации партии, он сохранит moving_mean и moving_variance без изменений, но эти значения были обработаны с другими данными. Если ваши данные не имеют того же среднего значения и дисперсии, ваша модель будет работать хорошо, но проверка станет катастрофой
    • Предложение: посмотрите, является ли потеря / подтверждение проверки ужасно неправильной с самого начала
    • Предложение: не замораживайте слои BatchNormalization в базовой модели (перебирайте слои и замораживайте только другие типы)
    • Предложение: если вам случится знать базу данных, с которой была базовая модельобучите, сравните среднее значение и разброс ваших данных со средним значением и вариацией в этой базе данных (обработайте каждый канал отдельно)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...