首页 > 解决方案 > 使用 Keras 评估滑动窗口中的函数

问题描述

我正在尝试跨序列扩展匹配匹配算法。我的比赛有 20 个单位长,每个时间点有 4 个频道。我已经建立了一个封装匹配的模型,我只是不知道如何在滑动窗口中使用它来将它应用到更长的序列中以找到序列中的匹配。

我有 2 个(20, 4)输入张量 (querytarget),我将它们连接、添加、展平,然后应用一个简单的密集层。我在这个阶段有数据可以用 100K 查询、目标对进行训练。

def sum_seqs(seqs):
    return K.sum(seqs, axis=3)

def pad_dims(seq):
    return K.expand_dims(seq, axis=3)

def pad_outshape(in_shape):
    return (in_shape[0], in_shape[1], in_shape[2], 1)


query = Input((20, 4))
query_pad = Lambda(pad_dims, output_shape=pad_outshape, name='gpad')(query)

target = Input((20,4))
target_pad = Lambda(pad_dims, output_shape=pad_outshape)(target)

matching = Concatenate(axis = 3)([query_pad, target_pad])
matching = Lambda(sum_seqs)(matching)

matching = Flatten()(matching)
matching = Dropout(0.1)(matching)
matching = Dense(1, activation = 'sigmoid')(matching)

match_model = Model([query, target], matching)

这完美地工作。现在我想使用这个预训练模型来搜索target具有不同序列的更长query序列。

它似乎应该是这样的:

long_target = Input((100, 4))

short_target = Input((20, 4))
choose_query = Input((20, 4))

spec_match = match_model([choose_query, short_target])

mdl = TimeDistributed(spec_match)(long_target)

TimeDistributed需要 a Layernot a Tensor。有没有我缺少的包装?我会以错误的方式解决这个问题吗?我是否需要以某种方式将其重新表述为卷积问题?

继续实验:在我用键盘敲了一天的头之后,很明显,两者TimeDistributed都只backend.rnn允许您将模型/层应用于数据的单个时间片。似乎没有办法做到这一点。看起来唯一可以跨时间维度的多个切片“行走”的是Conv1D.

所以,我将我的问题重新定义为卷积,但这也不能很好地工作。我能够构建一个Conv1D过滤器来匹配特定的query. 这工作得相当好,它确实允许我扫描更长的序列并获得匹配。但是每个过滤器对于每个张量都是唯一的,query并且似乎没有一种方法可以在query不训练全新Conv1D层的情况下从小说到适当的过滤器权重。由于我的目标是找到query与大多数目标匹配的新 s,因此这并没有多大帮助。

query由于我的“匹配”需要目标和每个窗口处的查询的交互,因此似乎没有一种方法可以target通过Conv1D.

有没有办法在 Keras/tensorflow 中进行这种滑动窗口类型评估?这似乎是一件如此简单却又如此遥远的事情。有没有我找不到的方法可以做到这一点?

响应和进一步的实验。

@today 和 @nuric 的解决方案有效,但它们最终target以平铺类型的方式复制输入数据。因此,对于长度查询,图表中的输入数据副本m会少一些。m我希望找到一种解决方案,可以在target没有重复的情况下真正“滑动”评估。

Conv1D这是我想出的几乎解决方案的一个版本。

query_weights = []

for query, (targets, scores) in query_target_gen():
    single_query_model = Sequential()
    single_query_model.add(Conv1D(1, 20, input_shape = (20, 4)))
    single_query_model.add(Flatten())

    single_query_model.fit(targets, scores)

    query_weights.append(single_query_model.layers[0].get_weights())

multi_query_model_long_targets = Sequential()
multi_query_model_long_targets.add(Conv1D(len(query_weights), 20, input_shape = (100, 4)))

multi_query_model_long_targets.layers[0].set_weights(combine_weights(query_weights))

multi_query_model_long_targets.summary()

combine_weights函数只是进行一些拆包和矩阵重新排列,以按照需要的方式堆叠过滤器Conv1D

该解决方案解决了数据重复问题,但它以其他方式困扰着我。一个是基于数据的......我的数据包含许多query,target对,但它往往是相同的targetmany querys,因为在那个方向上生成真实世界的数据更容易。因此,这样做会使培训变得困难。其次,这假设每个都query以独立的方式工作,而实际上,我知道query,target配对实际上很重要。因此,使用可以查看许多配对示例而不是个人的模型是有意义的。

有没有办法结合这两种方法?有没有办法让它在沿着序列行走时Conv1D将长target张量与常数结合起来?query

标签: pythontensorflowkerasconv-neural-networksliding-window

解决方案


只是为了提供使用 Keras 后端功能的替代解决方案。

您还可以使用K.arange和生成滑动窗口K.map_fn

def sliding_windows(inputs):
    target, query = inputs
    target_length = K.shape(target)[1]  # variable-length sequence, shape is a TF tensor
    query_length = K.int_shape(query)[1]
    num_windows = target_length - query_length + 1  # number of windows is also variable

    # slice the target into consecutive windows
    start_indices = K.arange(num_windows)
    windows = K.map_fn(lambda t: target[:, t:(t + query_length), :],
                       start_indices,
                       dtype=K.floatx())

    # `windows` is a tensor of shape (num_windows, batch_size, query_length, ...)
    # so we need to change the batch axis back to axis 0
    windows = K.permute_dimensions(windows, (1, 0, 2, 3))

    # repeat query for `num_windows` times so that it could be merged with `windows` later
    query = K.expand_dims(query, 1)
    query = K.tile(query, [1, num_windows, 1, 1])

    # just a hack to force the dimensions 2 to be known (required by Flatten layer)
    windows = K.reshape(windows, shape=K.shape(query))
    return [windows, query]

要使用它:

long_target = Input((None, 4))
choose_query = Input((20, 4))
windows, query = Lambda(sliding_windows)([long_target, choose_query])

鉴于您的 pretrained match_model,问题TimeDistributed在于它无法包装Model具有多个输入的 Keras。

但是,由于逻辑匹配targetquery是在 之后的层中实现的Concatenate,因此您可以将这些层收集到 中Model,并应用于TimeDistributed它:

submodel_input = Input((20, 4, 2))
x = submodel_input
for layer in match_model.layers[-4:]:  # the `Lambda(sum_seqs)` layer
    x = layer(x)
submodel = Model(submodel_input, x)

现在您只需要以与sliding_windows以下相同的方式处理和合并 的输出match_model

long_target = Input((None, 4))
choose_query = Input((20, 4))
windows, query = Lambda(sliding_windows)([long_target, choose_query])

windows_pad = Lambda(lambda x: K.expand_dims(x))(windows)
query_pad = Lambda(lambda x: K.expand_dims(x))(query)
merged = Concatenate()([windows_pad, query_pad])

match_scores = TimeDistributed(submodel)(merged)
max_score = GlobalMaxPooling1D()(match_scores)
model = Model([long_target, choose_query], max_score)

model然后可以以端到端的方式用于匹配长目标。

您还可以通过应用于滑动窗口来验证 的输出model确实是匹配分数的最大值:match_model

target_arr = np.random.rand(32, 100, 4)
query_arr = np.random.rand(32, 20, 4)

match_model_scores = np.array([
    match_model.predict([target_arr[:, t:t + 20, :], query_arr])
    for t in range(81)
])
scores = model.predict([target_arr, query_arr])

print(np.allclose(scores, match_model_scores.max(axis=0)))
True

推荐阅读