【问题标题】:One class classification using Keras and Python使用 Keras 和 Python 进行一类分类
【发布时间】:2019-12-10 02:33:14
【问题描述】:

介绍和问题:

我正在尝试制作一个分类卷积神经网络。一类我的意思是我有一个图像数据集,其中包含大约 200 张尼古拉斯凯奇的图像。通过一个类别分类,我的意思是看一张图像,如果 Nicolas Cage 包含在该图像中,则预测为 1,然后预测为 0 Nicolas Cage 不包含在图像中。

我绝对是机器学习/深度学习的初学者,所以我希望有更多知识和经验的人可以帮助指导我朝着正确的方向前进。这是我现在的问题和疑问。我的网络表现非常糟糕。我尝试使用 Nicolas Cage 的图像进行一些预测,每次都预测为 0。

  • 我是否应该收集更多数据才能使其发挥作用?我正在使用包含 207 个图像的小型数据集执行数据增强。我希望数据增强能够帮助网络泛化,但我认为我错了
  • 我应该尝试调整 epoch 的数量、每个 epoch 的步数、val 步数,还是我用于梯度下降的优化算法?我正在使用 Adam,但我在想也许我应该尝试使用不同学习率的随机梯度下降?
  • 我应该添加更多卷积层还是密集层来帮助我的网络更好地泛化和学习?
  • 我是否应该停止尝试进行一类分类并转到正常的二元分类,因为使用具有一类分类的神经网络不太可行?我在这里看到了这篇文章one class classification with keras,看起来 OP 最终使用了隔离林。所以我想我可以尝试使用一些卷积层并输入隔离森林或 SVM?我找不到很多关于人们使用具有一类图像分类的隔离森林的信息或教程。

数据集:

这是我使用名为 google-images-download 的软件包收集的数据集的屏幕截图。它包含大约 200 张尼古拉斯凯奇的图像。我做了两次搜索以下载 500 张图片。在手动清理图像后,我只剩下 200 张 Nic Cage 的高质量图片。 Dataset


导入和模型:

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.layers import Dropout
from keras.layers import Activation

classifier = Sequential()

classifier.add(Conv2D(32, (3, 3), input_shape = (200, 200, 3), activation = 'relu'))
classifier.add(MaxPooling2D(pool_size = (2, 2)))

classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
classifier.add(MaxPooling2D(pool_size=(2, 2)))

classifier.add(Conv2D(64, (3, 3), activation = 'relu'))
classifier.add(MaxPooling2D(pool_size=(2, 2)))

classifier.add(Flatten())

classifier.add(Dense(units = 64, activation = 'relu'))

classifier.add(Dropout(0.5))

# output layer
classifier.add(Dense(1))
classifier.add(Activation('sigmoid'))

编译和图像增强

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


from keras.preprocessing.image import ImageDataGenerator

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

test_datagen = ImageDataGenerator(rescale = 1./255)

training_set = train_datagen.flow_from_directory('/Users/ginja/Desktop/Code/Nic_Cage/Small_Dataset/train/',
                                                 target_size = (200, 200),
                                                 batch_size = 32,
                                                 class_mode = "binary")

test_set = test_datagen.flow_from_directory('/Users/ginja/Desktop/Code/Nic_Cage/Small_Dataset/test/',
                                            target_size = (200, 200),
                                            batch_size = 32,
                                            class_mode = "binary")

拟合模型

history = classifier.fit_generator(training_set,
                         steps_per_epoch = 1000,
                         epochs = 25,
                         validation_data = test_set,
                         validation_steps = 500)

Epoch 1/25
1000/1000 [==============================] - 1395s 1s/step - loss: 0.0012 - acc: 0.9994 - val_loss: 1.0000e-07 - val_acc: 1.0000
Epoch 2/25
1000/1000 [==============================] - 1350s 1s/step - loss: 1.0000e-07 - acc: 1.0000 - val_loss: 1.0000e-07 - val_acc: 1.0000
Epoch 3/25
1000/1000 [==============================] - 1398s 1s/step - loss: 1.0000e-07 - acc: 1.0000 - val_loss: 1.0000e-07 - val_acc: 1.0000
Epoch 4/25
1000/1000 [==============================] - 1342s 1s/step - loss: 1.0000e-07 - acc: 1.0000 - val_loss: 1.0000e-07 - val_acc: 1.0000
Epoch 5/25
1000/1000 [==============================] - 1327s 1s/step - loss: 1.0000e-07 - acc: 1.0000 - val_loss: 1.0000e-07 - val_acc: 1.0000
Epoch 6/25
1000/1000 [==============================] - 1329s 1s/step - loss: 1.0000e-07 - acc: 1.0000 - val_loss: 1.0000e-07 - val_acc: 1.0000
.
.
.

该模型看起来收敛到 1.0000e-07 的损失值,因为这在其余时期不会改变


绘制的训练和测试准确度

Training and Test accuracy

绘制训练和测试损失

Training and Test loss


做出预测

from keras.preprocessing import image
import numpy as np 

test_image = image.load_img('/Users/ginja/Desktop/Code/Nic_Cage/nic_cage_predict_1.png', target_size = (200, 200))
#test_image.show()
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = classifier.predict(test_image)
training_set.class_indices
if result[0][0] == 1:
    prediction = 'This is Nicolas Cage'
else:
    prediction = 'This is not Nicolas Cage'

print(prediction)

对于预测,我们每次都会得到“这不是尼古拉斯凯奇”。 我感谢任何花时间阅读本文的人,我感谢任何对此部分的帮助。

【问题讨论】:

  • 它不是一类分类。这是二元分类你有多少图像有“尼古拉斯凯奇”,有多少图像没有尼古拉斯凯奇?基本上你有多少张图片 1 & 0 ?
  • 你不能只用一个类的样本来训练一个模型,类的最小数量是两个,所以你需要“不是尼古拉斯凯奇”的样本。
  • @MatiasValdenegro 如果我只是从谷歌图像中收集“不是尼古拉斯笼子”的随机图像,然后得到尼古拉斯笼子的图像,这会起作用吗?我的直觉是“不是尼古拉斯笼”的图像集太宽泛了,然后网络很难泛化。
  • @venkatakrishnan 是的,我认为这是我的问题,我只将图像设置为 1 而不是 0。所以 1 代表 Nicolas Cage 类。
  • 是的。尝试一些随机的图像作为非尼古拉斯笼。并在您的最终预测中设置一个阈值以使其绝对正确

标签: python keras deep-learning classification conv-neural-network


【解决方案1】:

每个人都倾向于二元分类方法。这可能是一个解决方案,但消除了可能是用一类分类器解决它的基本设计目标。 根据您希望使用一类分类器实现的目标,这可能是一个病态问题。 根据我的经验,您的最后一点通常适用。

https://arxiv.org/pdf/1801.05365.pdf中所述:

在经典的多类分类中,学习特征的目的是最大化类之间的类间距离并最小化类内的类内方差 [2]。然而,在没有多个类的情况下,这种区分方法是不可能的。

它产生了一个简单的解决方案。原因稍后解释:

这种方法最终产生微不足道的解决方案的原因是由于损失函数中没有考虑到网络判别能力的正则化项。例如,由于所有类标签都是相同的,因此可以通过使所有权重为零来获得零损失。确实,在只有普通椅子对象存在的封闭世界中,这是一个有效的解决方案。但是这样的网络在出现异常椅子物体时的判别能力为零

请注意,这里的描述是关于尝试使用一个类分类器来解决不同的类。一类分类器的另一个有用目标是检测异常,例如工厂操作信号。这就是我目前正在做的事情。在这种情况下,很难获得有关各种损坏状态的知识。破坏一台机器只是为了看看它在被破坏时是如何工作的,这样才能做出一个像样的多项式分类器,这将是荒谬的。下面描述了该问题的一种解决方案:https://arxiv.org/abs/1912.12502。注意,在本文中,由于类的随机相似性,也实现了类的判别能力。

我发现通过遵循所描述的指导方针,特别是删除最后一个激活函数,我的一类分类器可以正常工作,并且准确度没有给出 0 值。请注意,在您的情况下,您可能还希望删除二进制交叉熵,因为这需要二进制输入才有意义(使用 RMSE)。

此方法也应该适用于您的情况。在这种情况下,网络将能够确定哪些照片在数字上距离训练照片类别更远。然而,根据我的经验,由于图片中包含的差异,它可能仍然是一个难以解决的问题,例如不同的背景,角度等...为此,我要解决的问题要容易得多,因为同一条件阶段的操作条件之间的相似性要大得多。打个比方,在我的例子中,训练课更像是同一张图片,不同的噪音水平,只有轻微的物体移动。

【讨论】:

    【解决方案2】:

    如果有人从谷歌找到这个我想通了。我做了几件事:

    1. 我在我的训练和测试文件夹中添加了一个随机图像数据集。我基本上添加了一个“0”类。这些图像被标记为“not_nicolas”,我下载了与第一个数据集中相同数量的图像,大约 200 张图像。所以我有 200 张 Nicolas Cage 的图片和 200 张随机的图片。随机图片生成在这个链接https://picsum.photos/200/200/?random我只是用python脚本生成了200张图片。确保当您使用 flow_from_directory 时,它会按字母数字顺序读取文件夹。所以目录中的第一个文件夹将是类“0”。我花了太长时间才弄清楚这一点。
    path = "/Users/ginja/Desktop/Code/Nic_Cage/Random_images"
    
    for i in range(200):
        url = "https://picsum.photos/200/200/?random"
        response = requests.get(url)
        if response.status_code == 200:
            file_name = 'not_nicolas_{}.jpg'.format(i)
            file_path = path + "/" + file_name
            with open(file_path, 'wb') as f:
                print("saving: " + file_name)
                f.write(response.content)
    
    1. 我将优化器更改为随机梯度下降而不是 Adam。
    2. 我在 flow_from_directory 中添加了shuffle = True 作为参数来打乱我们的图像,让我们的网络更好地泛化

      我现在有 99% 的训练准确率和 91% 的测试准确率,我能够成功预测 Nicolas Cage 的图像!

    【讨论】:

    • 随机播放默认为真
    【解决方案3】:

    将您的问题视为监督问题:

    您正在解决人脸识别问题。如果您想区分“Nicolas Cage”或任何其他随机图像,您的问题就是二元分类问题。对于二进制分类,您需要有一个带有 0 标签的类或不是“Nicolas Cage”类。

    如果我举一个非常著名的例子,那就是 Hotdog-Not-Hotdog 问题(硅谷)。 这些链接可能会对您有所帮助。

    https://towardsdatascience.com/building-the-hotdog-not-hotdog-classifier-from-hbos-silicon-valley-c0cb2317711f

    https://github.com/J-Yash/Hotdog-Not-Hotdog/blob/master/Hotdog_classifier_transfer_learning.ipynb

    将您的问题视为无监督问题:

    在此您可以将图像表示为嵌入向量。将您的 Nicolas Cage 图像传递到预训练的 facenet 中,这将为您提供人脸嵌入并绘制该嵌入以查看每张图像之间的关系。

    https://paperswithcode.com/paper/facenet-a-unified-embedding-for-face

    【讨论】:

    • 我终于弄明白了,感谢您的帮助和您的时间!
    猜你喜欢
    • 2017-09-27
    • 1970-01-01
    • 2018-02-17
    • 2020-06-18
    • 2021-02-17
    • 2016-12-27
    • 2018-04-12
    • 2019-08-08
    相关资源
    最近更新 更多