Conv2d for time series transformation

Hi all,

I am working on a time series dataset where 30 days of data are given to the model during training for each batch. The shape of the tensor is [5,9,30,10] (batch_size, time_steps, #days, features).

I want to pass this tensor to a conv2d layer to transform it to a shape [5,1,30,10] and do squeeze(1) to finally get a shape of [5,30,10]. What I want to do is to summarise the time steps for each day.

I am having a hard time understanding if the order of the tensor’s dimensions is correct, and how they should be passed to the conv2d function.

The RGB channels equivalent for my case is the time_steps or the #days dimension?

The “RGB channels” would be located in dim1 so would correspond to time_steps.
This would mean that each conv kernel would use all time_steps and would then convolve over the days and features dimensions using its spatial size (in the default setup).
I.e. since you would like to get a single output channel, you could use e.g.:

x = torch.randn(5, 9, 30, 10)
conv = nn.Conv2d(9, 1, 3, 1, 1)
out = conv(x)
print(out.shape)
> torch.Size([5, 1, 30, 10])

which would use a single kernel with in_channel=9, a spatial kernel size of 3x3, a stride of 1, and padding of 1 to keep the “spatial size” of the output equal.

Hi @ptrblck,

Thanks for the quick response. At first I planned to do it this way, but a part of me said that it was not the correct way. I basically wanted to summarise the time_series that belong to each one of the 30 days for each feature.

What I ended up doing is this:

x = torch.randn(5, 30, 9, 10) #[batch, #days, time_steps, features]

from torch import nn

class ConvNet(nn.Module):

    def __init__(self, channels, features):
        super().__init__()
        
        self.layer1 = nn.Sequential(
            nn.Conv1d(in_channels=channels,
                      out_channels=channels,
                      kernel_size=(5,1)),
            nn.BatchNorm2d(num_features=channels),
            nn.ReLU(),
            nn.AdaptiveMaxPool2d(output_size=(7, features)))

        self.layer2 = nn.Sequential(
           nn.Conv1d(in_channels=channels,
                     out_channels=channels,
                     kernel_size=(3,1)),
            nn.BatchNorm2d(num_features=channels),
            nn.ReLU(),
            nn.AdaptiveMaxPool2d(output_size=(1, features)))

    def forward(self, x):
        out = self.layer1(x)
        out = self.layer2(out)

        return out
  1. dim1 (#days): maintained the same size across the network
  2. dim2 (time_steps): decreased its size in the first and second convolution operation, and enforced an ouput of 1 in the last AdaptiveMaxPool2d.
  3. dim3 (features): maintained the same across the network

My tensor ended up being of shape [5, 30, 10] #[batch_size, #days, features]

I used the 1D convolution operation because I found out that it is the most common to use when you have time series data (although I have 4 dimensions in total) because the filter only moves to one direction.

I would really appreciate your feedback on my approach!

Your approach sounds also valid. It would be interesting to hear from other experiments in case you are changing the architecture in the future and see which approach worked best. :slight_smile:

Great, thanks @ptrblck! Will give you a heads-up :slightly_smiling_face: