Thank you for the reply. I tried with a single sample and it was able to overfit it so I guess the model is fine. When I train on multiple samples (even about 10 or 30 samples), the loss decreases but the model generates the same sentence. I’m using the following function to evaluate my model.
def evaluate_sent(encoder, decoder, sent, max_len):
    input_tensor = prepare_tensors(sent, source, 21, pad=True)
    ip_len = input_tensor.size(0)
        
    h0 = torch.zeros(2, 1, encoder.hidden_size, device=device)
    c0 = torch.zeros(2, 1, encoder.hidden_size, device=device)
    enc_hidden = (h0, c0)
    
    for ei in range(ip_len):
        enc_out, (ht, ct) = encoder(input_tensor[ei], enc_hidden)
        enc_hidden = (ht, ct)

    decoder_in = torch.tensor([[0]], device=device)
    dec_hidden = ht, ct
    
    decoded_words = []
    
    for di in range(max_len):
        dec_out, dec_hidden = decoder(decoder_in, dec_hidden)

#         print(f"Decoder out: {dec_out.size()}")
        topv, topi = dec_out.topk(1)
        decoder_in = topi.squeeze().detach().view(-1, 1)
        
        if topi.item() == 2:
            decoded_words.append('<eos>')
            break
        else:
            decoded_words.append(target.ix2word[topi.item()])
        
    return " ".join(decoded_words)