编程语言
首页 > 编程语言> > python – 使用Keras在滑动窗口中评估函数

python – 使用Keras在滑动窗口中评估函数

作者:互联网

我正在尝试在序列中扩展匹配匹配算法.我的比赛长20个单位,每个时间点有4个频道.我已经构建了一个封装匹配的模型,我无法弄清楚如何在滑动窗口中使用它来跨更长的序列应用它来查找序列中的匹配.

我有2(20,4)个输入张量(查询和目标),我连接,添加,展平,然后应用一个简单的密集层.我在这个阶段有数据来训练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)

这非常有效.现在我想使用这个预先训练的模型来搜索具有不同查询序列的更长的目标序列.

它似乎应该是这样的:

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需要一个Layer而不是Tensor.有没有我错过的包装纸?我是以错误的方式来做这件事的吗?我是否需要以某种方式将其重新表述为卷积问题?

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

所以,我把我的问题重新定义为卷积,但这也行不通.我能够构建一个与特定查询匹配的Conv1D过滤器.这工作得相当好,它确实允许我扫描更长的序列并获得匹配.但是每个过滤器对于每个查询张量都是唯一的,并且似乎没有办法从新颖的查询转到适当的过滤器权重而不需要训练全新的Conv1D层.由于我的目标是找到与大多数目标匹配的新查询,因此这无济于事.

由于我的“匹配”需要目标与每个窗口的查询的交互,似乎没有办法通过Conv1D在100个长度的目标张量的每个窗口上获得20个长度的查询张量的交互.

有没有办法在Keras / tensorflow中进行滑动窗口类型评估?它看起来像是如此简单但却如此遥远.我有没有办法做到这一点,我找不到?

回应和进一步的实验.

来自@today和@nuric的解决方案可以工作,但他们最终以平铺类型的方式复制输入目标数据.因此,对于长度为m的查询,图中的输入数据将有少许m个副本.我希望找到一种解决方案,实际上可以在整个目标中“滑动”评估而不会重复.

这是我提出的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的方式堆叠过滤器.

此解决方案修复了数据重复问题,但它以其他方式使我感到困惑.一个是基于数据…我的数据包含许多查询,目标对,但它往往是相同目标的许多查询,因为它更容易生成该方向的真实世界数据.所以,这样做会使训练变得困难.其次,这假设每个查询以独立的方式工作,而实际上,我知道查询,目标配对实际上是重要的.因此,使用可以查看对的许多示例的模型是有意义的,而不是个体.

有没有办法结合两种方法?是否有一种方法可以使Conv1D同时采用长目标张量将它与常量查询结合起来沿着序列行进?

解决方法:

仅提供使用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])

鉴于您的预训练match_model,TimeDistributed的问题在于它无法用多个输入包装Keras模型.

但是,由于逻辑匹配目标和查询是在Concatenate之后的层中实现的,因此您可以将这些层收集到模型中,并将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)

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

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)

然后,模型可以以端到端的方式用于匹配长目标.

您还可以通过将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

标签:python,tensorflow,keras,conv-neural-network,sliding-window
来源: https://codeday.me/bug/20190910/1801071.html