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 (
AbsEmbedderDataArguments as DecoderOnlyEmbedderDataArguments,
AbsEmbedderTrainingArguments as DecoderOnlyEmbedderTrainingArguments,
)
from .arguments import DecoderOnlyEmbedderModelArguments
from .modeling import BiDecoderOnlyEmbedderModel
from .trainer import DecoderOnlyEmbedderTrainer
from .runner import DecoderOnlyEmbedderRunner
__all__ = [
'DecoderOnlyEmbedderDataArguments',
'DecoderOnlyEmbedderTrainingArguments',
'DecoderOnlyEmbedderModelArguments',
'BiDecoderOnlyEmbedderModel',
'DecoderOnlyEmbedderTrainer',
'DecoderOnlyEmbedderRunner',
]
@@ -0,0 +1,31 @@
from transformers import HfArgumentParser
from FlagEmbedding.finetune.embedder.decoder_only.base import (
DecoderOnlyEmbedderDataArguments,
DecoderOnlyEmbedderTrainingArguments,
DecoderOnlyEmbedderModelArguments,
DecoderOnlyEmbedderRunner,
)
def main():
parser = HfArgumentParser((
DecoderOnlyEmbedderModelArguments,
DecoderOnlyEmbedderDataArguments,
DecoderOnlyEmbedderTrainingArguments
))
model_args, data_args, training_args = parser.parse_args_into_dataclasses()
model_args: DecoderOnlyEmbedderModelArguments
data_args: DecoderOnlyEmbedderDataArguments
training_args: DecoderOnlyEmbedderTrainingArguments
runner = DecoderOnlyEmbedderRunner(
model_args=model_args,
data_args=data_args,
training_args=training_args
)
runner.run()
if __name__ == "__main__":
main()
@@ -0,0 +1,76 @@
from typing import Optional, List
from dataclasses import dataclass, field
from FlagEmbedding.abc.finetune.embedder import AbsEmbedderModelArguments
def default_target_modules() -> List[int]:
return ['v_proj', 'q_proj', 'k_proj', 'gate_proj', 'down_proj', 'o_proj', 'up_proj']
@dataclass
class DecoderOnlyEmbedderModelArguments(AbsEmbedderModelArguments):
"""
Model argument class for decoder only base model.
"""
peft_model_path: str = field(
default='', metadata={"help": "The peft model checkpoint for initialization."}
)
use_lora: bool = field(
default=True,
metadata={"help": "If passed, will use LORA (low-rank parameter-efficient training) to train the model."}
)
lora_rank: int = field(
default=64,
metadata={"help": "The rank of lora."}
)
lora_alpha: float = field(
default=16,
metadata={"help": "The alpha parameter of lora."}
)
lora_dropout: float = field(
default=0.1,
metadata={"help": "The dropout rate of lora modules."}
)
target_modules: List[str] = field(
default_factory=default_target_modules,
metadata={"help": "The target modules to apply LORA."}
)
use_flash_attn: bool = field(
default=False,
metadata={"help": "If passed, will use flash attention to train the model."}
)
use_slow_tokenizer: bool = field(
default=False,
metadata={"help": "If passed, will use a slow tokenizer (not backed by the 🤗 Tokenizers library)."}
)
# low_cpu_mem_usage: bool = field(
# default=False,
# metadata={"help": "It is an option to create the model as an empty shell,"
# "then only materialize its parameters when the pretrained weights are loaded."
# "If passed, LLM loading time and RAM consumption will be benefited."}
# )
from_peft: str = field(
default=None
)
modules_to_save: List[str] = field(
default=None,
)
raw_peft: str = field(
default=None
)
additional_special_tokens: Optional[str] = field(
default=None,
metadata={"help": "additional special tokens", "nargs": "+"}
)
save_merged_lora_model: bool = field(
default=False,
metadata={"help": "If passed, will merge the lora modules and save the entire model."}
)
only_merge_lora_model: bool = field(
default=False,
metadata={"help": "If passed, will only merge the lora modules and save the entire model."}
)
@@ -0,0 +1,185 @@
import os
import re
import torch
import logging
from transformers import AutoConfig, AutoModel, AutoTokenizer
from peft import LoraConfig, TaskType, get_peft_model, PeftModel
from .arguments import DecoderOnlyEmbedderModelArguments
logger = logging.getLogger(__name__)
def find_largest_checkpoint(checkpoint_dir):
"""Find the largest checkpoint from directory.
Args:
checkpoint_dir (str): Directory to the checkpoint.
Returns:
str: Directory to the checkpoint, None no matching found.
"""
checkpoint_pattern = re.compile(r'checkpoint-(\d+)')
max_number = -1
max_checkpoint_file = None
for file in os.listdir(checkpoint_dir):
match = checkpoint_pattern.search(file)
if match:
number = int(match.group(1))
if number > max_number:
max_number = number
max_checkpoint_file = file
if max_checkpoint_file:
return os.path.join(checkpoint_dir, max_checkpoint_file)
else:
return None
def get_model(model_args: DecoderOnlyEmbedderModelArguments, output_dir: str, resize: bool, resize_tokens: int):
"""Get the model.
Args:
model_args (DecoderOnlyEmbedderModelArguments): Model arguments instance.
output_dir (str): Directory to save the model.
resize (bool): Whether to resize the number of tokens.
resize_tokens (int): The new token size.
Returns:
transformers.PreTrainedModel or PeftModel: The loaded model.
"""
if model_args.config_name:
config = AutoConfig.from_pretrained(
model_args.config_name,
token=model_args.token,
cache_dir=model_args.cache_dir,
trust_remote_code=model_args.trust_remote_code,
)
elif model_args.model_name_or_path:
config = AutoConfig.from_pretrained(
model_args.model_name_or_path,
token=model_args.token,
cache_dir=model_args.cache_dir,
trust_remote_code=model_args.trust_remote_code,
)
else:
raise ValueError(
"You are instantiating a new config instance from scratch. This is not supported by this script."
)
config.use_cache = False
if model_args.model_name_or_path:
model = AutoModel.from_pretrained(
model_args.model_name_or_path,
# torch_dtype=torch.bfloat16,
attn_implementation = "flash_attention_2" if model_args.use_flash_attn else None,
token=model_args.token,
cache_dir=model_args.cache_dir,
from_tf=bool(".ckpt" in model_args.model_name_or_path),
config=config,
trust_remote_code=model_args.trust_remote_code,
)
else:
logger.info("Training new model from scratch")
model = model_args.from_config(config)
if model_args.raw_peft is not None:
model.set_input_embeddings(torch.load(os.path.join(model_args.raw_peft, 'embedding', 'emb.pth')))
model = PeftModel.from_pretrained(model, model_args.raw_peft)
model = model.merge_and_unload()
if resize:
model.resize_token_embeddings(resize_tokens)
os.makedirs(os.path.join(output_dir, 'embedding'), exist_ok=True)
torch.save(model.embed_tokens, os.path.join(output_dir, 'embedding', 'emb.pth'))
target_modules = model_args.target_modules
else:
target_modules = model_args.target_modules
if 'embed_tokens' in target_modules:
target_modules.remove('embed_tokens')
if model_args.from_peft is not None:
if os.path.exists(os.path.join(model_args.from_peft, 'embedding')):
model.set_input_embeddings(torch.load(os.path.join(model_args.from_peft, 'embedding', 'emb.pth')))
torch.save(model.embed_tokens, os.path.join(output_dir, 'embedding', 'emb.pth'))
model = PeftModel.from_pretrained(model, model_args.from_peft, is_trainable=True)
model.print_trainable_parameters()
else:
if model_args.use_lora:
peft_config = LoraConfig(
task_type=TaskType.FEATURE_EXTRACTION,
inference_mode=False,
r=model_args.lora_rank,
target_modules=target_modules,
modules_to_save=model_args.modules_to_save,
lora_alpha=model_args.lora_alpha,
lora_dropout=model_args.lora_dropout
)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
return model
def save_merged_model(model_args: DecoderOnlyEmbedderModelArguments, output_dir: str):
"""
Loads a model with specified configurations, merges it with PEFT layers if available.
Args:
model_args (DecoderOnlyEmbedderModelArguments): Model arguments instance.
output_dir (str): Directory to save the model.
"""
if model_args.config_name:
config = AutoConfig.from_pretrained(
model_args.config_name,
token=model_args.token,
cache_dir=model_args.cache_dir,
trust_remote_code=model_args.trust_remote_code,
)
elif model_args.model_name_or_path:
config = AutoConfig.from_pretrained(
model_args.model_name_or_path,
token=model_args.token,
cache_dir=model_args.cache_dir,
trust_remote_code=model_args.trust_remote_code,
)
else:
raise ValueError(
"You are instantiating a new config instance from scratch. This is not supported by this script."
)
config.use_cache = False
if model_args.model_name_or_path:
model = AutoModel.from_pretrained(
model_args.model_name_or_path,
# torch_dtype=torch.bfloat16,
attn_implementation = "flash_attention_2" if model_args.use_flash_attn else None,
token=model_args.token,
cache_dir=model_args.cache_dir,
from_tf=bool(".ckpt" in model_args.model_name_or_path),
config=config,
trust_remote_code=model_args.trust_remote_code,
)
else:
model = model_args.from_config(config)
if model_args.raw_peft is not None:
model.set_input_embeddings(torch.load(os.path.join(model_args.raw_peft, 'embedding', 'emb.pth')))
model = PeftModel.from_pretrained(model, model_args.raw_peft)
model = model.merge_and_unload()
if os.path.exists(os.path.join(output_dir, 'embedding', 'emb.pth')):
model.set_input_embeddings(torch.load(os.path.join(output_dir, 'embedding', 'emb.pth')))
# modify the vocab size in the model configuration
model.config.vocab_size = len(tokenizer)
try:
model = PeftModel.from_pretrained(model, output_dir)
model = model.merge_and_unload()
except:
model = PeftModel.from_pretrained(model, find_largest_checkpoint(output_dir))
model = model.merge_and_unload()
tokenizer = AutoTokenizer.from_pretrained(output_dir, trust_remote_code=model_args.trust_remote_code)
tokenizer.save_pretrained(os.path.join(output_dir, 'merged_model'))
model.save_pretrained(os.path.join(output_dir, 'merged_model'))
@@ -0,0 +1,207 @@
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 BiDecoderOnlyEmbedderModel(AbsEmbedderModel):
"""Embedder model class for decoder 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 ``'last_token'``.
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 = 'last_token',
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,134 @@
import logging
from typing import Tuple
from pathlib import Path
from transformers import AutoConfig, AutoTokenizer, PreTrainedTokenizer
from FlagEmbedding.abc.finetune.embedder.AbsArguments import AbsEmbedderDataArguments, AbsEmbedderTrainingArguments
from FlagEmbedding.abc.finetune.embedder import AbsEmbedderRunner, AbsEmbedderModel, EmbedderTrainerCallbackForDataRefresh
from .arguments import DecoderOnlyEmbedderModelArguments
from .trainer import DecoderOnlyEmbedderTrainer
from .modeling import BiDecoderOnlyEmbedderModel
from .load_model import get_model, save_merged_model
logger = logging.getLogger(__name__)
class DecoderOnlyEmbedderRunner(AbsEmbedderRunner):
"""Runner class for decoder only embedding model.
Args:
model_args (DecoderOnlyEmbedderModelArguments): Model arguments instance.
data_args (AbsEmbedderDataArguments): Data arguments instance.
training_args (AbsEmbedderTrainingArguments): Trainer arguments.
"""
def __init__(
self,
model_args: DecoderOnlyEmbedderModelArguments,
data_args: AbsEmbedderDataArguments,
training_args: AbsEmbedderTrainingArguments
):
super().__init__(model_args, data_args, training_args)
self.model_args: DecoderOnlyEmbedderModelArguments
self.data_args: AbsEmbedderDataArguments
self.training_args: AbsEmbedderTrainingArguments
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.tokenizer_name if self.model_args.tokenizer_name else self.model_args.model_name_or_path,
token=self.model_args.token,
cache_dir=self.model_args.cache_dir,
use_fast=self.model_args.use_fast_tokenizer,
add_eos_token=True,
trust_remote_code=self.model_args.trust_remote_code,
)
if tokenizer.pad_token is None:
if tokenizer.unk_token is not None:
tokenizer.pad_token = tokenizer.unk_token
tokenizer.pad_token_id = tokenizer.unk_token_id
else:
tokenizer.pad_token = tokenizer.eos_token
tokenizer.pad_token_id = tokenizer.eos_token_id
tokenizer.padding_side = 'left'
resize = False
if self.model_args.additional_special_tokens is not None:
special_tokens_dict = {'additional_special_tokens': self.model_args.additional_special_tokens}
add_num = tokenizer.add_special_tokens(special_tokens_dict)
if add_num > 0:
resize = True
logger.info(f"Add {add_num} special tokens to the tokenizer. Special tokens: {self.model_args.additional_special_tokens}")
else:
logger.warning(f"Special tokens {self.model_args.additional_special_tokens} already exists in the tokenizer.")
base_model = get_model(self.model_args, self.training_args.output_dir, resize, len(tokenizer))
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 = BiDecoderOnlyEmbedderModel(
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) -> DecoderOnlyEmbedderTrainer:
"""Load the trainer.
Returns:
DecoderOnlyEmbedderTrainer: Loaded trainer instance.
"""
trainer = DecoderOnlyEmbedderTrainer(
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
def run(self):
"""
Run the finetune.
"""
if not self.model_args.only_merge_lora_model:
Path(self.training_args.output_dir).mkdir(parents=True, exist_ok=True)
# Training
self.trainer.train(resume_from_checkpoint=self.training_args.resume_from_checkpoint)
self.trainer.save_model()
# save merged model
if self.model_args.save_merged_lora_model and self.training_args.process_index == 0:
save_merged_model(self.model_args, self.training_args.output_dir)
@@ -0,0 +1,45 @@
import os
import torch
import logging
from typing import Optional
from FlagEmbedding.abc.finetune.embedder import AbsEmbedderTrainer
logger = logging.getLogger(__name__)
class DecoderOnlyEmbedderTrainer(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)