README.md 9.95 KB
Newer Older
Rayyyyy's avatar
Rayyyyy committed
1
# Finetune
2
在本例中,我们将展示如何使用您的数据对baai-general-embedding进行微调。
Rayyyyy's avatar
Rayyyyy committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

## 环境配置
参考[环境配置](../../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
101
from LM_Cocktail.LM_Cocktail import mix_models, mix_models_with_data
Rayyyyy's avatar
Rayyyyy committed
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

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
```