使用 Keras 和 Python 进行一类分类

Dre*_*day 3 python classification deep-learning conv-neural-network keras

介绍和问题:

我正在尝试制作一个一类分类卷积神经网络。一类是指我有一个图像数据集,其中包含大约 200 张 Nicolas Cage 的图像。通过一类分类,我的意思是查看图像并预测 1 如果此图像中包含 Nicolas Cage 并预测 0 Nicolas Cage 不包含在图像中。

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

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

数据集:

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


进口及型号:

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'))
Run Code Online (Sandbox Code Playgroud)

编译和图像增强

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")
Run Code Online (Sandbox Code Playgroud)

拟合模型

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
.
.
.
Run Code Online (Sandbox Code Playgroud)

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


绘制训练和测试准确度

训练和测试精度

绘制训练和测试损失

训练和测试损失


做出预测

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)
Run Code Online (Sandbox Code Playgroud)

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

Dre*_*day 11

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

  1. 我在训练和测试文件夹中添加了一个随机图像数据集。我基本上添加了一个“0”类。这些图像被标记为“not_nicolas”,我下载了与第一个数据集中相同数量的图像,大约有 200 张图像。所以我有 200 张尼古拉斯凯奇的图片和 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)
Run Code Online (Sandbox Code Playgroud)
  1. 我将优化器改为随机梯度下降而不是 Adam。
  2. shuffle = True在 flow_from_directory 中添加了一个参数来洗牌我们的图像,让我们的网络更好地泛化

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

  • 默认情况下,shuffle 为 True (2认同)

Seb*_*ars 5

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

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

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

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

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

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

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

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