-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Add inter-document attention masking to GPTDataset #5298
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -76,6 +76,10 @@ class GPTDatasetConfig(BlendedMegatronDatasetConfig): | |
| context_parallel_size: Optional[int] = None | ||
| """The size of the context parallel group. Needed for padding in packed sequences.""" | ||
|
|
||
| inter_document_masking: bool = False | ||
| """When True, return cu_seqlens marking document boundaries within each sample so | ||
| that attention is restricted to individual documents.""" | ||
|
|
||
| def __post_init__(self) -> None: | ||
| """Do asserts and set fields post init""" | ||
| super().__post_init__() | ||
|
|
@@ -233,9 +237,9 @@ def __getitem__(self, idx: Optional[int]) -> Dict[str, torch.Tensor]: | |
| """ | ||
| if idx is None: | ||
| # Batch padding sequence so the index does not matter | ||
| text, _ = self._query_document_sample_shuffle_indices(0) | ||
| text, _, document_lengths = self._query_document_sample_shuffle_indices(0) | ||
| else: | ||
| text, _ = self._query_document_sample_shuffle_indices(idx) | ||
| text, _, document_lengths = self._query_document_sample_shuffle_indices(idx) | ||
|
|
||
| text = torch.from_numpy(text).long() | ||
| if self.config.add_extra_token_to_sequence: | ||
|
|
@@ -279,32 +283,83 @@ def __getitem__(self, idx: Optional[int]) -> Dict[str, torch.Tensor]: | |
| if idx is None: | ||
| loss_mask = torch.zeros_like(loss_mask) | ||
|
|
||
| if self.config.create_attention_mask: | ||
| return { | ||
| if self.config.inter_document_masking: | ||
| # document_lengths come from _query_document_sample_shuffle_indices | ||
| # which fetches sequence_length + add_extra_token_to_sequence tokens | ||
| # total. The extra token is appended to the last document part (used | ||
| # to produce the shifted labels), so subtract it before computing | ||
| # cu_seqlens which should index into the sequence_length-sized tokens | ||
| # tensor. | ||
| if self.config.add_extra_token_to_sequence: | ||
| document_lengths[-1] -= 1 | ||
|
deepakn94 marked this conversation as resolved.
deepakn94 marked this conversation as resolved.
|
||
| if document_lengths[-1] == 0: | ||
| document_lengths.pop() | ||
| # If the sample was padded (e.g., the last validation sample), | ||
| # fold the padding into the last document so cu_seqlens[-1] | ||
| # equals sequence_length. | ||
| shortfall = self.config.sequence_length - sum(document_lengths) | ||
| if shortfall > 0: | ||
| if document_lengths: | ||
| document_lengths[-1] += shortfall | ||
| else: | ||
| document_lengths.append(shortfall) | ||
| cu_seqlens = torch.tensor(numpy.cumsum([0] + document_lengths), dtype=torch.int32) | ||
|
|
||
| max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As you point out in megatron/training/arguments.py, since we don't yet support CP we can compute this here, but we should move it outside to a helper. In #5017 I'm doing so for SFT samples, will have to update it to support this new feature |
||
|
|
||
| # Reset position IDs per document. | ||
| position_ids = position_ids.clone() | ||
| for i in range(1, cu_seqlens.numel()): | ||
| start = cu_seqlens[i - 1].item() | ||
| end = cu_seqlens[i].item() | ||
| position_ids[start:end] = torch.arange(end - start, dtype=torch.long) | ||
|
|
||
| # Pad cu_seqlens to a fixed length so that default_collate can | ||
| # stack samples with different numbers of documents. Trailing | ||
| # entries are filled with sequence_length; the merge helper | ||
| # strips them later. | ||
| padded_cu_seqlens = torch.full( | ||
| (self.config.sequence_length + 1,), self.config.sequence_length, dtype=torch.int32 | ||
| ) | ||
| padded_cu_seqlens[: cu_seqlens.numel()] = cu_seqlens | ||
|
|
||
| result = { | ||
| "tokens": tokens, | ||
| "labels": labels, | ||
| "loss_mask": loss_mask, | ||
| "position_ids": position_ids, | ||
| "cu_seqlens": padded_cu_seqlens, | ||
| "max_seqlen": max_seqlen, | ||
| } | ||
| elif self.config.create_attention_mask: | ||
| result = { | ||
| "tokens": tokens, | ||
| "labels": labels, | ||
| "attention_mask": attention_mask, | ||
| "loss_mask": loss_mask, | ||
| "position_ids": position_ids, | ||
| } | ||
| else: | ||
| return { | ||
| result = { | ||
| "tokens": tokens, | ||
| "labels": labels, | ||
| "loss_mask": loss_mask, | ||
| "position_ids": position_ids, | ||
| } | ||
|
|
||
| return result | ||
|
|
||
| def _query_document_sample_shuffle_indices( | ||
| self, idx: int | ||
| ) -> Tuple[numpy.ndarray, numpy.ndarray]: | ||
| ) -> Tuple[numpy.ndarray, numpy.ndarray, list]: | ||
| """Get the text (token ids) and document ids for a given index | ||
|
|
||
| Args: | ||
| idx (int): The index into the dataset | ||
|
|
||
| Returns: | ||
| Tuple[numpy.ndarray, numpy.ndarray]: The text ids and document ids | ||
| Tuple[numpy.ndarray, numpy.ndarray, list]: The text ids, document ids, | ||
| and per-document token counts (before any padding). | ||
| """ | ||
| if self.shuffle_index is None: | ||
| # NOTE(asolergi-nv): Lazy memmap the indexes | ||
|
|
@@ -366,6 +421,8 @@ def _query_document_sample_shuffle_indices( | |
|
|
||
| length = sum(map(len, sample_parts)) | ||
|
|
||
| document_lengths = [len(p) for p in sample_parts] | ||
|
|
||
| # Pad the sample if necessary | ||
| if length < (self.config.sequence_length + self.config.add_extra_token_to_sequence): | ||
| sample_parts.append( | ||
|
|
@@ -376,6 +433,7 @@ def _query_document_sample_shuffle_indices( | |
| return ( | ||
| numpy.concatenate(sample_parts, dtype=numpy.int64), | ||
| numpy.array(document_ids, dtype=numpy.int64), | ||
| document_lengths, | ||
|
asolergi-nv marked this conversation as resolved.
|
||
| ) | ||
|
|
||
| def _build_document_sample_shuffle_indices( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1252,6 +1252,12 @@ def validate_args(args, defaults={}): | |
| 'seq-length should be a multiple of 2 * context-parallel-size ' \ | ||
| 'if context-parallel-size > 1.' | ||
|
|
||
| if getattr(args, 'dataloader_inter_document_masking', False): | ||
| # The dataset omits attention_mask when inter-document masking is | ||
| # enabled; disable the flag to avoid a TP broadcast mismatch. | ||
| if args.create_attention_mask_in_dataloader: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This attention mask functionality is quite old & is only taking effect when using the local spec, not the TE one. Should we drop it at least from GPTDataset?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only hesitation I would have is if people use local and weird masks in their research outside of things TE supports. But I would lean toward dropping it too. |
||
| args.create_attention_mask_in_dataloader = False | ||
|
|
||
| if args.seq_length is not None: | ||
| assert args.encoder_seq_length is None | ||
| args.encoder_seq_length = args.seq_length | ||
|
|
@@ -2980,6 +2986,10 @@ def _add_data_args(parser): | |
| group.add_argument('--reset-attention-mask', action='store_true', | ||
| help='Reset self attention mask after ' | ||
| 'end-of-document token.') | ||
| group.add_argument('--dataloader-inter-document-masking', action='store_true', | ||
| help='Return cu_seqlens marking document boundaries ' | ||
| 'within each sample so that attention is restricted ' | ||
| 'to individual documents.') | ||
| group.add_argument('--eod-mask-loss', action='store_true', | ||
| help='Mask loss for the end of document tokens.') | ||
| group.add_argument('--no-create-attention-mask-in-dataloader', action='store_false', | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.