为什么这个 tf.keras 模型在切片输入上的行为与预期不同?
Why does this tf.keras model behave differently than expected on sliced inputs?
我正在编写一个 Keras 模型,给定(迷你)批张量,将同一层应用于它们的每个元素。只是为了提供一点上下文,我给出了字符串的输入组(固定大小),这些字符串必须由编码层一个一个地编码。因此,包含(迷你)批量大小的输入大小为 (None, n_sentences_per_sample, ),其中 n_sentences_per_sample 是先验已知的固定值。
为此,我在函数 API 中创建模型时使用了此自定义函数 API:
def _branch_execute(layer_in: keras.layers.Layer, sublayer: [keras.layers.Layer, Callable], **args) -> keras.layers.Layer:
instance_cnt = layer_in.shape[1]
sliced_inputs = [tf.keras.layers.Lambda(lambda x: x[:, i])(layer_in) for i in range(instance_cnt)]
branch_layers = [sublayer(**{**{'layer_in': sliced_inputs[i]}, **args}) for i in range(instance_cnt)]
expand_layer = tf.keras.layers.Lambda(lambda x: K.expand_dims(x, axis=1))
expanded_layers = [expand_layer(branch_layers[i]) for i in range(instance_cnt)]
concated_layer = tf.keras.layers.Concatenate(axis=1)(expanded_layers)
return concated_layer
我是这样用的
model_input = keras.layers.Input(shape=(self.max_sents, ),
dtype=tf.string,
)
sentences_embedded = self._branch_execute(model_input, self._get_nnlm_128)
model = keras.models.Model(model_input, sentences_embedded)
其中 self._get_nnlm_128() 只是一个函数,returns 将缓存的预训练嵌入层应用于输入的结果,即
def _get_nnlm_128(self, layer_in: keras.layers.Layer, trainable: bool = False):
if 'nnlm_128_layer_shared' not in self.shared_layers_cache:
self.shared_layers_cache['nnlm_128_layer_shared'] = {
'encoder': hub.KerasLayer("https://tfhub.dev/google/nnlm-en-dim128-with-normalization/2", trainable=trainable)
}
shared_layers = self.shared_layers_cache['nnlm_128_layer_shared']
encoder = shared_layers['encoder'](layer_in)
return encoder
我遇到的问题如下:
- 如果我调用 self._branch_execute(input_tensor, self._get_nnlm_128) 其中输入张量只是一个形状良好的张量,它完美运行;
- 如果我在同一个 input_tensor[=46 上调用 model(无论是直接调用还是通过 .predict(),无论编译与否) =],我得到样本中每个句子的重复结果(奇怪的是,它是对应于 LAST 句子的输出,重复 - 见下文);
举个例子(虽然我对每个可能的输入都有同样的问题),让我们考虑一个由 7 个句子(7 个字符串)组成的 input_tensor,重塑为 (1, 7, ) 以包括小批量轴。 1)的结果是
[[[ 0.216900051 0.037066862 0.163929373 ... 0.050420273 0.082906663 0.059960182],
[ 0.531883411 -0.000807280 0.107559107 ... -0.079948671 -0.020143294 0.007032406],
...
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -1.2199926 -0.13466084]]]
我得到了 7 个 vectors/embeddings,大小为 128,如预期的那样彼此不同;
奇怪的是,2) 的结果是
[[[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084]]]
我得到了 7 次相同的向量(正如我所说,它总是对应于所有句子重复的最后一个向量)。我从实际 运行.
中获取了结果
在我进行的许多试验中,我尝试从模型中输出 model_input,效果很好,即它对应于输入字符串。嵌入模型直接取自Tensorflow hub,所以应该没有问题。此外,对于任何其他嵌入层,无论是自定义的还是预训练的,都会观察到相同的行为。因此,我认为问题可能出在 _branch_execute() 函数中,但我不知道单独使用时可以正常工作的问题是什么。也许它可能与keras模型内部的一些特殊广播行为有关,但我不知道如何测试它,更不用说如何解决它了。
如果您能就出现此问题的原因以及解决方法提出任何建议,我将不胜感激。我不是 Tensorflow 的专家,所以也许我只是误判了一些东西(以防万一,请原谅我!)。我很乐意根据需要分享更多信息以帮助解决问题。非常感谢:)
最后得出结论,问题出在线路上
sliced_inputs = [tf.keras.layers.Lambda(lambda x: x[:, i])(layer_in) for i in range(instance_cnt)]
这显然没有按预期工作(我是 运行 Tensorflow 2.4.0,但我在 Tensorflow 2.5.0-nightly 上也遇到了同样的问题)。我只是用一个自定义层替换了 Lambda 层,它做的事情完全相同,即
class Slicer(keras.layers.Layer):
def __init__(self, i, **kwargs):
self.i = i
super(Slicer, self).__init__(**kwargs)
def call(self, inputs, **kwargs):
return inputs[:, self.i]
然后我在 _branch_execute() 函数中使用它就像这样
def _branch_execute(self, layer_in: keras.layers.Layer, sublayer: [keras.layers.Layer, Callable], **args) -> keras.layers.Layer:
instance_cnt = layer_in.shape[1]
sliced_inputs = [Slicer(i)(layer_in) for i in range(instance_cnt)]
branch_layers = [sublayer(**{**{'layer_in': sliced_inputs[i]}, **args}) for i in range(instance_cnt)]
expand_layer = tf.keras.layers.Lambda(lambda x: K.expand_dims(x, axis=1))
expanded_layers = [expand_layer(branch_layers[i]) for i in range(instance_cnt)]
concated_layer = tf.keras.layers.Concatenate(axis=1)(expanded_layers)
return concated_layer
我不确定这是否是解决问题的最佳选择,但它看起来非常简洁并且运行良好。
由于这可能是 Lambda 层的意外行为,我将在 Tensorflow github 和 post 上开一个问题,回复以供参考。
我正在编写一个 Keras 模型,给定(迷你)批张量,将同一层应用于它们的每个元素。只是为了提供一点上下文,我给出了字符串的输入组(固定大小),这些字符串必须由编码层一个一个地编码。因此,包含(迷你)批量大小的输入大小为 (None, n_sentences_per_sample, ),其中 n_sentences_per_sample 是先验已知的固定值。
为此,我在函数 API 中创建模型时使用了此自定义函数 API:
def _branch_execute(layer_in: keras.layers.Layer, sublayer: [keras.layers.Layer, Callable], **args) -> keras.layers.Layer:
instance_cnt = layer_in.shape[1]
sliced_inputs = [tf.keras.layers.Lambda(lambda x: x[:, i])(layer_in) for i in range(instance_cnt)]
branch_layers = [sublayer(**{**{'layer_in': sliced_inputs[i]}, **args}) for i in range(instance_cnt)]
expand_layer = tf.keras.layers.Lambda(lambda x: K.expand_dims(x, axis=1))
expanded_layers = [expand_layer(branch_layers[i]) for i in range(instance_cnt)]
concated_layer = tf.keras.layers.Concatenate(axis=1)(expanded_layers)
return concated_layer
我是这样用的
model_input = keras.layers.Input(shape=(self.max_sents, ),
dtype=tf.string,
)
sentences_embedded = self._branch_execute(model_input, self._get_nnlm_128)
model = keras.models.Model(model_input, sentences_embedded)
其中 self._get_nnlm_128() 只是一个函数,returns 将缓存的预训练嵌入层应用于输入的结果,即
def _get_nnlm_128(self, layer_in: keras.layers.Layer, trainable: bool = False):
if 'nnlm_128_layer_shared' not in self.shared_layers_cache:
self.shared_layers_cache['nnlm_128_layer_shared'] = {
'encoder': hub.KerasLayer("https://tfhub.dev/google/nnlm-en-dim128-with-normalization/2", trainable=trainable)
}
shared_layers = self.shared_layers_cache['nnlm_128_layer_shared']
encoder = shared_layers['encoder'](layer_in)
return encoder
我遇到的问题如下:
- 如果我调用 self._branch_execute(input_tensor, self._get_nnlm_128) 其中输入张量只是一个形状良好的张量,它完美运行;
- 如果我在同一个 input_tensor[=46 上调用 model(无论是直接调用还是通过 .predict(),无论编译与否) =],我得到样本中每个句子的重复结果(奇怪的是,它是对应于 LAST 句子的输出,重复 - 见下文);
举个例子(虽然我对每个可能的输入都有同样的问题),让我们考虑一个由 7 个句子(7 个字符串)组成的 input_tensor,重塑为 (1, 7, ) 以包括小批量轴。 1)的结果是
[[[ 0.216900051 0.037066862 0.163929373 ... 0.050420273 0.082906663 0.059960182],
[ 0.531883411 -0.000807280 0.107559107 ... -0.079948671 -0.020143294 0.007032406],
...
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -1.2199926 -0.13466084]]]
我得到了 7 个 vectors/embeddings,大小为 128,如预期的那样彼此不同; 奇怪的是,2) 的结果是
[[[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084],
[ 0.15044811 0.00890037 0.10413752 ... -0.05391502 -0.12199926 -0.13466084]]]
我得到了 7 次相同的向量(正如我所说,它总是对应于所有句子重复的最后一个向量)。我从实际 运行.
中获取了结果在我进行的许多试验中,我尝试从模型中输出 model_input,效果很好,即它对应于输入字符串。嵌入模型直接取自Tensorflow hub,所以应该没有问题。此外,对于任何其他嵌入层,无论是自定义的还是预训练的,都会观察到相同的行为。因此,我认为问题可能出在 _branch_execute() 函数中,但我不知道单独使用时可以正常工作的问题是什么。也许它可能与keras模型内部的一些特殊广播行为有关,但我不知道如何测试它,更不用说如何解决它了。
如果您能就出现此问题的原因以及解决方法提出任何建议,我将不胜感激。我不是 Tensorflow 的专家,所以也许我只是误判了一些东西(以防万一,请原谅我!)。我很乐意根据需要分享更多信息以帮助解决问题。非常感谢:)
最后得出结论,问题出在线路上
sliced_inputs = [tf.keras.layers.Lambda(lambda x: x[:, i])(layer_in) for i in range(instance_cnt)]
这显然没有按预期工作(我是 运行 Tensorflow 2.4.0,但我在 Tensorflow 2.5.0-nightly 上也遇到了同样的问题)。我只是用一个自定义层替换了 Lambda 层,它做的事情完全相同,即
class Slicer(keras.layers.Layer):
def __init__(self, i, **kwargs):
self.i = i
super(Slicer, self).__init__(**kwargs)
def call(self, inputs, **kwargs):
return inputs[:, self.i]
然后我在 _branch_execute() 函数中使用它就像这样
def _branch_execute(self, layer_in: keras.layers.Layer, sublayer: [keras.layers.Layer, Callable], **args) -> keras.layers.Layer:
instance_cnt = layer_in.shape[1]
sliced_inputs = [Slicer(i)(layer_in) for i in range(instance_cnt)]
branch_layers = [sublayer(**{**{'layer_in': sliced_inputs[i]}, **args}) for i in range(instance_cnt)]
expand_layer = tf.keras.layers.Lambda(lambda x: K.expand_dims(x, axis=1))
expanded_layers = [expand_layer(branch_layers[i]) for i in range(instance_cnt)]
concated_layer = tf.keras.layers.Concatenate(axis=1)(expanded_layers)
return concated_layer
我不确定这是否是解决问题的最佳选择,但它看起来非常简洁并且运行良好。
由于这可能是 Lambda 层的意外行为,我将在 Tensorflow github 和 post 上开一个问题,回复以供参考。