Дискуссия классового дисбаланса: масштабирование вклада в потери и sgd
(добавлено обновление к этому вопросу.)
Я аспирант в университете Гента, Бельгия; мои исследования касаются распознавания эмоций глубокими сверточными нейронными сетями. Я использую Caffe для реализации CNN.
Недавно я столкнулся с проблемой дисбаланса классов. Я использую 9216 учебных образцов, прибл. 5% помечены положительно (1), остальные образцы обозначены отрицательно (0).
Я использую слой SigmoidCrossEntropyLoss для вычисления потерь. Во время обучения потери уменьшаются, и точность достигается даже после нескольких эпох. Это связано с дисбалансом: сеть просто всегда предсказывает отрицательный (0). (Точность и отзыв ноль, поддерживая это требование)
Чтобы решить эту проблему, я хотел бы масштабировать вклад в потерю в зависимости от комбинации предсказания-истины (строго наказать ложные негативы). Мой наставник также посоветовал мне использовать масштабный коэффициент, когда backpropagating через стохастический градиентный спуск (sgd): фактор будет коррелирован с дисбалансом в партии. Партия, содержащая только отрицательные образцы, вообще не обновляет веса.
Я добавил только один пользовательский слой в Caffe: чтобы сообщить другие показатели, такие как точность и отзыв. Мой опыт с кодом Caffe ограничен, но у меня много опыта написания кода на С++.
Может ли кто-нибудь помочь мне или указать мне в правильном направлении, как настроить SigmoidCrossEntropyLoss и Sigmoid для размещения следующих изменений:
- отрегулировать вклад образца в общую потерю в зависимости от комбинации предсказания-истины (истинный положительный, ложноположительный, истинный отрицательный, ложноотрицательный).
- масштабировать обновление веса, выполняемое стохастическим градиентным спуском, в зависимости от дисбаланса в партии (отрицательные и положительные).
Спасибо заранее!
Update
Я включил InfogainLossLayer, как было предложено Shai. Я также добавил еще один настраиваемый уровень, который создает infogain-матрицу H
на основе дисбаланса в текущей партии.
В настоящее время матрица конфигурируется следующим образом:
H(i, j) = 0 if i != j
H(i, j) = 1 - f(i) if i == j (with f(i) = the frequency of class i in the batch)
Я планирую экспериментировать с различными конфигурациями для матрицы в будущем.
Я тестировал это на дисбалансе 10: 1. Результаты показали, что сеть сейчас изучает полезные вещи: (результаты после 30 эпох)
- Точность ок. ~ 70% (от ~ 97%);
- Точность ок. ~ 20% (от 0%);
- Напомним, ок. ~ 60% (от 0%).
Эти числа были достигнуты примерно в 20 эпох и после этого существенно не изменились.
!! Результаты, изложенные выше, являются просто доказательством концепции, они были получены путем обучения простой сети с несбалансированным набором данных 10: 1.!!
Ответы
Ответ 1
Почему бы вам не использовать слой InfogainLoss, чтобы компенсировать дисбаланс в вашем учебном наборе?
Потери Infogain определяются с использованием весовой матрицы H
(в вашем случае 2 на 2). Значение его записей
[cost of predicting 1 when gt is 0, cost of predicting 0 when gt is 0
cost of predicting 1 when gt is 1, cost of predicting 0 when gt is 1]
Итак, вы можете установить записи H
, чтобы отразить разницу между ошибками в прогнозировании 0 или 1.
Вы можете найти, как определить матрицу H
для caffe в этой теме.
Что касается весов образцов, вы можете найти этот пост интересный: он показывает, как изменить слой SoftmaxWithLoss, чтобы учитывайте весы образцов.
Недавно модификация потери кросс-энтропии была предложена Цун-Йи Линь, Прия Гойял, Росс Гиршик, Каимлинг Он, Петр Доллар Фокусное потеря для обнаружения плотных объектов, (ICCV 2017).
Идея фокальной потери - назначить разный вес для каждого примера, основываясь на относительной сложности предсказания этого примера (скорее, на основе размера класса и т.д.). С короткого времени, которое я получил, чтобы поэкспериментировать с этой потерей, он чувствует себя выше "InfogainLoss"
с весами класса.
Ответ 2
Я также столкнулся с проблемой дисбаланса класса в моей задаче классификации. Прямо сейчас я использую CrossEntropyLoss с весом (документация здесь), и он отлично работает. Идея состоит в том, чтобы дать больше потерь образцам в классах с меньшим количеством изображений.
Вычисление веса
вес для каждого класса обратно пропорционально числу изображений в этом классе. Вот фрагмент для вычисления веса для всего класса с помощью numpy,
cls_num = []
# train_labels is a list of class labels for all training samples
# the labels are in range [0, n-1] (n classes in total)
train_labels = np.asarray(train_labels)
num_cls = np.unique(train_labels).size
for i in range(num_cls):
cls_num.append(len(np.where(train_labels==i)[0]))
cls_num = np.array(cls_num)
cls_num = cls_num.max()/cls_num
x = 1.0/np.sum(cls_num)
# the weight is an array which contains weight to use in CrossEntropyLoss
# for each class.
weight = x*cls_num