I tried the following approaches:

using pin_memory=True

loading entire dataset in __init__ of Dataset as:

class MakeDataset(Dataset):
    def __init__(self, key):
        data = pd.read_csv('../input/reberseq/train_data.csv') if key == 'train' else pd.read_csv('../input/reberseq/test_data.csv')
        
        self.strings = list(data['string'])
        self.valid = list(data['valid'])
        
        self.strings, self.valid = self.make_variables(self.strings, self.valid)
        
        self.len = len(self.valid)
        self.valid_list = [0, 1]
        
    def str2ascii(self, string):
        ascii_arr = [ord(s) for s in string]
        return ascii_arr, len(ascii_arr)

    def pad_seq(self, vect_seqs, seq_lens, valid):
        seq_tensor = torch.zeros((len(vect_seqs), seq_lens.max())).long()

        for index, (seq, seq_len) in enumerate(zip(vect_seqs, seq_lens)):
            seq_tensor[index, :seq_len] = torch.LongTensor(seq)

        return seq_tensor, valid

    def make_variables(self, strings, valid):
        seqs_and_lens = [self.str2ascii(string) for string in strings]
        vect_seqs = [s[0] for s in seqs_and_lens]
        seq_lens = torch.LongTensor([s[1] for s in seqs_and_lens])
        valid = torch.LongTensor(valid)
        return self.pad_seq(vect_seqs, seq_lens, valid)

    def __getitem__(self, index):
        return Variable(self.strings[index].cuda()), Variable(self.valid[index].cuda())

    def __len__(self):
        return self.len

Even after trying all this, My GPU utilization is still around 40% while CPU is always 100%.
My dataset set consists of 25000 reber sequence. In size, the entire csv is no more than 700 kB, but still during training, 3 GB of CPU and 1.5 GB of GPU is occupied.