在Keras中使用sample_weight进行序列标记

时间:2018-01-18 06:30:10

标签: python deep-learning keras data-science

我正在处理不平衡类的顺序标签问题,我想使用sample_weight来解决不平衡问题。基本上如果我训练模型大约10个时代,我会得到很好的结果。如果我训练更多的时代,val_loss不断下降,但我的结果会更糟。我猜测模型只是检测到更多的优势类而不利于较小的类。

该模型有两个输入,用于字嵌入和字符嵌入,输入是0到6的7个可能类之一。

使用填充时,用于单词嵌入的输入图层的形状为(3000, 150),并且用于单词嵌入的输入图层为(3000, 150, 15)。我使用0.3分割来测试和训练数据,这意味着X_train用于字嵌入的(2000, 150)(2000, 150, 15)用于字符嵌入。 y包含每个单词的正确类,以维度7的单热矢量编码,因此其形状为(3000, 150, 7)y同样分为训练和测试集。然后将每个输入馈入双向LSTM。

输出是一个矩阵,其中为2000个训练样本的每个单词分配了7个类别之一,因此大小为(2000, 150, 7)

首先,我只是尝试将sample_weight定义为长度为7的np.array,其中包含每个类的权重:

count = [list(array).index(1) for arrays in y for array in arrays]
count = dict(Counter(count))
count[0] = 0
total = sum([count[key] for key in count])
count = {k: count[key] / total for key in count}
category_weights = np.zeros(7)
for f in count:
    category_weights[f] = count[f]

但是我收到以下错误ValueError: Found a sample_weight array with shape (7,) for an input with shape (2000, 150, 7). sample_weight cannot be broadcast.

查看文档,看起来我应该通过a 2D array with shape (samples, sequence_length)。所以我创建了一个(3000, 150)数组,其中包含每个序列的每个单词的权重:

weights = []

for sample in y:
    current_weight = []
    for line in sample:
        current_weight.append(frequency[list(line).index(1)])
    weights.append(current_weight)

weights = np.array(weights)

并在sample_weight中添加sample_weight_mode="temporal"选项后,通过compile()参数将其传递给fit函数。

我首先得到一个错误,告诉我维度是错误的,但是在仅为训练样本生成权重后,我最终得到了一个(2000, 150)数组,我可以用它来拟合我的模型。

  • 这是定义sample_weights的正确方法还是我做错了?我不能说我注意到添加重量有任何改进,所以我一定错过了什么。

2 个答案:

答案 0 :(得分:4)

我认为你让sample_weightsclass_weights感到困惑。检查docs一点,我们可以看到它们之间的差异:

sample_weights用于为每个训练样本提供权重。这意味着您应该传递一个与您的元素数量相同的一维数组训练样本(表示每个样本的重量)。如果您使用时态数据,您可以改为传递2D数组,使您能够为每个样本的每个时间步长赋予权重。

class_weights用于为每个输出类提供权重或偏差。这意味着您应该为您尝试分类的每个班级传递一个权重。此外,此参数需要将字典传递给它(不是数组,这就是您遇到该错误的原因)。例如,考虑这种情况:

class_weight = {0 : 1. , 1: 50.}

在这种情况下(二进制分类问题),与类1相比,您为类0的样本提供了50倍的权重(或“相关性”)。这样您就可以补偿不平衡的数据集。这是另一个有用的post,它解释了在处理不平衡数据集时要考虑的这个和其他选项。

  

如果我训练更多的纪元,val_loss会不断下降,但结果会更差。

可能你过度拟合了,正如你所怀疑的那样,你的数据集所具有的不平衡类可能会导致这种情况。补偿班级权重应该有助于缓解这种情况,但是仍然可能有其他因素导致过度拟合,从而超出了这个问题/答案的范围(所以在解决这个问题之后一定要注意那些)。

根据您的帖子判断,在我看来,您需要的是使用class_weight来平衡您的数据集进行培训,您需要传递一个字典来指示重量比你的7个班级之间。仅当您希望为每个示例提供自定义权重时才考虑使用sample_weight

如果您想在这两者之间进行更详细的比较,请考虑检查我在相关问题上发布的this answer掠夺者:sample_weight会覆盖class_weight,因此您必须使用其中一种,但不能同时使用,所以请注意不要混合它们。

答案 1 :(得分:0)

我在网上搜索了同样的问题,在我的案例中正确使用 sample_weight 后,我确实提高了准确度。

我认为你的理解是正确的,程序也是正确的。您的案例没有改进的一个可能原因是,当您传入 sample_weight 时,较高的值意味着较高的权重。这意味着您不能直接使用字数统计。您可能会考虑使用倒计数频率:

total = sum([count[key] for key in count])
count = {k: count[key] / total for key in count}
for f in count:
category_weights = np.zeros(7)
    category_weights[f] = 1 - count[f]