Я работаю над тестовым заданием по обнаружению вулканов по изображениям с радара. Изображения имеют размер 100x100 пикселей и одноканальные. Набор обучающих данных был сильно несбалансированным (количество изображений без вулканов в 5 раз больше, чем с вулканами).

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

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

Давайте сначала рассмотрим распределение классов в данных. Здесь мы видим количество выборок на класс до передискретизации:

Мы можем увидеть 1K образцов с вулканами и около 6K образцов без вулканов.

Передискретизация означает, что мы увеличиваем количество выборок в второстепенных классах, так что количество выборок в разных классах становится равным или близким к нему, таким образом, становится более сбалансированным.

Давайте применим ручную передискретизацию при подготовке обучающих выборок.

Я применил передискретизацию в методе prepareImages:

def prepareImages(train, shape, data_path, mode):
    for index, row in train.iterrows():
        has_volcano = row['Volcano?']
        ...
        if has_volcano and mode == 'train':
            x_train[count] = img_to_array( cv2.flip( img, 1 ) )
            y_train[count] = int(has_volcano)
            count += 1
            // repeat the same step three more times applying different transformation and incrementing count

Здесь читаем атрибут образца «Вулкан?». Если изображение содержит вулкан, мы применяем некоторые преобразования к исходному изображению и добавляем измененное изображение в набор данных вместе с соответствующей меткой. В моем случае я применил 3 переворота (со значениями 0, 1 и -1) и повернул (cv2.ROTATE_90_CLOCKWISE).

Давайте отобразим распределение классов после передискретизации

Экспериментальная оценка передискретизации

Результаты с передискретизацией

Когда не применяется передискретизация

loss: 0.2359 — acc: 0.9202 — val_loss: 0.4253 — val_acc: 0.8626
AUC = 0.500

и график потерь / точности

При выполнении прогноза на тестовом наборе данных мы получаем следующие результаты:

number of images with volcanoes: 0
number of images without volcanoes: 2734

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

Результаты с передискретизацией

Когда мы применяем передискретизацию

loss: 0.6885 — acc: 0.5264 — val_loss: 0.6856 — val_acc: 0.5718
AUC = 0.504

и наши кривые обучения

При выполнении прогноза на тестовом наборе данных мы получаем следующие результаты:

number of images with volcanoes: 27
number of images without volcanoes: 2707

Вот и все.