# Copyright (c) ModelScope Contributors. All rights reserved. import torch.nn import torch.nn.functional as F from functools import partial from swift.loss import loss_map from swift.metrics import eval_metrics_map from swift.utils import get_logger from .base import BaseMegatronTrainer logger = get_logger() class MegatronEmbeddingTrainer(BaseMegatronTrainer): def __init__(self, args, template): super().__init__(args, template) self._loss_func = loss_map[args.loss_type](args, self) eval_metric = 'infonce' if args.loss_type == 'infonce' else 'paired' self.eval_metrics = eval_metrics_map[eval_metric](args, self) def loss_func(self, output_tensor: torch.Tensor, *, labels: torch.Tensor, packed_seq_params=None, attention_mask=None): training = self.unwrapped_models[0].training last_hidden_state = self.get_last_tokens(output_tensor, packed_seq_params, attention_mask) if not training: self.eval_metrics.update(last_hidden_state.detach(), labels) mrl_dims = self.args.mrl_dims if mrl_dims: # Matryoshka Representation Learning: compute loss on each truncated dimension # and aggregate with the corresponding weights. loss = None for dim, weight in mrl_dims.items(): if dim > last_hidden_state.shape[-1]: logger.warning_once(f'MRL: skipping dimension {dim} because it exceeds the model hidden size ' f'({last_hidden_state.shape[-1]}).') continue sliced = F.normalize(last_hidden_state[..., :dim], p=2, dim=-1) cur_loss = weight * self._loss_func({'last_hidden_state': sliced}, labels) loss = cur_loss if loss is None else loss + cur_loss else: loss = self._loss_func({'last_hidden_state': last_hidden_state}, labels) metric = {'loss': loss.detach().clone()} metric = self._all_reduce_metric(metric) return loss, metric def forward_step(self, data_iterator, model): vp_stage = model.module.module.vp_stage data = self.get_batch(data_iterator, vp_stage) labels = data.pop('labels', None) output_tensor = model(**data) loss_func = partial( self.loss_func, labels=labels, packed_seq_params=data.get('packed_seq_params'), attention_mask=data.get('attention_mask') if data.get('attention_mask') is not None else data.get('attention_mask_2d')) return output_tensor, loss_func