I think it’s easier to comment in the code:
def train_model(model, criterion, optimizer, num_epochs):
    liveloss = PlotLosses()
    model = model.to(device)
    
    for epoch in range(num_epochs):
        logs = {}
        for phase in ['train', 'validation']:
            if phase == 'train':
                model.train()
            else:
                model.eval()
            correct = 0
            epoch_loss = 0
            
            # This part was on the same indentation level as the outer loop
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.reshape(-1, input_size).to(device)
                labels = labels.to(device)
                prediction = model(inputs)
                loss = criterion(prediction, labels)
            
                if phase == 'train':
                    optimizer.zero_grad()
                    loss.backward()
                    optimizer.step() 

                _, predicted = torch.max(prediction, 1)
                epoch_loss += loss.detach() * inputs.size(0)
                correct += torch.sum(predicted == labels.data)
        
            epoch_loss = epoch_loss / len(train_loader.dataset)
            acc = correct.float() / len(test_loader.dataset)
        
        
            prefix = ''
            if phase == 'validation':
                prefix = 'val_'
            
            logs[prefix + 'log loss'] = epoch_loss.item()
            logs[prefix + 'accuracy'] = acc.item()
            print(f'Epoch [{epoch+1}/{num_epochs}]], Loss: {epoch_loss:.4f} Test acc: {acc}')

Could you try this code and check, if your model starts to learn something?