Как я могу вычислить вес класса для выхода, который имеет 4 нейрона с керасом? - PullRequest
3 голосов
/ 05 марта 2019

Я видел, как сделать некоторую коррекцию дисбаланса веса класса для отдельной классификации. Но в моем случае мой выходной слой:

model.add(Dense(4, activation='sigmoid'))

Мой target - это DataFrame, который имеет:

       0  1  2  3
0      1  1  0  0
1      0  0  0  0
2      1  1  1  0
3      1  1  0  0
4      1  1  0  0
5      1  1  0  0
6      1  0  0  0
...   .. .. .. ..
14989  1  1  1  1
14990  1  1  1  0
14991  1  1  1  1
14992  1  1  1  0

[14993 rows x 4 columns]

Мои прогнозы могут принимать форму одного из 5 возможных значений:

[[0, 0, 0, 0],
[1, 0, 0, 0],
[1, 1, 0, 0],
[1, 1, 1, 0],
[1, 1, 1, 1]]

Однако эти классы определенно не сбалансированы. Я видел, как вычислять веса классов , если у меня есть 1 целевой выход с softmax, но это немного отличается.

В частности,

model.fit(..., class_weights=weights)

Как я могу определить weights в этом случае?

Ответы [ 4 ]

2 голосов
/ 11 марта 2019

Возможное решение

ИМО, вы должны использовать почти стандартный categorical_crossentropy и выводить логиты из сети, которые будут отображаться в функции потерь в значения [0,1,2,3,4] с использованием операции argmax (та же процедура будет применяться кone-hot-encoded меток, см. Последнюю часть этого ответа в качестве примера).

Используя взвешенное значение crossentropy, вы можете по-разному относиться к ошибкам в зависимости от значений predicted vs correct, как вы сказали, что указали в комментариях.

Все, что вам нужно сделать, это взять абсолютное значение вычитаемого правильного и прогнозируемого значения и умножить его на потерю , см. Пример ниже:

Давайте сопоставим каждую кодировку с ееунарное значение (можно сделать, используя argmax, как будет показано позже):

[0, 0, 0, 0] -> 0
[1, 0, 0, 0] -> 1
[1, 1, 0, 0] -> 2
[1, 1, 1, 0] -> 3
[1, 1, 1, 1] -> 4

И давайте сделаем несколько случайных целей и прогнозов по модели, чтобы увидеть суть:

   correct  predicted with Softmax
0        0                       4
1        4                       3
2        3                       3
3        1                       4
4        3                       1
5        1                       0

Теперь, когда вы вычитаете correct и predicted и берете абсолют, вы по существу получаете весовой столбец, подобный этому:

   weights
0        4
1        1
2        0
3        3
4        2
5        1

Как видите, прогноз 0 при истинной цели 4 будетвесил в 4 раза больше, чем прогноз 3 с той же целью 4, и это именно то, что вам нужно, по сути IIUC.

Как указывает Даниэль Меллер в своем ответе, я бы посоветовал вам:Также создайте собственную функцию потерь, но немного проще:

import tensorflow as tf

# Output logits from your network, not the values after softmax activation
def weighted_crossentropy(labels, logits):
    return tf.losses.softmax_cross_entropy(
        labels,
        logits,
        weights=tf.abs(tf.argmax(logits, axis=1) - tf.argmax(labels, axis=1)),
    )

И эту потерю вы должны использовать и в своем model.compile, я думаю, нет необходимости повторять уже набранные очки.

Недостаткиэтого решения:

  • Для правильных прогнозов градиент будет равен нулю, что означает, что сети будет сложнее укреплять соединения (максимизировать / минимизировать логиты в направлении +inf/-inf)
  • Выше можно уменьшить, добавив случайный шум (дополнительную регуляризацию) к каждой взвешенной потере.Также может помочь в регуляризации.
  • Лучшим решением может быть исключить из весового случая, когда прогнозы равны (или сделать его 1), это не добавит рандомизацию в сетьОптимизация.

Преимущества этого решения:

  • Вы можете легко добавить взвешивание для несбалансированного набора данных (например, некоторые классы чаще встречаются)
  • Отображает чисто насуществующий API
  • Концептуально прост и остается в области классификации
  • Ваша модель не может предсказать несуществующие значения классификации, например, для вашего многоцелевого случая она может предсказать [1, 0, 1, 0], такого с подходом выше не существует.Меньшая степень свободы поможет ему обучаться и устранять шансы на бессмысленные (если я правильно понял описание вашей проблемы) прогнозы.

Дополнительное обсуждение в комментариях * в чате

Пример сети с пользовательскими потерями

Вот пример сети с пользовательской функцией потерь, определенной выше.Ваши ярлыки должны быть one-hot-encoded, чтобы он работал правильно.

import keras    
import numpy as np
import tensorflow as tf

# You could actually make it a lambda function as well
def weighted_crossentropy(labels, logits):
    return tf.losses.softmax_cross_entropy(
        labels,
        logits,
        weights=tf.abs(tf.argmax(logits, axis=1) - tf.argmax(labels, axis=1)),
    )


model = keras.models.Sequential(
    [
        keras.layers.Dense(32, input_shape=(10,)),
        keras.layers.Activation("relu"),
        keras.layers.Dense(10),
        keras.layers.Activation("relu"),
        keras.layers.Dense(5),
    ]
)

data = np.random.random((32, 10))
labels = keras.utils.to_categorical(np.random.randint(5, size=(32, 1)))

model.compile(optimizer="rmsprop", loss=weighted_crossentropy)
model.fit(data, labels, batch_size=32)
1 голос
/ 08 марта 2019

(Удалено) Во-первых, вы должны исправить вашу горячую кодировку:

(удалено) pd.get_dummies (target)

Рассчитайте вес каждого класса, суммируя сумму np.unique(target) и разделив на target.shape[0], получив пропорции:

target=np.array([0 0 0 0], [1 0 0 0], [1 1 0 0], [1 1 1 0], [1 1 1 1])

proportion=[]
for i in range(0,len(target)):
    proportion.append([i,len(np.where(target==np.unique(target)[i])[0])/target.shape[0]])

class_weight = dict(proportion)


model.fit(..., class_weights=class_weight)
1 голос
/ 11 марта 2019

Учитывая, что у вас есть цели (базовая правда y) с формой (samples, 4), вы можете просто:

positives = targetsAsNumpy.sum(axis=0)
totals = len(targetsAsNumpy)

negativeWeights = positives / totals
positiveWeights = 1 - negativeWeights

Веса классов в методе подбора предназначены для категориальных задач (только один правильный класс),

Я предлагаю вам создать собственную потерю с этим.Предположим, вы используете binary_crossentropy.

import keras.backend as K

posWeightsK = K.constant(positiveWeights.reshape((1,4)))
negWeightsK = K.constant(negativeWeights.reshape((1,4)))

def weightedLoss(yTrue, yPred):

    loss = K.binary_crossentropy(yTrue, yPred)
    loss = K.switch(K.greater(yTrue, 0.5), loss * posWeigthsK, loss *  negWeightsK)
    return K.mean(loss) #optionally K.mean(loss, axis=-1) for further customization

Используйте эту потерю в модели:

model.compile(loss = weightedLoss, ...)
0 голосов
/ 08 марта 2019

ошибок по нейронам

Для этой кодировки значения (одинарной, также называемой «кодом термометра») вы можете просто измерить погрешность для каждого значения в отдельности и добавить их, например, используя. двоичная_кросентропия или даже средний квадрат / средняя абсолютная погрешность. Учитывая этот вывод, это не проблема классификации, а дискретное представление задачи регрессии; но такие представления эффективны в определенных случаях - например, как написано Кодировка термометра: один горячий способ противостоять состязательным примерам описывает.

Хотя такие отдельные измерения ошибок не гарантируют, что «недопустимые» выходы (например, [1 0 0 0 1]) невозможны, они будут очень маловероятными для любой хорошо подходящей сети, и у нее есть свойство, которое , если правильное значение равно [1 1 1 1 0], то прогноз [1 1 0 0 0] «вдвое невернее», чем прогноз [1 1 1 0 0]. И вам не нужно корректировать «веса классов» для достижения этих результатов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...