Keras:针对独热编码的类别权重(class_weight)

37
我希望在keras model.fit中使用class_weight参数来处理不平衡的训练数据。通过查看一些文档,我了解到可以传递像这样的字典:
class_weight = {0 : 1,
    1: 1,
    2: 5}

在这个例子中,class-2在损失函数中会受到更高的惩罚。
问题是我的神经网络输出采用了one-hot编码,即class-0=(1,0,0),class-1=(0,1,0),class-3=(0,0,1)。
我们如何为one-hot编码的输出使用class_weight?
通过查看 Keras中的一些代码,可以看出_feed_output_names包含一个输出类别列表,但在我的情况下,model.output_names/model._feed_output_names返回['dense_1']
相关:如何在Keras中为不平衡的类别设置类权重?
4个回答

42

这里有一个更短更快的解决方案。如果您的one-hot编码后的y是一个np.array:

import numpy as np
from sklearn.utils.class_weight import compute_class_weight

y_integers = np.argmax(y, axis=1)
class_weights = compute_class_weight('balanced', np.unique(y_integers), y_integers)
d_class_weights = dict(enumerate(class_weights))

d_class_weights 可以传递给 .fit 中的 class_weight


对我来说,在model.fit()函数中,直接将class_weight作为numpy数组传递而不将其转换为字典也可以正常工作。 - tsveti_iko
1
@tsveti_iko 发送类别权重对于独热编码不起作用,elif isinstance(class_weight, dict): ... else: return np.ones((y.shape[0],), dtype=K.floatx())。您必须提供样本权重,例如 class_weight.compute_sample_weight('balanced', y_train),使用sklearn。 - user1589759

7
_standardize_weights函数中,Keras会执行以下操作:
if y.shape[1] > 1:
    y_classes = y.argmax(axis=1)

基本上,如果您选择使用one-hot编码,类别就是列索引。

您可能还想知道如何将列索引映射到原始数据的类别。如果您使用scikit learn的LabelEncoder类执行one-hot编码,则列索引会映射到由.fit函数计算的唯一标签的顺序。 文档中写到:

提取有序的唯一标签数组

示例:

from sklearn.preprocessing import LabelBinarizer
y=[4,1,2,8]
l=LabelBinarizer()
y_transformed=l.fit_transorm(y)
y_transormed
> array([[0, 0, 1, 0],
   [1, 0, 0, 0],
   [0, 1, 0, 0],
   [0, 0, 0, 1]])
l.classes_
> array([1, 2, 4, 8])

作为结论,class_weights字典的键应该反映编码器中classes_属性的顺序。

7
我猜我们可以使用sample_weights代替。实际上,在Keras中,class_weights被转换为sample_weights

sample_weight: 可选数组,与x长度相同,包含每个样本应用于模型损失的权重。对于时间数据,您可以传递一个形状为(samples,sequence_length)的2D数组,以在每个样本的每个时间步骤上应用不同的权重。在这种情况下,您应该确保在compile()中指定sample_weight_mode =“temporal”。

sample_weights用于为每个训练样本提供权重。这意味着您应该传递与您的训练样本数量相同元素数量的1D数组(表示每个样本的权重)。 https://github.com/fchollet/keras/blob/d89afdfd82e6e27b850d910890f4a4059ddea331/keras/engine/training.py#L1392

2
sample_weight_mode="temporal"参数如何帮助处理多类别的独热编码目标?你有没有想法如何处理每个样本可以属于多个类别的情况?谢谢。 - olix20

6

这个答案可能有点复杂,但是目前我找到的最好的答案。这假设你的数据已经进行了一位有效编码,是多类别的,并且仅在标签 DataFrame df_y 上工作:

import pandas as pd
import numpy as np

# Create a pd.series that represents the categorical class of each one-hot encoded row
y_classes = df_y.idxmax(1, skipna=False)

from sklearn.preprocessing import LabelEncoder

# Instantiate the label encoder
le = LabelEncoder()

# Fit the label encoder to our label series
le.fit(list(y_classes))

# Create integer based labels Series
y_integers = le.transform(list(y_classes))

# Create dict of labels : integer representation
labels_and_integers = dict(zip(y_classes, y_integers))

from sklearn.utils.class_weight import compute_class_weight, compute_sample_weight

class_weights = compute_class_weight('balanced', np.unique(y_integers), y_integers)
sample_weights = compute_sample_weight('balanced', y_integers)

class_weights_dict = dict(zip(le.transform(list(le.classes_)), class_weights))

这将导致计算出一个“sample_weights”向量,用于平衡不平衡的数据集,可以传递给Keras“sample_weight”属性,以及一个“class_weights_dict”,可以在“fit”方法中提供给Keras“class_weight”属性。您不需要同时使用两者,只需选择一个即可。我现在正在使用“class_weight”,因为使用“fit_generator”使“sample_weight”起作用很复杂。

对我来说,在model.fit()函数中,直接将class_weight作为numpy数组传递而不将其转换为字典也可以正常工作。 - tsveti_iko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接