orqa_wiki_dataset.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. # coding=utf-8
  2. # Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """Wikipedia dataset from DPR code for ORQA."""
  16. from abc import ABC
  17. import csv
  18. import numpy as np
  19. import random
  20. import torch
  21. from torch.utils.data import Dataset
  22. from megatron import print_rank_0, get_args, get_tokenizer, mpu
  23. from megatron.data.biencoder_dataset_utils import make_attention_mask
  24. def get_open_retrieval_wiki_dataset():
  25. args = get_args()
  26. tokenizer = get_tokenizer()
  27. dataset = OpenRetrievalEvidenceDataset('2018 Wikipedia from DPR codebase',
  28. 'evidence',
  29. args.evidence_data_path,
  30. tokenizer,
  31. args.retriever_seq_length)
  32. return dataset
  33. def get_open_retrieval_batch(data_iterator):
  34. # Items and their type.
  35. keys = ['row_id', 'context', 'context_mask', 'context_types',
  36. 'context_pad_mask']
  37. datatype = torch.int64
  38. # Broadcast data.
  39. data = None if data_iterator is None else next(data_iterator)
  40. data_b = mpu.broadcast_data(keys, data, datatype)
  41. # Unpack.
  42. row_id = data_b['row_id'].long()
  43. context = data_b['context'].long()
  44. # TODO: make the context mask a binary one
  45. context_mask = (data_b['context_mask'] < 0.5)
  46. context_types = data_b['context_types'].long()
  47. context_pad_mask = data_b['context_pad_mask'].long()
  48. return row_id, context, context_mask, context_types, context_pad_mask
  49. def build_tokens_types_paddings_from_text(row, tokenizer, max_seq_length):
  50. """Build token types and paddings, trim if needed, and pad if needed."""
  51. title_ids = tokenizer.tokenize(row['title'])
  52. context_ids = tokenizer.tokenize(row['text'])
  53. # Appending the title of the context at front
  54. extended_context_ids = title_ids + [tokenizer.sep_id] + context_ids
  55. context_ids, context_types, context_pad_mask = \
  56. build_tokens_types_paddings_from_ids(extended_context_ids,
  57. max_seq_length, tokenizer.cls, tokenizer.sep, tokenizer.pad)
  58. return context_ids, context_types, context_pad_mask
  59. # noinspection DuplicatedCode
  60. def build_tokens_types_paddings_from_ids(text_ids, max_seq_length,
  61. cls_id, sep_id, pad_id):
  62. """Build token types and paddings, trim if needed, and pad if needed."""
  63. enc_ids = []
  64. tokentypes_enc = []
  65. # [CLS].
  66. enc_ids.append(cls_id)
  67. tokentypes_enc.append(0)
  68. # A.
  69. len_src = len(text_ids)
  70. enc_ids.extend(text_ids)
  71. tokentypes_enc.extend([0] * len_src)
  72. # Cap the size.
  73. if len(enc_ids) > max_seq_length - 1:
  74. enc_ids = enc_ids[0: max_seq_length - 1]
  75. tokentypes_enc = tokentypes_enc[0: max_seq_length - 1]
  76. # [SEP].
  77. enc_ids.append(sep_id)
  78. tokentypes_enc.append(0)
  79. num_tokens_enc = len(enc_ids)
  80. # Padding.
  81. padding_length = max_seq_length - len(enc_ids)
  82. if padding_length > 0:
  83. enc_ids.extend([pad_id] * padding_length)
  84. tokentypes_enc.extend([pad_id] * padding_length)
  85. pad_mask = ([1] * num_tokens_enc) + ([0] * padding_length)
  86. pad_mask = np.array(pad_mask, dtype=np.int64)
  87. return enc_ids, tokentypes_enc, pad_mask
  88. def build_sample(row_id, context_ids, context_types, context_pad_mask):
  89. """Convert to numpy and return a sample consumed by the batch producer."""
  90. context_ids = np.array(context_ids, dtype=np.int64)
  91. context_types = np.array(context_types, dtype=np.int64)
  92. context_mask = make_attention_mask(context_ids, context_ids)
  93. sample = ({
  94. 'row_id': row_id,
  95. 'context': context_ids,
  96. 'context_mask': context_mask,
  97. 'context_types': context_types,
  98. 'context_pad_mask': context_pad_mask
  99. })
  100. return sample
  101. class OpenRetrievalEvidenceDataset(ABC, Dataset):
  102. """Open Retrieval Evidence dataset class."""
  103. def __init__(self, task_name, dataset_name, datapath, tokenizer,
  104. max_seq_length):
  105. # Store inputs.
  106. self.task_name = task_name
  107. self.dataset_name = dataset_name
  108. self.tokenizer = tokenizer
  109. self.max_seq_length = max_seq_length
  110. print_rank_0(' > building {} dataset for {}:'.format(self.task_name,
  111. self.dataset_name))
  112. # Process the files.
  113. print_rank_0(datapath)
  114. self.samples, self.id2text = self.process_samples_from_single_path(
  115. datapath)
  116. args = get_args()
  117. if args.sample_rate < 1: # subsample
  118. k = int(len(self.samples) * args.sample_rate)
  119. self.samples = random.sample(self.samples, k)
  120. print_rank_0(' >> total number of samples: {}'.format(
  121. len(self.samples)))
  122. def __len__(self):
  123. return len(self.samples)
  124. def __getitem__(self, idx):
  125. row = self.samples[idx]
  126. context_ids, context_types, context_pad_mask = \
  127. build_tokens_types_paddings_from_text(row, self.tokenizer,
  128. self.max_seq_length)
  129. sample = build_sample(row['doc_id'],
  130. context_ids,
  131. context_types,
  132. context_pad_mask)
  133. return sample
  134. @staticmethod
  135. def process_samples_from_single_path(filename):
  136. print_rank_0(' > Processing {} ...'.format(filename))
  137. total = 0
  138. rows = []
  139. id2text = {}
  140. with open(filename) as tsvfile:
  141. reader = csv.reader(tsvfile, delimiter='\t')
  142. next(reader, None) # skip the headers
  143. for row in reader:
  144. # file format: doc_id, doc_text, title
  145. doc_id = int(row[0])
  146. text = row[1]
  147. title = row[2]
  148. rows.append({'doc_id': doc_id,
  149. 'text': text,
  150. 'title': title})
  151. assert doc_id not in id2text
  152. id2text[doc_id] = (text, title)
  153. total += 1
  154. if total % 100000 == 0:
  155. print_rank_0(' > processed {} rows so far ...'.format(
  156. total))
  157. print_rank_0(' >> processed {} samples.'.format(len(rows)))
  158. return rows, id2text