Tensorflow: разбиение тензора на перекрывающиеся блоки

#tensorflow

#тензорный поток

Вопрос:

У меня есть одномерный тензор, который я хочу разбить на перекрывающиеся блоки. Я думаю о чем-то вроде: tensor = tf.constant([1, 2, 3, 4, 5, 6, 7])

 overlapping_blocker(tensor,block_size=3,stride=2)
=> [[1 2 3], [3, 4, 5], [5, 6, 7]]
  

Пока я нашел только способы разбиения тензора на неперекрывающиеся блоки. Кто-нибудь знает способ решить эту проблему?

Это должно работать для произвольного входного измерения (т. Е. Мой ввод похож на tf.placeholder([None])

Комментарии:

1. Начиная с версии tf 0.11rc (несколько ранее), вы можете использовать многие стандартные методы нарезки python. Например, если i0 = tf.constant(0) и i3 = tf.constant (i3), то ваш первый блок может быть сгенерирован просто как тензор[i0: i3]. Помогает ли это?

2. Боюсь, на самом деле это не помогает. Мой входной тензор произвольно длинный, поэтому я не могу просто написать цикл for, который разрезает вектор N раз. Кроме того, я подозреваю, что было бы очень неэффективно создавать экземпляр N независимого тензора для N срезов — конечно, все блоки должны сворачиваться в другое измерение, а не занимать пространство имен отдельных переменных…

3. Я нашел функцию tf.extract_image_patches здесь чрезвычайно полезной. Вам придется вручную изменять свои входные и выходные данные, и вам придется быть осторожным с тем, как вы обрабатываете первый и последний «патч». Помимо этих предостережений, это сделало именно то, что я хотел.

Ответ №1:

Вы можете использовать tf.nn.conv2d, чтобы помочь. По сути, вы применяете скользящий фильтр block_size к входным данным, шаг за шагом. Чтобы выровнять все индексы матрицы, вам нужно выполнить некоторые изменения.

Общее решение

 import tensorflow as tf


def overlap(tensor, block_size=3, stride=2):
  reshaped = tf.reshape(tensor, [1,1,-1,1])

  # Construct diagonal identity matrix for conv2d filters.
  ones = tf.ones(block_size, dtype=tf.float32)
  ident = tf.diag(ones)
  filter_dim = [1, block_size, block_size, 1]
  filter_matrix = tf.reshape(ident, filter_dim)

  stride_window = [1, 1, stride, 1]

  # Save the output tensors of the convolutions
  filtered_conv = []
  for f in tf.unstack(filter_matrix, axis=1):
    reshaped_filter = tf.reshape(f, [1, block_size, 1, 1])
    c = tf.nn.conv2d(reshaped, reshaped_filter, stride_window, padding='VALID')
    filtered_conv.append(c)

  # Put the convolutions into a tensor and squeeze to get rid of extra dimensions.
  t = tf.stack(filtered_conv, axis=3)
  return tf.squeeze(t)


# Calculate the overlapping strided slice for the input tensor.
tensor = tf.constant([1, 2, 3, 4, 5, 6, 7], dtype=tf.float32)
overlap_tensor = overlap(tensor, block_size=3, stride=2)

with tf.Session() as sess:
  sess.run(tf.initialize_all_variables())
  in_t, overlap_t = sess.run([tensor, overlap_tensor])
  print 'input tensor:'
  print in_t
  print 'overlapping strided slice:'
  print overlap_t
  

Должно дать вам результат:

 input tensor:
[ 1.  2.  3.  4.  5.  6.  7.]
overlapping strided slice:
[[ 1.  2.  3.]
 [ 3.  4.  5.]
 [ 5.  6.  7.]]
  

Более понятное решение

Это начальная версия, над которой я начал работать, которая не допускает переменной block_size, но я думаю, что так проще увидеть, что происходит с фильтрами свертки — мы берем вектор из 3 значений, каждый шаг stride.

 def overlap(tensor, stride=2):
  # Reshape the tensor to allow it to be passed in to conv2d.
  reshaped = tf.reshape(tensor, [1,1,-1,1])

  # Construct the block_size filters.
  filter_dim = [1, -1, 1, 1]
  x_filt = tf.reshape(tf.constant([1., 0., 0.]), filter_dim)
  y_filt = tf.reshape(tf.constant([0., 1., 0.]), filter_dim)
  z_filt = tf.reshape(tf.constant([0., 0., 1.]), filter_dim)

  # Stride along the tensor with the above filters.
  stride_window = [1, 1, stride, 1]
  x = tf.nn.conv2d(reshaped, x_filt, stride_window, padding='VALID')
  y = tf.nn.conv2d(reshaped, y_filt, stride_window, padding='VALID')
  z = tf.nn.conv2d(reshaped, z_filt, stride_window, padding='VALID')

  # Pack the three tensors along 4th dimension.
  result = tf.stack([x, y, z], axis=4)
  # Squeeze to get rid of the extra dimensions.
  result = tf.squeeze(result)
  return result
  

Комментарии:

1. Является ли это эффективным способом сделать это? Я не уверен, что доверяю оптимизации tensorflow для этого крайнего случая, когда почти все значения в свернутом фильтре равны 0.

Ответ №2:

Вы можете достичь того же с помощью tf.extract_image_patches .

РЕДАКТИРОВАТЬ — Изменение API для TF 2.0

 def overlapping_blocker(tensor,block_size=3,stride=2):
    return tf.squeeze(tf.image.extract_patches(tensor[None,...,None, None], sizes=[1, block_size, 1, 1], strides=[1, stride, 1, 1], rates=[1, 1, 1, 1], padding='VALID'))
  

Для более старого API:

 tensor = tf.placeholder(tf.int32, [None])

def overlapping_blocker(tensor,block_size=3,stride=2):
   return tf.squeeze(tf.extract_image_patches(tensor[None,...,None, None], ksizes=[1, block_size, 1, 1], strides=[1, stride, 1, 1], rates=[1, 1, 1, 1], padding='VALID'))

result = overlapping_blocker(tensor,block_size=3,stride=2)
sess = tf.InteractiveSession()
print(result.eval({tensor:np.array([1, 2, 3, 4, 5, 6, 7], np.int32)}))

#[[1 2 3]
#[3 4 5]
#[5 6 7]]
  

Ответ №3:

Вот относительно простой подход, использующий ваш пример:

 def overlapping_blocker(tensor,block_size,stride):
    blocks = []
    n = tensor.get_shape().as_list()[0]
    ilo = range(0, n, stride)
    ihi = range(block_size, n 1, stride)
    ilohi = zip(ilo, ihi).
    for ilo, ihi in ilohi:
        blocks.append(tensor[ilo:ihi])
    return(tf.pack(blocks, 0))

with tf.Session() as sess:
    tensor = tf.constant([1., 2., 3., 4., 5., 6., 7.])
    block_tensor = overlapping_blocker(tensor, 3, 2)
    print(sess.run(block_tensor))
  

Вывод:

 [[ 1.  2.  3.]
 [ 3.  4.  5.]
 [ 5.  6.  7.]]
  

Комментарии:

1. tensor.get_shape()[0] не определен. Я ищу решение, которое работает для произвольного входного измерения, а не только для определенной длины ввода.

2. Вы продолжаете изменять ограничения. Если вы хотите получить ответ, полностью укажите проблему, которую вы хотите решить.

3. На самом деле я уже указал «мой ввод произвольно длинный» 21 октября, как вы можете видеть в комментариях к моему вопросу. Но, честно говоря, я должен был добавить это к моему первоначальному вопросу (который у меня сейчас есть после редактирования)

4. Если ‘tensor’ передается через заполнитель, то изначально это должен быть массив numpy, а не константа tf, как я предположил в примере выше. Если это так, то длину соответствующего np-вектора легко установить и передать в overlapping_blocker() в качестве другого параметра python, ‘n’, так же, как передаются block_size и stride . Разве это не соответствовало бы вашим требованиям? Если это так, я могу соответствующим образом изменить предлагаемое решение.

5. используйте tf.stack вместо tf.pack в версии tensorflow >= 1.0

Ответ №4:

Если вы ищете способ получить каждое переходящее окно в виде отдельного тензора (т. Е. При каждом вызове window.eval() ваше окно перемещает одно из них. Вы можете использовать tf.FIFOQueue а также tf.train.range_input_producer для создания очереди, которая выполняет это:

РЕДАКТИРОВАТЬ: обновлено для работы с тензорами переменной длины, как запрошено в вашем первоначальном ответе

 def window_input_producer(tensor, window_size, capacity=32, num_epochs=None):
  num_windows = tf.shape(tensor)[0] - window_size
  range_queue = tf.train.range_input_producer(
      num_windows,
      shuffle=False,
      capacity=capacity,
      num_epochs=num_epochs
  )
  index = range_queue.dequeue()
  window = tensor[index:index   window_size]
  queue = tf.FIFOQueue(capacity=capacity,
                       dtypes=[tensor.dtype.base_dtype],
                       shapes=[window_size])

  enq = queue.enqueue(window)
  tf.train.add_queue_runner(
      tf.train.QueueRunner(queue, [enq])
  )

  return queue.dequeue()
  

Ответ №5:

Я не уверен, что на этот вопрос был дан достаточный ответ, но вы можете использовать функцию генератора python для создания перекрывающихся окон:

 def gen_batch():

# compute number of batches to emit
num_of_batches = round(((len(sequence) - batch_size) / stride))

# emit batches
for i in range(0, num_of_batches * stride, stride):
    result = np.array(sequence[i:i   batch_size])
    yield result

sequence = np.array[1,2,3,4,5,6,7,8,9]
batch_size = 3
stride = 2
gen = gen_batch()

print(next(gen))
[1,2,3]
print(next(gen))
[3,4,5]
...
print(next(gen))
[7,8,9]
  

После того, как вы определили свою функцию генератора, вы можете использовать класс Dataset TensorFlow для вызова каждого фрагмента:

 ds = tf.data.Dataset.from_generator(
gen_batch,
(tf.float64),
(tf.TensorShape([batch_size, dim_width])))

ds_out = ds.make_one_shot_iterator().get_next()

print(sess.run(ds_out)))
[1,2,3]
print(sess.run(ds_out)))
[3,4,5]
...
print(sess.run(ds_out)))
[7,8,9]
  

Ответ №6:

Я думаю, что функция

 tf.signal.overlap_and_add(
    signal, frame_step, name=None
)
  

может быть, это то, что вы хотите. Это решило мою проблему. На этот вопрос был дан ответ в