【问题标题】:Keras: show loss for each label in a multi-label regressionKeras:在多标签回归中显示每个标签的损失
【发布时间】:2018-11-04 15:35:18
【问题描述】:

假设你有一个以n 神经元作为输出的 Keras 模型,其中每个神经元都与一个回归变量(例如汽车的速度、汽车的高度……)相关联,如以下代码 sn -p:

# define Keras model
input_layer = Input(shape=shape)
... # e.g. conv layers
x = Dense(n, activation='linear')(x)
model = Model(inputs=input_layer, outputs=x)

model.compile(loss='mean_absolute_error', optimizer='sgd', metrics=['mean_squared_error'])

history = model.fit_generator(...)

现在,历史字典中存储的 MAE 损失是单个数字,它是根据 n - 维度 y_predy_true 计算得出的 数组。因此,单个损失值是n 标签的单个损失值的平均值,这可以在 Keras MAE 函数中看到:

def mean_absolute_error(y_true, y_pred):
    return K.mean(K.abs(y_pred - y_true), axis=-1)

但是,我想获得一个历史对象,其中包含每个 n 标签的损失,即 {loss: {'speed': loss_value_speed, 'height': loss_value_height}}。理想情况下,训练过程中的进度条也应该显示单个损失,而不是组合损失。

我该怎么做?

我想可以为每个输出神经元编写一个自定义指标,它只计算 y_predy_true 向量中单个索引的损失,但这感觉像是一种解决方法:

def mean_absolute_error_label_0(y_true, y_pred):
    # calculate the loss only for the first label, label_0
    return K.mean(K.abs(y_pred[0] - y_true[0]), axis=-1)

【问题讨论】:

  • 您可以为每个功能返回一个带有损失的数组,然后使用回调函数,如我在此处的回答:stackoverflow.com/questions/50061393/…
  • 谢谢!不幸的是,我仍然有一个问题:我将自定义指标定义为return K.abs(y_pred - y_true),它返回形状为 5 的 TF 张量。但是,如果我通过日志字典访问指标,则指标是标量而不是向量还有吗?
  • 我认为您忘记将度量传递给编译方法的metrics 属性:metrics=['mean_squared_error', mean_absolute_error])
  • 嗯,我不这么认为。我在这段代码 sn-p 中做到了:pastebin.com/P1C80gi0
  • 您无法从logs 获取数组。插入logs 的损失值是平均标量值。

标签: python keras regression


【解决方案1】:

一种可能的解决方案是为每个目标使用单独的输出层,并为每个目标分配一个name(即Dense(1, name='...'))。在您的情况下,这与使用 Dense(n) 输出层进行训练相同,因为总损失只是各个损失的总和。

例如,

input_layer = Input(shape=(1000,))
x = Dense(100)(input_layer)

# name each output layer
target_names = ('speed', 'height')
outputs = [Dense(1, name=name)(x) for name in target_names]

model = Model(inputs=input_layer, outputs=outputs)
model.compile(loss='mean_absolute_error', optimizer='sgd', metrics=['mean_squared_error'])

现在,当您拟合模型时,您应该能够分别查看每个目标的损失(和指标)。

X = np.random.rand(10000, 1000)
y = [np.random.rand(10000) for _ in range(len(outputs))]
history = model.fit(X, y, epochs=3)

Epoch 1/1
10000/10000 [==============================] - 1s 127us/step - loss: 0.9714 - speed_loss: 0.4768 - height_loss: 0.4945 - speed_mean_squared_error: 0.5253 - height_mean_squared_error: 0.5939
Epoch 2/3
10000/10000 [==============================] - 1s 101us/step - loss: 0.5109 - speed_loss: 0.2569 - height_loss: 0.2540 - speed_mean_squared_error: 0.0911 - height_mean_squared_error: 0.0895
Epoch 3/3
10000/10000 [==============================] - 1s 107us/step - loss: 0.5040 - speed_loss: 0.2529 - height_loss: 0.2511 - speed_mean_squared_error: 0.0873 - height_mean_squared_error: 0.0862

保存到返回的history 对象的损失也将被命名。

print(history.history)

{'height_loss': [0.49454938204288484, 0.2539591451406479, 0.25108356306552887],
 'height_mean_squared_error': [0.5939331066846848,
  0.08951960142850876,
  0.08619525188207626],
 'loss': [0.9713814586639404, 0.5108571118354798, 0.5040025643348693],
 'speed_loss': [0.47683207807540895, 0.25689796624183653, 0.25291900217533114],
 'speed_mean_squared_error': [0.5252606071352959,
  0.09107607080936432,
  0.0872862442612648]}

编辑:如果输出height 的丢失取决于speed 的值,您可以:

  • 连接输出,因为您需要这两个值来计算自定义损失
  • Concatenate 层命名为“height”,这将是历史对象中height 的输出
  • model.compile() 提供两个损失函数(一个用于speed,一个用于连接输出height
def custom_loss(y_true, y_pred):
    y_pred_height = y_pred[:, 0]
    y_pred_speed = y_pred[:, 1]

    # some loss which depends on the value of `speed`
    loss = losses.mean_absolute_error(y_true, y_pred_height * y_pred_speed)
    return loss

input_layer = Input(shape=(1000,))
x = Dense(100, activation='relu')(input_layer)

output_speed = Dense(1, activation='relu', name='speed')(x)
output_height = Dense(1, activation='relu')(x)
output_merged = Concatenate(name='height')([output_height, output_speed])

model = Model(inputs=input_layer, outputs=[output_speed, output_merged])
model.compile(loss={'speed': 'mean_absolute_error', 'height': custom_loss},
              optimizer='sgd',
              metrics={'speed': 'mean_squared_error'})

输出将是:

X = np.random.rand(10000, 1000)
y = [np.random.rand(10000), np.random.rand(10000)]
history = model.fit(X, y, epochs=3)

Epoch 1/3
10000/10000 [==============================] - 5s 501us/step - loss: 1.0001 - speed_loss: 0.4976 - height_loss: 0.5026 - speed_mean_squared_error: 0.3315
Epoch 2/3
10000/10000 [==============================] - 2s 154us/step - loss: 0.9971 - speed_loss: 0.4960 - height_loss: 0.5011 - speed_mean_squared_error: 0.3285
Epoch 3/3
10000/10000 [==============================] - 1s 149us/step - loss: 0.9971 - speed_loss: 0.4960 - height_loss: 0.5011 - speed_mean_squared_error: 0.3285.

print(history.history)
{'height_loss': [0.502568191242218, 0.5011419380187988, 0.5011419407844544],
 'loss': [1.0001451692581176, 0.9971360887527466, 0.9971360870361328],
 'speed_loss': [0.4975769768714905, 0.4959941484451294, 0.4959941472053528],
 'speed_mean_squared_error': [0.33153974375724793,
                              0.32848617186546325,
                              0.32848617215156556]}

【讨论】:

  • 感谢您的精彩回答,它对我帮助很大!不过我还有一个问题:假设输出“高度”的损失是自定义损失,它需要来自“速度”输出的 y_pred 预测。这可能吗?我只能想象再次将输出连接成单个输出,然后单个损失的损失历史将丢失。
  • @0vbb 是的,这是可能的。如果这是您想要的,请查看我的编辑。
  • 不幸的是我遇到了这个方法的另一个问题:假设速度和高度连接到两个独立的密集网络,即speeddense_nn_1heightdense_nn_2。然后两个网络都连接到主网络main_nn。现在,我想通过将K.stop_gradient 放在两个网络之间来停止从dense_nn_1main_nn 的渐变。然而,由于Concatenate 层,speed 损失仍将通过dense_nn_2height 传播。有可能解决这个问题吗?
  • 我认为在custom_loss 中使用y_pred_speed = K.stop_gradient(y_pred[:, 1]) 可以解决它。
  • @Yu-Yang 你能看看这个问题吗? stackoverflow.com/q/58900947/5904928我挣扎了好几个小时,找不到答案。
猜你喜欢
  • 2017-03-09
  • 2019-08-16
  • 2018-05-25
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 2019-01-06
  • 2017-07-12
  • 2018-10-27
相关资源
最近更新 更多