Классификация изображений Keras — точность прогнозирования в наборе данных проверки не соответствует val_acc

#machine-learning #keras #deep-learning #classification #conv-neural-network

#машинное обучение #keras #глубокое обучение #классификация #conv-нейронная сеть

Вопрос:

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

Я создал CNN с использованием Keras, мой классификатор, похоже, работает хорошо:

  • У меня есть 1939 изображений, используемых для обучения (50% слева, 50% справа)
  • У меня есть 648 изображений, используемых для проверки (50% слева, 50% справа)
  • Все изображения имеют размер 115×45, в оттенках серого
  • acc увеличивается до 99,53%
  • val_acc увеличивается до 98,38%
  • Как потери, так и val_loss сходятся близко к 0

Keras verbose для меня выглядит нормально:

 60/60 [==============================] - 6s 98ms/step - loss: 0.6295 - acc: 0.6393 - val_loss: 0.4877 - val_acc: 0.7641
Epoch 2/32
60/60 [==============================] - 5s 78ms/step - loss: 0.4825 - acc: 0.7734 - val_loss: 0.3403 - val_acc: 0.8799
Epoch 3/32
60/60 [==============================] - 5s 77ms/step - loss: 0.3258 - acc: 0.8663 - val_loss: 0.2314 - val_acc: 0.9042
Epoch 4/32
60/60 [==============================] - 5s 83ms/step - loss: 0.2498 - acc: 0.8942 - val_loss: 0.2329 - val_acc: 0.9042
Epoch 5/32
60/60 [==============================] - 5s 76ms/step - loss: 0.2408 - acc: 0.9002 - val_loss: 0.1426 - val_acc: 0.9432
Epoch 6/32
60/60 [==============================] - 5s 80ms/step - loss: 0.1968 - acc: 0.9260 - val_loss: 0.1484 - val_acc: 0.9367
Epoch 7/32
60/60 [==============================] - 5s 77ms/step - loss: 0.1621 - acc: 0.9319 - val_loss: 0.1141 - val_acc: 0.9578
Epoch 8/32
60/60 [==============================] - 5s 81ms/step - loss: 0.1600 - acc: 0.9361 - val_loss: 0.1229 - val_acc: 0.9513
Epoch 9/32
60/60 [==============================] - 4s 70ms/step - loss: 0.1358 - acc: 0.9462 - val_loss: 0.0884 - val_acc: 0.9692
Epoch 10/32
60/60 [==============================] - 4s 74ms/step - loss: 0.1193 - acc: 0.9542 - val_loss: 0.1232 - val_acc: 0.9529
Epoch 11/32
60/60 [==============================] - 5s 79ms/step - loss: 0.1075 - acc: 0.9595 - val_loss: 0.0865 - val_acc: 0.9724
Epoch 12/32
60/60 [==============================] - 4s 73ms/step - loss: 0.1209 - acc: 0.9531 - val_loss: 0.1067 - val_acc: 0.9497
Epoch 13/32
60/60 [==============================] - 4s 73ms/step - loss: 0.1135 - acc: 0.9609 - val_loss: 0.0860 - val_acc: 0.9838
Epoch 14/32
60/60 [==============================] - 4s 70ms/step - loss: 0.0869 - acc: 0.9682 - val_loss: 0.0907 - val_acc: 0.9675
Epoch 15/32
60/60 [==============================] - 4s 71ms/step - loss: 0.0960 - acc: 0.9637 - val_loss: 0.0996 - val_acc: 0.9643
Epoch 16/32
60/60 [==============================] - 4s 73ms/step - loss: 0.0951 - acc: 0.9625 - val_loss: 0.1223 - val_acc: 0.9481
Epoch 17/32
60/60 [==============================] - 4s 70ms/step - loss: 0.0685 - acc: 0.9729 - val_loss: 0.1220 - val_acc: 0.9513
Epoch 18/32
60/60 [==============================] - 4s 73ms/step - loss: 0.0791 - acc: 0.9715 - val_loss: 0.0959 - val_acc: 0.9692
Epoch 19/32
60/60 [==============================] - 4s 71ms/step - loss: 0.0595 - acc: 0.9802 - val_loss: 0.0648 - val_acc: 0.9773
Epoch 20/32
60/60 [==============================] - 4s 71ms/step - loss: 0.0486 - acc: 0.9844 - val_loss: 0.0691 - val_acc: 0.9838
Epoch 21/32
60/60 [==============================] - 4s 70ms/step - loss: 0.0499 - acc: 0.9812 - val_loss: 0.1166 - val_acc: 0.9627
Epoch 22/32
60/60 [==============================] - 4s 71ms/step - loss: 0.0481 - acc: 0.9844 - val_loss: 0.0875 - val_acc: 0.9734
Epoch 23/32
60/60 [==============================] - 4s 70ms/step - loss: 0.0533 - acc: 0.9814 - val_loss: 0.1094 - val_acc: 0.9724
Epoch 24/32
60/60 [==============================] - 4s 70ms/step - loss: 0.0487 - acc: 0.9812 - val_loss: 0.0722 - val_acc: 0.9740
Epoch 25/32
60/60 [==============================] - 4s 72ms/step - loss: 0.0441 - acc: 0.9828 - val_loss: 0.0992 - val_acc: 0.9773
Epoch 26/32
60/60 [==============================] - 4s 71ms/step - loss: 0.0667 - acc: 0.9726 - val_loss: 0.0964 - val_acc: 0.9643
Epoch 27/32
60/60 [==============================] - 4s 73ms/step - loss: 0.0436 - acc: 0.9835 - val_loss: 0.0771 - val_acc: 0.9708
Epoch 28/32
60/60 [==============================] - 4s 71ms/step - loss: 0.0322 - acc: 0.9896 - val_loss: 0.0872 - val_acc: 0.9756
Epoch 29/32
60/60 [==============================] - 5s 80ms/step - loss: 0.0294 - acc: 0.9943 - val_loss: 0.1414 - val_acc: 0.9578
Epoch 30/32
60/60 [==============================] - 5s 76ms/step - loss: 0.0348 - acc: 0.9870 - val_loss: 0.1102 - val_acc: 0.9659
Epoch 31/32
60/60 [==============================] - 5s 76ms/step - loss: 0.0306 - acc: 0.9922 - val_loss: 0.0794 - val_acc: 0.9659
Epoch 32/32
60/60 [==============================] - 5s 76ms/step - loss: 0.0152 - acc: 0.9953 - val_loss: 0.1051 - val_acc: 0.9724
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
conv2d_1 (Conv2D)            (None, 113, 43, 32)       896
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 56, 21, 32)        0
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 54, 19, 32)        9248
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 27, 9, 32)         0
_________________________________________________________________
flatten_1 (Flatten)          (None, 7776)              0
_________________________________________________________________
dense_1 (Dense)              (None, 128)               995456
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 129
=================================================================
Total params: 1,005,729
Trainable params: 1,005,729
Non-trainable params: 0
  

Итак, все выглядит великолепно, но когда я попытался предсказать категорию из 2000 образцов, я получил очень странные результаты с точностью < 70%.

Сначала я подумал, что этот образец может быть предвзятым, поэтому вместо этого я попытался предсказать изображения в наборе данных проверки.

У меня должна была быть точность 98,38% и идеальное разделение 50 на 50, но вместо этого я снова получил:

  • 170 изображений, предсказанных правильно, вместо 324, с точностью 98,8%
  • осталось 478 предсказанных изображений вместо 324 с точностью 67,3%
  • Средняя точность: 75,69%, а не 98,38%

Я предполагаю, что что-то не так либо в моем CNN, либо в моем сценарии прогнозирования.

Код классификатора CNN:

 from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense
from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

# Init CNN
classifier = Sequential()

# Step 1 - Convolution
classifier.add(Conv2D(32, (3, 3), input_shape = (115, 45, 3), activation = 'relu'))

# Step 2 - Pooling
classifier.add(MaxPooling2D(pool_size = (2, 2)))

# Adding a second convolutional layer
classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
classifier.add(MaxPooling2D(pool_size = (2, 2)))

# Step 3 - Flattening
classifier.add(Flatten())

# Step 4 - Full connection
classifier.add(Dense(units = 128, activation = 'relu'))
classifier.add(Dense(units = 1, activation = 'sigmoid'))

# Compiling the CNN
classifier.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])

# Part 2 - Fitting the CNN to the images
from keras.preprocessing.image import ImageDataGenerator
import numpy

train_datagen = ImageDataGenerator(rescale = 1./255, shear_range = 0.2, zoom_range = 0.2, horizontal_flip = False)
test_datagen = ImageDataGenerator(rescale = 1./255)

training_set = train_datagen.flow_from_directory('./dataset/training_set',
                                                 target_size = (115, 45),
                                                 batch_size = 32,
                                                 class_mode = 'binary')

test_set = test_datagen.flow_from_directory('./dataset/test_set',
                                            target_size = (115, 45),
                                            batch_size = 32,
                                            class_mode = 'binary')

classifier.fit_generator(training_set,
                         steps_per_epoch = 1939/32, # total samples / batch size
                         epochs = 32,
                         validation_data = test_set,
                         validation_steps = 648/32)

# Save the classifier
classifier.evaluate_generator(generator=test_set)
classifier.summary()
classifier.save('./classifier.h5')
  

Код прогнозирования:

 from keras.models import Sequential
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Flatten
from keras.layers import Dense
from keras.models import load_model
from keras.preprocessing.image import ImageDataGenerator
import os
import numpy as np
from keras.preprocessing import image
from shutil import copyfile

classifier = load_model('./classifier.h5')
folder = './small/'
files = os.listdir(folder)
pleft = 0
pright = 0
for f in files:
    test_image = image.load_img(folder f, target_size = (115, 45))
    test_image = image.img_to_array(test_image)
    test_image = np.expand_dims(test_image, axis = 0)
    result = classifier.predict(test_image)
        #print training_set.class_indices
    if result[0][0] == 1:
        pright=pright 1
        prediction = 'right'
        copyfile(folder '../' f, '/found_right/' f)
    else:
        prediction = 'left'
        copyfile(folder '../' f, '/found_left/' f)
        pleft=pleft 1

ptot = pleft   pright
print 'Left = ' str(pleft) ' (' str(pleft / (ptot / 100)) '%)'
print 'Right = ' str(pright)
print 'Total = ' str(ptot)
  

Вывод:

 Left = 478 (79%)
Right = 170
Total = 648
  

Ваша помощь будет высоко оценена.

Комментарии:

1. Я не проводил много тренировок с генератором, но ваша точность подозрительно высока в первую эпоху, всего 60 изображений на партию, мое лучшее предположение заключается в том, что это переоснащение для каждой партии. Но я не слишком уверен в этом. Возможно, попробуйте запустить с большими пакетами?

2. Вы неправильно используете прогнозы модели, сравнение выходных данных модели с 1 не имеет никакого смысла, вам нужно использовать настраиваемый порог (keras использует 0,5 для вычисления точности).

3. @MatiasValdenegro, я перепроверил, и в моем случае вывод Keras всегда [[1.]] или [[0.]], я читал, что это может быть связано с используемой функцией активации.

4. Не имеет значения, что вы наблюдали, Keras вычисляет точность, устанавливая пороговое значение выходного значения на уровне 0,5. Если вы печатаете значения, они могут быть округлены, и именно поэтому вы видите 1.0 вместо 0.99999. Также это плохая практика сравнивать числа с плавающей запятой для равенства.

5. Также вы не масштабируете (делите на 255) значения вашего изображения во время прогнозирования, в то время как генератор делает это за вас.

Ответ №1:

Я решил эту проблему, выполнив две вещи:

  1. Как предположил @Matias Valdenegro, мне пришлось изменить масштаб значений изображения перед прогнозированием, я добавил test_image /= 255. перед вызовом predict().

  2. Поскольку мой val_loss все еще был немного высоким, я добавил обратный вызов с ранней остановкой, а также два Dropout() перед моими плотными слоями.

Мои результаты прогнозирования теперь соответствуют результатам, полученным во время обучения / проверки.