【问题标题】:Error using class_weights parameter with Keras in Multi-Class Classification problem在多类分类问题中使用带有 Keras 的 class_weights 参数时出错
【发布时间】:2021-03-16 23:00:55
【问题描述】:

这个问题已经在其他论坛被问过,我尝试了他们的变种但没有成功:class_weight for imbalanced data - Keras

how to set class-weights for imbalanced classes in keras

但它似乎过时了,因为没有人回答这个问题。有谁知道在使用categorical_crossentropy 时如何在 Keras 中实现class_weight 参数? 我一直在尝试在 Keras 中使用 class_weight 参数,但一直收到此错误:

ValueError: 预期 class_weight 是一个字典,其键从 0 到比类数少一,发现 {'prediction': {0: 1.217169570760731, 1: 5.323420074349443, 2: 0.5023680056130504}

每个样本将被分类为 0、1 或 2 (softmax)。该数据集中的偏差很大。我的模型使用 Keras 函数式 API。

class_weights 是使用 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

这是我的最后一层:

prediction = Dense(3, activation="softmax", name = 'prediction')(x)

这是我的模型:

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()

这是我的 class_weight 参数:

class_weight= {'prediction': {0:1.217169570760731, 1:5.323420074349443, 2:0.5023680056130504} })

这是完整的错误:

---------------------------------------------------------------------------
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}}

编辑1:

我尝试了你的建议@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})

但是,我收到此错误:

ValueError: `class_weight` is only supported for Models with a single output.

完全错误:

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.

【问题讨论】:

    标签: python tensorflow keras nlp


    【解决方案1】:

    只需使用 class_weights 如下:

    class_weight= {0:1.217169570760731, 1:5.323420074349443, 2:0.5023680056130504}
    

    这应该足够了。

    更新:

    tensorflow目前存在bug,请尝试使用TF2.1.0

    【讨论】:

    • 我得到这个“ValueError:class_weight 仅支持具有单个输出的模型。”这个错误的问题是我只有一个输出层,即我的预测层。这个错误是否意味着 class_weights 不适用于多类问题?
    • 看来 tensorflow 有一个开放的 bug,我会要求将您的 tensorflow 版本降级到 TF2.1.0 并检查一次 - github.com/tensorflow/tensorflow/issues/40457
    • 是的,希望有人有解决方法。对我来说,问题是降级到 TF 2.1.0 会破坏我程序的主要部分。
    • 哦,是的,那么请密切注意该错误(自该错误打开以来已经 6 个月)得到解决,但正如我在回答中所说的那样。这是使用 class_weights 的正确方法。一切顺利。
    【解决方案2】:

    如果您有 pandas 数据框,您可以首先使用 compute_class_weight 函数计算 class_weight 参数并传递目标列,例如:

    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))
    

    然后在拟合时传递 class_weights:

    history = model.fit(X, Y,epochs=n,class_weight=class_weights)
    

    然后确保您使用单热编码标签使用 categorical_crossentropy 进行损失计算。如果您使用索引值,请改用 sparse_categorical_crossentropy。

    【讨论】:

    • 我像这样使用to_categorical() 来获得单热编码标签:y_train_2_cat = to_categorical(y_trn_labels_HB_2 , 3)。我这样做是为了验证集和测试集标签。
    【解决方案3】:

    我看到你有 3 节课。在您的训练集中,您将有 X 个训练样本标记为 0 类,Y 个训练样本标记为 1 类,Z 个训练样本标记为 2 类。 现在从 X、Y 或 Z 中选择最大值。例如,假设 X=100、Y=200 和 Z=400 个样本。所以 Z=400 是最大的。权重字典可以确定为

    weight_dict={0:400/100, 1:400/200, 2:400/400}
    

    这里的想法是,如果不使用 weights_dict,则具有 400 个样本的类 2 对损失函数的影响是类 0 的 4 倍和类 1 的 2 倍。 weights_dict 试图根据对损失函数的净影响来重新平衡这一点

    【讨论】:

    • 感谢您的意见。这是一种出色且直观的方法。如果有的话,我会告诉我的班级。谢谢!我遇到的问题与 Keras 接受调整后的权重有关。鉴于另一个用户在此处也链接了错误页面,似乎任何人都不会很快修复该错误:github.com/tensorflow/tensorflow/issues/40457
    • 我一直使用类权重进行分类。我不明白错误消息,因为您只有一个输出。我使用张量流 2.1.0 .
    • 啊。是的,我正在使用 TF 2.1.0,它似乎正在工作,但我相信 TF 正在将其转换为 sample_weights。运行时我收到此警告。 WARNING:tensorflow:sample_weight modes were coerced from ... to ['...']
    • 它似乎也没有解决我的过度拟合问题,尽管我可能只需要再纠结一下。我正在使用的数据集有一个样本严重不足的类。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 2018-08-05
    • 2020-12-22
    • 1970-01-01
    • 2020-10-05
    • 2019-10-10
    • 1970-01-01
    相关资源
    最近更新 更多