5

I am trying to implement a scan loop in theano, which given a tensor will use a "moving slice" of the input. It doesn't have to actually be a moving slice, it can be a preprocessed tensor to another tensor that represents the moving slice.

Essentially:

[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
 |-------|                                 (first  iteration)
   |-------|                               (second iteration)
     |-------|                             (third  iteration)
               ...
                    ...
                        ...
                               |-------|   (last   iteration)

where |-------| is the input for each iteration.

I am trying to figure out the most efficient way to do it, maybe using some form of referencing or manipulating strides, but I haven't managed to get something to work even for pure numpy.

One possible solution I found can be found here, but I can't figure out how to use strides and I don't see a way to use that with theano.

Community
  • 1
  • 1
Makis Tsantekidis
  • 2,698
  • 23
  • 38
  • 1
    [`theano.sandbox.neighbours.images2neibs`](http://deeplearning.net/software/theano/library/tensor/nnet/neighbours.html#module-sandbox.neighbours) is close to what you're looking for. As far as I'm aware it only works with a 4D tensor, but you could try reshaping your 1D vector into (1, 1, 1, N). – ali_m Jul 30 '15 at 22:07
  • 1
    Hm, this actually needs some more specific information, such as whether the function to be applied to the segment needs to see the past results or not. The most general solution is using `theano.scan`, as in the answer below, but with some extra information, this may reduce to e.g. a convolution with an appropriate kernel followed by some other function. – eickenberg Jul 31 '15 at 20:40
  • The reason I want this moving slice is because I am trying to apply a 1d convolution to each slice. I think what I want is already implemented [here](https://github.com/Lasagne/Lasagne/blob/master/lasagne/theano_extensions/conv.py#L97) but I want I want the results in tandem with other operation during a scan, and I am assuming that it would be more efficient to get each convolution result for each slice, and after I am done with it move to the next. I think @carriepl answered with exactly what I wanted to test my theory. If you have any other examples of this using theano please share! Thanks – Makis Tsantekidis Jul 31 '15 at 21:35

2 Answers2

3

You can build a vector containing the starting index for the slice at each timestep and call Scan with that vector as a sequence and your original vector as a non-sequence. Then, inside Scan, you can obtain the slice you want at every iteration.

I included an example in which I also made the size of the slices a symbolic input, in case you want to change it from one call of your Theano function to the next:

import theano
import theano.tensor as T

# Input variables
x = T.vector("x")
slice_size = T.iscalar("slice_size")


def step(idx, vect, length):

    # From the idx of the start of the slice, the vector and the length of
    # the slice, obtain the desired slice.
    my_slice = vect[idx:idx + length]

    # Do something with the slice here. I don't know what you want to do
    # to I'll just return the slice itself.
    output = my_slice

    return output

# Make a vector containing the start idx of every slice
slice_start_indices = T.arange(x.shape[0] - slice_size + 1)

out, updates = theano.scan(fn=step,
                        sequences=[slice_start_indices],
                        non_sequences=[x, slice_size])

fct = theano.function([x, slice_size], out)

Running the function with your parameters produces the output :

print fct(range(17), 5)

[[  0.   1.   2.   3.   4.]
 [  1.   2.   3.   4.   5.]
 [  2.   3.   4.   5.   6.]
 [  3.   4.   5.   6.   7.]
 [  4.   5.   6.   7.   8.]
 [  5.   6.   7.   8.   9.]
 [  6.   7.   8.   9.  10.]
 [  7.   8.   9.  10.  11.]
 [  8.   9.  10.  11.  12.]
 [  9.  10.  11.  12.  13.]
 [ 10.  11.  12.  13.  14.]
 [ 11.  12.  13.  14.  15.]
 [ 12.  13.  14.  15.  16.]]
carriepl
  • 96
  • 4
1

You could use this rolling_window recipe:

import numpy as np

def rolling_window_lastaxis(arr, winshape):
    """
    Directly taken from Erik Rigtorp's post to numpy-discussion.
    http://www.mail-archive.com/numpy-discussion@scipy.org/msg29450.html
    (Erik Rigtorp, 2010-12-31)

    See also:
    http://mentat.za.net/numpy/numpy_advanced_slides/ (Stéfan van der Walt, 2008-08)
    https://stackoverflow.com/a/21059308/190597 (Warren Weckesser, 2011-01-11)
    https://stackoverflow.com/a/4924433/190597 (Joe Kington, 2011-02-07)
    https://stackoverflow.com/a/4947453/190597 (Joe Kington, 2011-02-09)
    """
    if winshape < 1:
        raise ValueError("winshape must be at least 1.")
    if winshape > arr.shape[-1]:
        print(winshape, arr.shape)
        raise ValueError("winshape is too long.")
    shape = arr.shape[:-1] + (arr.shape[-1] - winshape + 1, winshape)
    strides = arr.strides + (arr.strides[-1], )
    return np.lib.stride_tricks.as_strided(arr, shape=shape, strides=strides)

x = np.arange(17)
print(rolling_window_lastaxis(x, 5))

which prints

[[ 0  1  2  3  4]
 [ 1  2  3  4  5]
 [ 2  3  4  5  6]
 [ 3  4  5  6  7]
 [ 4  5  6  7  8]
 [ 5  6  7  8  9]
 [ 6  7  8  9 10]
 [ 7  8  9 10 11]
 [ 8  9 10 11 12]
 [ 9 10 11 12 13]
 [10 11 12 13 14]
 [11 12 13 14 15]
 [12 13 14 15 16]]

Note that there are even fancier extensions of this, such as Joe Kington's rolling_window which can roll over multi-dimensional windows, and Sebastian Berg's implementation which, in addition, can jump by steps.

Community
  • 1
  • 1
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • I managed to make the rolling window work with `np.lib.stride_tricks.as_strided` for specific strides and shapes but this recipe was exactly what I needed to make my life easier. You saved me a lot of fiddling with numpy and debugging. I am going to wait a bit in case somebody has something to suggest for doing this in theano's computational graph and if nothing comes up I will accept your answer. Thanks a bunch! – Makis Tsantekidis Jul 30 '15 at 21:29
  • This is btw accessible out of the box in `sklearn.feature_extraction.image.extract_patches`. – eickenberg Jul 31 '15 at 20:36
  • The question requested operation over theano tensor though. – tdihp Jun 03 '17 at 09:31