我目前正试图在CIFAR 100上使用Keras得到一个不错的分数(> 40%的准确率)。但是,我遇到了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
替换为relu
。 history csv看起来没问题,但可视化也存在同样的问题:
请注意,此处的验证准确率仅为3.44%。
删除dropout,用relu替换tanh,在任何地方将border模式设置为相同:history csv
可视化代码仍然比keras训练代码提供更低的准确度(这次是8.50%)。
以下是评论摘要:
答案 0 :(得分:5)
如果您在训练和验证期间获得了良好的准确性,但在测试时没有获得准确性,请确保在两种情况下对数据集执行完全相同的预处理。 在培训时你有这个:
X_train /= 255
X_val /= 255
X_test /= 255
但是在预测混淆矩阵时没有这样的代码。加入测试:
X_val /= 255.
给出以下漂亮的混淆矩阵:
答案 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
。这导致
,准确度为12.73%。显然,由于validation history的准确率为45.1%,因此仍然存在问题。
答案 3 :(得分:-1)
即使在datagen中,我也没有看到你做中庸之道。我怀疑这是主要原因。要使用ImageDataGenerator
进行居中,请设置featurewise_center = 1
。另一种方法是从每个RGB像素中减去ImageNet均值。要减去的平均向量是[103.939, 116.779, 123.68]
。
进行所有激活relu
,除非您有特定原因需要一个tanh
。
删除两个0.25的丢失,看看会发生什么。如果要将丢包应用于卷积层,最好使用SpatialDropout2D
。它以某种方式从Keras在线文档中删除,但您可以在source中找到它。
您有两个conv
图层same
和两个valid
图层。这没有任何问题,但将所有conv
图层与same
保持在一起并根据最大池控制您的尺寸会更简单。