Поскольку существует 4 класса, и вы установили categorical_crossentropy
в качестве потерь, то метки закодированы как горячие и будут иметь форму (n_samples, 4)
. Поэтому сначала вам нужно найти истинные и предсказанные классы, используя функцию argmax
, а затем использовать функцию floor
(далее вы хотите создать метрику, а не функцию потерь; поэтому вам не следует использовать K.categorical_crossentropy
):
from keras import backend as K
import tensorflow as tf
def custom_metric(y_true, y_pred):
tr = tf.floor(K.argmax(y_true, axis=-1) / 2)
pr = tf.floor(K.argmax(y_pred, axis=-1) / 2)
return K.cast(K.equal(tr, pr), K.floatx())
Теперь давайте проверим это. Сначала мы создаем простую модель и компилируем ее:
model = Sequential()
model.add(Dense(4, activation='softmax', input_shape=(2,)))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy', custom_metric])
Затем мы создаем фиктивные данные:
import numpy as np
data = np.array([1, 2]).reshape(1, 2)
и использовать нашу модель для прогнозирования меток для данных:
print(model.predict(data))
# prints: [0.04662106, 0.8046941 , 0.07660434, 0.0720804 ]
Таким образом, второй класс имеет наибольшую вероятность и будет предсказанной меткой. Теперь, рассматривая пользовательскую метрику, которую мы определили, учитывая либо [1, 0, 0, 0]
, либо [0, 1, 0, 0]
как истинные метки, пользовательская метрика должна дать нам 1 (то есть 100%). Давайте подтвердим это:
true_labels = np.array([1, 0, 0, 0]).reshape(1,4)
print(model.evaluate(data, true_labels)) # gives: [3.0657029151916504, 0.0, 1.0]
Первый элемент возвращаемого списка соответствует потерям, второй - accuracy
, а третий - нашей пользовательской метрике. Как видите, точность равна нулю (поскольку истинный класс - это класс один, а прогнозируемый класс - класс два), а пользовательский показатель равен 1, как и ожидалось.
И другой случай:
true_labels = np.array([0, 1, 0, 0]).reshape(1,4)
print(model.evaluate(data, true_labels)) # gives: [0.21729297935962677, 1.0, 1.0]
Здесь точность равна единице (поскольку истинные и прогнозируемые классы являются классом два), а пользовательская метрика также равна единице. Далее вы можете подтвердить это для оставшихся двух случаев [0, 0, 1, 0]
и [0, 0, 0, 1]
как истинных меток; оба должны возвращать ноль для значения пользовательской метрики.
Бонус: что делать, если метки редкие, то есть 0, 1, 2 и 3? Затем вы можете использовать метод keras.np_utils.to_categorical()
для быстрого их кодирования, а затем использовать пользовательскую метрику, определенную выше.