如何使用Keras(tensorflow)限制神经网络回归预测输出的总和

4
我正在用Keras(Python,后端:TensorFlow)训练一个神经网络作为回归模型。因此,我的输出层不包含激活函数,并且我使用均方误差作为损失函数。
我的问题是:我想确保所有输出估计值之和(几乎)等于所有实际标签的总和。
我的意思是:我不仅希望每个训练样本i满足(y_real)^i ~ (y_predict)^i,还要保证对所有i求和时sum(y_real) = sum(y_predict)。常规线性回归使添加此限制变得简单,但我没有看到神经网络的类似内容。我可以通过将最终结果乘以sum(y_real) / sum(y_predict)来解决这个问题,但我担心如果我不想损害单个预测结果,这不是理想的解决方法。
我还有哪些其他选项?
(我无法分享我的数据,也无法轻松地用不同的数据重现问题,但这是请求的代码:)
from keras.models import Sequential
from keras.layers import Dense

model = Sequential()
model.add(Dense(128, activation = 'relu', input_dim = 459))
model.add(Dense(32, activation = 'relu'))
model.add(Dense(1))

model.compile(loss = 'mean_squared_error',
              optimizer = 'adam')

model.fit(X_train, Y_train, epochs = 5, validation_data = (X_val, 
Y_val), batch_size = 128)

请展示你的代码。 - VegardKT
1个回答

7

从优化的角度来看,您希望为问题引入一个等式约束。您要寻找网络权重,使得预测值y1_hat、y2_hat和y3_hat相对于标签y1、y2、y3的均方误差最小化。此外,您还希望满足以下条件:

sum(y1, y2, y3) = sum(y1_hat, y2_hat, y3_hat)

由于您使用的是神经网络,因此希望以一种方式施加该约束,使您仍然可以使用反向传播来训练网络。

一种方法是在损失函数中添加一个项,惩罚 sum(y1, y2, y3)sum(y1_hat, y2_hat, y3_hat) 之间的差异。

最小工作示例:

import numpy as np
import keras.backend as K
from keras.layers import Dense, Input
from keras.models import Model

# Some random training data and labels
features = np.random.rand(100, 20)
labels = np.random.rand(100, 3)

# Simple neural net with three outputs
input_layer = Input((20,))
hidden_layer = Dense(16)(input_layer)
output_layer = Dense(3)(hidden_layer)

# Model
model = Model(inputs=input_layer, outputs=output_layer)

# Write a custom loss function
def custom_loss(y_true, y_pred):
    # Normal MSE loss
    mse = K.mean(K.square(y_true-y_pred), axis=-1)
    # Loss that penalizes differences between sum(predictions) and sum(labels)
    sum_constraint = K.square(K.sum(y_pred, axis=-1) - K.sum(y_true, axis=-1))

    return(mse+sum_constraint)

# Compile with custom loss
model.compile(loss=custom_loss, optimizer='sgd')
model.fit(features, labels, epochs=1, verbose=1)

请注意,这种限制是以“软”限制的方式而不是硬性限制的方式施加的。您仍然会得到偏差,但网络应该学习权重,使它们变小。

1
谢谢您提供非常有用的答案。我对您对 sum_constraint 的定义感到困惑。如果您先求和,然后再平方差值,那么这个数字将会非常大并且主导损失函数。我认为之后再求平均值没有什么作用,因为它已经给出了一个单一的数字。也许像这样更合适? sum_constraint = K.square(K.mean(y_pred, axis=-1) - K.mean(y_true, axis=-1)) - Joe_P
1
是的,那是一个很好的建议。我只是想阐明我的思路。我更新了我的帖子,删除了冗余的 K.mean() - sdcbr
你好,这个约束条件最终能否应用于变分自编码器损失中? - James Arten

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