Точность валидации не может превышать 70%.

Вопрос или проблема

Я строю классификационную модель для предсказания изображений по 3 классам. Данные сбалансированы, с 10,5k изображениями для обучения (по 3,5k для каждого класса), 3k изображениями для валидации (по 1k для каждого класса).

Я увеличил свою валидационную точность до около 70%-75%, но не могу продвинуться дальше.

Я использовал keras_tuner.Hyperband для поиска оптимальных гиперпараметров, но самое лучшее, что я получил от него — это эта модель:

def build_model():
    """Создает сверточную модель."""

    model = tf.keras.Sequential([
      tf.keras.layers.Input(shape=(80, 80, 3)),
      tf.keras.layers.Rescaling(1./255)
    ])
   # 1-й - фильтры 1 = 48
    model.add(tf.keras.layers.Conv2D(
            filters=48,
            kernel_size=4,
            padding="same",
        ))
    model.add(tf.keras.layers.Activation('relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.BatchNormalization())
    model.add(tf.keras.layers.ReLU())
    model.add(tf.keras.layers.Dropout(rate=0.1))

    # 2-й - фильтры 192, ядро 4, макспулинг, без дропаутов
    model.add(tf.keras.layers.Conv2D(
            filters=192,
            kernel_size=4,
            padding="same",
        ))
    model.add(tf.keras.layers.Activation('relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.BatchNormalization())
    model.add(tf.keras.layers.ReLU())


    # 3-й - фильтры: 64, ядро - 5, макспулинг, дропаут = 0.3
    model.add(tf.keras.layers.Conv2D(
            filters=256,
            kernel_size=5,
            padding="same",
        ))
    model.add(tf.keras.layers.Activation('relu'))
    model.add(tf.keras.layers.MaxPooling2D())
    model.add(tf.keras.layers.BatchNormalization())
    model.add(tf.keras.layers.ReLU())
    model.add(tf.keras.layers.Dropout(rate=0.1))
    # глобальный Avg pooling
    model.add(tf.keras.layers.GlobalAveragePooling2D())
    # свертка - без гиперпараметров
    model.add(tf.keras.layers.Flatten())
    # плотные слои: 2, БЕЗ РЕГУЛЯРИЗАЦИИ
    # 1-й слой: 288 единиц
    model.add(tf.keras.layers.Dense(units=288))
    model.add(tf.keras.layers.Activation('relu'))
       

    # 2-й плотно - 192, 0.3 дропаут, без регуляризации!!
    model.add(tf.keras.layers.Dense(units=192))
    model.add(tf.keras.layers.Activation('relu'))
    model.add(tf.keras.layers.Dropout(rate=0.3))

    # добавление последнего плотного слоя для num_classes:
    model.add(tf.keras.layers.Dense(3))
    model.add(tf.keras.layers.Activation('softmax'))

    lr = 0.0010297

    optimizer = tf.keras.optimizers.Adam(learning_rate=lr)
    model.compile(
        optimizer, loss="sparse_categorical_crossentropy", metrics=["accuracy"]
    )

    return model

Это дало около 0.73 val_accuracy, без какой-либо увеличенной выборки данных. Дело в том, что после достижения 0.7, как тренировка, так и val были на этом уровне, довольно стабильно, не продвигаясь дальше и не уменьшаясь.

Я пытался изменить скорость обучения, чтобы использовать Cosine decay, переключившись на SGD как оптимизатор, и попробовать некоторые процессы data_augmentation, а также сделать модель более сложной, чтобы training_accuracy увеличилась.

Я пришел к двум вариантам, которые, как мне кажется, ближе к лучшей модели, хотя val_accuracy не увеличилась:

Для обоих из них это был cosine_decay lr scheduler и optimizer:

def lr_warmup_cosine_decay(
    global_step,
    warmup_steps,
    hold=0,
    total_steps=0,
    start_lr=0.0,
    target_lr=1e-2,
):
    # Косинусное затухание
    learning_rate = (
        0.5
        * target_lr
        * (
            1
            + ops.cos(
                math.pi
                * ops.convert_to_tensor(
                    global_step - warmup_steps - hold, dtype="float32"
                )
                / ops.convert_to_tensor(
                    total_steps - warmup_steps - hold, dtype="float32"
                )
            )
        )
    )

    warmup_lr = target_lr * (global_step / warmup_steps)

    if hold > 0:
        learning_rate = ops.where(
            global_step > warmup_steps + hold, learning_rate, target_lr
        )

    learning_rate = ops.where(global_step < warmup_steps, warmup_lr, learning_rate)
    return learning_rate


class WarmUpCosineDecay(schedules.LearningRateSchedule):
    def __init__(self, warmup_steps, total_steps, hold, start_lr=0.0, target_lr=1e-2):
        super().__init__()
        self.start_lr = start_lr
        self.target_lr = target_lr
        self.warmup_steps = warmup_steps
        self.total_steps = total_steps
        self.hold = hold

    def __call__(self, step):
        lr = lr_warmup_cosine_decay(
            global_step=step,
            total_steps=self.total_steps,
            warmup_steps=self.warmup_steps,
            start_lr=self.start_lr,
            target_lr=self.target_lr,
            hold=self.hold,
        )

        return ops.where(step > self.total_steps, 0.0, lr)

оптимизатор:

total_images = 10500
total_steps = (total_images // BATCH_SIZE) * EPOCHS
warmup_steps = int(0.1 * total_steps)
hold_steps = int(0.45 * total_steps)
schedule = WarmUpCosineDecay(
    start_lr=0.05,
    target_lr=1e-2,
    warmup_steps=warmup_steps,
    total_steps=total_steps,
    hold=hold_steps,
)
optimizer = tf.keras.optimizers.SGD(
    weight_decay=5e-4,
    learning_rate=schedule,
    momentum=0.9,
)

1. Без data augumentation, один дополнительный dense layer и без kernel_regularization

та же модель, просто повторите 1-й плотный слой, без дропаутов

# 0.73 val_accuracy, в то время как training_accuracy увеличилась до 0.9 за 40 эпох

2. Без data augumentation, один дополнительный dense layer и kernel_regularization=L2(0.01)

так же, как и в первом, просто добавлено kernel_regularization=L2(0.01) к dense layers

# около 0.75 val_accuracy, в то время как training_accuracy увеличилась до 0.98 за 40 эпох

Итак, я увидел в этом overfit и попытался настроить обучающие данные с помощью некоторых процессов data_augmentation. Сначала я слишком усердствовал, с слишком большим количеством увеличений, вот так:

# простой случайный флип
random_flip = keras_cv.layers.RandomFlip()
augmenters = [random_flip]

# обрезка + изменение размера
crop_and_resize = keras_cv.layers.RandomCropAndResize(
    target_size=(IMAGE_SIZE, IMAGE_SIZE),
    crop_area_factor=(0.8, 1.0),
    aspect_ratio_factor=(0.9, 1.1),
)
augmenters += [crop_and_resize]


# добавление случайных увеличений
rand_augment = keras_cv.layers.RandAugment(
    augmentations_per_image=3,
    value_range=(0, 255),
    magnitude=0.3,
    magnitude_stddev=0.2,
    rate=0.7,
)
augmenters += [rand_augment]


# добавление случайного выбора между cutmix и mixup
cut_mix = keras_cv.layers.CutMix()
mix_up = keras_cv.layers.MixUp()


cut_mix_or_mix_up = keras_cv.layers.RandomChoice([cut_mix, mix_up], batchwise=True)
augmenters += [cut_mix_or_mix_up]

Но это привело к низкой training_accuracy и колеблющейся val_accuracy. Затем я попробовал просто два простых увеличения, такие как random_flip и random_zoom, но это снова привело к очень скачущей val_accuracy и низкой training_accuracy. val_acc будет колебаться от 0.4 до 0.5, затем снова до 0.45, затем 0.6, абсолютно нестабильно.

Что мне делать дальше? Кажется, я застрял между стабильной моделью с максимальной мощностью 0.7 и двумя моделями с переобучением с 0.7 validation accuracy в лучшем случае.

Матрица путаницы для модели:
Confusion_Matrix

Кажется, что сеть путает 1 с 2, и 2 с 1. Это указывает на то, что сеть не может различить классы. Некоторые знания в этой области могут помочь понять, почему они путаются, такие как «какие существенные различия между этими классами» и «на какой шкале они наиболее видимы».

Может быть полезно использовать более крупный размер ядра на входе (в первом сверточном слое) – что-то в диапазоне от 5 до 7. Это поможет сети учитывать более широкий радиус восприятия при различении классов. Я думаю, что это было бы особенно важно, если различия между 1 и 2 имеют более глобальный характер.

Я бы попробовал совместить это с stride=2, чтобы выполнить уменьшение изображения и уменьшить количество параметров.

Кроме того, рассмотрите возможность использования только $3\times3$ ядер после входного слоя. Вы можете сделать это, заменив слой kernel_size=4 на =3, а слой kernel_size=5 на два слоя, оба из которых имеют kernel_size=3.

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

Чтобы смягчить переобучение, попробуйте более постепенно увеличивать количество признаков. Например, вы все еще можете удвоить признаки на каждом шаге, но начинать с меньших значений. Таким образом, вы можете пойти 16, 32, 64, 128.

Изменяйте по одному элементу за раз и смотрите, приведет ли это к улучшению.

Ответ или решение

Увеличение точности валидации модели классификации изображений может быть сложной задачей, особенно если она не превышает 70%. Ниже приведены рекомендации и возможные причины для этого, основываясь на предоставленных данных.

1. Анализ проблемы: Причины низкой точности

  • Переобучение: Ваша модель показывает высокую точность на обучающей выборке, но не может достичь аналогичной точности на валидационной. Это указывает на то, что модель запоминает обучающие данные, а не учится обобщать. Использование регуляризации (например, L2-регуляризация и более высокой Dropout) может помочь.

  • Недостаток данных: Хотя у вас сбалансированная выборка, она может быть недостаточной для эффективного обучения модели. В случае небольшого количества обучающих изображений наличие шумов и схожесть между классами могут привести к трудностям в классификации.

  • Качество данных: Если изображения низкого качества или их недостаточно для выступления в определении классов, это также может ограничить производительность вашей модели. Обязательно проведите анализ изображений на предмет их разнородности и значимости для задачи.

2. Улучшение структуры модели

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

  • Изменение ядра свертки: Попробуйте использовать более крупные ядра (например, 5х5 или 7х7) на первых слоях, чтобы охватывать более широкий контекст. Также рассмотрите возможность использования нескольких слоев с меньшими ядрами (например, два слоя 3х3 подряд) после первого слоя, что позволит уменьшить количество параметров.

  • Структура сети: Вместо резкого увеличения количества фильтров (например, 48 → 192), стоит плавно наращивать количество каналов (например, 16 → 32 → 64). Это может помочь модели лучше обучиться.

  • Регулирование: Включите разные техники регуляризации, такие как L2 или L1 для полносвязных слоев.

3. Использование аугментации данных

Хотя вы уже пробовали аугментацию данных, попробуйте придерживаться более простых методов. Например:

  • Случайное отражение и обрезка: Используйте простую аугментацию, такую как возврат по оси X и изменяйте размер изображения.

  • Коррекция яркости и контраста: Эти простые изменения могут помочь сделать модель более устойчивой к изменениям в данных.

4. Оптимизация гиперпараметров

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

  • Размер мини-батчей: Экспериментируйте с разными размерами мини-батчей. Меньшие батчи могут дать больше стабильных обновлений, но больших размеров могут быть полезны для более стабильного градиентного спуска.

5. Пост-обработка и анализ ошибок

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

  • Визуализация: Использование методов визуализации, таких как Grad-CAM, может помочь определить, какие части изображения модель считает наиболее значимыми, и впоследствии эту информацию можно будет использовать для улучшения предобработки изображений.

Заключение

Достигнуть точности валидации выше 70% может потребовать итеративного процесса, который включает в себя тестирование различных конфигураций модели, аугментаций, изменение параметров обучения и переосмысление источников данных. Применяя предложенные стратегии и анализируя результаты, вы сможете постепенно улучшить производительность вашей модели. Успехов в дальнейшей работе!

Оцените материал
Добавить комментарий

Капча загружается...