有没有办法将时间权重传递给损失函数?

Posted

技术标签:

【中文标题】有没有办法将时间权重传递给损失函数?【英文标题】:Is there a way to pass along temporal weights to a loss function? 【发布时间】:2020-07-27 21:08:07 【问题描述】:

背景

目前,我正在使用 LSTM 执行回归。我正在使用具有相当大的时间步数的小批量(但比我拥有的时间步数要少得多)。

我正在尝试过渡到时间步长更少但启用了有状态的更大批次,以允许使用更多生成的训练数据。

但是,我目前正在使用基于 sqrt(timestep) 的正则化,(这是经过消融测试的,有助于提高收敛速度,由于问题的统计性质,它可以工作,预期误差会减少 sqrt 的一个因子(时间步))。这是通过使用tf.range 在损失函数中生成适当大小的列表来执行的。启用有状态时,此方法将不正确,因为它会计算错误的时间步数(此批次中的时间步数,而不是到目前为止的整体)。

问题

有没有办法将整数或浮点数的偏移量或列表传递给损失函数?最好不要修改模型,但我认识到可能需要这种性质的 hack。

代码

简化模型:

def create_model():    
    inputs = Input(shape=(None,input_nodes))
    next_input = inputs
    for i in range(dense_layers):
        dense = TimeDistributed(Dense(units=dense_nodes,
                activation='relu',
                kernel_regularizer=l2(regularization_weight),
                activity_regularizer=l2(regularization_weight)))\
            (next_input)
        next_input = TimeDistributed(Dropout(dropout_dense))(dense)

    for i in range(lstm_layers):
        prev_input = next_input
        next_input = LSTM(units=lstm_nodes,
                dropout=dropout_lstm,
                recurrent_dropout=dropout_lstm,
                kernel_regularizer=l2(regularization_weight),
                recurrent_regularizer=l2(regularization_weight),
                activity_regularizer=l2(regularization_weight),
                stateful=True,
                return_sequences=True)\
            (prev_input)
        next_input = add([prev_input, next_input])

    outputs = TimeDistributed(Dense(output_nodes,
            kernel_regularizer=l2(regularization_weight),
            activity_regularizer=l2(regularization_weight)))\
        (next_input)

    model = Model(inputs=inputs, outputs=outputs)

损失函数

def loss_function(y_true, y_pred):
    length = K.shape(y_pred)[1]

    seq = K.ones(shape=(length,))
    if use_sqrt_loss_scaling:
        seq = tf.range(1, length+1, dtype='int32')
        seq = K.sqrt(tf.cast(seq, tf.float32))

    seq = K.reshape(seq, (-1, 1))

    if separate_theta_phi:
        angle_loss = phi_loss_weight * phi_metric(y_true, y_pred, angle_loss_fun)
        angle_loss += theta_loss_weight * theta_metric(y_true, y_pred, angle_loss_fun)
    else:
        angle_loss = angle_loss_weight * total_angle_metric(y_true, y_pred, angle_loss_fun)

    norm_loss = norm_loss_weight * norm_loss_fun(y_true, y_pred)
    energy_loss = energy_loss_weight * energy_metric(y_true, y_pred)
    stability_loss = stability_loss_weight * stab_loss_fun(y_true, y_pred)
    act_loss = act_loss_weight * act_loss_fun(y_true, y_pred)

    return K.sum(K.dot(0
        + angle_loss
        + norm_loss
        + energy_loss
        + stability_loss
        + act_loss
        , seq))

(计算损失函数片段的函数不应该是超级相关的。简单地说,它们也是损失函数。)

【问题讨论】:

我不确定我是否正确理解了您的目标。但是你想传递给损失函数的这个偏移量是否与批次相关?即每个批次是否有不同的偏移量,您可以将其作为输入传递给模型? 没错!就像,如果我正在查看时间步长 (0, 100),我希望能够计算从 0 到 100 的每个整数的平方根,并用我的原始损失值点它。但是,如果我正在查看第 4 批时间步长,我将查看时间步长 (300、400),因此我想将 300 到 400 之间的数字取平方,并用我的原始损失值点它。 【参考方案1】:

为此,您可以使用fit 方法的sample_weight 参数并将sample_weight_mode='temporal' 传递给compile 方法,以便您可以为批次中每个样本的每个时间步分配一个权重:

model.compile(..., sample_weight_mode='temporal')
model.fit(..., sample_weight=sample_weight)

sample_weight 应该是一个形状为 (num_samples, num_timesteps) 的数组。

请注意,如果您使用输入数据生成器或Sequence 的实例,则需要将样本权重作为生成器中生成的元组/列表的第三个元素或Sequence 实例传递。

【讨论】:

以上是关于有没有办法将时间权重传递给损失函数?的主要内容,如果未能解决你的问题,请参考以下文章

将元数据传递给自定义损失函数

Keras 自定义损失函数,用于传递 y_true 和 y_pred 以外的参数

过拟合解决办法之一:权重衰减

Keras 中具有样本权重的自定义损失函数

使用自定义目标/损失函数的随机森林回归器(Python/Sklearn)

在 pytorch 中使用多个损失函数