Reputation: 284
This problem has been asked in other forums and I have tried their variations with no avail: class_weight for imbalanced data - Keras
how to set class-weights for imbalanced classes in keras
However it seems stale as no one has answered the question. Does anyone know how to implement the class_weight
parameter in Keras when using categorical_crossentropy
?
I have been trying to use the class_weight
parameter in Keras but keep getting this error:
ValueError: Expected
class_weight
to be a dict with keys from 0 to one less than the number of classes, found {'prediction': {0: 1.217169570760731, 1: 5.323420074349443, 2: 0.5023680056130504}
Each sample will be classified either 0, 1, or 2 (softmax). The bias in this dataset is significant. My model using the Keras functional API.
The class_weights were calculated using Sklearn:
class_weights = class_weight.compute_class_weight('balanced', np.unique(np.array(y_trn_labels_HB_2_pd['labels'])), y_trn_labels_HB_2_pd['labels'])
class_weight_dict = dict(enumerate(class_weights))
class_weight_dict
Here is my last layer:
prediction = Dense(3, activation="softmax", name = 'prediction')(x)
Here is my model:
tf.__version__ = 2.3.0
model = Model(inputs = [sequence_input_head, sequence_input_body, semantic_feat,
wordOL_feat, avg_subj_feat], outputs = [prediction])
model.compile(loss = 'categorical_crossentropy',
optimizer='adam',
metrics = ['accuracy'])
model.summary()
Here is my class_weight parameter:
class_weight= {'prediction': {0:1.217169570760731, 1:5.323420074349443, 2:0.5023680056130504} })
Here is the full error:
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-271-e3bb78b84171> in <module>()
26 y_val_2_cat),
27 callbacks = [es],
---> 28 class_weight= {'prediction': class_weights})
29
30 modeled = model.save(os.path.join(save_path, path_model))
3 frames
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py in _method_wrapper(self, *args, **kwargs)
106 def _method_wrapper(self, *args, **kwargs):
107 if not self._in_multi_worker_mode(): # pylint: disable=protected-access
--> 108 return method(self, *args, **kwargs)
109
110 # Running inside `run_distribute_coordinator` already.
/usr/local/lib/python3.6/dist-packages/tensorflow/python/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, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)
1061 use_multiprocessing=use_multiprocessing,
1062 model=self,
-> 1063 steps_per_execution=self._steps_per_execution)
1064
1065 # Container that configures and calls `tf.keras.Callback`s.
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/data_adapter.py in __init__(self, x, y, sample_weight, batch_size, steps_per_epoch, initial_epoch, epochs, shuffle, class_weight, max_queue_size, workers, use_multiprocessing, model, steps_per_execution)
1120 dataset = self._adapter.get_dataset()
1121 if class_weight:
-> 1122 dataset = dataset.map(_make_class_weight_map_fn(class_weight))
1123 self._inferred_steps = self._infer_steps(steps_per_epoch, dataset)
1124
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/data_adapter.py in _make_class_weight_map_fn(class_weight)
1299 "Expected `class_weight` to be a dict with keys from 0 to one less "
1300 "than the number of classes, found {}").format(class_weight)
-> 1301 raise ValueError(error_msg)
1302
1303 class_weight_tensor = ops.convert_to_tensor_v2(
ValueError: Expected `class_weight` to be a dict with keys from 0 to one less than the number of classes, found {'prediction': {0: 1.217169570760731, 1: 5.323420074349443}}
EDIT1:
I tried your suggestion @Prateek Bhatt
history = model.fit({'headline': hl_pd_tr, 'articleBody':bd_pd_train, 'semantic': semantic_sim_180_train_x, 'wordOverlap': wrd_OvLp_train_x, 'avg_subjectivity': avg_subj_hb_train_x}, #@param ["model.fit({'headline': hl_pd_tr, 'articleBody':bd_pd_train},", "model.fit({'headline': hl_pd_tr, 'articleBody':bd_pd_train, 'semantic': semantic_x_tr},", "model.fit({'headline': hl_pd_tr, 'articleBody':bd_pd_train, 'semantic': semantic_x_tr, 'wordOverlap': wrd_OvLp_x_tr},", "model.fit({'headline': hl_pd_tr, 'articleBody':bd_pd_train, 'semantic': semantic_x_tr, 'wordOverlap': wrd_OvLp_x_tr, 'avgsubj': avg_subj_x_tr},"] {type:"raw", allow-input: true}
{'prediction':y_train_2_cat},
epochs=100,
batch_size= BATCH__SIZE,
shuffle= True,
validation_data = ([hl_pd_val, bd_pd_val, semantic_sim_180_val_x, wrd_OvLp_val_x, avg_subj_hb_val_x], y_val_2_cat),
callbacks = [es],
class_weight= {0:1.217169570760731, 1:5.323420074349443, 2:0.5023680056130504})
However, I get this error:
ValueError: `class_weight` is only supported for Models with a single output.
Full error:
ValueError Traceback (most recent call last)
<ipython-input-272-bfbab936a723> in <module>()
26 y_val_2_cat),
27 callbacks = [es],
---> 28 class_weight= {0:1.217169570760731, 1:5.323420074349443, 2:0.5023680056130504})
29
30 modeled = model.save(os.path.join(save_path, path_model))
16 frames
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py in _method_wrapper(self, *args, **kwargs)
106 def _method_wrapper(self, *args, **kwargs):
107 if not self._in_multi_worker_mode(): # pylint: disable=protected-access
--> 108 return method(self, *args, **kwargs)
109
110 # Running inside `run_distribute_coordinator` already.
/usr/local/lib/python3.6/dist-packages/tensorflow/python/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, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)
1061 use_multiprocessing=use_multiprocessing,
1062 model=self,
-> 1063 steps_per_execution=self._steps_per_execution)
1064
1065 # Container that configures and calls `tf.keras.Callback`s.
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/data_adapter.py in __init__(self, x, y, sample_weight, batch_size, steps_per_epoch, initial_epoch, epochs, shuffle, class_weight, max_queue_size, workers, use_multiprocessing, model, steps_per_execution)
1120 dataset = self._adapter.get_dataset()
1121 if class_weight:
-> 1122 dataset = dataset.map(_make_class_weight_map_fn(class_weight))
1123 self._inferred_steps = self._infer_steps(steps_per_epoch, dataset)
1124
/usr/local/lib/python3.6/dist-packages/tensorflow/python/data/ops/dataset_ops.py in map(self, map_func, num_parallel_calls, deterministic)
1693 """
1694 if num_parallel_calls is None:
-> 1695 return MapDataset(self, map_func, preserve_cardinality=True)
1696 else:
1697 return ParallelMapDataset(
/usr/local/lib/python3.6/dist-packages/tensorflow/python/data/ops/dataset_ops.py in __init__(self, input_dataset, map_func, use_inter_op_parallelism, preserve_cardinality, use_legacy_function)
4043 self._transformation_name(),
4044 dataset=input_dataset,
-> 4045 use_legacy_function=use_legacy_function)
4046 variant_tensor = gen_dataset_ops.map_dataset(
4047 input_dataset._variant_tensor, # pylint: disable=protected-access
/usr/local/lib/python3.6/dist-packages/tensorflow/python/data/ops/dataset_ops.py in __init__(self, func, transformation_name, dataset, input_classes, input_shapes, input_types, input_structure, add_to_graph, use_legacy_function, defun_kwargs)
3369 with tracking.resource_tracker_scope(resource_tracker):
3370 # TODO(b/141462134): Switch to using garbage collection.
-> 3371 self._function = wrapper_fn.get_concrete_function()
3372 if add_to_graph:
3373 self._function.add_to_graph(ops.get_default_graph())
/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py in get_concrete_function(self, *args, **kwargs)
2937 """
2938 graph_function = self._get_concrete_function_garbage_collected(
-> 2939 *args, **kwargs)
2940 graph_function._garbage_collector.release() # pylint: disable=protected-access
2941 return graph_function
/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py in _get_concrete_function_garbage_collected(self, *args, **kwargs)
2904 args, kwargs = None, None
2905 with self._lock:
-> 2906 graph_function, args, kwargs = self._maybe_define_function(args, kwargs)
2907 seen_names = set()
2908 captured = object_identity.ObjectIdentitySet(
/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py in _maybe_define_function(self, args, kwargs)
3211
3212 self._function_cache.missed.add(call_context_key)
-> 3213 graph_function = self._create_graph_function(args, kwargs)
3214 self._function_cache.primary[cache_key] = graph_function
3215 return graph_function, args, kwargs
/usr/local/lib/python3.6/dist-packages/tensorflow/python/eager/function.py in _create_graph_function(self, args, kwargs, override_flat_arg_shapes)
3073 arg_names=arg_names,
3074 override_flat_arg_shapes=override_flat_arg_shapes,
-> 3075 capture_by_value=self._capture_by_value),
3076 self._function_attributes,
3077 function_spec=self.function_spec,
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/func_graph.py in func_graph_from_py_func(name, python_func, args, kwargs, signature, func_graph, autograph, autograph_options, add_control_dependencies, arg_names, op_return_value, collections, capture_by_value, override_flat_arg_shapes)
984 _, original_func = tf_decorator.unwrap(python_func)
985
--> 986 func_outputs = python_func(*func_args, **func_kwargs)
987
988 # invariant: `func_outputs` contains only Tensors, CompositeTensors,
/usr/local/lib/python3.6/dist-packages/tensorflow/python/data/ops/dataset_ops.py in wrapper_fn(*args)
3362 attributes=defun_kwargs)
3363 def wrapper_fn(*args): # pylint: disable=missing-docstring
-> 3364 ret = _wrapper_helper(*args)
3365 ret = structure.to_tensor_list(self._output_structure, ret)
3366 return [ops.convert_to_tensor(t) for t in ret]
/usr/local/lib/python3.6/dist-packages/tensorflow/python/data/ops/dataset_ops.py in _wrapper_helper(*args)
3297 nested_args = (nested_args,)
3298
-> 3299 ret = autograph.tf_convert(func, ag_ctx)(*nested_args)
3300 # If `func` returns a list of tensors, `nest.flatten()` and
3301 # `ops.convert_to_tensor()` would conspire to attempt to stack
/usr/local/lib/python3.6/dist-packages/tensorflow/python/autograph/impl/api.py in wrapper(*args, **kwargs)
253 try:
254 with conversion_ctx:
--> 255 return converted_call(f, args, kwargs, options=options)
256 except Exception as e: # pylint:disable=broad-except
257 if hasattr(e, 'ag_error_metadata'):
/usr/local/lib/python3.6/dist-packages/tensorflow/python/autograph/impl/api.py in converted_call(f, args, kwargs, caller_fn_scope, options)
530
531 if not options.user_requested and conversion.is_whitelisted(f):
--> 532 return _call_unconverted(f, args, kwargs, options)
533
534 # internal_convert_user_code is for example turned off when issuing a dynamic
/usr/local/lib/python3.6/dist-packages/tensorflow/python/autograph/impl/api.py in _call_unconverted(f, args, kwargs, options, update_cache)
337
338 if kwargs is not None:
--> 339 return f(*args, **kwargs)
340 return f(*args)
341
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/data_adapter.py in _class_weights_map_fn(*data)
1310 if nest.is_sequence(y):
1311 raise ValueError(
-> 1312 "`class_weight` is only supported for Models with a single output.")
1313
1314 if y.shape.rank > 2:
ValueError: `class_weight` is only supported for Models with a single output.
Upvotes: 5
Views: 8546
Reputation: 1
I had the same error and was able to solve it by removing the name in my output layer. In your case, change
prediction = Dense(3, activation="softmax", name = 'prediction')(x)
to
prediction = Dense(3, activation="softmax")(x)
Upvotes: 0
Reputation: 8092
I see you have 3 classes. In your training set you will have X training samples labelled as class 0, Y training samples labelled as class 1 and Z training samples labelled as class 2. Now choose from X,Y or Z the largest value. AS an example say X=100, Y=200 and Z=400 samples. So Z=400 is the largest. The weight dictionary can be determined as
weight_dict={0:400/100, 1:400/200, 2:400/400}
The idea here is that class 2 with 400 samples influences the loss function 4 times more than class 0 and 2 times more than class 1 if you do not use a weights_dict. The weights_dict tries to rebalance this in terms of net impact on the loss function
Upvotes: 1
Reputation: 1538
Just use class_weights as following:
class_weight= {0:1.217169570760731, 1:5.323420074349443, 2:0.5023680056130504}
This should be enough.
Update:
There is currently a bug in tensorflow, try using TF2.1.0
Upvotes: 0
Reputation: 302
If you have a pandas dataframe you can first compute the class_weight parameter using compute_class_weight function and passing the target column like:
from sklearn.utils import class_weight
class_weights = class_weight.compute_class_weight('balanced',np.unique(df['target']),df['target'])
class_weights = dict(enumerate(class_weights))
then when fitting you pass the class_weights:
history = model.fit(X, Y,epochs=n,class_weight=class_weights)
Then make sure you are using one-hot-encoded labels for loss calculation using categorical_crossentropy. In case you are using the index values use sparse_categorical_crossentropy instead.
Upvotes: 1