Custom collate_fn returns a list. I can't fed the model!

Hello,

My code is very simple, but the Dataset is a little bit problematic, because it has a variable size.
I have a custom dataset with the following structure: Notes x N_bins x Temp_values

Notes is 2904
N_bins is ALWAYS 252
Temp_values varies depending on the note.

I prepare my own dataset and I make the transforms manually.

class MusicDataSet(Dataset):
    def __init__(self, transform=None):
        self.ms, self.target, self.tam = sd.loadData()  
    def __len__(self):
        return self.tam
    def __getitem__(self, idx):
        inp = torch.from_numpy(self.ms[idx]).float()    
        #Transpose
        inp = inp.t()
        #to cuda
        inp = inp.to('cuda')
        target = self.target[idx]
        target= torch.from_numpy(self.target[idx])
        target = target.long()
        target = target.t()
        return inp, target

I have created my own collate_fn to be able to split the dataset into small batches but the problem is that I can’t return the batches as a tensor, just as a list.

def music_collate_fn(batch):
    data = [item[0] for item in batch]
    target = [item[1] for item in batch]
    return torch.tensor(data), torch.tensor(target)

train_loader = torch.utils.data.DataLoader(musicSet, batch_size=10, shuffle=False, collate_fn=music_collate_fn)

class MLP(nn.Module):
    def __init__(self):
        super(MLP, self).__init__()
        self.dense1 = nn.Linear(252, 512)
        self.dense2 = nn.Linear(512, 88)
    def forward(self, x):
        x = self.dense1(x)
        x = self.relu(x)
        x = self.dense2(x)
        return x

The train is very simple:

mlp = MLP(input_dim, hidden_dim, output_dim).to(device)
optimizer = torch.optim.RMSprop(mlp.parameters(), lr = learning_rate)
mlp.train()
for batch_idx, (x,y) in enumerate(train_loader):
         outputs = mlp(x.to(device))
         loss = criterion(outputs, y)
         optimizer.zero_grad()           
         loss.backward()                 
         optimizer.step()                

Fail:


  File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 704, in runfile
    execfile(filename, namespace)

  File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 108, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/Users/Antonio López León/Documents/Universidad/TFG/PROYECTO/BBDD/audioCQT/ProcesamientoNN.py", line 102, in <module>
    for batch_idx, (x,y) in enumerate(train_loader):

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 615, in __next__
    batch = self.collate_fn([self.dataset[i] for i in indices])

  File "C:/Users/Antonio López León/Documents/Universidad/TFG/PROYECTO/BBDD/audioCQT/ProcesamientoNN.py", line 65, in music_collate_fn
    return torch.tensor(data), torch.tensor(target)

ValueError: only one element tensors can be converted to Python scalars

I tried to make a pad_sequence over the data in the music_collate_fn and it works at the moment of fed the network, but the target give me problems when I calculate the loss so I can’t use the padding with the target.

I need some ideas to make it possible. Thank so much!

the return of your collate_fn should be a tensor and not a python list, as the model doesn’t work directly on python list. The return should be “return torch.tensor(data), torch.tensor(target)”. You also have to transfer the tensor before you pass it to the model to the device, it should be “outputs = mlp(x.to(device))”;

Thank you for answering.

I should say that data and target are lists containing tensors.
I tried to do what you said, so I update code. I update code.


  File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 704, in runfile
    execfile(filename, namespace)

  File "C:\ProgramData\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 108, in execfile
    exec(compile(f.read(), filename, 'exec'), namespace)

  File "C:/Users/Antonio López León/Documents/Universidad/TFG/PROYECTO/BBDD/audioCQT/ProcesamientoNN.py", line 102, in <module>
    for batch_idx, (x,y) in enumerate(train_loader):

  File "C:\ProgramData\Anaconda3\lib\site-packages\torch\utils\data\dataloader.py", line 615, in __next__
    batch = self.collate_fn([self.dataset[i] for i in indices])

  File "C:/Users/Antonio López León/Documents/Universidad/TFG/PROYECTO/BBDD/audioCQT/ProcesamientoNN.py", line 65, in music_collate_fn
    return torch.tensor(data), torch.tensor(target)

ValueError: only one element tensors can be converted to Python scalars

could you try leaving the “inp, target” from MusicDataSet class to a numpy array. I think the problem that you are trying a convert a list of tensor to a tensor. Or you could call torch.stack(dataset), torch.stack(target).

The main problem I think is the tensors have different size, so I can’t use stack. Is for this I need a custom collate_fn.

well because you only have a two linear layers you can actually collapse the inp to a shape of (batch, 252) and the target to (batch, 88), where the batch is just a concatenation of all the different items, to a single axis.