已合并
extra token优化, mtp layer重计算和tp支持 #2242
AtomGit-Bot创建于 2025年2月18日
extra token优化, mtp layer重计算和tp支持 #2242
已合并
AtomGit-Bot创建于 2025年2月18日
refs/pull/2242/head合入到master
7 个文件变更+87-184
@@ -21,16 +21,6 @@ from .blended_megatron_dataset_builder import need_to_build_dataset
21logger = logging.getLogger(__name__)21logger = logging.getLogger(__name__)
22 22 
23 23 
24-def gpt_dataset_init_wrapper(fn):
25- @wraps(fn)
26- def wrapper(self, *args, **kwargs):
27- # Adapt to MTP
28- _args = get_args()
29- self.num_nextn_predict_layers = _args.num_nextn_predict_layers
30- fn(self, *args, **kwargs)
31- 
32- return wrapper
33- 
34def gpt_dataset_getitem_wrapper(fn):24def gpt_dataset_getitem_wrapper(fn):
35 @wraps(fn)25 @wraps(fn)
36 def wrapper(self, idx):26 def wrapper(self, idx):
@@ -55,84 +45,6 @@ def gpt_dataset_getitem_wrapper(fn):
55 return wrapper45 return wrapper
56 46 
57 47 
58-def _query_document_sample_shuffle_indices(
59- self, idx: int
60-) -> Tuple[numpy.ndarray, numpy.ndarray]:
61- """Get the text (token ids) and document ids for a given index
62- 
63- Args:
64- idx (int): The index into the dataset
65- 
66- Returns:
67- Tuple[numpy.ndarray, numpy.ndarray]: The text ids and document ids
68- """
69- # Do the shuffle mapping
70- idx = self.shuffle_index[idx]
71- 
72- # Get the beginning and end documents and offsets
73- doc_index_beg, doc_index_beg_offset = self.sample_index[idx]
74- doc_index_end, doc_index_end_offset = self.sample_index[idx + 1]
75- 
76- document_ids = []
77- sample_parts = []
78- 
79- # Sample spans a single document
80- if doc_index_beg == doc_index_end:
81- # Add the document id
82- document_ids.append(self.document_index[doc_index_beg])
83- 
84- # Add the entire sample
85- # Adapt to MTP
86- sample_parts.append(
87- self.dataset.get(
88- self.document_index[doc_index_beg],
89- offset=doc_index_beg_offset,
90- length=doc_index_end_offset
91- - doc_index_beg_offset
92- + self.config.add_extra_token_to_sequence + self.num_nextn_predict_layers,
93- )
94- )
95- 
96- # Sample spans multiple documents
97- else:
98- for i in range(doc_index_beg, doc_index_end + 1):
99- # Add the document id
100- document_ids.append(self.document_index[i])
101- 
102- # Add the sample part
103- offset = 0 if i > doc_index_beg else doc_index_beg_offset
104- # Adapt to MTP
105- length = (
106- None
107- if i < doc_index_end
108- else doc_index_end_offset + self.config.add_extra_token_to_sequence + self.num_nextn_predict_layers
109- )
110- sample_parts.append(
111- self.dataset.get(self.document_index[i], offset=offset, length=length)
112- )
113- assert len(document_ids) == len(
114- sample_parts
115- ), f"len(document_ids) ({len(document_ids)}) != len(sample_parts) ({len(sample_parts)})"
116- 
117- length = sum(map(len, sample_parts))
118- 
119- # Pad the sample if necessary
120- # Adapt to MTP
121- if length < (
122- self.config.sequence_length + self.config.add_extra_token_to_sequence + self.num_nextn_predict_layers):
123- sample_parts.append(
124- [self._pad_token_id]
125- * (
126- self.config.sequence_length + self.config.add_extra_token_to_sequence +
127- self.num_nextn_predict_layers - length)
128- )
129- 
130- return (
131- numpy.concatenate(sample_parts, dtype=numpy.int64),
132- numpy.array(document_ids, dtype=numpy.int64),
133- )
134- 
135- 
136def _build_document_sample_shuffle_indices(48def _build_document_sample_shuffle_indices(
137 self,49 self,
138) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]:50) -> Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]:
@@ -206,16 +118,13 @@ def _build_document_sample_shuffle_indices(
206 separate_final_epoch = False118 separate_final_epoch = False
207 else:119 else:
208 # Get the number of samples for the last epoch120 # Get the number of samples for the last epoch
209- # Adapt to MTP
210 num_samples_sans_final_epoch = (121 num_samples_sans_final_epoch = (
211 (num_epochs - 1) * num_tokens_per_epoch122 (num_epochs - 1) * num_tokens_per_epoch
212 - self.config.add_extra_token_to_sequence123 - self.config.add_extra_token_to_sequence
213- - self.num_nextn_predict_layers
214 ) // sequence_length124 ) // sequence_length
215 num_samples_from_final_epoch = self.num_samples - num_samples_sans_final_epoch125 num_samples_from_final_epoch = self.num_samples - num_samples_sans_final_epoch
216 num_samples_per_epoch = (126 num_samples_per_epoch = (
217 num_tokens_per_epoch - self.config.add_extra_token_to_sequence127 num_tokens_per_epoch - self.config.add_extra_token_to_sequence
218- - self.num_nextn_predict_layers
219 ) // sequence_length128 ) // sequence_length
220 129 
221 # num_samples_from_final_epoch should be non-negative130 # num_samples_from_final_epoch should be non-negative
@@ -273,7 +182,6 @@ def _build_document_sample_shuffle_indices(
273 sequence_lengths_for_cpp = self.dataset.sequence_lengths.copy()182 sequence_lengths_for_cpp = self.dataset.sequence_lengths.copy()
274 else:183 else:
275 sequence_lengths_for_cpp = self.dataset.sequence_lengths184 sequence_lengths_for_cpp = self.dataset.sequence_lengths
276- # Adapt to MTP
277 sample_index = helpers.build_sample_idx(185 sample_index = helpers.build_sample_idx(
278 sequence_lengths_for_cpp,186 sequence_lengths_for_cpp,
279 document_index,187 document_index,
@@ -281,7 +189,7 @@ def _build_document_sample_shuffle_indices(
281 num_epochs,189 num_epochs,
282 num_tokens_per_epoch,190 num_tokens_per_epoch,
283 drop_last_partial_sequence,191 drop_last_partial_sequence,
284- self.config.add_extra_token_to_sequence + self.num_nextn_predict_layers,192+ self.config.add_extra_token_to_sequence,
285 )193 )
286 194 
287 if any(sample_index[:, 0] < 0):195 if any(sample_index[:, 0] < 0):
@@ -368,39 +276,6 @@ def _build_document_sample_shuffle_indices(
368 return document_index, sample_index, shuffle_index276 return document_index, sample_index, shuffle_index
369 277 
370 278 
371-def _get_num_tokens_per_epoch(self) -> int:
372- """Calculate the number of tokens in a single epoch
373- 
374- Returns:
375- int: The number of tokens in a single epoch
376- """
377- return int(numpy.sum(self.dataset.sequence_lengths[self.indices])) + self.num_nextn_predict_layers
378- 
379- 
380-def _get_num_epochs(self, num_tokens_per_epoch: int) -> int:
381- """Calculate the number of epochs
382- 
383- Args:
384- num_tokens_per_epoch (int): The number of tokens in a single epoch
385- 
386- Returns:
387- int: The number of epochs
388- """
389- num_epochs = 1
390- num_tokens = num_tokens_per_epoch
391- if self.num_samples is None:
392- return num_epochs
393- else:
394- # Adapt to MTP
395- num_tokens_requested = (
396- self.num_samples * self.config.sequence_length
397- ) + self.config.add_extra_token_to_sequence + self.num_nextn_predict_layers
398- while num_tokens < num_tokens_requested:
399- num_epochs += 1
400- num_tokens += num_tokens_per_epoch
401- return num_epochs
402- 
403- 
404def _get_ltor_masks_and_position_ids(279def _get_ltor_masks_and_position_ids(
405 data: torch.Tensor,280 data: torch.Tensor,
406 eod_token: int,281 eod_token: int,
@@ -432,7 +307,7 @@ def _get_ltor_masks_and_position_ids(
432 torch.Tensor: The position ID's of the token307 torch.Tensor: The position ID's of the token
433 """308 """
434 args = get_args()309 args = get_args()
435- seq_length = data.numel() - args.num_nextn_predict_layers310+ seq_length = data.numel()
436 311 
437 if create_attention_mask:312 if create_attention_mask:
438 attention_mask = torch.tril(313 attention_mask = torch.tril(
@@ -310,26 +310,35 @@ def inputs_slice(slice_num, input_ids, labels, position_ids, attention_mask):
310 [attention_mask],310 [attention_mask],
311 )311 )
312 312 
313- window_size = input_ids.shape[-1] - slice_num
314 return (313 return (
315- tensor_slide(input_ids, window_size),314+ tensor_slide(input_ids, slice_num),
316- tensor_slide(labels, window_size),315+ tensor_slide(labels, slice_num),
317- generate_nextn_position_ids(position_ids, window_size),316+ generate_nextn_position_ids(position_ids, slice_num),
318 # not compatible with ppo attn_mask317 # not compatible with ppo attn_mask
319- tensor_slide(attention_mask, window_size, dims=[-2, -1]),318+ tensor_slide(attention_mask, slice_num, dims=[-2, -1]),
320 )319 )
321 320 
322 321 
323-def generate_nextn_position_ids(tensor, window_size):322+def generate_nextn_position_ids(tensor, slice_num):
324- slides = tensor_slide(tensor, window_size)323+ slides = tensor_slide(tensor, slice_num)
324+ if slides[0] is None:
325+ return slides
325 326 
326 for idx in range(1, len(slides)):327 for idx in range(1, len(slides)):
327- for i in range(slides[idx].size(0)):328+ slides[idx] = regenerate_position_ids(slides[idx], idx)
328- row = slides[idx][i]
329- zero_mask = (row == 0)
330- if zero_mask.any():
331- first_zero_idx = torch.argmax(zero_mask.int()).item()
332- slides[idx][i, :first_zero_idx] = torch.arange(first_zero_idx)
333- else:
334- slides[idx] = slides[idx] - idx
335 return slides329 return slides
330+ 
331+ 
332+def regenerate_position_ids(tensor, offset):
333+ if tensor is None:
334+ return None
335+ tensor = tensor.clone()
336+ for i in range(tensor.size(0)):
337+ row = tensor[i]
338+ zero_mask = (row == 0)
339+ if zero_mask.any():
340+ first_zero_idx = torch.argmax(zero_mask.int()).item()
341+ tensor[i, :first_zero_idx] = torch.arange(first_zero_idx)
342+ else:
343+ tensor = tensor - offset
344+ return tensor
@@ -518,24 +518,11 @@ class CoreAdaptation(MegatronAdaptationABC):
518 finalize_wrapper)518 finalize_wrapper)
519 # MTP need extra token519 # MTP need extra token
520 from ..core.datasets.gpt_dataset import (520 from ..core.datasets.gpt_dataset import (
521- gpt_dataset_init_wrapper,
522 gpt_dataset_getitem_wrapper,521 gpt_dataset_getitem_wrapper,
523- _query_document_sample_shuffle_indices,
524- _get_num_tokens_per_epoch,
525- _get_num_epochs,
526 _get_ltor_masks_and_position_ids522 _get_ltor_masks_and_position_ids
527 )523 )
528- MegatronAdaptation.register('megatron.core.datasets.gpt_dataset.GPTDataset.__init__',
529- gpt_dataset_init_wrapper)
530 MegatronAdaptation.register('megatron.core.datasets.gpt_dataset.GPTDataset.__getitem__',524 MegatronAdaptation.register('megatron.core.datasets.gpt_dataset.GPTDataset.__getitem__',
531 gpt_dataset_getitem_wrapper)525 gpt_dataset_getitem_wrapper)
532- MegatronAdaptation.register(
533- 'megatron.core.datasets.gpt_dataset.GPTDataset._query_document_sample_shuffle_indices',
534- _query_document_sample_shuffle_indices)
535- MegatronAdaptation.register('megatron.core.datasets.gpt_dataset.GPTDataset._get_num_tokens_per_epoch',
536- _get_num_tokens_per_epoch)
537- MegatronAdaptation.register('megatron.core.datasets.gpt_dataset.GPTDataset._get_num_epochs',
538- _get_num_epochs)
539 MegatronAdaptation.register('megatron.core.datasets.gpt_dataset._get_ltor_masks_and_position_ids',526 MegatronAdaptation.register('megatron.core.datasets.gpt_dataset._get_ltor_masks_and_position_ids',
540 _get_ltor_masks_and_position_ids)527 _get_ltor_masks_and_position_ids)
541 528 
@@ -74,6 +74,7 @@ class MultiTokenPredication(MegatronModule):
74 # share with main model74 # share with main model
75 self.share_mtp_embedding_and_output_weight = share_mtp_embedding_and_output_weight75 self.share_mtp_embedding_and_output_weight = share_mtp_embedding_and_output_weight
76 self.recompute_layer_norm = args.recompute_mtp_norm76 self.recompute_layer_norm = args.recompute_mtp_norm
77+ self.recompute_mtp_layer = args.recompute_mtp_layer
77 78 
78 self.embedding = LanguageModelEmbedding(79 self.embedding = LanguageModelEmbedding(
79 config=self.config,80 config=self.config,
@@ -243,14 +244,32 @@ class MultiTokenPredication(MegatronModule):
243 hidden_states.register_hook(self.hnorm_ckpt.recompute)244 hidden_states.register_hook(self.hnorm_ckpt.recompute)
244 # hidden_states -> [s, b, h]245 # hidden_states -> [s, b, h]
245 hidden_states, _ = self.eh_proj(hidden_states)246 hidden_states, _ = self.eh_proj(hidden_states)
246- hidden_states, _ = self.transformer_layer(247+ 
247- hidden_states=hidden_states,248+ if self.config.tensor_model_parallel_size > 1:
248- attention_mask=attention_mask,249+ hidden_states = tensor_parallel.gather_from_tensor_model_parallel_region(hidden_states)
249- rotary_pos_emb=rotary_pos_emb,250+ if self.config.sequence_parallel:
250- inference_params=inference_params,251+ hidden_states = tensor_parallel.scatter_to_sequence_parallel_region(hidden_states)
251- packed_seq_params=packed_seq_params,252+ if self.recompute_mtp_layer:
252- **(extra_block_kwargs or {}),253+ hidden_states, context = tensor_parallel.checkpoint(
253- )254+ self.transformer_layer,
255+ self.config.distribute_saved_activations,
256+ hidden_states,
257+ attention_mask,
258+ None,
259+ None,
260+ rotary_pos_emb,
261+ inference_params,
262+ packed_seq_params,
263+ )
264+ else:
265+ hidden_states, _ = self.transformer_layer(
266+ hidden_states=hidden_states,
267+ attention_mask=attention_mask,
268+ rotary_pos_emb=rotary_pos_emb,
269+ inference_params=inference_params,
270+ packed_seq_params=packed_seq_params,
271+ **(extra_block_kwargs or {}),
272+ )
254 273 
255 # Final layer norm.274 # Final layer norm.
256 if self.final_layernorm is not None:275 if self.final_layernorm is not None:
@@ -156,6 +156,8 @@ def _add_mtp_args(parser):
156 group.add_argument('--mtp-loss-scale', type=float, default=0.3, help='Multi-Token prediction loss scale')156 group.add_argument('--mtp-loss-scale', type=float, default=0.3, help='Multi-Token prediction loss scale')
157 group.add_argument('--recompute-mtp-norm', action='store_true', default=False,157 group.add_argument('--recompute-mtp-norm', action='store_true', default=False,
158 help='Multi-Token prediction recompute norm')158 help='Multi-Token prediction recompute norm')
159+ group.add_argument('--recompute-mtp-layer', action='store_true', default=False,
160+ help='Multi-Token prediction recompute layer')
159 group.add_argument('--share-mtp-embedding-and-output-weight', action='store_true', default=False,161 group.add_argument('--share-mtp-embedding-and-output-weight', action='store_true', default=False,
160 help='Main model share embedding and output weight with mtp layer.')162 help='Main model share embedding and output weight with mtp layer.')
161 return parser163 return parser
@@ -294,10 +294,12 @@ def get_batch_on_this_tp_rank(data_iterator):
294 _broadcast(batch['position_ids'])294 _broadcast(batch['position_ids'])
295 295 
296 elif mpu.is_pipeline_last_stage():296 elif mpu.is_pipeline_last_stage():
297+ if args.num_nextn_predict_layers:
298+ _broadcast(batch['tokens'])
297 _broadcast(batch['labels'])299 _broadcast(batch['labels'])
298 _broadcast(batch['loss_mask'])300 _broadcast(batch['loss_mask'])
299 _broadcast(batch['attention_mask'])301 _broadcast(batch['attention_mask'])
300- if args.reset_position_ids:302+ if args.reset_position_ids or args.num_nextn_predict_layers:
301 _broadcast(batch['position_ids'])303 _broadcast(batch['position_ids'])
302 else:304 else:
303 _broadcast(batch['attention_mask'])305 _broadcast(batch['attention_mask'])
@@ -306,20 +308,25 @@ def get_batch_on_this_tp_rank(data_iterator):
306 308 
307 else:309 else:
308 310 
309- tokens = torch.empty((args.micro_batch_size, args.seq_length), dtype=torch.int64,311+ tokens = torch.empty((args.micro_batch_size, args.seq_length + args.num_nextn_predict_layers),
312+ dtype=torch.int64,
310 device=torch.cuda.current_device())313 device=torch.cuda.current_device())
311- labels = torch.empty((args.micro_batch_size, args.seq_length), dtype=torch.int64,314+ labels = torch.empty((args.micro_batch_size, args.seq_length + args.num_nextn_predict_layers),
315+ dtype=torch.int64,
312 device=torch.cuda.current_device())316 device=torch.cuda.current_device())
313- loss_mask = torch.empty((args.micro_batch_size, args.seq_length), dtype=torch.float32,317+ loss_mask = torch.empty((args.micro_batch_size, args.seq_length + args.num_nextn_predict_layers),
318+ dtype=torch.float32,
314 device=torch.cuda.current_device())319 device=torch.cuda.current_device())
315 if args.create_attention_mask_in_dataloader:320 if args.create_attention_mask_in_dataloader:
316 attention_mask = torch.empty(321 attention_mask = torch.empty(
317- (args.micro_batch_size, 1, args.seq_length, args.seq_length), dtype=torch.bool,322+ (args.micro_batch_size, 1, args.seq_length + args.num_nextn_predict_layers,
323+ args.seq_length + args.num_nextn_predict_layers), dtype=torch.bool,
318 device=torch.cuda.current_device()324 device=torch.cuda.current_device()
319 )325 )
320 else:326 else:
321 attention_mask = None327 attention_mask = None
322- position_ids = torch.empty((args.micro_batch_size, args.seq_length), dtype=torch.int64,328+ position_ids = torch.empty((args.micro_batch_size, args.seq_length + args.num_nextn_predict_layers),
329+ dtype=torch.int64,
323 device=torch.cuda.current_device())330 device=torch.cuda.current_device())
324 331 
325 if args.pipeline_model_parallel_size == 1:332 if args.pipeline_model_parallel_size == 1:
@@ -337,11 +344,14 @@ def get_batch_on_this_tp_rank(data_iterator):
337 _broadcast(position_ids)344 _broadcast(position_ids)
338 345 
339 elif mpu.is_pipeline_last_stage():346 elif mpu.is_pipeline_last_stage():
340- tokens = None347+ if args.num_nextn_predict_layers:
348+ _broadcast(tokens)
349+ else:
350+ tokens = None
341 _broadcast(labels)351 _broadcast(labels)
342 _broadcast(loss_mask)352 _broadcast(loss_mask)
343 _broadcast(attention_mask)353 _broadcast(attention_mask)
344- if args.reset_position_ids:354+ if args.reset_position_ids or args.num_nextn_predict_layers:
345 _broadcast(position_ids)355 _broadcast(position_ids)
346 else:356 else:
347 position_ids = None357 position_ids = None
@@ -511,26 +521,27 @@ def _get_batch_on_this_cp_rank_in_megatron_cp_general(batch):
511 521 
512def tensor_slide(522def tensor_slide(
513 tensor: Optional[torch.Tensor],523 tensor: Optional[torch.Tensor],
514- window_size: int = None,524+ slice_num: int,
515 dims: Union[int, List[int]] = -1,525 dims: Union[int, List[int]] = -1,
516- step: int = 1526+ step: int = 1,
527+ return_first=False,
517) -> List[Union[torch.Tensor, None]]:528) -> List[Union[torch.Tensor, None]]:
518- """slide window slice for n-D tensor"""529+ """通用滑动窗口函数,支持任意维度"""
519 if tensor is None:530 if tensor is None:
520 # return `List[None]` to avoid NoneType Error531 # return `List[None]` to avoid NoneType Error
521- return [None]532+ return [None] * (slice_num + 1)
522- 533+ if slice_num == 0:
523- if window_size is None:
524- window_size = tensor.shape[-1]
525- if window_size == tensor.shape[-1]:
526 return [tensor]534 return [tensor]
527- 535+ window_size = tensor.shape[-1] - slice_num
528 dims = [dims] if isinstance(dims, int) else sorted(dims, reverse=True)536 dims = [dims] if isinstance(dims, int) else sorted(dims, reverse=True)
529 537 
538+ # 连续多维度滑动
530 slices = []539 slices = []
531 for i in range(0, tensor.size(dims[-1]) - window_size + 1, step):540 for i in range(0, tensor.size(dims[-1]) - window_size + 1, step):
532 slice_obj = [slice(None)] * tensor.dim()541 slice_obj = [slice(None)] * tensor.dim()
533 for dim in dims:542 for dim in dims:
534 slice_obj[dim] = slice(i, i + window_size)543 slice_obj[dim] = slice(i, i + window_size)
535- slices.append(tensor[tuple(slice_obj)].clone())544+ slices.append(tensor[tuple(slice_obj)])
545+ if return_first:
546+ return slices
536 return slices547 return slices
@@ -124,14 +124,14 @@ def loss_func(loss_mask: torch.Tensor, output_tensor: torch.Tensor):
124 args = get_args()124 args = get_args()
125 125 
126 losses = output_tensor.float()126 losses = output_tensor.float()
127+ if args.num_nextn_predict_layers > 0:
128+ loss_mask = tensor_slide(loss_mask, args.num_nextn_predict_layers, return_first=True)[0]
127 loss_mask = loss_mask.view(-1).float()129 loss_mask = loss_mask.view(-1).float()
128 if args.context_parallel_size > 1:130 if args.context_parallel_size > 1:
129 loss = torch.cat([torch.sum(losses.view(-1) * loss_mask).view(1), loss_mask.sum().view(1)])131 loss = torch.cat([torch.sum(losses.view(-1) * loss_mask).view(1), loss_mask.sum().view(1)])
130 torch.distributed.all_reduce(loss, group=mpu.get_context_parallel_group())132 torch.distributed.all_reduce(loss, group=mpu.get_context_parallel_group())
131 loss = loss[0] / loss[1]133 loss = loss[0] / loss[1]
132 else:134 else:
133- if args.num_nextn_predict_layers > 0:
134- loss_mask = tensor_slide(loss_mask, loss_mask.shape[-1] - args.num_nextn_predict_layers)[0]
135 loss = torch.sum(losses.view(-1) * loss_mask) / loss_mask.sum()135 loss = torch.sum(losses.view(-1) * loss_mask) / loss_mask.sum()
136 136 
137 # Check individual rank losses are not NaN prior to DP all-reduce.137 # Check individual rank losses are not NaN prior to DP all-reduce.
@@ -178,7 +178,7 @@ def core_gpt_dataset_config_from_args(args):
178 178 
179 return GPTDatasetConfig(179 return GPTDatasetConfig(
180 random_seed=args.seed,180 random_seed=args.seed,
181- sequence_length=args.seq_length,181+ sequence_length=args.seq_length + args.num_nextn_predict_layers,
182 blend=get_blend_from_list(args.data_path),182 blend=get_blend_from_list(args.data_path),
183 blend_per_split=[183 blend_per_split=[
184 get_blend_from_list(args.train_data_path),184 get_blend_from_list(args.train_data_path),