如何在生成器提供的 Keras 自定义损失函数中访问样本权重?
Posted
技术标签:
【中文标题】如何在生成器提供的 Keras 自定义损失函数中访问样本权重?【英文标题】:How to access sample weights in a Keras custom loss function supplied by a generator? 【发布时间】:2020-01-19 19:00:55 【问题描述】:我有一个生成器函数,可以无限循环遍历一些图像目录并以表单形式输出 3 元组
[img1, img2], label, weight
img1
和 img2
是 batch_size x M x N x 3
张量,label
和 weight
分别是 batch_size
x 1 张量。
在使用 Keras 训练模型时,我将此生成器提供给 fit_generator
函数。
对于这个模型,我有一个自定义余弦对比损失函数,
def cosine_constrastive_loss(y_true, y_pred):
cosine_distance = 1 - y_pred
margin = 0.9
cdist = y_true * y_pred + (1 - y_true) * keras.backend.maximum(margin - y_pred, 0.0)
return keras.backend.mean(cdist)
我的模型在结构上一切正常。没有错误,它正在按预期消耗来自生成器的输入和标签。
但现在我正在寻求直接使用每个批次的权重参数,并根据样本特定的权重在 cosine_contrastive_loss
内部执行一些自定义逻辑。
如何在损失函数执行时从一批样本的结构中获取该参数?
请注意,由于它是一个无限循环的生成器,因此无法预先计算权重或动态计算权重以将权重放入损失函数或生成它们。
它们必须与正在生成的样本一致生成,并且确实在我的数据生成器中有自定义逻辑,可以根据 img1
、img2
和 label
的属性动态确定权重。为批次生成。
【问题讨论】:
【参考方案1】:手动训练循环替代方案
我唯一能想到的是手动训练循环,您可以自己获得权重。
有一个权重张量和一个不可变的批量大小:
weights = K.variable(np.zeros((batch_size,)))
在您的自定义损失中使用它们:
def custom_loss(true, pred):
return someCalculation(true, pred, weights)
对于“生成器”:
for e in range(epochs):
for s in range(steps_per_epoch):
x, y, w = next(generator) #or generator.next(), not sure
K.set_value(weights, w)
model.train_on_batch(x, y)
对于keras.utils.Sequence
:
for e in range(epochs):
for s in range(len(generator)):
x,y,w = generator[s]
K.set_value(weights, w)
model.train_on_batch(x,y)
我知道这个答案不是最优的,因为它不会像 fit_generator
那样并行从生成器获取数据。但这是我能想到的最简单的解决方案。 Keras 没有公开权重,它们在一些隐藏的源代码中自动应用。
让模型计算权重替代
如果可以从x
和y
计算权重,您可以将此任务委托给损失函数本身。
这有点骇人听闻,但可能会奏效:
input1 = Input(shape1)
input2 = Input(shape2)
# .... model creation .... #
model = Model([input1, input2], outputs)
让损失可以访问input1
和input2
:
def custom_loss(y_true, y_pred):
w = calculate_weights(input1, input2, y_pred)
# .... rest of the loss .... #
这里的问题是您是否可以根据输入计算权重作为张量。
【讨论】:
谢谢!像这样手动调用 train_on_batch 绝对不理想,但它提供了可靠的解决方法,非常感谢! 最后添加了一个替代答案,测试起来可能很有趣。 添加的答案是个好主意,但不幸的是,在我的情况下,权重取决于有关输入的元数据,这些元数据在生成输入并加载到批处理生成器的内部时访问。基本上,批处理生成器迭代文件清单和有关这些文件的元数据,以确定如何对正负图像对进行采样。根据其他元数据,一些正面和负面图像会获得更大的训练权重,因为模型必须在训练后的预测时间对它们进行良好的表现。 我明白了....如果您确实需要更好的性能,您可以尝试将此元数据作为额外输入传递给模型。然后将其乘以零并与主输入相加(只是为了连接到输出)。这听起来可能特别复杂,但根据加载批次的时间,它可能会显着提高您的训练速度。 元数据是大量的字符串数据,即文件元数据(创建的时间戳、创建者、创建时的环境属性)。其中大部分是预先计算成一组简单的标准,数据生成器可以检查这些标准,然后必须实际加载一批图像对、标签等。训练速度非常快为此,并不受批处理加载时间的限制。我认为找到一种结构化的方式将所有字符串数据接受到模型中的张量中确实会使代码复杂性非常成问题。【参考方案2】:Keras Tensorflow v2 中的损失函数是用样本权重调用的
output_loss = loss_fn(y_true, y_pred, sample_weight=sample_weight)
https://github.com/keras-team/keras/blob/tf-2/keras/engine/training.py
您可以使用 GradientTape 进行自定义训练,请参阅https://www.tensorflow.org/guide/keras/train_and_evaluate#part_ii_writing_your_own_training_evaluation_loops_from_scratch
【讨论】:
任何直接在 Tensorflow 中的类似 API 的抽象,如 Estimator API、Dataset 或这个 GradientTape,都必须被忽略。依赖 Tensorflow 开发人员为这些抽象选择的内容太不安全且容易出错。以上是关于如何在生成器提供的 Keras 自定义损失函数中访问样本权重?的主要内容,如果未能解决你的问题,请参考以下文章
keras:如何编写自定义的损失函数来聚合帧级预测到歌曲级预测
Keras 中的自定义损失函数(IoU 损失函数)和梯度误差?