Как настроить модель для редкого бинарного результата с помощью Tensorflow или GBM

В настоящее время я работаю с данными с редким бинарным исходом, т.е. вектор отклика содержит в основном 0 и только несколько единиц (примерно 1,5%). У меня есть около 20 непрерывных объясняющих переменных. Я пытался обучать модели с помощью GBM, Random Forests, TensorFlow с бэкендом Keras.

Я наблюдал особое поведение моделей, независимо от того, какой метод я использовал:

Точность высокая (~98%), но модель предсказывает вероятности для класса «0» для всех исходов как ~98,5%, а для класса «1» ~1,5%.

Как я могу предотвратить такое поведение?

Я использую RStudio. Например, модель TF с Keras будет:

model <- keras_model_sequential()

model %>%
  layer_dense(units = 256, activation = "relu", input_shape = c(20)) %>%
  layer_dense(units = 256, activation = "relu") %>%
  layer_dense(units = 2, activation = "sigmoid")

parallel_model <- multi_gpu_model(model, gpus=2)
parallel_model %>% compile(
  optimizer = "adam",             
  loss = "binary_crossentropy",
  metrics = "binary_accuracy")

histroy <- parallel_model %>% fit(
  x_train, y_train,
  batch_size = 64,
  epochs = 100,
  class_weight = list("0"=1,"1"=70),
  verbose = 1,
  validation_split = 0.2
)

Но мое наблюдение не ограничивается ТФ. Это делает мой вопрос более общим. Я не прошу каких-то конкретных корректировок для приведенной выше модели, скорее я хотел бы обсудить, в какой момент всем исходам присваивается одинаковая вероятность.

Я могу предположить, что проблема связана с функцией потерь. Я знаю, что нет никакого способа использовать AUC в качестве функции потерь, поскольку она не дифференцируема. Если я тестирую модели с AUC с неизвестными данными, результат не лучше случайного угадывания.

Я не возражаю против ответов с кодом на Python, поскольку проблема не в кодировании, а в общем поведении и алгоритмах.


person Toby    schedule 15.08.2019    source источник
comment
Я вижу, у вас есть два класса, и каждый может иметь значения 0 или 1. Я не уверен, правильно ли учитываются веса классов. Вопрос: возможно ли, чтобы оба класса были правильными в образце? Или это строго категориальная задача (правильный только один из классов).   -  person Daniel Möller    schedule 15.08.2019
comment
@DanielMöller результат либо 0, либо 1. Никогда не может быть 1,1 или 0,0. Я никогда не думал об этом. Как это повлияет на модель?   -  person Toby    schedule 15.08.2019
comment
Итак, мой ответ ниже таков. Наиболее важным является наличие большого размера партии (я бы сказал, 200) и использование правильной метрики.   -  person Daniel Möller    schedule 15.08.2019


Ответы (2)


Если в вашей задаче есть несбалансированные классы, я предлагаю использовать SMOTE (только для обучающих данных!!! Никогда не используйте smote для ваших тестовых данных!!!) перед обучением модели.

Например:

from imblearn.over_sampling import SMOTE
X_trn_balanced, Y_trn_balanced = SMOTE(random_state=1, ratio=1).fit_sample(X_trn, Y_trn)
#next fit the model with the balanced data
model.fit(X_trn_balanced, Y_trn_balanced )
person Roee Anuar    schedule 15.08.2019

По своему (не очень большому) опыту проблем с AUC и редких срабатываний я вижу модели с одним классом (а не с двумя). Либо результат положительный (1), либо результат отрицательный (0).

Метрики, такие как точность, бесполезны для этих проблем, вы должны использовать метрики на основе AUC с большими размерами пакетов.

Для этих задач не имеет значения, слишком ли малы вероятности исхода, если между ними есть разница. (Леса, GBM и т. д. действительно будут выводить эти маленькие значения, но это не проблема)

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

  • Для первого класса малые веса: предсказать все хорошо
  • Для второго класса высокие веса: предсказать, что все нули являются хорошими (взвешенными до очень хороших)

Итак, в качестве начального решения можно:

  • Используйте активацию 'softmax' (чтобы гарантировать, что ваша модель будет иметь только один правильный выход) и потерю 'categorical_crossentropy'.
  • (Или, что предпочтительнее) Используйте модель только с одним классом и сохраните 'sigmoid' с 'binary_crossentropy'.

Я всегда работаю с предпочтительным вариантом выше. В этом случае, если вы используете размеры партии, достаточно большие, чтобы содержать один или два положительных примера (размер партии около 100 для вас), веса могут даже отбрасываться. Если размеры пакетов слишком малы и многие пакеты не содержат положительных результатов, у вас может быть слишком много обновлений веса в сторону простых нулей, что плохо.

Вы также можете выполнить повторную выборку данных и, например, умножить на 10 количество положительных примеров, чтобы ваши партии содержали больше положительных результатов и обучение стало проще.

Пример метрики AUC, чтобы определить, когда обучение должно закончиться:

#in python - considering outputs with only one class
def aucMetric(true, pred):
    true= K.flatten(true)
    pred = K.flatten(pred)

    totalCount = K.shape(true)[0]

    values, indices = tf.nn.top_k(pred, k = totalCount)        
    sortedTrue = K.gather(true, indices)

    tpCurve = K.cumsum(sortedTrue)
    negatives = 1 - sortedTrue
    auc = K.sum(tpCurve * negatives)

    totalCount = K.cast(totalCount, K.floatx())
    positiveCount = K.sum(true)
    negativeCount = totalCount - positiveCount
    totalArea = positiveCount * negativeCount
    return  auc / totalArea
person Daniel Möller    schedule 15.08.2019