Классифицируйте необученный объект как неизвестный объект в машинном обучении, используя python - PullRequest
0 голосов
/ 13 ноября 2018

Я новичок в машинном обучении. В настоящее время я работаю над проектом по классификации фруктов и овощей с использованием глубокого изучения кераса. Я смог тренировать модель. До сих пор он может правильно определять уже обученные объекты, но когда я предоставляю другой фрукт / овощ, который не был обучен, это предсказывает обученная модель, тогда как он должен распознавать его как неизвестный фрукт.

Предположим, я тренировал модель с изображениями Apple, Potato и Banana. Пока я предоставляю изображения Apple / Potato / Banana, это правильно предсказывает. Но в тот момент, когда я предоставляю изображение Апельсина, он предсказывает, как Картофель или лимон предсказывает как Яблоко. Вот фрагмент кода, который я использую для обучения и прогнозирования фруктов:

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)

Как преодолеть эту ситуацию? Любая помощь будет оценена.

1 Ответ

0 голосов
/ 14 ноября 2018

При обучении с loss='categorical_crossentropy' и activation='softmax' на последнем уровне каждый класс представлен 1 узлом на графике на последнем уровне. Softmax обеспечивает суммирование всех узлов до 1 путем нормализации всех значений.
Категориальная кроссцентропия предполагает, что узел с наибольшим значением является прогнозируемым классом, и сравнивает его с метками.

Например, после прогнозирования выборки у вас могут быть следующие значения для узлов: 0.33 banana, 0.33 apple, 0.34 potato, таким образом, potato будет классом, выбранным для прогнозирования.

Если вы хотите иметь прогноз «неизвестный класс», вы должны добавить его в качестве еще одного возможного класса и иметь некоторые образцы, помеченные как неизвестный класс во время обучения.

Редактировать:
Обратите внимание, что хотя то, что я предложил выше, может работать для вас, обучение может быть очень трудным, поскольку может потребоваться большее количество выборок, как известных, так и неизвестных, поскольку «неизвестные» функции сложнее обобщить и могут повредить другие обобщение классов.

Более практичным способом было бы определить порог, как я упоминал ранее, после предсказания, что вы получите какое-то значение для каждого из возможных ярлыков. При выборе метки это значение является показателем достоверности метки.
Изучив приведенный выше пример, мы выберем картофель с уверенностью 0,34.
Вы можете определить какой-то порог, скажем, 0,6, и, принимая метку для изображения, проверить другую доверительную оценку, если самая высокая («выбранная») метка ниже порога, вы можете пометить его как «неизвестный фрукт».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...