Вопрос или проблема
Я строю классификационную модель для предсказания изображений по 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
в лучшем случае.
Кажется, что сеть путает 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% может потребовать итеративного процесса, который включает в себя тестирование различных конфигураций модели, аугментаций, изменение параметров обучения и переосмысление источников данных. Применяя предложенные стратегии и анализируя результаты, вы сможете постепенно улучшить производительность вашей модели. Успехов в дальнейшей работе!