Як встановити ваги класів для незбалансованих класів у Керасі?


129

Я знаю, що в Керасі є можливість зі class_weightsсловником параметрів при встановленні, але я не міг знайти жодного прикладу. Хтось такий добрий надати?

До речі, у цьому випадку відповідна практика полягає у простому зважуванні класу меншин пропорційно його недооціненості?


Чи є новий оновлений метод із використанням Keras? чому словник складається з трьох класів і для класу: 0: 1,0 1: 50,0 2: 2.0 ???? не повинен: 2: 1.0 також?
Чак

Відповіді:


112

Якщо ви говорите про звичайний випадок, коли ваша мережа виробляє лише один вихід, то ваше припущення є правильним. Щоб змусити ваш алгоритм розглядати кожен екземпляр класу 1 як 50 екземплярів класу 0, ви повинні:

  1. Визначте словник із вашими мітками та відповідними вагами

    class_weight = {0: 1.,
                    1: 50.,
                    2: 2.}
  2. Подати словник як параметр:

    model.fit(X_train, Y_train, nb_epoch=5, batch_size=32, class_weight=class_weight)

EDIT: "трактувати кожен екземпляр класу 1 як 50 екземплярів класу 0 " означає, що в функції втрати ви призначаєте цим екземплярам більш високе значення. Отже, втрата стає середньозваженою, де вага кожного зразка визначається вагою class_ та його відповідним класом.

Від Документів Кераса : class_weight : Необов'язковий словник зіставлення показників класів (цілих чисел) до вагового (плаваючого) значення, що використовується для зважування функції втрат (лише під час тренувань).


1
Також погляньте на github.com/fchollet/keras/isissue/3653, якщо ви працюєте з 3D-даними.
herve

Для мене це дає помилку dic не має атрибуту форми.
Flávio Filho

Я вважаю, що Керас міг би змінити спосіб роботи. Це для версії серпня 2016 року. Я
перевірю

4
@layser Чи працює це лише для втрати 'category_crossentropy'? Як ви даєте клас-вага керам для втрати "сигмоїди" та "бінарної_кросцентропії"?
Наман

1
@layser Чи можете ви пояснити, як "трактувати кожен екземпляр класу 1 як 50 екземплярів класу 0"? Це те, що в навчальному наборі рядок, що відповідає класу 1, дублюється 50 разів, щоб зробити його збалансованим чи слід якийсь інший процес?
Дівяншу Шехар

122

Ви могли б просто реалізувати class_weightз sklearn:

  1. Давайте спочатку імпортуємо модуль

    from sklearn.utils import class_weight
  2. Для розрахунку ваги класу виконайте наступне

    class_weights = class_weight.compute_class_weight('balanced',
                                                     np.unique(y_train),
                                                     y_train)
  3. По-третє, і останнє, додайте його до моделі підгонки

    model.fit(X_train, y_train, class_weight=class_weights)

Увага : я відредагував цю публікацію та змінив ім'я змінної з class_weight на class_weight s , щоб не перезаписати імпортований модуль. Відповідно відрегулюйте під час копіювання коду з коментарів.


21
Для мене class_weight.compute_class_weight виробляє масив, мені потрібно змінити його на дікт, щоб працювати з Керасом. Більш конкретно, після кроку 2 використовуйтеclass_weight_dict = dict(enumerate(class_weight))
C.Lee

5
Це не працює для мене. Для проблеми трьох класів у керах y_train- це (300096, 3)масивний масив. Тож class_weight=рядок надає мені TypeError: unhashable type: 'numpy.ndarray'
Lembik

3
@ Lembik У мене була подібна проблема, де кожен рядок y є однокольоровим кодованим вектором класового індексу. Я зафіксував його шляхом перетворення одного гарячого уявлення до міжнар як це: y_ints = [y.argmax() for y in y_train].
tkocmathla

3
Що робити, якщо я роблю багаторівневе маркування так, щоб мої вектори y_true мали в собі кілька 1-х: наприклад, де деякі x мають мітки 0 і 4. Навіть тоді загальна кількість кожного з моїх мітки не врівноважені. Як я можу використовувати для цього ваги класу?
Аалок

22

Я використовую таке правило для class_weight:

import numpy as np
import math

# labels_dict : {ind_label: count_label}
# mu : parameter to tune 

def create_class_weight(labels_dict,mu=0.15):
    total = np.sum(labels_dict.values())
    keys = labels_dict.keys()
    class_weight = dict()

    for key in keys:
        score = math.log(mu*total/float(labels_dict[key]))
        class_weight[key] = score if score > 1.0 else 1.0

    return class_weight

# random labels_dict
labels_dict = {0: 2813, 1: 78, 2: 2814, 3: 78, 4: 7914, 5: 248, 6: 7914, 7: 248}

create_class_weight(labels_dict)

math.logрозгладжує ваги для дуже незбалансованих занять! Це повертає:

{0: 1.0,
 1: 3.749820767859636,
 2: 1.0,
 3: 3.749820767859636,
 4: 1.0,
 5: 2.5931008483842453,
 6: 1.0,
 7: 2.5931008483842453}

3
Навіщо використовувати журнал, а не просто ділити кількість вибірок для класу на загальну кількість зразків? Я припускаю, що є щось, чого я не розумію, переходить у парам class_weight на model.fit_generator (...)
startoftext

@startoftext Ось так я це зробив, але, думаю, ви це перевернули. Я використовував n_total_samples / n_class_samplesдля кожного класу.
colllin

2
У вашому прикладі клас 0 (має 2813 прикладів) та клас 6 (має 7914 прикладів) мають вагу рівно 1,0. Чому так? 6 клас у кілька разів більший! Ви хочете, щоб клас 0 був збільшений, а клас 6 зменшено, щоб привести їх до рівня.
Владислав Довгалець

9

ПРИМІТКА. Див. Коментарі, ця відповідь застаріла.

Щоб зважувати всі класи однаково, тепер ви можете просто встановити class_weight на "auto" так:

model.fit(X_train, Y_train, nb_epoch=5, batch_size=32, class_weight = 'auto')

1
Я не міг знайти жодної посилання class_weight='auto'на документацію Keras, ні у вихідному коді. Чи можете ви показати нам, де ви це знайшли?
Фабіо Перес

2
Ця відповідь, ймовірно, неправильна. Перевірте це питання: github.com/fchollet/keras/isissue/5116
Fábio Perez

Незвичайно. Під час публікації коментаря я використовував class_balanced = 'auto', але зараз не можу знайти посилання на нього. Можливо, це було змінено, оскільки Керас швидко розвивався.
Девід Гроппе

Як зазначено у вказаному вище випуску Keras , ви можете передавати будь-яку випадкову рядок, class_weightі це не матиме жодного ефекту. Тому ця відповідь не є правильною.
ncasas

3

class_weight чудово, але, як @Aalok сказав, це не вийде, якщо ви однокласні кодування багатомісних класів. У цьому випадку використовуйте sample_weight :

sample_weight: необов'язковий масив тієї ж довжини, що і x, містить ваги, що застосовуються до втрати моделі для кожного зразка. У випадку тимчасових даних ви можете пропустити 2D масив з формою (зразки, довжина_довжини), щоб застосувати різну вагу до кожного кроку кожного зразка. У цьому випадку вам слід обов’язково вказати sample_weight_mode = "тимчасовий" у compile ().

sample_weights використовується для забезпечення ваги для кожного навчального зразка . Це означає, що ви повинні пройти 1D масив з такою ж кількістю елементів, що і ваші навчальні зразки (із зазначенням ваги для кожного з цих зразків).

class_weights використовується для забезпечення ваги або зміщення для кожного класу виводу . Це означає, що ви повинні передати вагу для кожного класу, який ви намагаєтеся класифікувати.

sample_weight необхідно надати масивний масив, оскільки його форма буде оцінена.

Дивіться також цю відповідь: https://stackoverflow.com/questions/48315094/using-sample-weight-in-keras-for-sequence-labelling


2

Додавання до рішення за адресою https://github.com/keras-team/keras/isissue/2115 . Якщо вам потрібно більше, ніж класове зважування, де ви хочете різних витрат на помилкові позитиви та хибні негативи. З новою версією keras тепер ви можете просто перекрити відповідну функцію втрат, як зазначено нижче. Зауважимо, що weightsце квадратна матриця.

from tensorflow.python import keras
from itertools import product
import numpy as np
from tensorflow.python.keras.utils import losses_utils

class WeightedCategoricalCrossentropy(keras.losses.CategoricalCrossentropy):

    def __init__(
        self,
        weights,
        from_logits=False,
        label_smoothing=0,
        reduction=losses_utils.ReductionV2.SUM_OVER_BATCH_SIZE,
        name='categorical_crossentropy',
    ):
        super().__init__(
            from_logits, label_smoothing, reduction, name=f"weighted_{name}"
        )
        self.weights = weights

    def call(self, y_true, y_pred):
        weights = self.weights
        nb_cl = len(weights)
        final_mask = keras.backend.zeros_like(y_pred[:, 0])
        y_pred_max = keras.backend.max(y_pred, axis=1)
        y_pred_max = keras.backend.reshape(
            y_pred_max, (keras.backend.shape(y_pred)[0], 1))
        y_pred_max_mat = keras.backend.cast(
            keras.backend.equal(y_pred, y_pred_max), keras.backend.floatx())
        for c_p, c_t in product(range(nb_cl), range(nb_cl)):
            final_mask += (
                weights[c_t, c_p] * y_pred_max_mat[:, c_p] * y_true[:, c_t])
        return super().call(y_true, y_pred) * final_mask

0

Я знайшов наступний приклад кодування ваг класу у функції втрат за допомогою даних міністерства. Дивіться посилання тут: https://github.com/keras-team/keras/isissue/2115

def w_categorical_crossentropy(y_true, y_pred, weights):
    nb_cl = len(weights)
    final_mask = K.zeros_like(y_pred[:, 0])
    y_pred_max = K.max(y_pred, axis=1)
    y_pred_max = K.reshape(y_pred_max, (K.shape(y_pred)[0], 1))
    y_pred_max_mat = K.equal(y_pred, y_pred_max)
    for c_p, c_t in product(range(nb_cl), range(nb_cl)):
        final_mask += (weights[c_t, c_p] * y_pred_max_mat[:, c_p] * y_true[:, c_t])
    return K.categorical_crossentropy(y_pred, y_true) * final_mask

0
from collections import Counter
itemCt = Counter(trainGen.classes)
maxCt = float(max(itemCt.values()))
cw = {clsID : maxCt/numImg for clsID, numImg in itemCt.items()}

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

вага ваг приймає тип словника

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.