keras: Multi label classification with binary_crossentropy and class_weight - must contain all classes error

Hello,

I have run into an issue with class_weights and binary_crossentropy for multi label outputs.

In essence: Output is an ndarray Y with Y.shape[1] the number of label outputs, e.g.: [[0,0,1,0,1,1,0], [0,0,1,0,1,1,1], … [0,0,0,0,1,0,0]]

I have defined the output layer as follows (method A):

...
out_dense=Dense(Y.shape[1], 
                 activation='sigmoid', name='OUTPUT_BINARY')(intermediate_dense)

and defined the model:

model = Model(inputs=[... some inputs...], outputs=out_dense)
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

weight dictionary defined as follows: W = {0.0: 0.54356142358280057, 1.0: 6.2390227278685089} Now, when I fit: model.fit([...some inputs...], Y, batch_size=64, epochs=1, validation_split=0.25, class_weight={'OUTPUT_BINARY':W}, sample_weight=None) I get the error:

/usr/local/lib/python3.5/dist-packages/keras/engine/training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)
   1521             check_batch_axis=False,
   1522             batch_size=batch_size)
-> 1523         # Prepare validation data.
   1524         do_validation = False
   1525         if validation_data:

/usr/local/lib/python3.5/dist-packages/keras/engine/training.py in _standardize_user_data(self, x, y, sample_weight, class_weight, check_batch_axis, batch_size)
   1388                           for (ref, sw, cw, mode)
   1389                           in zip(y, sample_weights, class_weights, self._feed_sample_weight_modes)]
-> 1390         _check_array_lengths(x, y, sample_weights)
   1391         _check_loss_and_target_compatibility(y,
   1392                                              self._feed_loss_fns,

/usr/local/lib/python3.5/dist-packages/keras/engine/training.py in <listcomp>(.0)
   1387         sample_weights = [_standardize_weights(ref, sw, cw, mode)
   1388                           for (ref, sw, cw, mode)
-> 1389                           in zip(y, sample_weights, class_weights, self._feed_sample_weight_modes)]
   1390         _check_array_lengths(x, y, sample_weights)
   1391         _check_loss_and_target_compatibility(y,

/usr/local/lib/python3.5/dist-packages/keras/engine/training.py in _standardize_weights(y, sample_weight, class_weight, sample_weight_mode)
    556                              '`class_weight`.'
    557                              % (existing_classes - existing_class_weight))
--> 558         return weights
    559     else:
    560         if sample_weight_mode is None:

ValueError: `class_weight` must contain all classes in the data. The classes {2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 15, 17, 19, 20, 21, 28, 29, 31, 34, 36, 37, 38, 41, 43, 44, 45, 47} exist in the data but not in `class_weight`.

I have traced it to :

def _standardize_weights(y, sample_weight=None, class_weight=None,
                         sample_weight_mode=None):
...
        if y.shape[1] > 1:
            y_classes = y.argmax(axis=1)
        elif y.shape[1] == 1:
            y_classes = np.reshape(y, y.shape[0])
        else:
            y_classes = y

in training.py.

It seems that here keras sees the output as a softmax type one hot encoded and returns the index of the maximum for each row.

The only way I saw to fix this was to create multiple single outputs by using a for loop (method B):

outputs=[]
l_weights=[]
for i in range(y_in_service_indicator.shape[1]):
    out_dense=Dense(1, activation='sigmoid', name='O_'+str(1+i))(intermediate_dense)
    outputs.append(out_dense)
    l_weights.append(1.0)

model = Model([...some inputs...], outputs=outputs)
model.compile(loss='binary_crossentropy', loss_weights=l_weights,
              optimizer='adam', metrics=['accuracy'])

(I appreciate may not need the loss weights) and define the outputs as :

y_s_binary = []
for i in list(range(Y.shape[1])):
    y_s_binary.append(y_in_service_indicator[:,i])

and now fit:

model.fit(X_intervals, y_s_binary, batch_size=64, epochs=1, validation_split=0.25,
          class_weight={'OUTPUT_BINARY':W}, sample_weight=None)

This works, but it feels to me like I am missing somehting - would appreciate input on how to make Keras work with single defined Y ndarray and model output as in method A.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 13
  • Comments: 17

Most upvoted comments

Keras has fix this problem.This issue should be closed.

Multilabel : Binary Classification : Class weights for biased data. I have similar issue as mentioned in the thread. Wondered if a solution now exists ? My Y is of of shape : [[0,0,0,1], [0,1,0,0], [1,1,1,0] … [0,1,1,1]]

So its multilabel : binary classification with biased Data.

if i restrict the problem to just ONE label, binary classfication i.e. y = [ [0],[1],[0]…] i can set the class_weight= {0: weight_neg, 1: weight_pos} in tf.model.fit() And the model runs/trains well. However when i extend it to multiple labels it wont accept a dict of dicts or any variant i have tried.

hi @alanlisten, is there any corresponding new api in keras? i get a similar error when setting the class weight with keras version 2.2.0. And my Y is something like: [[1,0,0,1], [1,1,0,0] [1,0,1,0]] so for this multi-label problem i set class_weight={0.0:0.6,1.0:0.4} in the model.fit() function,and i get an error message says: ValueError: class_weightmust contain all classes in the data. The classes {2, 3} exist in the data but not inclass_weight.,

@sharifza Of course. That’s why this bug is open.

Here’s a workaround (see a discussion on StackOverflow): add two artificial classes in out_dense in positions 0 and 1 and make

out_dense[:,0] = 0
out_dense[:,1] = 1

All actual classes start from index 2 onward. This will force keras think that there are 2 classes.