# Finetune In this example, we show how to finetune the baai-general-embedding with your data. ## 环境配置 参考[环境配置](../../README.md#环境配置) ## 数据集 ``` ├── finetune │ ├── toy_evaluation_data │ └── toy_finetune_data.jsonl ``` 训练数据是一个json文件,其中每一行都是这样的字典: ``` {"query": str, "pos": List[str], "neg":List[str]} ``` `query` 是查询语句, `pos` 是正文本list, `neg` 是负文本list。 如果针对查询语句,你没有负文本,你可以随机从整个语料库中选取样本作为负样本。如[toy_finetune_data.jsonl](./toy_finetune_data.jsonl) ## 微调 ### Hard Negatives Hard negatives是一种广泛使用的提高句子嵌入质量的方法,使用方法如下: ```bash python -m FlagEmbedding.baai_general_embedding.finetune.hn_mine \ --model_name_or_path BAAI/bge-base-en-v1.5 \ --input_file toy_finetune_data.jsonl \ --output_file toy_finetune_data_minedHN.jsonl \ --range_for_sampling 2-200 \ --negative_number 15 \ --use_gpu_for_searching ``` 主要参数解释如下: - `input_file`: 用于微调的json数据。该脚本将为每个query检索前k个文档,并从前k个文档中随机采样负样本(不包括肯定文档)。 - `output_file`: 保存微调所需的带有mined hard negatives的JSON数据保存地址 - `Negative_number`: 采样的负样本个数 - `Range_for_sampling`: 在哪里进行负样本采样。例如,m `2-100`表示从top2-top200个文档中抽样`negative_number`个负样本。**您可以设置更大的值来降低负样本的难度(例如,将其设置为`60-300`来从top60-300篇文章中取样本)**。 - `candidate_pool`: 要检索的池。默认值为None,该脚本将从`input_file`中的所有`neg`的组合中检索。该文件的格式与[预训练数据](./examples/pretrain#2-data-format)相同。如果输入一个candidate_pool,该脚本将从该文件中检索负样本。 - `use_gpu_for_searching`: 是否使用faiss-gpu检索负样本。 ### 微调方法 ```bash torchrun --nproc_per_node {number of gpus} \ -m FlagEmbedding.baai_general_embedding.finetune.run \ --output_dir {path to save model} \ --model_name_or_path BAAI/bge-large-zh-v1.5 \ --train_data ./toy_finetune_data.jsonl \ --learning_rate 1e-5 \ --fp16 \ --num_train_epochs 5 \ --per_device_train_batch_size {large batch size; set 1 for toy data} \ --dataloader_drop_last True \ --normlized True \ --temperature 0.02 \ --query_max_len 64 \ --passage_max_len 256 \ --train_group_size 2 \ --negatives_cross_device \ --logging_steps 10 \ --save_steps 1000 \ --query_instruction_for_retrieval "" ``` **关键参数解释**: - `per_device_train_batch_size`: 训练的batch size。在大量的案例中,batch size越大,模型效果越好。您还可以通过增加`--fp16`, `--deepspeed ./df_config.json` (df_config.json 请参考 [ds_config.json](./ds_config.json)), `--gradient_checkpointing`等参数来扩大batch size大小。 - `train_group_size`: 训练中查询语句的的正负样本数。总是有一个正样本,所以这个参数将控制负样本的数量(#negatives=train_group_size-1)。注意,负样本设置的数量大小不可以超过数据`"neg":List[str]`的大小。除了这组的负样本,批量的负样本也会用于微调中。 - `negatives_cross_device`: 在所有gpu上共享负样本。这个参数将会扩大负样本的数量。 - `learning_rate`: 学习率。为您的模型选择一个合适的大小如:1e-5/2e-5/3e-5 分别对应模型的 large/base/small 尺寸。 - `temperature`: 影响相似度得分分布的参数。 **建议大小: 0.01-0.1.** - `query_max_len`: 查询语句的最长长度。请根据您的数据中查询语句的平均长度来设置。 - `passage_max_len`:通道的最长长度。请根据您的数据中通道的平均长度来设置。 - `query_instruction_for_retrieval`: 每个查询语句的指令,你也可以设置成 `""`。 - `use_inbatch_neg`: 使用同一批中的通道作为否定。默认值是True。 - `save_steps`: 设定多少步保存模型。 更多的参数解释可以参考[transformers.TrainingArguments](https://huggingface.co/docs/transformers/main_classes/trainer#transformers.TrainingArguments) ### 通过[LM-Cocktail](../../LM_Cocktail)完成模型融合[optional] 更多信息可参考[LM-Cocktail](https://github.com/FlagOpen/FlagEmbedding/tree/master/LM_Cocktail). 对基本模型进行微调可以提高其在目标任务上的性能,但超出目标域也可能导致模型综合性能的严重退化(例如,c-mteb任务的性能较低)。 通过合并微调模型和基本模型,LM-Cocktail可以显著提高下游任务的效果,同时在其他不相关的任务中保持性能。 ```python from LM_Cocktail import mix_models, mix_models_with_data # Mix fine-tuned model and base model; then save it to output_path: ./mixed_model_1 model = mix_models( model_names_or_paths=["BAAI/bge-large-en-v1.5", "your_fine-tuned_model"], model_type='encoder', weights=[0.5, 0.5], # you can change the weights to get a better trade-off. output_path='./mixed_model_1') ``` 如果你有一个新任务,并且当前没有任何数据或者资源可以用于微调,你可以试试使用`LM-Cocktail`将已有的模型进行合并(从开源社区或者其他任务上自有微调的模型)来完成一个特殊任务的模型。 使用这个方法,你只需要构造一些示例数据,并不需要去微调一个基础模型。比如,你可以合并来自[huggingface](https://huggingface.co/Shitao)上的模型,使用针对你的任务的示例数据。使用代码如下: ```python from LM_Cocktail import mix_models, mix_models_with_data example_data = [ {"query": "How does one become an actor in the Telugu Film Industry?", "pos": [" How do I become an actor in Telugu film industry?"], "neg": [" What is the story of Moses and Ramesses?", " Does caste system affect economic growth of India?"]}, {"query": "Why do some computer programmers develop amazing software or new concepts, while some are stuck with basic programming work?", "pos": [" Why do some computer programmers develops amazing softwares or new concepts, while some are stuck with basics programming works?"], "neg": [" When visiting a friend, do you ever think about what would happen if you did something wildly inappropriate like punch them or destroy their furniture?", " What is the difference between a compliment and flirting?"]} ] model = mix_models_with_data( model_names_or_paths=["BAAI/bge-base-en-v1.5", "Shitao/bge-hotpotqa", "Shitao/bge-quora"], model_type='encoder', example_ata=example_data, temperature=5.0, max_input_length=512, neg_number=2) ``` **尽管[这里](https://huggingface.co/Shitao)只有9个`bge-*`模型, 当您的任务与所有9个微调任务不同时,效果可能不令人满意,你可以在基础模型智商微调更多的任务,然后将他们合并使其在你的任务上达到更好的表现。** ### 加载自己的模型 微调BGE模型之后,你可以参考[here](https://github.com/FlagOpen/FlagEmbedding/tree/master/FlagEmbedding/baai_general_embedding#usage),使用同样的方法轻松的加载它。 如果在微调过程中,你将超参数`--query_instruction_for_retrieval` 设置成了不一样的值,请将参数`query_instruction_for_retrieval`替换成你设置的值。 ### 验证 项目提供了[简单脚本](https://github.com/FlagOpen/FlagEmbedding/tree/master/FlagEmbedding/baai_general_embedding/finetune/eval_msmarco.py)去验证模型的效果 脚本的工作原理概述如下: 1. 通过[DataParallel](https://pytorch.org/docs/stable/generated/torch.nn.DataParallel.html)在所有可用显卡上加载模型; 2. 对语料库进行编码,并将嵌入数据卸载到`faiss` 平面索引中。`faiss`会默认将索引加载到所有可用的 GPU 上。 3. 对查询语句进行编码,为每个查询语句搜索最接近的 `100` 条邻居。 4. 计算召回和MRR指标。 #### MSMARCO 数据集 MSMARCO是默认的验证数据集,它是一个使用很广的检索基准。 你可以通过[msmarco corpus](https://huggingface.co/datasets/namespace-Pt/msmarco-corpus) 和 [evaluation queries](https://huggingface.co/datasets/namespace-Pt/msmarco) 检查数据格式。 执行脚本: ``` python -m FlagEmbedding.baai_general_embedding.finetune.eval_msmarco \ --encoder BAAI/bge-base-en-v1.5 \ --fp16 \ --add_instruction \ --k 100 ``` **一些主要参数解释** - `encoder`: 特殊的encoder模型, 可以是huggingface或者本地模型。 - `fp16`: 使用半精进行推理。 - `add_instruction`: 添加检索指令,如`Represent this sentence for searching relevant passages: `. - `k`: 指定为每个查询检索多少个最接近的答案。 结果示例: ```python { 'MRR@1': 0.2330945558739255, 'MRR@10': 0.35786976395142633, 'MRR@100': 0.3692618036917553, 'Recall@1': 0.22606255969436478, 'Recall@10': 0.6412965616045848, 'Recall@100': 0.9012774594078318 } ``` #### 自己的数据集 需要准备两个`jsonl`格式文件 - 一个是包含你想检索的文档信息的corpus_data, 如示例[toy_corpus.json](./toy_evaluation_data/toy_corpus.json) ``` {"content": "A is ..."} {"content": "B is ..."} {"content": "C is ..."} {"content": "Panda is ..."} {"content": "... is A"} ``` - 另一个是包含检索和ground truth的query_data, 如示例[toy_corpus.json](./toy_evaluation_data/toy_query.json) ``` {"query": "What is A?", "positive": ["A is ...", "... is A"]} {"query": "What is B?", "positive": ["B is ..."]} {"query": "What is C?", "positive": ["C is ..."]} ``` 修改脚本中的两个数据地址,执行: ```bash python -m FlagEmbedding.baai_general_embedding.finetune.eval_msmarco \ --encoder BAAI/bge-base-en-v1.5 \ --fp16 \ --add_instruction \ --k 100 \ --corpus_data ./toy_evaluation_data/toy_corpus.json \ --query_data ./toy_evaluation_data/toy_query.json ```