如何在Keras中强制(回归)模型输出的单调性?

时间:2018-06-12 18:35:47

标签: python keras deep-learning regression

我目前正在解决一个问题,即我提供了一个输入变量 a 的神经网络,另一个输入 x 这是N个数字的单调递增序列。

所以我的网络基本上看起来像这样:

a_input = Input(shape=[1], name='a')
x_input = Input(shape=[N], name='x')
nn = concatenate([a_input, x_input])
nn = Dense(100, activation='relu')(nn)
nn = Dense(N, activation='relu')(nn)
model = Model(inputs=[a_input, x_input], outputs=[nn])
model.compile(loss='mean_squared_error', optimizer="adam")

我对输入空间执行回归(每个 a ,序列 x 是唯一的) ,我希望网络为每组输入输出一个单调递增的(非负) N 数字序列 a X 即可。

现在,我注意到到目前为止我的输出并不严格地说单调,但如果你'缩小',它们看起来就像它们一样。我的意思是,对于给定的 a x 的选择,如果我希望我的输出数组看起来喜欢:

[0, 0.5, 0.51, 0.7, 0.75, 0.9, 1.], 

我可能会得到:

[0.001, 0.5, 0.48, 0.7, 0.75, 0.9, 1.].

因此,我想知道Keras中是否存在标准方法或特定工具,以限制模型仅输出单调增加的序列?

1 个答案:

答案 0 :(得分:1)

要强制执行非负输出,请在输出图层中使用非负激活,例如ReLU或sigmoid。

我不知道任何神经方法在输出中强制单调性,但在我看来,一种明智的方法是改变输出表示,使网络预测两个连续元素之间的差异。例如,您可以转换输出数组:

a=[0, 0.5, 0.51, 0.7, 0.75, 0.9, 1.]

为:

b=[0, 0.5, 0.01, 0.19, 0.05, 0.15, 0.1]

b[0] = a[0]b[i] = a[i]-a[i-1] i>0。在此上下文中,使用循环层作为输出层是有意义的,因为现在每个输出单元都依赖于先前的输出单元。对于a[0] = b[0],您的原始表示可以轻松恢复为a[i] = b[i]+a[i-1]i>0,并且生成的序列将单调递增,因为每个输出b[i]都是非负数。

更新1 。 LSTM应返回完整序列。您可以尝试按如下方式构建模型:

a_input = Input(shape=[1], name='a')
x_input = Input(shape=[N], name='x')
nn = concatenate([a_input, x_input])
nn = Dense(100, activation='relu')(nn)
nn = Dense(N, activation='relu')(nn)
nn = Lambda(lambda x: x[..., None])(nn)  # Output shape=(batch_size, nb_timesteps=N, input_dim=1)
nn = LSTM(1, return_sequences=True, activation='relu')(nn)  # Output shape=(batch_size, nb_timesteps=N, output_dim=1)
nn = Lambda(lambda x: keras.backend.squeeze(x, axis=-1))(nn)  # Output shape=(batch_size, N)
model = Model(inputs=[a_input, x_input], outputs=[nn])
model.compile(loss='mean_squared_error', optimizer="adam")

更新2 。具有一个隐藏单元的LSTM可能不够强大。我不确定这是否会有所帮助,但您可以尝试在最后一个之前添加另一个LSTM图层(即10个):

...
nn = Lambda(lambda x: x[..., None])(nn)  # Output shape=(batch_size, nb_timesteps=N, input_dim=1)
nn = LSTM(10, return_sequences=True)(nn)  # Output shape=(batch_size, nb_timesteps=N, output_dim=10)
nn = LSTM(1, return_sequences=True, activation='relu')(nn)  # Output shape=(batch_size, nb_timesteps=N, output_dim=1)
nn = Lambda(lambda x: keras.backend.squeeze(x, axis=-1))(nn)  # Output shape=(batch_size, N)
...