def train_backprop(self, train_loader, val_loader, n_epoch=10000, ES_max=30, decay_max=10, decay_step=0.1, lr=(10 ** (- 3))):
    optimizer = Adam(self.parameters(), lr=lr)
    scheduler = StepLR(optimizer, step_size=1, gamma=decay_step)
    patience = 0
    bestloss = inf
    if (self.categories_arr is None):
        get_correct = (lambda x: 0)
    else:
        get_correct = (lambda outputs: (argmax(outputs['celltypes'], axis=1) == celltypes).sum())
    for epoch in range(n_epoch):
        with no_grad():
            (running_loss, rtype_acc) = (0.0, 0.0)
            self.eval()
            for (batch, inputs) in enumerate(val_loader):
                (mod1, mod2, protein_bools, celltypes) = inputs
                outputs = self(mod1)
                n_correct = get_correct(outputs)
                mod2_loss = self.loss2(outputs['modality 2'], mod2, protein_bools)
                rtype_acc += n_correct
                running_loss += (mod2_loss.item() * len(mod2))
            if (self.categories_arr is None):
                print(f'Epoch {epoch} prediction loss = {(running_loss / len(val_loader)):.3f}')
            else:
                print(f'Epoch {epoch} prediction loss = {(running_loss / len(val_loader)):.3f}, validation accuracy = {(rtype_acc / len(val_loader)):.3f}')
            patience += 1
            if ((bestloss / 1.005) > running_loss):
                (bestloss, patience) = (running_loss, 0)
            if (((patience + 1) % decay_max) == 0):
                scheduler.step()
                print(f"Decaying loss to {optimizer.param_groups[0]['lr']}")
            if ((patience + 1) > ES_max):
                break
        self.train()
        for (batch, inputs) in enumerate(train_loader):
            optimizer.zero_grad()
            (mod1, mod2, protein_bools, celltypes) = inputs
            outputs = self(mod1)
            mod1_loss = self.loss1(outputs['celltypes'], celltypes)
            mod2_loss = self.loss2(outputs['modality 2'], mod2, protein_bools)
            loss = (mod1_loss + mod2_loss)
            loss.backward()
            optimizer.step()
def train_backprop(self, train_loader, val_loader, n_epoch=10000, ES_max=30, decay_max=10, decay_step=0.1, lr=(10 ** (- 3))):
    optimizer = Adam(self.parameters(), lr=lr)
    scheduler = StepLR(optimizer, step_size=1, gamma=decay_step)
    patience = 0
    bestloss = inf
    if (self.categories_arr is None):
        get_correct = (lambda x: 0)
    else:
        get_correct = (lambda outputs: (argmax(outputs['celltypes'], axis=1) == celltypes).sum())
    for epoch in range(n_epoch):
        with no_grad():
            (running_loss, rtype_acc) = (0.0, 0.0)
            self.eval()
            for (batch, inputs) in enumerate(val_loader):
                (mod1, mod2, protein_bools, celltypes) = inputs
                outputs = self(mod1)
                n_correct = get_correct(outputs)
                mod2_loss = self.loss2(outputs['modality 2'], mod2, protein_bools)
                rtype_acc += n_correct
                running_loss += (mod2_loss.item() * len(mod2))
            if (self.categories_arr is None):
                print(f'Epoch {epoch} prediction loss = {(running_loss / len(val_loader)):.3f}')
            else:
                print(f'Epoch {epoch} prediction loss = {(running_loss / len(val_loader)):.3f}, validation accuracy = {(rtype_acc / len(val_loader)):.3f}')
            patience += 1
            if ((bestloss / 1.005) > running_loss):
                (bestloss, patience) = (running_loss, 0)
            if (((patience + 1) % decay_max) == 0):
                scheduler.step()
                print(f"Decaying loss to {optimizer.param_groups[0]['lr']}")
            if ((patience + 1) > ES_max):
                break
        self.train()
        for (batch, inputs) in enumerate(train_loader):
            optimizer.zero_grad()
            (mod1, mod2, protein_bools, celltypes) = inputs
            outputs = self(mod1)
            mod1_loss = self.loss1(outputs['celltypes'], celltypes)
            mod2_loss = self.loss2(outputs['modality 2'], mod2, protein_bools)
            loss = (mod1_loss + mod2_loss)
            loss.backward()
            optimizer.step()