Возможное решение
ИМО, вы должны использовать почти стандартный 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)