PyToch Skorch passing 3 dimensional input
Im trying to fit a model in PyTorch while using skorch. My problem is, that my model uses an LSTM layer, which expects 3d input and I don't know how to pass the input correctly.

When passing a 2d-array to fit() I get an error from PyTorch for the expected 3d input. If passing a 3d-array, I get the error from the fit() method for inconsistent lengths (which both make perfectly sense to me).

Example code below:

import numpy as np
import torch
from torch import nn
import skorch

class lstmNet(nn.Module):

    def __init__(self, input_size, hidden_size, num_layers):
        super(lstmNet, self).__init__()
        self.rnn = nn.LSTM(
            input_size,
            hidden_size,
            num_layers        )
        self.lin = nn.Linear(
            in_features=hidden_size,
            out_features=1
        )

    def forward(self, x):
        print(x.size())
        out, hn = self.rnn(x)
        out = self.lin(out)
        return out


input_feat = 5
hidden_size = 10
lstmLayers = 2

seq = 20
batch = 30
features = 5

net = skorch.NeuralNet(
    module=lstmNet(
        input_size=input_feat,
        hidden_size=hidden_size,
        num_layers=lstmLayers
    ),
    criterion=torch.nn.MSELoss,
    optimizer=torch.optim.SGD,
    lr=0.1,
    max_epochs=10
)

#inputArr2d = np.random.rand(seq * batch, features)
inputArr3d = np.random.rand(seq, batch, features)
print('input:\n {}\nshape: {}'.format(inputArr3d, inputArr3d.shape))
targetArr = np.random.rand((seq * batch))
#print('target:\n {}\nshape: {}'.format(targetArr, targetArr.shape))

net.fit(X=inputArr3d, y=targetArr)


This is the error when calling net.fit(X=inputArr2d, y=targetArr):

Traceback (most recent call last):
File "C:\Spielplatz\Python\examples\playground.py", line 64, in &lt;module&gt;
net.fit(X=inputArr2d, y=targetArr)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 686, in fit
self.partial_fit(X, y, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 646, in partial_fit
self.fit_loop(X, y, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 584, in fit_loop
step = self.train_step(Xi, yi, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 507, in train_step
y_pred = self.infer(Xi, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 810, in infer
return self.module_(x, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\torch\nn\modules\module.py", line 491, in __call__
result = self.forward(*input, **kwargs)
File "C:\Spielplatz\Python\examples\playground.py", line 33, in forward
out, hn = self.rnn(x)
File "C:\ProgramData\Anaconda3\lib\site-packages\torch\nn\modules\module.py", line 491, in __call__
result = self.forward(*input, **kwargs)
File "C:\ProgramData\Anaconda3\lib\site-packages\torch\nn\modules\rnn.py", line 178, in forward
self.check_forward_args(input, hx, batch_sizes)
File "C:\ProgramData\Anaconda3\lib\site-packages\torch\nn\modules\rnn.py", line 126, in check_forward_args
expected_input_dim, input.dim()))
RuntimeError: input must have 3 dimensions, got 2


And this is the error when calling net.fit(X=inputArr3d, y=targetArr):

Traceback (most recent call last):
File "C:\Spielplatz\Python\examples\playground.py", line 64, in &lt;module&gt;
net.fit(X=inputArr3d, y=targetArr)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 686, in fit
self.partial_fit(X, y, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 646, in partial_fit
self.fit_loop(X, y, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 573, in fit_loop
X, y, **fit_params)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 1004, in get_split_datasets
dataset = self.get_dataset(X, y)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\net.py", line 961, in get_dataset
return dataset(X, y, **kwargs)
File "C:\ProgramData\Anaconda3\lib\site-packages\skorch\dataset.py", line 104, in __init__
raise ValueError("X and y have inconsistent lengths.")
ValueError: X and y have inconsistent lengths.


Passing a 2D-array is, as you said yourself, not going to work since you don't have a time dimension then, so the error message is correct in this case (LSTM expects 3 dimensions).

The confiusing part is the error message when passing a 3D array. What is happening is that you are formatting your data as (sequence, batch, feature), i.e. "batch second". There is an alternative format called "batch first" where the data is formatted as (batch, sequence, feature) which is the default in sklearn (and consequently in skorch). If you re-format your data this way and configure your LSTM to use this format (pass batch_first=True) the error should go away:

    self.rnn = nn.LSTM(
        input_size,
        hidden_size,
        num_layers,
        batch_first=True,   
    )
# ...
inputArr3d = np.random.rand(batch, seq, features)


Note that your module also needs to convert the return value of the RNN
into something that the following layers can process (i.e. flatten
the time dimension) or otherwise the linear layer will not know what
to do with the extra time dimension.
