Оценка функции в скользящем окне с помощью Keras
я пытаюсь расширить алгоритм сопоставления соответствия через последовательность. Мои матчи имеют длину 20 единиц и имеют 4 канала в каждый момент времени. Я построил модель, которая инкапсулирует соответствие, я просто не могу понять, как использовать это в скользящем окне, чтобы применить его в более длинной последовательности, чтобы найти совпадения в последовательности.
у меня 2 (20, 4)
вход тензоров (query
и target
), которые я объединяю, добавляю, выравниваю, а затем применяю простой плотный слой. У меня есть данные на этом этапе для обучения с запросом 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
принимает Layer
не Tensor
. Есть ли обертка, которую я потерял? Я делаю что-то не так? Нужно ли переформулировать это как проблему свертки почему?
продолжение экспериментов:
После целого дня битья головой о клавиатуру становится ясно, что оба TimeDistributed
и backend.rnn
позволяет применять модель / слой только к одному временному срезу данных. Не похоже, что есть способ сделать это. Похоже, единственное, что может "ходить" по нескольким срезам измерения времени, - это Conv1D
.
Итак, я переформулировал свою проблему как свертку, но это тоже не работает. Я смог строительство Conv1D
фильтр, который будет соответствовать определенному query
. Это работало достаточно хорошо, и это позволило мне сканировать более длинные последовательности и получать совпадения. Но каждый фильтр уникален для каждого query
Тензор и, похоже, нет способа уйти от Романа query
к соответствующим Весам фильтра без обучения совершенно новый Conv1D
слой. Так как моя цель-найти новое query
s, которые соответствуют большинству целей, это не очень помогает.
так как мое "соответствие" требует взаимодействие цели и запроса в каждом окне, похоже, не существует способа получить взаимодействие 20-длины query
тензор в каждом окне через 100-length 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
пары, но это, как правило, то же самое target
много query
s, так как легче генерировать реальные данные в этой ориентации. Таким образом, это затрудняет тренировку. Во-вторых, это предполагает, что каждый query
работает в независимом кстати, когда на самом деле, я знаю, что query
, target
сопряжение-это то, что действительно важно. Поэтому имеет смысл использовать модель, которая может смотреть на многие примеры пар, а не отдельных лиц.
есть ли способ объединить оба метода? Есть ли способ сделать это так Conv1D
берет как долго target
тензор объединить его с постоянной query
как он идет по последовательности?
2 ответов
как раз обеспечить альтернативное решение используя функции backend 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
это то, что он не может обернуть Keras Model
с несколькими входами.
однако, так как логика соответствия target
и query
реализуется в слоях после 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
Примечание: посмотрите на решение @Yu-Yang. Так гораздо лучше.
Ну, как я уже упоминал в своем комментарии, Вы можете использовать tf.exctract_image_patches()
(если документация кажется немного расплывчатой, прочитайте ответ on SO) для извлечения патчей (редактировать: я только что добавил две переменные win_len
и feat_len
и изменен 100
to None
и 81
to -1
чтобы заставить его работать с целевыми последовательностями произвольных длина):
import tensorflow as tf
from keras import layers, models
import keras.backend as K
win_len = 20 # window length
feat_len = 4 # features length
def extract_patches(data):
data = K.expand_dims(data, axis=3)
patches = tf.extract_image_patches(data, ksizes=[1, win_len, feat_len, 1], strides=[1, 1, 1, 1], rates=[1, 1, 1, 1], padding='VALID')
return patches
target = layers.Input((None, feat_len))
patches = layers.Lambda(extract_patches)(target)
patches = layers.Reshape((-1, win_len, feat_len))(patches)
model = models.Model([target], [patches])
model.summary()
Layer (type) Output Shape Param #
=================================================================
input_2 (InputLayer) (None, None, 4) 0
_________________________________________________________________
lambda_2 (Lambda) (None, None, None, 80) 0
_________________________________________________________________
reshape_2 (Reshape) (None, None, 20, 4) 0
=================================================================
Total params: 0
Trainable params: 0
Non-trainable params: 0
_________________________________________________________________
например, если входная цель имеет форму (100, 4)
, форма выводится (81, 20, 4)
.
тест:
import numpy as np
# an array consisting of numbers 0 to 399 with shape (100, 4)
target = np.arange(1*100*4*1).reshape(1, 100, 4)
print(model.predict(a))
вот вывод:
[[[[ 0. 1. 2. 3.]
[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]
...
[ 68. 69. 70. 71.]
[ 72. 73. 74. 75.]
[ 76. 77. 78. 79.]]
[[ 4. 5. 6. 7.]
[ 8. 9. 10. 11.]
[ 12. 13. 14. 15.]
...
[ 72. 73. 74. 75.]
[ 76. 77. 78. 79.]
[ 80. 81. 82. 83.]]
[[ 8. 9. 10. 11.]
[ 12. 13. 14. 15.]
[ 16. 17. 18. 19.]
...
[ 76. 77. 78. 79.]
[ 80. 81. 82. 83.]
[ 84. 85. 86. 87.]]
...
[[312. 313. 314. 315.]
[316. 317. 318. 319.]
[320. 321. 322. 323.]
...
[380. 381. 382. 383.]
[384. 385. 386. 387.]
[388. 389. 390. 391.]]
[[316. 317. 318. 319.]
[320. 321. 322. 323.]
[324. 325. 326. 327.]
...
[384. 385. 386. 387.]
[388. 389. 390. 391.]
[392. 393. 394. 395.]]
[[320. 321. 322. 323.]
[324. 325. 326. 327.]
[328. 329. 330. 331.]
...
[388. 389. 390. 391.]
[392. 393. 394. 395.]
[396. 397. 398. 399.]]]]