Я пытаюсь придумать модель Keras, которая делала бы двоичную классификацию последовательностей изображений. Я разбил свой набор данных на обучающие (1200 выборок), действительные (320 выборок) и тестовые (764 выборки) разделы.
На первом шаге я использовал InceptionV3 для извлечения объектов из моего набора данныхсостоящий из последовательностей изображений. Я сохранил эти последовательности извлеченных функций на диск.
Следующим шагом является подача этих последовательностей в качестве входных данных в мою модель на основе LSTM.
Вот минимальный работоспособный пример, который компилируется:
import numpy as np
import copy
import random
from keras.layers.recurrent import LSTM
from keras.layers import Dense, Flatten, Dropout
from keras.models import Sequential
from keras.optimizers import Adam
def get_sequence(sequence_no, sample_type):
# Load sequence from disk according to 'sequence_no' and 'sample_type'
# eg. 'sample_' + sample_type + '_' + sequence_no
return np.zeros([100, 2048]) # added only for demo purposes, so it compiles
def frame_generator(batch_size, generator_type, labels):
# Define list of sample indexes
sample_list = []
for i in range(0, len(labels)):
sample_list.append(i)
# sample_list = [0, 1, 2, 3 ...]
while 1:
X, y = [], []
# Generate batch_size samples
for _ in range(batch_size):
# Reset to be safe
sequence = None
# Get a random sample
random_sample = random.choice(sample_list)
# Get sequence from disk
sequence = get_sequence(random_sample, generator_type)
X.append(sequence)
y.append(labels[random_sample])
yield np.array(X), np.array(y)
# Data mimicking
train_samples_num = 1200
valid_samples_num = 320
labels_train = np.random.randint(0, 2, size=(train_samples_num), dtype='uint8')
labels_valid = np.random.randint(0, 2, size=(valid_samples_num), dtype='uint8')
batch_size = 32
train_generator = frame_generator(batch_size, 'train', labels_train)
val_generator = frame_generator(batch_size, 'valid', labels_valid)
# Model
model = Sequential()
model.add(LSTM(2048, input_shape=(100, 2048), dropout = 0.5))
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer=Adam(), metrics=['accuracy'])
model.summary()
# Training
model.fit_generator(
generator=train_generator,
steps_per_epoch=len(labels_train) // batch_size,
epochs=10,
validation_data=val_generator,
validation_steps=len(labels_valid) // batch_size)
Этот пример был скомпилирован с:
python 3.6.8
keras 2.2.4
tensorflow 1.13.1
Это хорошо работает, япровел 3 тренинга по этой модели (та же самая установка, тот же состав / действительный / тестовый раздел), и средняя точность теста составила 96,9%.
Тогда я начал спрашивать себя, является ли это хорошей идеей всегдавыберите полностью случайную выборку из списка выборок внутри функции frame_generator()
, в частности, строки
random_sample = random.choice(sample_list)
. Выборка образцов полностью случайным образом делает возможным, чтобы некоторые образцы использовались намного больше, чем другие, на тренировке. Кроме того, некоторые из них, возможно, никогда не будут использованы. Это заставило меня думать, что модель будет меньше обобщать в этой настройке по сравнению с ней, если она будет видеть образцы одинаково во время тренировки.
Как исправление, изменения применяются к frame_generator()
:
Сделайте резервную копию списка образцов, а затем каждый раз удаляйте образец из списка образцов после его использования. Когда список образцов станет пустым, замените список образцов его резервной копией. Делайте это на протяжении всей тренировки. Это гарантирует, что ALL образцов будут видны моделью во время тренировки с почти той же частотой .
Это новая версия frame_generator()
. Отмеченные #ADDED
добавлены 4 строки:
def frame_generator(batch_size, generator_type, labels):
# Define list of sample indexes
sample_list = []
for i in range(0, len(labels)):
sample_list.append(i)
# sample_list = [0, 1, 2, 3 ...]
sample_list_backup = copy.deepcopy(sample_list) # ADDED
while 1:
X, y = [], []
# Generate batch_size samples
for _ in range(batch_size):
# Reset to be safe
sequence = None
# Get a random sample
random_sample = random.choice(sample_list)
sample_list.remove(random_sample) # ADDED
if len(sample_list) == 0: # ADDED
sample_list = copy.deepcopy(sample_list_backup) # ADDED
# Get sequence from disk
sequence = get_sequence(random_sample, generator_type)
X.append(sequence)
y.append(labels[random_sample])
yield np.array(X), np.array(y)
То, что я ожидал:
Учитывая, что теперь модель должна быть способна обобщать немного лучше, из-зачтобы гарантировать, что все образцы видны на тренировках с одинаковой частотой, я ожидал, что точность теста немного увеличится.
Что произошло:
Я провел 3 тренировки по этой модели (один и тот же поезд / действительный / тестовый раздел) и средняя точность теста на этот раз составила 96,2%. Это снижение точности на 0,7% по сравнению с первой установкой. Так что, похоже, модель сейчас обобщает хуже.
Графики потерь для каждого прогона:

Вопрос:
Почему большая случайность в frame_generator()
приводит к лучшей точности набора тестов?
Мне кажется довольно нелогичным.