Thanks for your response. I’m still confused. This is what I did, But I’m stuck in point number 3:
1- I created two models (BERTClassA, BERTClassB), and detatch the classifier layers from them.
2- Then I concatenated the output from the two models in train function.
3- Then How I can loop through each data loader and pass each one to the models, then taking the output back from two models and concatenate it, after that pass it to the ensamble class.

Data
training_loader1 = DataLoader(training_set2, **train_params)
training_loader2 = DataLoader(training_set2, **train_params)

Model A
class BERTClassA(torch.nn.Module):
def init(self):
super(BERTClassB, self).init()
self.l1 = BertModel.from_pretrained(“bert-base-uncased”)
self.pre_classifier = torch.nn.Linear(768, 768)
self.dropout = torch.nn.Dropout(.3)
def forward(self, input_ids, attention_mask):
    output_1 = self.l1(input_ids=input_ids, attention_mask=attention_mask)
    hidden_state = output_1[0]
    pooler = hidden_state[:, 0]
    pooler = self.pre_classifier(pooler)
    pooler = torch.nn.ReLU()(pooler)
    pooler = self.dropout(pooler)
    return output


Model B
class BERTClassB(torch.nn.Module):
def init(self):
super(BERTClassB, self).init()
self.l1 = BertModel.from_pretrained(“bert-base-uncased”)
self.pre_classifier = torch.nn.Linear(768, 768)
self.dropout = torch.nn.Dropout(.3)
def forward(self, input_ids, attention_mask):
    output_1 = self.l1(input_ids=input_ids, attention_mask=attention_mask)
    hidden_state = output_1[0]
    pooler = hidden_state[:, 0]
    pooler = self.pre_classifier(pooler)
    pooler = torch.nn.ReLU()(pooler)
    pooler = self.dropout(pooler)
    return output


Train function
def train(epoch):
tr_loss = 0
n_correct = 0
nb_tr_steps = 0
nb_tr_examples = 0
model.train()
for _,data1  in enumerate(training_loader1 , 0):
    for _,data2  in enumerate(training_loader2 , 0):
        ids = data1['ids'].to(device, dtype = torch.long)
        mask = data1['mask'].to(device, dtype = torch.long)
        targets1 = data1['targets'].to(device, dtype = torch.long)

        ids2 = data2['ids'].to(device, dtype = torch.long)
        mask2 = data2['mask'].to(device, dtype = torch.long)
        targets2 = data2['targets'].to(device, dtype = torch.long)



        outputs1 = modelBERT(ids, mask)
        outputs2 = modelBERT2(ids2, mask2)

        outputs = torch.cat((outputs1, outputs2), dim=1)

        

        
        ensamble_output = model(outputs)


        loss = loss_function(ensamble_output, targets1)
        tr_loss += loss.item()
        big_val, big_idx = torch.max(ensamble_output.data, dim=1)
        n_correct += calcuate_accu(big_idx, targets1)

        nb_tr_steps += 1
        nb_tr_examples+=targets1.size(0)
    
        if _%100==0:
            loss_step = tr_loss/nb_tr_steps
            accu_step = (n_correct*100)/nb_tr_examples 
            print(f"Training Loss per 100 steps: {loss_step}")
            print(f"Training Accuracy per 100 steps: {accu_step}")

        optimizer.zero_grad()
        loss.backward()

        optimizer.step()

    print(f'The Total Accuracy for Epoch {epoch}: {(n_correct*100)/nb_tr_examples}')
    epoch_loss = tr_loss/nb_tr_steps
    epoch_accu = (n_correct*100)/nb_tr_examples
    print(f"Training Loss Epoch: {epoch_loss}")
    print(f"Training Accuracy Epoch: {epoch_accu}")

    return 

for epoch in range(EPOCHS):
train(epoch)

Ensamble function
class MyEnsemble(torch.nn.Module):
def init(self, modelA, modelB):
super(MyEnsemble, self).init()
self.modelA = modelA
self.modelB = modelB
self.classifier = torch.nn.Linear(768, 3)
def forward(self, x1, x2):
    x1 = self.modelA(x1)
    x2 = self.modelB(x2)
    x = torch.cat((x1, x2), dim=1)
    x = self.classifier(F.relu(x))
    return x

modelA = BERTClass()
modelB = BERTClassB()
model = MyEnsemble(modelA, modelB)
model.to(device)