Files
wehub-resource-sync a203934033
Lint test / lint (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:34:58 +08:00

131 lines
5.6 KiB
Python

# Copyright (c) ModelScope Contributors. All rights reserved.
import torch
import torch.distributed as dist
import torch.nn
from collections import defaultdict
from functools import partial
from megatron.core import mpu
from torch.distributed.nn import all_reduce
from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
from typing import List, Optional
from swift.utils import get_logger
from .base import BaseMegatronTrainer
logger = get_logger()
class MegatronTrainer(BaseMegatronTrainer):
def seq_cls_loss_func(self, output_tensor, *, labels: torch.Tensor, packed_seq_params=None, attention_mask=None):
args = self.args
logits = self.get_last_tokens(output_tensor, packed_seq_params, attention_mask)
num_labels = args.num_labels
acc = None
if args.problem_type == 'regression':
loss_fct = MSELoss()
if num_labels == 1:
loss = loss_fct(logits.squeeze(), labels.squeeze())
else:
loss = loss_fct(logits, labels)
elif args.problem_type == 'single_label_classification':
loss_fct = CrossEntropyLoss()
logits = logits.view(-1, num_labels)
labels = labels.view(-1)
loss = loss_fct(logits, labels)
acc = (logits.detach().argmax(dim=-1) == labels).float().mean()
elif args.problem_type == 'multi_label_classification':
loss_fct = BCEWithLogitsLoss()
loss = loss_fct(logits, labels)
preds = logits.sigmoid() > 0.5
acc = (labels == preds).all(dim=-1).float().mean()
metric = {'loss': loss.detach().clone()}
if acc is not None:
metric['acc'] = acc
metric = self._all_reduce_metric(metric)
return loss, metric
def loss_func(self,
output_tensor: torch.Tensor,
*,
labels: torch.Tensor,
loss_scale: Optional[torch.Tensor] = None,
channels: Optional[List[str]] = None,
packed_seq_params=None):
args = self.args
losses = output_tensor.float()
loss_mask = labels != -100
if args.enable_dft_loss:
losses = losses * torch.exp(-losses.detach())
if loss_scale is not None:
losses = losses * loss_scale
loss = torch.cat([torch.sum(losses * loss_mask).view(1), loss_mask.sum().view(1)])
# Reduce loss for logging.
reporting_loss = loss.detach().clone()
torch.distributed.all_reduce(reporting_loss, group=mpu.get_data_parallel_group(with_context_parallel=True))
lm_loss = loss[0]
lm_loss = lm_loss.clone()
local_num_tokens = loss[1].detach().clone().to(torch.int)
metrics = {'loss': reporting_loss}
if args.enable_channel_loss:
metrics.update(self._compute_channel_loss(losses, loss_mask, channels, packed_seq_params))
return (lm_loss, local_num_tokens, metrics)
def _compute_channel_loss(self, losses, loss_mask, channels, packed_seq_params=None):
args = self.args
metrics = defaultdict(lambda: torch.tensor([0.0, 0.0], dtype=torch.float32, device=torch.cuda.current_device()))
if args.padding_free:
num_samples = packed_seq_params.seq_lens.shape[0]
cu_seqlens = packed_seq_params.cu_seqlens_q[:num_samples + 1] // args.context_parallel_size
for i in range(cu_seqlens.shape[0] - 1):
channel = None if channels is None else channels[i]
slice_ = slice(cu_seqlens[i], cu_seqlens[i + 1])
c_loss = losses[0, slice_][loss_mask[0, slice_]]
metrics[f'loss_{channel}'][0] += c_loss.detach().sum()
metrics[f'loss_{channel}'][1] += c_loss.shape[0]
else:
for i in range(losses.shape[0]):
channel = None if channels is None else channels[i]
c_loss = losses[i][loss_mask[i]]
metrics[f'loss_{channel}'][0] += c_loss.detach().sum()
metrics[f'loss_{channel}'][1] += c_loss.shape[0]
# Synchronize keys to avoid getting stuck.
dp_cp_group = mpu.get_data_parallel_group(with_context_parallel=True)
all_keys = [None] * torch.distributed.get_world_size(group=dp_cp_group)
dist.all_gather_object(all_keys, list(metrics.keys()), group=dp_cp_group)
new_metrics = {}
for key in sorted(set().union(*all_keys)):
new_metrics[key] = metrics[key]
new_metrics = self._all_reduce_metric(new_metrics, torch.distributed.ReduceOp.SUM, group=dp_cp_group)
return new_metrics
def forward_step(self, data_iterator, model):
vp_stage = model.module.module.vp_stage
data = self.get_batch(data_iterator, vp_stage)
loss_scale = data.pop('loss_scale', None)
channels = data.pop('channel', None)
labels = data.get('labels')
if self.args.task_type == 'seq_cls':
data.pop('labels', None)
output_tensor = model(**data)
packed_seq_params = data.get('packed_seq_params')
if self.args.task_type == 'seq_cls':
loss_func = partial(
self.seq_cls_loss_func,
labels=labels,
packed_seq_params=packed_seq_params,
attention_mask=data.get('attention_mask')
if data.get('attention_mask') is not None else data.get('attention_mask_2d'))
else:
loss_func = partial(
self.loss_func,
labels=labels,
loss_scale=loss_scale,
channels=channels,
packed_seq_params=packed_seq_params)
return output_tensor, loss_func