chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:39:21 +08:00
commit bc34f6df14
1149 changed files with 328099 additions and 0 deletions
@@ -0,0 +1,18 @@
from FlagEmbedding.abc.finetune.embedder import (
AbsEmbedderModelArguments as EncoderOnlyEmbedderModelArguments,
AbsEmbedderDataArguments as EncoderOnlyEmbedderDataArguments,
AbsEmbedderTrainingArguments as EncoderOnlyEmbedderTrainingArguments,
)
from .modeling import BiEncoderOnlyEmbedderModel
from .trainer import EncoderOnlyEmbedderTrainer
from .runner import EncoderOnlyEmbedderRunner
__all__ = [
'EncoderOnlyEmbedderModelArguments',
'EncoderOnlyEmbedderDataArguments',
'EncoderOnlyEmbedderTrainingArguments',
'BiEncoderOnlyEmbedderModel',
'EncoderOnlyEmbedderTrainer',
'EncoderOnlyEmbedderRunner',
]
@@ -0,0 +1,31 @@
from transformers import HfArgumentParser
from FlagEmbedding.finetune.embedder.encoder_only.base import (
EncoderOnlyEmbedderDataArguments,
EncoderOnlyEmbedderTrainingArguments,
EncoderOnlyEmbedderModelArguments,
EncoderOnlyEmbedderRunner,
)
def main():
parser = HfArgumentParser((
EncoderOnlyEmbedderModelArguments,
EncoderOnlyEmbedderDataArguments,
EncoderOnlyEmbedderTrainingArguments
))
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
model_args: EncoderOnlyEmbedderModelArguments
data_args: EncoderOnlyEmbedderDataArguments
training_args: EncoderOnlyEmbedderTrainingArguments
runner = EncoderOnlyEmbedderRunner(
model_args=model_args,
data_args=data_args,
training_args=training_args
)
runner.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,206 @@
import logging
from typing import List
import torch
from transformers import AutoModel, PreTrainedModel, PreTrainedTokenizer
from FlagEmbedding.abc.finetune.embedder import AbsEmbedderModel
logger = logging.getLogger(__name__)
class BiEncoderOnlyEmbedderModel(AbsEmbedderModel):
"""Embedder class for encoder only model.
Args:
base_model (PreTrainedModel): The base model to train on.
tokenizer (PreTrainedTokenizer, optional): The tokenizer to use. Defaults to ``None``.
negatives_cross_device (bool, optional): If True, will compute cross devices negative loss. Defaults to ``False``.
temperature (float, optional): Temperature to control the scale of scores. Defaults to ``1.0``.
sub_batch_size (int, optional): Sub-batch size during encoding. If negative, will not split to sub-batch.
Defaults to ``-1``.
kd_loss_type (str, optional): Type of knowledge distillation loss. Defaults to ``"kl_div"``.
use_mrl (bool, optional): Whether to use MRL for training. Defaults to ``False``.
mrl_dims (List[int], optional): The dimensions of MRL layers. Defaults to ``[]``.
sentence_pooling_method (str, optional): Pooling method to get sentence embedding. Defaults to ``'cls'``.
normalize_embeddings (bool, optional): If True, normalize the embedding vector. Defaults to ``False``.
"""
TRANSFORMER_CLS = AutoModel
def __init__(
self,
base_model: PreTrainedModel,
tokenizer: PreTrainedTokenizer = None,
negatives_cross_device: bool = False,
temperature: float = 1.0,
sub_batch_size: int = -1,
kd_loss_type: str = 'kl_div',
use_mrl: bool = False,
mrl_dims: List[int] = [],
sentence_pooling_method: str = 'cls',
normalize_embeddings: bool = False,
):
super().__init__(
base_model,
tokenizer=tokenizer,
negatives_cross_device=negatives_cross_device,
temperature=temperature,
sub_batch_size=sub_batch_size,
kd_loss_type=kd_loss_type,
use_mrl=use_mrl,
mrl_dims=mrl_dims,
)
self.sentence_pooling_method = sentence_pooling_method
self.normalize_embeddings = normalize_embeddings
self.cross_entropy = torch.nn.CrossEntropyLoss(reduction='mean')
def encode(self, features):
"""Encode and get the embedding.
Args:
features (Union[list, dict]): Features feed to the model.
Returns:
Union[torch.Tensor, List[torch.Tensor]]: The embedding vectors or a list of embedding vectors if MRL is used.
"""
if features is None:
return None
if not isinstance(features, list):
if self.sub_batch_size is not None and self.sub_batch_size > 0:
all_p_reps = []
for i in range(0, len(features['attention_mask']), self.sub_batch_size):
end_inx = min(i + self.sub_batch_size, len(features['attention_mask']))
sub_features = {}
for k, v in features.items():
sub_features[k] = v[i:end_inx]
last_hidden_state = self.model(**sub_features, return_dict=True).last_hidden_state
p_reps = self._sentence_embedding(last_hidden_state, sub_features['attention_mask'])
all_p_reps.append(p_reps)
all_p_reps = torch.cat(all_p_reps, 0).contiguous()
else:
last_hidden_state = self.model(**features, return_dict=True).last_hidden_state
all_p_reps = self._sentence_embedding(last_hidden_state, features['attention_mask'])
else:
all_p_reps = []
for sub_features in features:
last_hidden_state = self.model(**sub_features, return_dict=True).last_hidden_state
p_reps = self._sentence_embedding(last_hidden_state, sub_features['attention_mask'])
all_p_reps.append(p_reps)
all_p_reps = torch.cat(all_p_reps, 0).contiguous()
if self.use_mrl:
p_reps_list = []
ori_dim = all_p_reps.size(-1)
for dim in self.mrl_dims:
if dim > ori_dim:
logger.warning(f"MRL dim {dim} is larger than original dimension {ori_dim}, using original dimension instead.")
dim = min(dim, ori_dim)
dim_p_reps = all_p_reps[:, :dim]
if self.normalize_embeddings:
dim_p_reps = torch.nn.functional.normalize(dim_p_reps, dim=-1)
p_reps_list.append(dim_p_reps.contiguous())
return p_reps_list
else:
if self.normalize_embeddings:
all_p_reps = torch.nn.functional.normalize(all_p_reps, dim=-1)
return all_p_reps.contiguous()
def _sentence_embedding(self, last_hidden_state, attention_mask):
"""Use the pooling method to get the sentence embedding.
Args:
last_hidden_state (torch.Tensor): The model output's last hidden state.
attention_mask (torch.Tensor): Mask out padding tokens during pooling.
Raises:
NotImplementedError: Specified pooling method not implemented.
Returns:
torch.Tensor: The sentence embeddings.
"""
if self.sentence_pooling_method == "cls":
return last_hidden_state[:, 0]
elif self.sentence_pooling_method == "mean":
s = torch.sum(
last_hidden_state * attention_mask.unsqueeze(-1).float(), dim=1
)
d = attention_mask.sum(dim=1, keepdim=True).float()
return s / d
elif self.sentence_pooling_method == "last_token":
left_padding = attention_mask[:, -1].sum() == attention_mask.shape[0]
if left_padding:
return last_hidden_state[:, -1]
else:
sequence_lengths = attention_mask.sum(dim=1) - 1
batch_size = last_hidden_state.shape[0]
return last_hidden_state[
torch.arange(batch_size, device=last_hidden_state.device),
sequence_lengths,
]
else:
raise NotImplementedError(f"pooling method {self.sentence_pooling_method} not implemented")
def compute_score(self, q_reps, p_reps):
"""Computes the scores between query and passage representations.
Args:
q_reps (torch.Tensor): Query representations.
p_reps (torch.Tensor): Passage representations.
Returns:
torch.Tensor: The computed scores, adjusted by temperature.
"""
scores = self._compute_similarity(q_reps, p_reps) / self.temperature
scores = scores.view(q_reps.size(0), -1)
return scores
def _compute_similarity(self, q_reps, p_reps):
"""Computes the similarity between query and passage representations using inner product.
Args:
q_reps (torch.Tensor): Query representations.
p_reps (torch.Tensor): Passage representations.
Returns:
torch.Tensor: The computed similarity matrix.
"""
if len(p_reps.size()) == 2:
return torch.matmul(q_reps, p_reps.transpose(0, 1))
return torch.matmul(q_reps, p_reps.transpose(-2, -1))
def compute_loss(self, scores, target):
"""Compute the loss using cross entropy.
Args:
scores (torch.Tensor): Computed score.
target (torch.Tensor): The target value.
Returns:
torch.Tensor: The computed cross entropy loss.
"""
return self.cross_entropy(scores, target)
def gradient_checkpointing_enable(self, **kwargs):
"""
Activates gradient checkpointing for the current model.
"""
self.model.gradient_checkpointing_enable(**kwargs)
def enable_input_require_grads(self, **kwargs):
"""
Enables the gradients for the input embeddings.
"""
self.model.enable_input_require_grads(**kwargs)
def save(self, output_dir: str):
"""Save the model to the directory.
Args:
output_dir (str): Directory for saving the model.
"""
state_dict = self.model.state_dict()
state_dict = type(state_dict)(
{k: v.clone().cpu()
for k,
v in state_dict.items()})
self.model.save_pretrained(output_dir, state_dict=state_dict)
@@ -0,0 +1,87 @@
import logging
from typing import Tuple
from transformers import (
AutoModel, AutoConfig,
AutoTokenizer, PreTrainedTokenizer
)
from FlagEmbedding.abc.finetune.embedder import AbsEmbedderRunner, AbsEmbedderModel, EmbedderTrainerCallbackForDataRefresh
from .modeling import BiEncoderOnlyEmbedderModel
from .trainer import EncoderOnlyEmbedderTrainer
logger = logging.getLogger(__name__)
class EncoderOnlyEmbedderRunner(AbsEmbedderRunner):
"""
Finetune Runner for base embedding models.
"""
def load_tokenizer_and_model(self) -> Tuple[PreTrainedTokenizer, AbsEmbedderModel]:
"""Load tokenizer and model.
Returns:
Tuple[PreTrainedTokenizer, AbsEmbedderModel]: Tokenizer and model instances.
"""
tokenizer = AutoTokenizer.from_pretrained(
self.model_args.model_name_or_path,
cache_dir=self.model_args.cache_dir,
token=self.model_args.token,
use_fast=self.model_args.use_fast_tokenizer,
trust_remote_code=self.model_args.trust_remote_code
)
base_model = AutoModel.from_pretrained(
self.model_args.model_name_or_path,
cache_dir=self.model_args.cache_dir,
token=self.model_args.token,
trust_remote_code=self.model_args.trust_remote_code
)
num_labels = 1
config = AutoConfig.from_pretrained(
self.model_args.config_name if self.model_args.config_name else self.model_args.model_name_or_path,
num_labels=num_labels,
cache_dir=self.model_args.cache_dir,
token=self.model_args.token,
trust_remote_code=self.model_args.trust_remote_code,
)
logger.info('Config: %s', config)
model = BiEncoderOnlyEmbedderModel(
base_model,
tokenizer=tokenizer,
negatives_cross_device=self.training_args.negatives_cross_device,
temperature=self.training_args.temperature,
sub_batch_size=self.training_args.sub_batch_size,
kd_loss_type=self.training_args.kd_loss_type,
use_mrl=self.training_args.use_mrl,
mrl_dims=self.training_args.mrl_dims,
sentence_pooling_method=self.training_args.sentence_pooling_method,
normalize_embeddings=self.training_args.normalize_embeddings
)
if self.training_args.gradient_checkpointing:
model.enable_input_require_grads()
if self.training_args.fix_position_embedding:
for k, v in model.named_parameters():
if "position_embeddings" in k:
logging.info(f"Freeze the parameters for {k}")
v.requires_grad = False
return tokenizer, model
def load_trainer(self) -> EncoderOnlyEmbedderTrainer:
"""Load the trainer.
Returns:
EncoderOnlyEmbedderTrainer: Loaded trainer instance.
"""
trainer = EncoderOnlyEmbedderTrainer(
model=self.model,
args=self.training_args,
train_dataset=self.train_dataset,
data_collator=self.data_collator,
processing_class=self.tokenizer
)
if self.data_args.same_dataset_within_batch:
trainer.add_callback(EmbedderTrainerCallbackForDataRefresh(self.train_dataset))
return trainer
@@ -0,0 +1,44 @@
import os
import torch
import logging
from typing import Optional
from FlagEmbedding.abc.finetune.embedder import AbsEmbedderTrainer
logger = logging.getLogger(__name__)
class EncoderOnlyEmbedderTrainer(AbsEmbedderTrainer):
"""
Trainer class for base encoder models.
"""
def _save(self, output_dir: Optional[str] = None, state_dict=None):
"""Save the model to directory.
Args:
output_dir (Optional[str], optional): Output directory to save the model. Defaults to ``None``.
Raises:
NotImplementedError
"""
output_dir = output_dir if output_dir is not None else self.args.output_dir
os.makedirs(output_dir, exist_ok=True)
logger.info("Saving model checkpoint to %s", output_dir)
# Save a trained model and configuration using `save_pretrained()`.
# They can then be reloaded using `from_pretrained()`
if not hasattr(self.model, 'save'):
raise NotImplementedError(
f'MODEL {self.model.__class__.__name__} '
f'does not support save interface')
else:
self.model.save(output_dir)
if self.tokenizer is not None and self.is_world_process_zero():
self.tokenizer.save_pretrained(output_dir)
torch.save(self.args, os.path.join(output_dir, "training_args.bin"))
# save the checkpoint for sentence-transformers library
# if self.is_world_process_zero():
# save_ckpt_for_sentence_transformers(output_dir,
# pooling_mode=self.args.sentence_pooling_method,
# normlized=self.args.normlized)