Почему моя модель CIFAR 100 CNN в основном предсказывает два класса?

В настоящее время я пытаюсь получить приличный счет (точность> 40%) с Keras на CIFAR 100. Однако я испытываю странное поведение модели CNN: она склонна предсказывать некоторые классы (2 - 5) гораздо чаще, чем другие:

Пиксель в позиции (i, j) содержит счетчик того, сколько элементов набора проверки из класса i было предсказано как принадлежащее классу j. При этом диагональ содержит правильные классификации, все остальное является ошибкой. Две вертикальные полосы указывают, что модель часто предсказывает эти классы, хотя это не так.

CIFAR 100 идеально сбалансирован: все 100 классов имеют 500 тренировочных образцов.

Почему модель склонна предсказывать некоторые классы НАМНОГО чаще, чем другие классы? Как это можно исправить?

Код

Выполнение этого занимает некоторое время.

#!/usr/bin/env python

from __future__ import print_function
from keras.datasets import cifar100
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.utils import np_utils
from sklearn.model_selection import train_test_split
import numpy as np

batch_size = 32
nb_classes = 100
nb_epoch = 50
data_augmentation = True

# input image dimensions
img_rows, img_cols = 32, 32
# The CIFAR10 images are RGB.
img_channels = 3

# The data, shuffled and split between train and test sets:
(X, y), (X_test, y_test) = cifar100.load_data()
X_train, X_val, y_train, y_val = train_test_split(X, y,
                                                  test_size=0.20,
                                                  random_state=42)

# Shuffle training data
perm = np.arange(len(X_train))
np.random.shuffle(perm)
X_train = X_train[perm]
y_train = y_train[perm]

print('X_train shape:', X_train.shape)
print(X_train.shape[0], 'train samples')
print(X_val.shape[0], 'validation samples')
print(X_test.shape[0], 'test samples')

# Convert class vectors to binary class matrices.
Y_train = np_utils.to_categorical(y_train, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)
Y_val = np_utils.to_categorical(y_val, nb_classes)

model = Sequential()

model.add(Convolution2D(32, 3, 3, border_mode='same',
                        input_shape=X_train.shape[1:]))
model.add(Activation('relu'))
model.add(Convolution2D(32, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Convolution2D(64, 3, 3, border_mode='same'))
model.add(Activation('relu'))
model.add(Convolution2D(64, 3, 3))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(1024))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))
model.add(Activation('softmax'))

model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

X_train = X_train.astype('float32')
X_val = X_val.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_val /= 255
X_test /= 255

if not data_augmentation:
    print('Not using data augmentation.')
    model.fit(X_train, Y_train,
              batch_size=batch_size,
              nb_epoch=nb_epoch,
              validation_data=(X_val, y_val),
              shuffle=True)
else:
    print('Using real-time data augmentation.')
    # This will do preprocessing and realtime data augmentation:
    datagen = ImageDataGenerator(
        featurewise_center=False,  # set input mean to 0 over the dataset
        samplewise_center=False,  # set each sample mean to 0
        featurewise_std_normalization=False,  # divide inputs by std of the dataset
        samplewise_std_normalization=False,  # divide each input by its std
        zca_whitening=False,  # apply ZCA whitening
        rotation_range=0,  # randomly rotate images in the range (degrees, 0 to 180)
        width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
        height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
        horizontal_flip=True,  # randomly flip images
        vertical_flip=False)  # randomly flip images

    # Compute quantities required for featurewise normalization
    # (std, mean, and principal components if ZCA whitening is applied).
    datagen.fit(X_train)

    # Fit the model on the batches generated by datagen.flow().
    model.fit_generator(datagen.flow(X_train, Y_train,
                                     batch_size=batch_size),
                        samples_per_epoch=X_train.shape[0],
                        nb_epoch=nb_epoch,
                        validation_data=(X_val, Y_val))
    model.save('cifar100.h5')

Код визуализации

#!/usr/bin/env python


"""Analyze a cifar100 keras model."""

from keras.models import load_model
from keras.datasets import cifar100
from sklearn.model_selection import train_test_split
import numpy as np
import json
import io
import matplotlib.pyplot as plt
try:
    to_unicode = unicode
except NameError:
    to_unicode = str

n_classes = 100


def plot_cm(cm, zero_diagonal=False):
    """Plot a confusion matrix."""
    n = len(cm)
    size = int(n / 4.)
    fig = plt.figure(figsize=(size, size), dpi=80, )
    plt.clf()
    ax = fig.add_subplot(111)
    ax.set_aspect(1)
    res = ax.imshow(np.array(cm), cmap=plt.cm.viridis,
                    interpolation='nearest')
    width, height = cm.shape
    fig.colorbar(res)
    plt.savefig('confusion_matrix.png', format='png')

# Load model
model = load_model('cifar100.h5')

# Load validation data
(X, y), (X_test, y_test) = cifar100.load_data()

X_train, X_val, y_train, y_val = train_test_split(X, y,
                                                  test_size=0.20,
                                                  random_state=42)

# Calculate confusion matrix
y_val_i = y_val.flatten()
y_val_pred = model.predict(X_val)
y_val_pred_i = y_val_pred.argmax(1)
cm = np.zeros((n_classes, n_classes), dtype=np.int)
for i, j in zip(y_val_i, y_val_pred_i):
    cm[i][j] += 1

acc = sum([cm[i][i] for i in range(100)]) / float(cm.sum())
print("Validation accuracy: %0.4f" % acc)

# Create plot
plot_cm(cm)

# Serialize confusion matrix
with io.open('cm.json', 'w', encoding='utf8') as outfile:
    str_ = json.dumps(cm.tolist(),
                      indent=4, sort_keys=True,
                      separators=(',', ':'), ensure_ascii=False)
    outfile.write(to_unicode(str_))

Красная сельдь

TANH

Я заменил tanh от relu, История CSV выглядит нормально, но визуализация имеет ту же проблему:

Также обратите внимание, что точность проверки здесь составляет всего 3,44%.

Dropout + tanh + пограничный режим

Удаление выпадения, замена tanh на relu, установка режима границы везде одинаково: history csv

Код визуализации по-прежнему дает гораздо меньшую точность (на этот раз 8,50%), чем обучающий код керас.

Вопросы и ответы

Ниже приводится краткое изложение комментариев:

  • Данные равномерно распределены по классам. Таким образом, нет никакого "переобучения" этих двух классов.
  • Используется увеличение данных, но без увеличения данных проблема сохраняется.
  • Визуализация не проблема.

4 ответа

Решение

Если вы получаете хорошую точность во время обучения и проверки, но не во время тестирования, убедитесь, что вы выполняете одинаковую предварительную обработку набора данных в обоих случаях. Вот у вас при тренировках:

X_train /= 255
X_val /= 255
X_test /= 255

Но нет такого кода при прогнозировании вашей путаницы матрицы. Добавление к тестированию:

X_val /=  255.

Дает следующую красивую путаницу:

Я не очень хорошо себя чувствую с этой частью кода:

model.add(Dense(1024))
model.add(Activation('tanh'))
model.add(Dropout(0.5))
model.add(Dense(nb_classes))
model.add(Activation('softmax'))

Оставшаяся модель полна relus, но здесь есть tanh,

tanh иногда исчезает или взрывается (насыщается при -1 и 1), что может привести к переоценке вашего уровня 2.

В примере с Keras Cifar 10 в основном используется та же архитектура (размеры плотного слоя могут отличаться), но также используется relu там (нет танх вообще). То же самое касается внешнего кода на основе Keras Cifar 100.

Одна важная часть проблемы заключалась в том, что мой ~/.keras/keras.json было

{
    "image_dim_ordering": "th",
    "epsilon": 1e-07,
    "floatx": "float32",
    "backend": "tensorflow"
}

Следовательно, я должен был изменить image_dim_ordering в tf, Это ведет к

и точность 12,73%. Очевидно, что проблема остается, поскольку история проверки дала точность 45,1%.

  1. Я не вижу, что вы делаете центрирование, даже в датагене. Я подозреваю, что это главная причина. Чтобы сделать значит центрирование с помощью ImageDataGenerator, задавать featurewise_center = 1, Другой способ - вычесть среднее значение ImageNet из каждого пикселя RGB. Средний вектор, который нужно вычесть [103.939, 116.779, 123.68],

  2. Сделайте все активации reluс, если у вас нет конкретной причины иметь один tanh,

  3. Удалите две выпадения по 0,25 и посмотрите, что получится. Если вы хотите применить выпадения к слою свертки, лучше использовать SpatialDropout2D, Он каким-то образом удален из онлайн-документации Keras, но вы можете найти его в источнике.

  4. У вас есть два conv слои с same и два с valid, В этом нет ничего плохого, но было бы проще сохранить все conv слои с same и контролировать свой размер только на основе макс-пулов.

Другие вопросы по тегам