Вопрос или проблема
Я создаю многозадачную модель CNN и у меня есть два разных свойства классификации (одно с 10 классами, второе с 5 классами), и моя структура папок выглядит следующим образом:
-Train - image1.jpg ... - imageN.jpg -Test - image1.jpg ... - imageN.jpg -Vald - image1.jpg ... - imageN.jpg
И метки находятся в csv файле как propA, propB. Так, одно изображение будет иметь два класса, один из свойства A и один из свойства B.
Модель использует VGG16 :
baseModel = VGG16(weights="imagenet", include_top=False,input_tensor=Input(shape=(img_size, img_size, 3)))
flatLayer = baseModel.output
sharedLayer = Flatten(name="flatten")(flatLayer)
sharedLayer = Dense(1024,name="Shared")(sharedLayer)
sharedLayer = Dropout(0.5)(sharedLayer)
task1 = Dense(512, activation="relu")(sharedLayer)
task1 = Dense(10, activation="softmax",name="PFR")(task1)
task2 = Dense(512, activation="relu")(sharedLayer)
task2 = Dense(5, activation="softmax",name="FT")(task2)
model3 = Model(inputs=baseModel.input, outputs=[task1,task2])
Количество изображений велико, поэтому я не могу загрузить их в память и мне нужно использовать функциональность наподобие flow_from_directory. Но в моей учебной директории нет подкаталогов классов, потому что невозможно создать каталоги классов, так как в общей сложности 15 классов, и я не уверен, для какого свойства создавать подкаталоги классов. (и flow_from_directory не работает, если нет подкаталогов классов)
Метки доступны в массиве, propALab и propBLab.
Пока мне не удалось найти что-то полезное, может кто-нибудь помочь?
Вы пробовали создать собственный генератор данных для вашего случая? Структура для него приведена ниже. Вам понадобится метод __data_generation, такой что вы возвращаете y как массив из 2 элементов, с первым элементом, являющимся вашими метками для задачи 1, и вторым элементом для задачи 2.
import numpy as np
import keras
class DataGenerator(keras.utils.Sequence):
'Generates data for Keras'
def __init__(self, list_IDs, labels, batch_size=32, dim=(32,32,32), n_channels=1,
n_classes=10, shuffle=True):
'Initialization'
self.dim = dim
self.batch_size = batch_size
self.labels = labels
self.list_IDs = list_IDs
self.n_channels = n_channels
self.n_classes = n_classes
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
'Denotes the number of batches per epoch'
return int(np.floor(len(self.list_IDs) / self.batch_size))
def __getitem__(self, index):
'Generate one batch of data'
# Generate indexes of the batch
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
# Find list of IDs
list_IDs_temp = [self.list_IDs[k] for k in indexes]
# Generate data
X, y = self.__data_generation(list_IDs_temp)
return X, y
def on_epoch_end(self):
'Updates indexes after each epoch'
self.indexes = np.arange(len(self.list_IDs))
if self.shuffle == True:
np.random.shuffle(self.indexes)
def __data_generation(self, list_IDs_temp):
'Generates data containing batch_size samples' # X : (n_samples, *dim, n_channels)
# Initialization
X = np.empty((self.batch_size, *self.dim, self.n_channels))
y = np.empty((self.batch_size), dtype=int)
# Generate data
for i, ID in enumerate(list_IDs_temp):
# Store sample
X[i,] = np.load('data/' + ID + '.npy')
# Store class
y[i] = self.labels[ID]
return X, keras.utils.to_categorical(y, num_classes=self.n_classes)
Затем вы можете использовать свой собственный генератор с созданной моделью, как показано ниже:
training_generator = DataGenerator(partition[‘train’], labels, **params)
validation_generator = DataGenerator(partition[‘validation’], labels, **params)
# Design model
model = Sequential()
[...] # Architecture
model.compile()
# Train model on dataset
model.fit_generator(generator=training_generator,
validation_data=validation_generator,
use_multiprocessing=True,
workers=6)
Коды были взяты с https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly
.
Ответ или решение
Создание сложной модели с несколькими задачами в Keras, такой как многозадачная CNN с использованием VGG16, часто требует нестандартного подхода, особенно когда мы сталкиваемся с ограничениями, такими как отсутствие подкаталогов классов в директории изображений. Давайте подробно рассмотрим решение этой задачи, задействовав пользовательский генератор данных, который позволит обрабатывать изображения и соответствующие метки напрямую из основного каталога.
Теория
В Keras метод flow_from_directory
удобно использовать, когда изображения организованы по подкаталогам, соответствующим классам. Однако в вашей задаче такая структура отсутствует, так как у каждого изображения две независимые классификации. Вместо этого вы можете создать пользовательский генератор данных, который решает эту проблему, извлекая изображения напрямую и соотнося их с метками из заранее подготовленных массивов меток propALab
и propBLab
.
Пример
Один из способов объединить задачи мультиклассовой классификации — создать наследуемый от keras.utils.Sequence
пользовательский генератор данных. Этот генератор позволит загружать изображения по частям, тем самым не загружая всё в оперативную память, и одновременно подготавливать к ним массивы меток для обеих задач. Пример имплементации:
import numpy as np
import keras
from keras.preprocessing import image
class DataGenerator(keras.utils.Sequence):
'Генератор данных для Keras'
def __init__(self, list_IDs, labelsA, labelsB, batch_size=32, dim=(224,224), n_channels=3,
n_classesA=10, n_classesB=5, shuffle=True):
self.dim = dim
self.batch_size = batch_size
self.labelsA = labelsA
self.labelsB = labelsB
self.list_IDs = list_IDs
self.n_channels = n_channels
self.n_classesA = n_classesA
self.n_classesB = n_classesB
self.shuffle = shuffle
self.on_epoch_end()
def __len__(self):
return int(np.floor(len(self.list_IDs) / self.batch_size))
def __getitem__(self, index):
indexes = self.indexes[index*self.batch_size:(index+1)*self.batch_size]
list_IDs_temp = [self.list_IDs[k] for k in indexes]
X, yA, yB = self.__data_generation(list_IDs_temp)
return X, [yA, yB]
def on_epoch_end(self):
self.indexes = np.arange(len(self.list_IDs))
if self.shuffle:
np.random.shuffle(self.indexes)
def __data_generation(self, list_IDs_temp):
X = np.empty((self.batch_size, *self.dim, self.n_channels))
yA = np.empty((self.batch_size), dtype=int)
yB = np.empty((self.batch_size), dtype=int)
for i, ID in enumerate(list_IDs_temp):
img = image.load_img(ID, target_size=self.dim)
X[i,] = image.img_to_array(img) / 255.0
yA[i] = self.labelsA[ID]
yB[i] = self.labelsB[ID]
return X, keras.utils.to_categorical(yA, num_classes=self.n_classesA), keras.utils.to_categorical(yB, num_classes=self.n_classesB)
Применение
После написания пользовательского генератора, его можно использовать для обучения вашей модели. Внизу представлен пример того, как можно воспользоваться этим генератором с моделью:
train_generator = DataGenerator(list_IDs=train_list, labelsA=propALab_train, labelsB=propBLab_train)
val_generator = DataGenerator(list_IDs=val_list, labelsA=propALab_val, labelsB=propBLab_val)
model3.compile(optimizer='adam', loss=['categorical_crossentropy', 'categorical_crossentropy'], metrics=['accuracy'])
model3.fit(train_generator, validation_data=val_generator, epochs=10, workers=4, use_multiprocessing=True)
Заключение
Создание пользовательского генератора данных — это мощное решение для задач, где структура данных не позволяет использовать стандартные функции Keras. Этот подход не только очищает память, обрабатывая данные партиями, но и позволяет точно настроить обработку данных в соответствии с конкретными требованиями многозадачных моделей.