使用Python在机器学习中将未经训练的对象分类为未知对象

问题描述 投票:0回答:1

我是机器学习领域的新手。目前,我正在开发一个使用 keras 深度学习的水果/蔬菜分类项目。我能够训练模型。到目前为止,它可以正确检测已经训练过的对象,但是当我提供其他未经训练的水果/蔬菜时,它会根据训练后的模型进行预测,而它应该将其识别为未知水果。

假设我用苹果、马铃薯和香蕉图像训练了一个模型。只要我提供苹果/土豆/香蕉的图像,它的预测就会正确。但当我提供橙子的图像时,它会像土豆或柠檬一样预测苹果。这是我用来训练和预测水果的代码片段:

import os.path
import numpy as np
np.random.seed(123)
from keras import applications
from keras.models import Sequential
from keras.layers import Dropout, Flatten, Dense
from keras.utils.np_utils import to_categorical
from keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img
from keras.layers.normalization import BatchNormalization
import matplotlib.pyplot as plt
import math
import cv2   

#dimensions of images
img_width, img_height = 224, 224  

#file paths & directories
top_model_weights_path = 'bottleneck_fc_model.h5'
bottleneck_train_path = 'bottleneck_features_train.npy'
bottleneck_validation_path = 'bottleneck_features_validation.npy'
train_data_dir = 'data/train'
validation_data_dir = 'data/validation/'

#hyperparameters
epochs = 10
batch_size = 16


def save_bottleneck_features():
    model = applications.VGG16(include_top=False, weights='imagenet')

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

    generator = datagen.flow_from_directory(train_data_dir,
                                            target_size=(img_width, img_height),
                                            batch_size=batch_size,
                                            shuffle=False)

    no_train_samples = len(generator.filenames)

    predict_size_train = int(math.ceil(no_train_samples / batch_size))
    bottleneck_features_train = model.predict_generator(generator, predict_size_train)
    np.save(bottleneck_train_path, bottleneck_features_train)

    datagen = ImageDataGenerator(rescale=1./255)

    generator = datagen.flow_from_directory(validation_data_dir,
                                            target_size=(img_width, img_height),
                                            batch_size=batch_size,
                                            class_mode=None,
                                            shuffle=False)

    no_validation_samples = len(generator.filenames)

    predict_size_validation = int(math.ceil(no_validation_samples / batch_size))
    bottleneck_features_validation = model.predict_generator(generator, predict_size_validation)
    np.save(bottleneck_validation_path, bottleneck_features_validation)



def train_top_model():
    datagen_top = ImageDataGenerator(rescale=1./255)

    generator_top = datagen_top.flow_from_directory(train_data_dir, 
                                                    target_size=(img_width, img_height),
                                                    batch_size=batch_size,
                                                    class_mode='categorical',
                                                    shuffle=False)

    num_classes = len(generator_top.class_indices)

    # save the class indices to use later in predictions
    np.save('class_indices.npy', generator_top.class_indices)

    # get the class labels for the training data, in the original order
    train_labels = generator_top.classes
    # convert the training labels to categorical vectors
    train_labels = to_categorical(train_labels, num_classes=num_classes)

    generator_top = datagen_top.flow_from_directory(validation_data_dir,
                                                    target_size=(img_width, img_height),
                                                    batch_size=batch_size,
                                                    class_mode=None,
                                                    shuffle=False)

    validation_labels = generator_top.classes
    validation_labels = to_categorical(validation_labels, num_classes=num_classes)

    # load the bottleneck features saved earlier
    train_data = np.load('bottleneck_features_train.npy')
    validation_data = np.load('bottleneck_features_validation.npy')


    # build the model
    model = Sequential()
    model.add(Flatten(input_shape=train_data.shape[1:]))
    model.add(BatchNormalization())
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(num_classes, activation='softmax'))

    model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])

    history = model.fit(train_data, train_labels, epochs=epochs, batch_size=batch_size)

    model.save_weights(top_model_weights_path)


def predict(image_path):
    class_dictionary = np.load('class_indices.npy').item()
    #print("Values: ",class_dictionary)

    num_classes = len(class_dictionary)

    orig = cv2.imread(image_path)

    print('[INFO] loading and preprocessing image...')
    image = load_img(image_path, target_size=(224, 224))
    image = img_to_array(image)
    image = image / 255
    image = np.expand_dims(image, axis=0)

    model = applications.VGG16(include_top=False, weights='imagenet')

    bottleneck_prediction = model.predict(image)

    # build top model
    model = Sequential()
    model.add(Flatten(input_shape=bottleneck_prediction.shape[1:]))
    model.add(BatchNormalization())
    model.add(Dense(256, activation='relu'))
    model.add(Dropout(0.5))
    model.add(Dense(num_classes, activation='sigmoid'))

    model.load_weights(top_model_weights_path)

    # use the bottleneck prediction on the top model to get the final classification
    class_predicted = model.predict_classes(bottleneck_prediction)

    probabilities = model.predict_proba(bottleneck_prediction)

    inID = class_predicted[0]

    inv_map = {v: k for k, v in class_dictionary.items()}

    label = inv_map[inID]

    print("Image ID: {}, Label: {}".format(inID, label))

    cv2.putText(orig, "Predicted: {}".format(label), (10, 30),
                cv2.FONT_HERSHEY_PLAIN, 1.5, (0, 0, 0), 2)

    cv2.imshow('Classification', orig)
    cv2.imwrite('predicted.jpg', orig)
    cv2.waitKey(0)


if __name__ == '__main__': 
    if not os.path.exists(bottleneck_train_path):
        save_bottleneck_features()
    if not os.path.exists(top_model_weights_path):
        train_top_model()

    image_path = 'fruits/orange.jpg'  
    predict(image_path)

如何克服这种情况?任何帮助将不胜感激。

python machine-learning keras deep-learning python-3.6
1个回答
0
投票

当在最后一层使用

loss='categorical_crossentropy'
activation='softmax'
进行训练时,每个类由最后一层图中的 1 个节点表示。
softmax
通过标准化所有值,确保所有节点的值之和为 1。
categorial_crossentropy
假设具有最高值的节点是预测类别,并将其与标签进行比较。

例如,在预测样本后,您可能可以获得以下节点值:
0.33 banana, 0.33 apple, 0.34 potato
,因此马铃薯将成为为预测选择的类。

如果您希望进行“未知类别”预测,则应将其添加为另一个可能的类别,并在训练期间将一些样本标记为未知类别。

编辑:
请注意,虽然我上面的建议可能对您有用,但训练它可能非常困难,因为它可能需要大量已知和未知的样本,因为“未知”特征更难概括,并且可能会产生负面影响其他类的概括。

正如我之前提到的,一种更实用的方法是定义一个阈值,在预测之后,您将收到每个可能标签的值。选择标签时,该值是标签的“置信度”分数。
检查上面的例子,我们将以 0.34 的置信度挑选土豆。
您可以定义一个阈值,例如 0.6,然后在假设图像的标签时检查置信度得分。如果最高(“选择”)标签低于阈值,您可以将其标记为“未知水果”。

另一个编辑:
此后出现了一些关于该主题的好作品,供将来参考: https://github.com/hendrycks/outlier-exposure

将是一个很好的起点。

© www.soinside.com 2019 - 2024. All rights reserved.