Reputation: 7667
I want to further bucket the output classes into fewer buckets in a classification problem. I have 4 output classes (i.e. 0, 1, 2, 3). But during training I also want to track the accuracy for 2 classes:
For that I created a new metric and compiled it with the model:
def new_classes_acc(y_true, y_pred):
actual = tf.floor( y_true / 2 )
predicted = tf.floor( y_pred / 2 )
return K.categorical_crossentropy(actual, predicted)
Compiled it like so:
model.compile(loss='categorical_crossentropy', optimizer=optimizer, metrics=['accuracy', new_classes_acc])
I get nan
as the accuracy value. What is the right way to do this?
Upvotes: 2
Views: 2035
Reputation: 33410
Since there are 4 classes and you have set categorical_crossentropy
as the loss, then the labels are one-hot encoded and would be in shape (n_samples, 4)
. Therefore, first you need to find the true and predicted classes using argmax
function and then use floor
function (further, you want to create a metric not a loss function; therefore you should not use 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())
Now, let's test it. First we create a simple model and compile it:
model = Sequential()
model.add(Dense(4, activation='softmax', input_shape=(2,)))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy', custom_metric])
Then we create dummy data:
import numpy as np
data = np.array([1, 2]).reshape(1, 2)
and use our model to predict the labels for the given data:
print(model.predict(data))
# prints: [0.04662106, 0.8046941 , 0.07660434, 0.0720804 ]
So the second class has the highest probability and would be the predicted label. Now, considering the custom metric we defined, given either of [1, 0, 0, 0]
or [0, 1, 0, 0]
as true labels, the custom metric should gives us 1 (i.e. 100%). Let's confirm this:
true_labels = np.array([1, 0, 0, 0]).reshape(1,4)
print(model.evaluate(data, true_labels)) # gives: [3.0657029151916504, 0.0, 1.0]
The first element of the returned list corresponds to loss, the second one to accuracy
and the third one to our custom metric. As you can see, the accuracy is zero (since the true class is class one but the predicted class is class two) and the custom metric is 1 as expected.
And the other case:
true_labels = np.array([0, 1, 0, 0]).reshape(1,4)
print(model.evaluate(data, true_labels)) # gives: [0.21729297935962677, 1.0, 1.0]
Here the accuracy is one (since both true and predicted classes are class two) and the custom metric is also one. You can further confirm this for the remaining two cases of [0, 0, 1, 0]
and [0, 0, 0, 1]
as true labels; both should return zero for the value of custom metric.
Bonus: what if the labels are sparse, i.e. 0, 1, 2 and 3? Then, you can use keras.np_utils.to_categorical()
method to one-hot encode them and then use the custom metric defined above.
Upvotes: 4