CNN给出了有偏见的结果

bla*_*bug 10 python convolution deep-learning keras

我在CNN上使用二进制分类器.我有两个类别"我"和"其他".我有大约250张自己的图像和500张其他图像(随机面部数据库).我目前的图层实现非常简单

    self.model.add(Conv2D(128, (2, 2), padding='same', 
    input_shape=dataset.X_train.shape[1:]))
    self.model.add(Activation('relu'))
    self.model.add(MaxPooling2D(pool_size=(2, 2)))
    self.model.add(Dropout(0.25))

    self.model.add(Conv2D(64, (2, 2), padding='same'))
    self.model.add(Activation('relu'))
    self.model.add(MaxPooling2D(pool_size=(2, 2)))
    self.model.add(Dropout(0.25))

    self.model.add(Conv2D(32, (1, 1), padding='same'))
    self.model.add(Activation('relu'))
    self.model.add(MaxPooling2D(pool_size=(2, 2)))
    self.model.add(Dropout(0.5))
    self.model.add(Dense(512))
    self.model.add(Activation('relu'))
    self.model.add(Dropout(0.25))
    self.model.add(Dense(2)) # for two classes
    self.model.add(Activation('softmax'))
Run Code Online (Sandbox Code Playgroud)

我的网络达到了93%的准确率 在此输入图像描述

在此输入图像描述 我的问题是,当我使用这个网络预测面孔时,它总是将任何面部识别为我的面孔.我已经裁剪了面部,应用了gabor过滤器,但没有任何作用.任何建议将不胜感激.

随机面的预测结果:[KK代表我的脸]概率总是超过97%:

KK identified!
1/1 [==============================] - 0s
[[ 0.9741978  0.0258022]]
1/1 [==============================] - 0s

KK identified!
1/1 [==============================] - 0s
[[ 0.9897241   0.01027592]]
1/1 [==============================] - 0s
Run Code Online (Sandbox Code Playgroud)

我的图像预测结果:[KK代表我的脸]概率总是超过99%:

KK identified!
1/1 [==============================] - 0s
[[ 0.99639165  0.00360837]]
1/1 [==============================] - 0s
KK identified!
1/1 [==============================] - 0s
[[ 0.99527925  0.00472075]]
1/1 [==============================] - 0s
Run Code Online (Sandbox Code Playgroud)

培训代码

   def get_data(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE, img_channels=3, nb_classes=2):

        images, labels = fetch_data('./data/')
        labels = np.reshape(labels, [-1])

        X_train, X_test, y_train, y_test =  \
            train_test_split(images, labels, test_size=0.3, random_state=random.randint(0, 100))
        X_valid, X_test, y_valid, y_test = \
            train_test_split(images, labels, test_size=0.3, random_state=random.randint(0, 100))
            #train_test_split(images, labels, test_size=0.3, random_state=np.random.seed(15))

        if K.image_dim_ordering() == 'th':
            X_train = X_train.reshape(X_train.shape[0], 3, img_rows, img_cols)
            X_valid = X_valid.reshape(X_valid.shape[0], 3, img_rows, img_cols)
            X_test = X_test.reshape(X_test.shape[0], 3, img_rows, img_cols)
            # input_shape = (3, img_rows, img_cols)
        else:
            X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 3)
            X_valid = X_valid.reshape(X_valid.shape[0], img_rows, img_cols, 3)
            X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 3)
            # input_shape = (img_rows, img_cols, 3)

        Y_train = np_utils.to_categorical(y_train, nb_classes)
        Y_valid = np_utils.to_categorical(y_valid, nb_classes)
        Y_test = np_utils.to_categorical(y_test, nb_classes)

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

        self.X_train = X_train
        self.X_valid = X_valid
        self.X_test = X_test
        self.Y_train = Y_train
        self.Y_valid = Y_valid
        self.Y_test = Y_test



def train_network(self, dataset, batch_size=32, nb_epoch=40, data_augmentation=True):
    sgd = SGD(lr=0.003, decay=0.0000001, momentum=0.9, nesterov=True)

    # adam = Adam(lr=0.01, beta_1=0.9, beta_2=0.999, epsilon=1e-08, decay=0.0001)
    self.model.compile(loss='binary_crossentropy',
                       optimizer=sgd,
                       metrics=['accuracy'])
    if not data_augmentation:
        processed_data = self.model.fit(dataset.X_train, dataset.Y_train,
                       batch_size=batch_size,
                       nb_epoch=nb_epoch,
                       validation_data=(dataset.X_valid, dataset.Y_valid),
                       shuffle=True)
    else:
        datagenerator = ImageDataGenerator(
            featurewise_center=False,
            samplewise_center=False,
            featurewise_std_normalization=False,
            samplewise_std_normalization=False,
            zca_whitening=False,
            rotation_range=20,
            width_shift_range=0.2,
            height_shift_range=0.2,
            horizontal_flip=True,
            vertical_flip=False)

        datagenerator.fit(dataset.X_train)

        processed_data = self.model.fit_generator(datagen.flow(dataset.X_train, dataset.Y_train, batch_size=batch_size, shuffle=True),
                                 samples_per_epoch=dataset.X_train.shape[0], nb_epoch=nb_epoch, validation_data=(dataset.X_valid, dataset.Y_valid))
Run Code Online (Sandbox Code Playgroud)

谢谢

[更新:6月11日]

图层

def build_model(self, dataset, nb_classes=2):
        self.model = Sequential()

        self.model.add(Conv2D(32, (3, 3), padding='same', input_shape=dataset.X_train.shape[1:]))
        self.model.add(Activation('relu'))
        self.model.add(Conv2D(32, (3, 3)))
        self.model.add(Activation('relu'))
        self.model.add(MaxPooling2D(pool_size=(2, 2)))
        self.model.add(Dropout(0.5))

        self.model.add(Conv2D(16, (3, 3), padding='same'))
        self.model.add(Activation('relu'))
        self.model.add(Conv2D(16, (3, 3)))
        self.model.add(Activation('relu'))
        self.model.add(MaxPooling2D(pool_size=(2, 2)))
        self.model.add(Dropout(0.5))

        self.model.add(Flatten())
        self.model.add(Dense(512))
        self.model.add(Activation('relu'))
        self.model.add(Dropout(0.5))
        self.model.add(Dense(nb_classes))
        self.model.add(Activation('softmax'))

        self.model.summary()
Run Code Online (Sandbox Code Playgroud)

数据增加

    # this will do preprocessing and realtime data augmentation
    datagen = ImageDataGenerator(
        featurewise_center=True,             # 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=20,                     # randomly rotate images in the range (degrees, 0 to 180)
        width_shift_range=0.2,                # randomly shift images horizontally (fraction of total width)
        height_shift_range=0.2,               # randomly shift images vertically (fraction of total height)
        # rescale=1. / 255,
        # shear_range=0.2,
        # zoom_range=0.2,
        horizontal_flip=True,                 # randomly flip images
        vertical_flip=False)                  # randomly flip images

    datagen.fit(dataset.X_train)

    checkpoint = ModelCheckpoint(self.FILE_PATH, monitor='val_acc', verbose=1, save_best_only=True, mode='max')
    callback_list = [checkpoint]

    # fit the model on the batches generated by datagen.flow()
    train_generator = datagen.flow(dataset.X_train, dataset.Y_train, batch_size=batch_size, shuffle=True)

    history = self.model.fit_generator(train_generator,
                                       samples_per_epoch=dataset.X_train.shape[0],
                                       nb_epoch=nb_epoch,
                                       validation_data=(dataset.X_valid, dataset.Y_valid),
                                       callbacks=callback_list)
Run Code Online (Sandbox Code Playgroud)

数据集

class DataSet(object):

    def __init__(self):
        self.X_train = None
        self.X_valid = None
        self.X_test = None
        self.Y_train = None
        self.Y_valid = None
        self.Y_test = None

    # support only binary classification for now, thus 2 class limit
    def get_data(self, img_rows=IMAGE_SIZE, img_cols=IMAGE_SIZE, img_channels=3, nb_classes=2):

        images, labels = fetch_data('./data/')
        labels = np.reshape(labels, [-1])

        X_train, X_test, y_train, y_test =  \
            train_test_split(images, labels, test_size=0.2, random_state=random.randint(0, 100))

        X_valid, X_test, y_valid, y_test = \
            train_test_split(images, labels, test_size=0.2, random_state=random.randint(0, 100))

        if K.image_dim_ordering() == 'th':
            X_train = X_train.reshape(X_train.shape[0], 3, img_rows, img_cols)
            X_valid = X_valid.reshape(X_valid.shape[0], 3, img_rows, img_cols)
            X_test = X_test.reshape(X_test.shape[0], 3, img_rows, img_cols)

        else:
            X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 3)
            X_valid = X_valid.reshape(X_valid.shape[0], img_rows, img_cols, 3)
            X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 3)


        # convert class vectors to binary class matrices
        Y_train = np_utils.to_categorical(y_train, nb_classes)
        Y_valid = np_utils.to_categorical(y_valid, nb_classes)
        Y_test = np_utils.to_categorical(y_test, nb_classes)

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

        self.X_train = X_train
        self.X_valid = X_valid
        self.X_test = X_test
        self.Y_train = Y_train
        self.Y_valid = Y_valid
        self.Y_test = Y_test
Run Code Online (Sandbox Code Playgroud)

Luk*_*ski 5

结果一点也不奇怪。网络从来没有知道是什么让你的脸与众不同,但只记得是什么让 500 集与你的不同。一旦你展示了一张新面孔,它就没有它的“记忆”,因此将它解释为你的,只是因为 500 张面孔中出现的特征都没有出现在第 501 张面孔中。

如何解决这个问题的一些想法:

  • 按照 petezurich 的建议,使用ImageDataGenerator扩充您的数据。
  • 增加内核大小。2*2 太小,无法捕捉面部特征。考虑3*3,甚至在第一个隐藏层堆叠两个3*3。
  • 考虑使用批量归一化和正则化。将 Dropout 增加到 0.5。
  • 考虑用稀释卷积替换池化层(在 Keras 中可用)。
  • 确保对输入数据进行规范化。
  • 减少第一层中特征图(过滤器)的数量。考虑使用例如 32 个 3*3 地图而不是 128 个小元素(如果我猜的话,这些是您的主要问题)。通过这种方式,您将强制网络进行泛化,而不是学习一些细微的差别。

对我最后一点假设的一个很好的测试是可视化隐藏层中的激活,尤其是第一个隐藏层。我有一种感觉,你的网络会激活一些不相关的特征(或者更确切地说 - 噪音),而不是“人类特征”(比如眼睛、发型)。

[添加更多代码后编辑]

  • 以零为中心输入数据。
  • 降低批量大小。有了这么少的例子,你不想在一个批次中进行太多的平均。

我仍然认为在第一个隐藏层中使用例如 16 或 32 个过滤器应该是首先要检查的。看看你的脸。你能发现 128 个“特征”吗?除非你有一些严重的痤疮,否则我不这么认为。


pet*_*ich 2

对于这样的分类任务,您没有足够的数据(250 + 500 个样本)。A 类(你)与 B 类(其他人)之间的 50/100 关系是一个很大的偏见。至少你应该在训练时尝试用 .fit() 函数中的 class_weight 参数来平衡它。

\n\n

更好的方法是从 Keras 应用程序重新训练现有的 ConvNet,例如 VGG16 或 Inception: https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html

\n\n

您可以轻松地通过数据增强来增加样本数量(请参阅此处的 ImageDataGenerator https://keras.io/preprocessing/image/)。

\n\n

为了分析您的代码,您需要展示如何分割数据以及如何训练数据。正确的训练方法是将训练数据和验证数据分开,然后对另一组单独的测试数据进行评估,这些数据是网络从未见过的,并且您还没有使用它来优化您的超参数。

\n\n

据我从您关于训练/测试/验证分割的评论中可以看出:您是否从同一组图像中分割两次?这可能会在验证和测试数据中为您提供相同的图像,这反过来会导致错误的结果。

\n\n

查看您的培训代码会很有帮助。

\n