K-кратная перекрестная проверка в keras с одним выходом для двоичного класса

0

Я работаю со сверточной нейронной сетью, которую я использую для классификации кошек и собак, которая имеет только один выход для двух классов. Мне нужно использовать k-кратную перекрестную проверку, чтобы найти, какие породы домашних животных дают наилучшую точность проверки. Самый близкий ответ на мою проблему заключается в следующем: перекрестная проверка K-сгиба с использованием keras , но она, по-видимому, не использует исходную сетевую модель и не работает для групп домашних животных с разными породами.

Внутри группы 1, 2 и 3 у меня есть 2 папки под названием Pets, а внутри каждой папки Pets у меня есть две папки, которые являются моими классами: Cats и Dogs: Например:

Group 1/
       Pets 1/
          cats/
            breeds_1_cats001.jpeg
            breeds_1_cats002.jpeg
     
           dogs/
            breeds_1_dogs001.jpeg
            breeds_1_dogs002.jpeg
      Pets 2/
          cats/
            breeds_2_cats001.jpeg
            breeds_2_cats002.jpeg
     
          dogs/
            breeds_2_dogs001.jpeg
            breeds_2_dogs002.jpeg
Group 2/
      Pets 1/
          cats/
            breeds_3_cats001.jpeg
            breeds_3_cats002.jpeg
           
          dogs/
            breeds_3_dogs001.jpeg
            breeds_3_dogs002.jpeg
      Pets 2/
          cats/
            breeds_4_cats001.jpeg
            breeds_4_cats002.jpeg
           
           dogs/
            breeds_4_dogs001.jpeg
            breeds_4_dogs002.jpeg
Group 3/
       Pets 1/
          cats/
            breeds_5_cats001.jpeg
            breeds_5_cats002.jpeg
           
          dogs/
            breeds_5_dogs001.jpeg
            breeds_5_dogs002.jpeg
      Pets 2/
          cats/
            breeds_6_cats001.jpeg
            breeds_6_cats002.jpeg
           
          dogs/
            breeds_6_dogs001.jpeg
            breeds_6_dogs002.jpeg
                  

Я хочу использовать kfold и использовать в качестве индексов мои группы.

Например: используйте группу 1 и группу 2 как тренировку, группу 3 как проверку. Затем группы 1 и 3 в качестве обучения и группы 2 в качестве проверки и, наконец, используют группу 2 и группу 3 в качестве обучения и группу 1 в качестве проверки.

Я выделил фиктивный набор данных, чтобы объяснить мои цели.

Моя проблема в том, что я не знаю, как использовать k-fold для заданных нескольких групп во вложенной папке с двоичными классами, где я использую генераторы данных для обучения и тестирования с двоичным выводом. Мне нужно использовать k-кратное увеличение для моей сверточной нейронной сети без необходимости изменять мои данные, дополняющие или разрушая мои слои, чтобы найти лучшую точность проверки и сохранить их веса, вот моя нейронная сеть:

        from keras.preprocessing.image import ImageDataGenerator
        from keras.models import Sequential
        from keras.layers import Conv2D, MaxPooling2D
        from keras.layers import Activation, Dropout, Flatten, Dense
        from keras import backend as K
        import numpy as np
        from keras.preprocessing import image
    
    img_width, img_height = 128, 160
    
    
    train_data_dir = '../input/pets/pets train'
    validation_data_dir = '../input/pets/pets testing'
    nb_train_samples = 4850 
    nb_validation_Samples = 3000 
    epochs = 100
    batch_size = 16
    
    
    if K.image_data_format() == 'channels_first':
       input_shape = (3, img_width, img_height)
    else:
       input_shape = (img_width, img_height, 3)
    
    train_datagen = ImageDataGenerator(
        zoom_range=0.2,
        rotation_range=40,
       horizontal_flip=True,
    )
    
    test_datagen = ImageDataGenerator(rescale=1./255)
    
        train_generator = train_datagen.flow_from_directory(
           train_data_dir,
           target_size=(img_width, img_height),
           batch_size=batch_size,
           class_mode='binary')
    
        validation_generator = test_datagen.flow_from_directory(
            validation_data_dir,
            target_size=(img_width, img_height),
            batch_size=batch_size,
            class_mode="binary")

model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=input_shape))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dropout(0.25))


model.add(Dense(64))
model.add(Dropout(0.5))


model.add(Dense(1))
model.add(Activation('sigmoid'))
model.summary()

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


model.fit_generator(
    train_generator,
    steps_per_epoch=nb_train_samples // batch_size,
    epochs=epochs,
    validation_data = validation_generator,
    validation_steps = nb_validation_Samples // batch_size)

model.save_weights('pets-weights.npy')
1
+50

Вы не сможете использовать, ImageDataGeneratorпотому что, согласно документации, cross_val_score ожидает массив формы (n_samples, features).

Что вы можете сделать, это загрузить ваши изображения в память и создать собственный разделитель резюме. У меня есть такая папка:

group1/
    cats/
        breeds_5_cats001.jpeg
        breeds_5_cats002.jpeg
    dogs/
        breeds_4_dogs001.jpeg
        breeds_4_dogs002.jpeg
group2/
    cats/
        breeds_5_cats001.jpeg
        breeds_5_cats002.jpeg
    dogs/
        breeds_4_dogs001.jpeg
        breeds_4_dogs002.jpeg
group3/
    cats/
        breeds_5_cats001.jpeg
        breeds_5_cats002.jpeg
    dogs/
        breeds_4_dogs001.jpeg
        breeds_4_dogs002.jpeg

Я начал с подстановки имен файлов и их группировки. Вам нужно будет немного изменить шаблон глобуса, так как моя структура каталогов немного отличается. Все, что для этого нужно, - это получить ВСЕ фотографии в любом порядке.

from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import cross_val_score
import numpy as np
from tensorflow.keras.layers import *
from tensorflow.keras import Sequential
import os
from glob2 import glob
from itertools import groupby
from itertools import accumulate
import cv2
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
import tensorflow as tf
tf.config.experimental.list_physical_devices('GPU')
os.chdir('c:/users/nicol/documents/datasets/catsanddogs')

filenames = glob('*/*/*.jpg')

groups = [list(v) for k, v in groupby(sorted(filenames), key=lambda x: x.split(os.sep)[0])]
lengths = [0] + list(accumulate(map(len, groups)))
groups = [i for s in groups for i in s]
['group1\\cats\\cat.4001.jpg',
 'group1\\cats\\cat.4002.jpg',
 'group1\\cats\\cat.4003.jpg',
 'group1\\cats\\cat.4004.jpg',
 'group1\\cats\\cat.4005.jpg',
 'group1\\cats\\cat.4006.jpg',
 'group1\\cats\\cat.4007.jpg',
 'group1\\cats\\cat.4008.jpg',
 'group1\\cats\\cat.4009.jpg',
 'group1\\cats\\cat.4010.jpg']

Затем я загрузил все изображения в массив и сделал массив из 0 и 1 для категорий. Вам нужно будет настроить это в соответствии со структурой каталогов.

images = list()

for image in filenames:
    array = cv2.imread(image)/255
    resized = cv2.resize(array, (32, 32))
    images.append(resized)

X = np.array(images).astype(np.float32)

y = np.array(list(map(lambda x: x.split(os.sep)[1] == 'cats', groups))).astype(int)

Затем я построил KerasClassifier:

def build_model():
    model = Sequential()
    model.add(Conv2D(32, (3, 3), input_shape=(32, 32, 3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Flatten())
    model.add(Dropout(0.25))
    model.add(Dense(64))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    model.add(Activation('sigmoid'))
    model.summary()

    model.compile(loss='binary_crossentropy',
                  optimizer='rmsprop',
                  metrics=['mse', 'accuracy'])
    return model


keras_clf = KerasClassifier(build_fn=build_model, epochs=1, batch_size=16, verbose=0)

Затем я сделал специальный разделитель резюме, как описано здесь :

def three_fold_cv():
    i = 1
    while i <= 3:
        min_length = lengths[i - 1]
        max_length = lengths[i]
        idx = np.arange(min_length, max_length, dtype=int)
        yield idx, idx
        i += 1

Затем я создал индивидуальный разделитель резюме и провел обучение:

tfc = three_fold_cv()
accuracies = cross_val_score(estimator=keras_clf, scoring="accuracy", X=X, y=y, cv=tfc)

print(accuracies)

Выход:

[0.648 0.666 0.73 ]

Полный код:

from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import cross_val_score
import numpy as np
from tensorflow.keras.layers import *
from tensorflow.keras import Sequential
import os
from glob2 import glob
from itertools import groupby
from itertools import accumulate
import cv2
os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
import tensorflow as tf
tf.config.experimental.list_physical_devices('GPU')
os.chdir('c:/users/nicol/documents/datasets/catsanddogs')

filenames = glob('*/*/*.jpg')

groups = [list(v) for k, v in groupby(sorted(filenames), key=lambda x: x.split(os.sep)[0])]
lengths = [0] + list(accumulate(map(len, groups)))
groups = [i for s in groups for i in s]


images = list()

for image in filenames:
    array = cv2.imread(image)/255
    resized = cv2.resize(array, (32, 32))
    images.append(resized)

X = np.array(images).astype(np.float32)

y = np.array(list(map(lambda x: x.split(os.sep)[1] == 'cats', groups))).astype(int)


def build_model():
    model = Sequential()
    model.add(Conv2D(32, (3, 3), input_shape=(32, 32, 3)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Flatten())
    model.add(Dropout(0.25))
    model.add(Dense(64))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    model.add(Activation('sigmoid'))
    model.summary()

    model.compile(loss='binary_crossentropy',
                  optimizer='rmsprop',
                  metrics=['mse', 'accuracy'])
    return model


keras_clf = KerasClassifier(build_fn=build_model, epochs=1, batch_size=16, verbose=0)

def three_fold_cv():
    i = 1
    while i <= 3:
        min_length = lengths[i - 1]
        max_length = lengths[i]
        idx = np.arange(min_length, max_length, dtype=int)
        yield idx, idx
        i += 1

tfc = three_fold_cv()
accuracies = cross_val_score(estimator=keras_clf, scoring="accuracy", X=X, y=y, cv=tfc)

print(accuracies)
[0.648 0.666 0.73 ]

Вот пример копирования / вставки с набором данных MNIST:

from tensorflow.keras.wrappers.scikit_learn import KerasClassifier
from sklearn.model_selection import cross_val_score
import numpy as np
from tensorflow.keras.layers import *
from tensorflow.keras import Sequential
from itertools import accumulate
import tensorflow as tf

# Here's your dataset.
(xtrain, ytrain), (_, _) = tf.keras.datasets.mnist.load_data()

# You have three groups, as you wanted. They are 20,000 each.
x_group1, y_group1 = xtrain[:20_000], ytrain[:20_000]
x_group2, y_group2 = xtrain[20_000:40_000:], ytrain[20_000:40_000:]
x_group3, y_group3 = xtrain[40_000:60_000], ytrain[40_000:60_000]

# You need the accumulated lengths of the datasets: [0, 20000, 40000, 60000]
lengths = [0] + list(accumulate(map(len, [y_group1, y_group2, y_group3])))

# Now you need all three in a single dataset.
X = np.concatenate([x_group1, x_group2, x_group3], axis=0)[..., np.newaxis]
y = np.concatenate([y_group1, y_group2, y_group3], axis=0)


# KerasClassifier needs a model building function.
def build_model():
    model = Sequential()
    model.add(Conv2D(32, (3, 3), input_shape=(28, 28, 1)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Flatten())
    model.add(Dropout(0.25))
    model.add(Dense(64))
    model.add(Dropout(0.5))
    model.add(Dense(1))
    model.add(Activation('sigmoid'))
    model.summary()

    model.compile(loss='binary_crossentropy',
                  optimizer='rmsprop',
                  metrics=['mse', 'accuracy'])
    return model


# Creating the KerasClassifier.
keras_clf = KerasClassifier(build_fn=build_model, epochs=1, batch_size=16, verbose=0)


# Creating the custom Cross-validation splitter. Splits are based on `lengths`.
def three_fold_cv():
    i = 1
    while i <= 3:
        min_length = lengths[i - 1]
        max_length = lengths[i]
        idx = np.arange(min_length, max_length, dtype=int)
        yield idx, idx
        i += 1

accuracies = cross_val_score(estimator=keras_clf, scoring="accuracy", X=X, y=y, cv=three_fold_cv())

print(accuracies)
5
  • Я также могу сделать вам пример с набором данных игрушек, если папка слишком запутана. 6 сен '20 в 1:27
  • Если бы это не было проблемой, я был бы признателен. 6 сен '20 в 16:35
  • Итак, должен ли я помещать собак и кошек в одну группу, прежде чем загружать их в память, и назначать ярлыки? Например: 'dataset / group_1 / cat1.jpg, dog1.jpg' затем назначить y_1 = [0, 1] для каждой группы? 6 сен '20 в 19:13
  • Я не понимаю, как следует загружать разделение классов для каждой группы при назначении индексов функции cv 6 сен '20 в 19:26
  • Да, они не обязательно должны быть в одной группе. Все, что вам нужно, это 3 группы имен файлов, все смешанные. 6 сен '20 в 19:29