为什么我的CIFAR 100 CNN模型主要预测两个类?

时间:2017-03-09 21:06:28

标签: neural-network computer-vision keras

我目前正试图在CIFAR 100上使用Keras得到一个不错的分数(> 40%的准确率)。但是,我遇到了CNN模型的奇怪行为:它倾向于预测某些类(2 - 5)比其他人更频繁:

enter image description here

位置(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替换为reluhistory csv看起来没问题,但可视化也存在同样的问题:

enter image description here

请注意,此处的验证准确率仅为3.44%。

Dropout + tanh + border mode

删除dropout,用relu替换tanh,在任何地方将border模式设置为相同:history csv

enter image description here

可视化代码仍然比keras训练代码提供更低的准确度(这次是8.50%)。

Q& A

以下是评论摘要:

  • 数据均匀分布在类上。因此,这两个班级没有“过度训练”。
  • 使用了数据扩充,但没有数据扩充,问题仍然存在。
  • 可视化不是问题。

4 个答案:

答案 0 :(得分:5)

如果您在训练和验证期间获得了良好的准确性,但在测试时没有获得准确性,请确保在两种情况下对数据集执行完全相同的预处理。 在培训时你有这个:

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

但是在预测混淆矩阵时没有这样的代码。加入测试:

X_val /=  255.

给出以下漂亮的混淆矩阵:

enter image description here

答案 1 :(得分:0)

我对这部分代码不太满意:

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-example cifar 10基本上使用相同的架构(密集层大小可能不同),但在那里也使用relu(根本没有tanh)。 this external keras-based cifar 100 code也是如此。

答案 2 :(得分:0)

问题的一个重要部分是我的~/.keras/keras.json

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

因此,我必须将image_dim_ordering更改为tf。这导致

enter image description here

,准确度为12.73%。显然,由于validation history的准确率为45.1%,因此仍然存在问题。

答案 3 :(得分:-1)

  1. 即使在datagen中,我也没有看到你做中庸之道。我怀疑这是主要原因。要使用ImageDataGenerator进行居中,请设置featurewise_center = 1。另一种方法是从每个RGB像素中减去ImageNet均值。要减去的平均向量是[103.939, 116.779, 123.68]

  2. 进行所有激活relu,除非您有特定原因需要一个tanh

  3. 删除两个0.25的丢失,看看会发生什么。如果要将丢包应用于卷积层,最好使用SpatialDropout2D。它以某种方式从Keras在线文档中删除,但您可以在source中找到它。

  4. 您有两个conv图层same和两个valid图层。这没有任何问题,但将所有conv图层与same保持在一起并根据最大池控制您的尺寸会更简单。