Почему большая случайность в генераторе данных приводит к лучшей точности набора тестов? - PullRequest
1 голос
/ 25 октября 2019

Я пытаюсь придумать модель 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% по сравнению с первой установкой. Так что, похоже, модель сейчас обобщает хуже.

Графики потерь для каждого прогона:

Losses plot

Вопрос:

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

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