def train_predictors_epoch(model, data_loader, loss_func, device, trial,
                           epoch, silent=True):
    """
    Train predictors networks for single epoch.

    Args:
        model (LDL): object of model to train
        data_loader (iterable): data loader of (x, y) samples
        loss_func (func): torch loss function
        device (torch.Device): device to move data to
        trial (int): number of trial (for logging)
        epoch (int): number of current epoch (for logging)
        silent (bool): if true outputs nothing

    Returns (list): list of lists of [trial_n, 'train', epoch_n, sample_n, predictor_n, loss]

    """

    n = len(data_loader)
    results_data = []  # trial | split | epoch | sample | predictor | value
    for batch_i, (x, y) in enumerate(data_loader):
        x = x.view(1, x.shape[0]).to(device)
        y = y.to(device)

        # Activate corresponding predictor and get corresponding optimizer
        model.activate_predictor(class_=y.item())
        optimizer = model.get_optimizer(y.item())

        # Forward sample and calucate loss
        predictor_z, target_z = model(x)
        loss = loss_func(predictor_z, target_z).mean()

        # Backpropagate gradients
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        # Logging info
        results_data.append(
            [trial, "train", epoch, batch_i, y.item(), loss.item()])

        if not silent and batch_i % 100 == 0:
            msg = 'Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'
            print(msg.format(epoch + 1, batch_i, n, batch_i / n * 100,
                             loss.item()))

    return results_data