Минимизация максимальных потерь в пакете дополненных данных - PullRequest
0 голосов
/ 12 июля 2020

Я пытаюсь реализовать подход MaxUp (https://arxiv.org/pdf/2002.09024v1.pdf), чтобы улучшить обобщение моего классификатора изображений. Насколько я понимаю, суть метода заключается в том, что для каждой точки данных из нашего набора данных мы генерируем небольшой набор возмущенных или расширенных точек данных (в моем случае я использовал аугментацию) и получаем партию m дополненных изображений. Затем мы пропускаем этот пакет через нейронную сеть и вычисляем потери для каждого примера в пакете. После этого оптимизируем сеть, используя только максимальные потери. Мы в основном минимизируем максимальные потери.

Я использую набор данных Imagenette (https://github.com/fastai/imagenette), который я предварительно сбалансировал.

Итак, я построил простой con vnet состоящий из 3 блоков VGG.

def define_model_VGG(loss):
    momentum = 0.9
    #VGG
    model = Sequential()
    model.add(Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal', input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(MaxPooling2D(2, 2))

    model.add(Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(MaxPooling2D(2, 2))

    model.add(Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(MaxPooling2D(2, 2))

    model.add(Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(MaxPooling2D(2, 2))

    model.add(Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal'))
    model.add(BatchNormalization(momentum = momentum, center=True, scale=False))
    model.add(MaxPooling2D(2, 2))

    model.add(Flatten())
    model.add(Dense(512, activation = 'relu', kernel_initializer = 'he_normal'))
    model.add(Dense(256, activation = 'relu', kernel_initializer = 'he_normal'))
    model.add(Dense(10, activation = 'softmax'))

    opt = SGD(lr=0.001, momentum=0.9)
    model.compile(optimizer=opt, loss=loss, metrics=['accuracy'])
    return model

Я определил настраиваемую функцию потерь, чтобы она возвращала максимальные потери вместо средних потерь.

def max_loss_minimization(y_true, y_pred):
    """
    Returns max loss
    """
    loss = tf.keras.losses.categorical_crossentropy(y_true, y_pred)
    max_loss = tf.keras.backend.max(loss)
    return max_loss

Я сделал генератор, который возвращает любые количество расширенных изображений на основе одного изображения.

def augmentation_generator(aug_batch_size=AUG_BATCH_SIZE):
    """
    Returns aug_batch_size augmented images of one data point
    """
    maxup_datagen = ImageDataGenerator(rotation_range=30,
                                   width_shift_range=0.2,
                                   height_shift_range=0.2,
                                   shear_range=0.2,
                                   zoom_range=0.2,
                                   horizontal_flip=True)

    train_datagen = ImageDataGenerator(rescale=1./255)
    maxup_train_generator = train_datagen.flow_from_directory(
            train_dir,
            target_size=(IMAGE_SIZE, IMAGE_SIZE),
            shuffle=True,
            batch_size=1,
            class_mode='categorical')

    while True:
        
        next_image, next_label = next(maxup_train_generator)
        i = 0
        X = np.zeros((aug_batch_size, IMAGE_SIZE, IMAGE_SIZE, 3))
        y = np.zeros((aug_batch_size, 10))

        for x_batch, y_batch in maxup_datagen.flow(next_image, next_label, batch_size=1):
            X[i,:,:,:] = x_batch
            y[i,:] = y_batch
            i += 1
            if i == aug_batch_size:
                break

        yield X, y

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

test_datagen = ImageDataGenerator(rescale=1./255)
test_gen = test_datagen.flow_from_directory(
    test_dir,
    target_size=(IMAGE_SIZE, IMAGE_SIZE),
    batch_size=TEST_BATCH_SIZE,
    shuffle=True,
    class_mode='categorical'
)

val_data, val_labels = next(test_gen)
val_labels = np.array([np.where(a==1)[0][0] for a in val_labels]) # validation data

model_maxup = define_model_VGG(loss=max_loss_minimization)

epochs = 20
for epoch in range(epochs):
    print("\nStart of epoch %d" % (epoch,))
    
    aug_generator = augmentation_generator()

    # Iterate over the batches of the dataset.
    for step in range(0, IMAGE_NUMBER):
        
        x_batch_train, y_batch_train = next(aug_generator)
        x_batch_train = tf.convert_to_tensor(x_batch_train, dtype='float32')
        y_batch_train = tf.convert_to_tensor(y_batch_train, dtype='float32')
        # Open a GradientTape to record the operations run
        # during the forward pass, which enables autodifferentiation.
        with tf.GradientTape() as tape:

            # Run the forward pass of the layer.
            # The operations that the layer applies
            # to its inputs are going to be recorded
            # on the GradientTape.
            logits = model_maxup(x_batch_train)  # Logits for this minibatch

            # Compute the loss value for this minibatch.
            loss_value = max_loss_minimization(y_batch_train, logits)

        # Use the gradient tape to automatically retrieve
        # the gradients of the trainable variables with respect to the loss.
        grads = tape.gradient(loss_value, model_maxup.trainable_weights)

        # Run one step of gradient descent by updating
        # the value of the variables to minimize the loss.
        opt.apply_gradients(zip(grads, model_maxup.trainable_weights))

        # Log every 200 batches.
        if step % 200 == 0:
            print(
                "Training loss (for one batch) at step %d: %.4f"
                % (step, float(loss_value))
            )
            print("Seen so far: %s samples" % ((step + 1)))
            
        if step % 1000 == 0:
            pred_labels = np.argmax(model_maxup.predict(val_data),axis=1)
            print("Validation accuracy: {}".format(sum(val_labels==pred_labels)/TEST_BATCH_SIZE))

Однако моя нейронная сеть, похоже, вообще не обучается. Я получаю следующий результат:

Start of epoch 0
Found 9300 images belonging to 10 classes.
Training loss (for one batch) at step 0: 2.3029
Seen so far: 1 samples
Validation accuracy: 0.0975
Training loss (for one batch) at step 200: 2.4079
Seen so far: 201 samples
Training loss (for one batch) at step 400: 2.3045
Seen so far: 401 samples
Training loss (for one batch) at step 600: 2.1799
Seen so far: 601 samples
Training loss (for one batch) at step 800: 2.3446
Seen so far: 801 samples
Training loss (for one batch) at step 1000: 2.3806
Seen so far: 1001 samples
Validation accuracy: 0.085
Training loss (for one batch) at step 1200: 2.2879
Seen so far: 1201 samples
Training loss (for one batch) at step 1400: 2.3160
Seen so far: 1401 samples

Я также пробовал высокоуровневый метод model.fit и получил те же результаты. Скорее всего, проблема в моем собственном генераторе потерь или в генераторе данных. Вы можете сказать мне, что я делаю не так? Я почти уверен, что есть способ сделать это проще.

Заранее спасибо.

UPD: Я пробовал случайные гауссовские возмущения вместо увеличения, и, похоже, это не изменилось много ... Кроме того, продолжая изучать проблему, я заметил, что когда я заменяю операцию «reduce_max» на «reduce_mean», точность обучения, а также точность проверки начинает увеличиваться. Однако в статье говорится, что они каким-то образом минимизируют максимальный убыток. Есть ли другой способ минимизировать максимальный убыток?

...