`uniq` для тензора 2D Theano

У меня есть этот код Numpy:

def uniq(seq):
  """
  Like Unix tool uniq. Removes repeated entries.
  :param seq: numpy.array. (time,) -> label
  :return: seq
  """
  diffs = np.ones_like(seq)
  diffs[1:] = seq[1:] - seq[:-1]
  idx = diffs.nonzero()
  return seq[idx]

Теперь я хочу расширить его для поддержки 2D-массивов и использовать Theano. Это должно быть быстро на GPU.

Я получу массив с несколькими последовательностями в виде нескольких пакетов в формате (время, пакет) и time_mask, который косвенно указывает длину каждой последовательности.

Моя текущая попытка:

def uniq_with_lengths(seq, time_mask):
  # seq is (time,batch) -> label
  # time_mask is (time,batch) -> 0 or 1
  num_batches = seq.shape[1]
  diffs = T.ones_like(seq)
  diffs = T.set_subtensor(diffs[1:], seq[1:] - seq[:-1])
  time_range = T.arange(seq.shape[0]).dimshuffle([0] + ['x'] * (seq.ndim - 1))
  idx = T.switch(T.neq(diffs, 0) * time_mask, time_range, -1)
  seq_lens = T.sum(T.ge(idx, 0), axis=0)  # (batch,) -> len
  max_seq_len = T.max(seq_lens)

  # I don't know any better way without scan.
  def step(batch_idx, out_seq_b1):
    out_seq = seq[T.ge(idx[:, batch_idx], 0).nonzero(), batch_idx][0]
    return T.concatenate((out_seq, T.zeros((max_seq_len - out_seq.shape[0],), dtype=seq.dtype)))

 out_seqs, _ = theano.scan(
    step,
    sequences=[T.arange(num_batches)],
    outputs_info=[T.zeros((max_seq_len,), dtype=seq.dtype)]
  )
  # out_seqs is (batch,max_seq_len)
  return out_seqs.T, seq_lens

Как построить out_seqs напрямую?

Я бы сделал что-то вроде out_seqs = seq[idx], но не совсем уверен, как это выразить.


person Albert    schedule 13.07.2015    source источник


Ответы (1)


Вот быстрый ответ, который касается только части вашей задачи:

def compile_theano_uniq(x):
    diffs = x[1:] - x[:-1]
    diffs = tt.concatenate([tt.ones_like([x[0]], dtype=diffs.dtype), diffs])
    y = diffs.nonzero_values()
    return theano.function(inputs=[x], outputs=y)

theano_uniq = compile_theano_uniq(tt.vector(dtype='int32'))

Ключ nonzero_values().

Обновление: я не могу представить, как это сделать без использования theano.scan. Чтобы было ясно, и используя 0 в качестве заполнения, я предполагаю, что с учетом ввода

1 1 2 3 3 4 0
1 2 2 2 3 3 4
1 2 3 4 5 0 0

вы хотели бы, чтобы результат был

1 2 3 4 0 0 0
1 2 3 4 0 0 0
1 2 3 4 5 0 0

или даже

1 2 3 4 0
1 2 3 4 0
1 2 3 4 5

Вы можете определить индексы элементов, которые хотите сохранить, не используя сканирование. Затем либо новый тензор должен быть построен с нуля, либо значения, которые вы хотите сохранить, каким-то образом перемещены, чтобы сделать последовательности непрерывными. Ни один из подходов не представляется возможным без theano.scan.

person Daniel Renshaw    schedule 13.07.2015
comment
Кажется, это сглаженный массив. Я не уверен, как добраться до того, что я хочу оттуда. - person Albert; 13.07.2015
comment
Обновленный ответ. Я думаю, что ваш новый подход к сканированию, вероятно, лучше всего. Подход без сканирования может быть возможен, но я подозреваю, что это займет много времени, и его будет очень сложно понять/поддерживать в будущем. - person Daniel Renshaw; 13.07.2015