212 lines
11 KiB
Markdown
212 lines
11 KiB
Markdown
# Embedding训练
|
||
|
||
SWIFT已经支持Embedding模型的训练,包括纯文本和多模态两个类型。目前已经支持的模型有:
|
||
|
||
1. modernbert embedding模型
|
||
- [ModelScope](https://modelscope.cn/models/iic/gte-modernbert-base) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-modernbert-base)
|
||
2. gte embedding模型
|
||
- 1.5B: [ModelScope](https://www.modelscope.cn/models/iic/gte_Qwen2-1.5B-instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-Qwen2-1.5B-instruct)
|
||
- 7B: [ModelScope](https://www.modelscope.cn/models/iic/gte_Qwen2-7B-instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gte-Qwen2-7B-instruct)
|
||
3. gme embedding模型
|
||
- 2B: [ModelScope](https://www.modelscope.cn/models/iic/gme-Qwen2-VL-2B-Instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gme-Qwen2-VL-2B-Instruct)
|
||
- 7B: [ModelScope](https://www.modelscope.cn/models/iic/gme-Qwen2-VL-7B-Instruct) [Hugging Face](https://huggingface.co/Alibaba-NLP/gme-Qwen2-VL-7B-Instruct)
|
||
4. qwen3-embedding模型
|
||
- 0.6B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Embedding-0.6B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Embedding-0.6B)
|
||
- 4B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Embedding-4B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Embedding-4B)
|
||
- 8B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-Embedding-8B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-Embedding-8B)
|
||
5. qwen3-vl-embedding模型
|
||
- 2B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-VL-Embedding-2B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-VL-Embedding-2B)
|
||
- 8B: [ModelScope](https://www.modelscope.cn/models/Qwen/Qwen3-VL-Embedding-8B) [Hugging Face](https://huggingface.co/Qwen/Qwen3-VL-Embedding-8B)
|
||
|
||
|
||
开发者可以自行集成自己的模型,模型forward输出值需要满足:
|
||
|
||
```text
|
||
{"last_hidden_state": some-embedding-tensor}
|
||
```
|
||
|
||
返回值是一个json,具有`last_hidden_state` key,value是embedding tensor即可,输入部分可以使用我们已经支持的template。用户也可以通过指定
|
||
|
||
```shell
|
||
--task_type embedding
|
||
```
|
||
参数来将任意一个其他模型转换为embedding模型进行训练。
|
||
|
||
需要注意的是,SWIFT目前支持的embedding模型均为符合纯文本或多模态LLM,目前并不支持CLIP类型的模型训练。
|
||
|
||
此外,SWIFT支持的所有embedding模型在模型forward最后都增加了normalize,如自行增加新模型请注意增加normalize层。
|
||
|
||
## loss
|
||
|
||
目前SWIFT支持的Embedding模型可以使用的loss有:
|
||
|
||
- cosine_similarity: cosine相似度loss,计算两个embedding的相似度,并根据label的值拟合,实际为MSE loss
|
||
- contrastive: 可调margin的对比学习loss,label仅支持0和1两个值
|
||
- online_contrastive: 考虑hard negative和hard positive部分的contrastive loss,label仅支持0和1两个值
|
||
- infonce: 在同一个batch中不同row两两计算cosine相似度,并使row内部相似度最大,不同row相似度最小,不需要label
|
||
|
||
loss的源代码可以在[这里](https://github.com/modelscope/ms-swift/blob/main/swift/loss/mapping.py)找到。
|
||
|
||
## 数据集格式
|
||
|
||
> 注:
|
||
> 1. `<image>`标签可以出现在`messages`/`positive_messages`/`negative_messages`的任意位置;它们各自拥有独立的`images`/`positive_images`/`negative_images`字段用于提供图片路径或URL。
|
||
> 2. 不再需要跨字段的“对应顺序”。对齐规则为:`images`的长度等于`messages`中`<image>`标签的数量;`positive_images`与`negative_images`均为“list of list”,其外层长度分别等于`positive_messages`与`negative_messages`的长度;并且外层每一项的内层列表长度等于该条消息序列中`<image>`标签的数量。
|
||
> 3. `messages`代表anchor样本(anchor sample);`positive_messages`/`negative_messages`为“list of messages”(因此多一层`[]`);相应地,`positive_images`/`negative_images`也多一层`[]`并与之逐项对齐。
|
||
> 4. 也支持`<video>`, `<audio>`标签;可按相同规则分别通过`videos`/`positive_videos`/`negative_videos`与`audios`/`positive_audios`/`negative_audios`提供对应模态数据。
|
||
> 5. 当前约束:`positive_messages`的外层长度必须为1(即仅提供一个positive样本);对应地,`positive_images`的外层长度也必须为1。
|
||
|
||
### cosine_similarity loss对应的格式
|
||
|
||
```json lines
|
||
# LLM
|
||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "sentence2"}]], "label": 0.8}
|
||
# MLLM
|
||
{"messages": [{"role": "user", "content": "<image>"}], "images": ["/some/images1.jpg"],"positive_messages": [[{"role": "user", "content": "<image>sentence"}]], "positive_images": [["/some/images2.jpg"]], "label": 0.7}
|
||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "<image>sentence2"}]], "positive_images": [["/some/images.jpg"]], "label": 0.7}
|
||
```
|
||
|
||
|
||
### contrastive/online_contrastive loss对应的格式
|
||
|
||
```json lines
|
||
# LLM
|
||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "sentence2"}]], "label": 1}
|
||
# MLLM
|
||
{"messages": [{"role": "user", "content": "<image>"}], "images": ["/some/images1.jpg"], "positive_messages": [[{"role": "user", "content": "<image>sentence"}]], "positive_images": [["/some/images2.jpg"]], "label": 1}
|
||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "<image>sentence2"}]], "positive_images": [["/some/images.jpg"]], "label": 0}
|
||
```
|
||
|
||
评测的指标分别是两个embedding的欧式距离、点积等的pearson系数以及spearman系数,共八个指标。
|
||
|
||
### infonce 格式
|
||
|
||
```json lines
|
||
# LLM
|
||
{"messages": [{"role": "user", "content": "sentence1"}], "positive_messages": [[{"role": "user", "content": "sentence2"}]]}
|
||
# MLLM
|
||
{"messages": [{"role": "user", "content": "<image>"}], "images": ["/some/images.jpg"], "positive_messages": [[{"role": "user", "content": "sentence"}]]}
|
||
{"messages": [{"role": "user", "content": "<image>sentence1"}], "images": ["/some/images.jpg"], "positive_messages": [[{"role": "user", "content": "<image>sentence2"}]], "positive_images": [["/some/positive_images.jpg"]], "negative_messages": [[{"role": "user", "content": "<image><image>sentence3"}], [{"role": "user", "content": "<image>sentence4"}]], "negative_images": [["/some/negative_images1.jpg", "/some/negative_images2.jpg"], ["/some/negative_images3.jpg"]]}
|
||
```
|
||
|
||
infonce loss支持几个环境变量:
|
||
1. `INFONCE_TEMPERATURE`: temperature参数,不设置的话默认值是0.1
|
||
2. `INFONCE_USE_BATCH`: 使用sample内部的`negative_messages`(hard negative样例)还是使用一个batch内其他样本作为in-batch negatives;默认为True,表示使用batch内部的样本作为负例
|
||
3. `INFONCE_HARD_NEGATIVES`: hard negatives的数量;如果不设置会使用数据中提供的所有`negative_messages`。由于长度未必一致,因此会采用for循环计算loss(计算会慢)。若设置为某个数值,则不足会随机采样补齐,超长会选用前`INFONCE_HARD_NEGATIVES`个
|
||
4. `INFONCE_MASK_FAKE_NEGATIVE`: mask掉假negative。默认为False,开启时会判断 `positive_similarity + INFONCE_FAKE_NEG_MARGIN`,比该阈值大的样本相似度会被设置为 `-inf`,以防止正样本泄露问题
|
||
5. `INFONCE_FAKE_NEG_MARGIN`:假负样本屏蔽的边际,默认 `0.1`。
|
||
6. `INFONCE_INCLUDE_QQ`:是否在分母中加入 q–q 分量(query 间相似度)作为负例,默认 `False`。
|
||
7. `INFONCE_INCLUDE_DD`:是否在分母中加入 d–d 分量(正样本文档与 batch 内所有文档的相似度)作为负例,默认 `False`。
|
||
|
||
> 也可以在数据集中将hard negatives数量设置为数量相等,这样即使不设置也不会使用for循环方式,加快计算速度
|
||
> `negative_messages`也可以不提供。在这种情况下,保持`INFONCE_USE_BATCH=True`,会使用一个batch内部的其他样本作为负例
|
||
|
||
infonce loss的评测会有下面几个指标:
|
||
- mean_neg 所有hard_negative的平均值
|
||
- mean_pos 所有positive的平均值
|
||
- margin positive-max_hard_negative的平均值
|
||
|
||
## 训练
|
||
|
||
SWIFT提供的脚手架训练脚本:
|
||
|
||
- [Qwen3-Embedding/Qwen3-VL-Embedding模型](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding/qwen3)
|
||
- [GME模型](https://github.com/modelscope/ms-swift/blob/main/examples/train/embedding/train_gme.sh)
|
||
|
||
## 推理
|
||
|
||
SWIFT已经支持GME、GTE、Qwen3-Embedding模型的部署,请查看[这里](https://github.com/modelscope/ms-swift/blob/main/examples/deploy/embedding/client.py)。
|
||
- 推理脚本参考[这里](https://github.com/modelscope/ms-swift/blob/main/examples/infer/demo_embedding.py)。
|
||
|
||
也可以使用原模型的代码进行推理:
|
||
|
||
https://www.modelscope.cn/models/iic/gte_Qwen2-7B-instruct
|
||
|
||
https://www.modelscope.cn/models/iic/gme-Qwen2-VL-7B-Instruct
|
||
|
||
如果使用了其他模型从0训练embedding(例如,原版`qwen2-vl`模型+`--task_type embedding`),也可以使用gme的推理代码,但请注意:
|
||
|
||
https://www.modelscope.cn/models/iic/gme-Qwen2-VL-7B-Instruct/file/view/master/gme_inference.py?status=1#L111
|
||
|
||
这里的模板请修改为模型自身的template,以免最后的embedding对不上。需要额外注意的是,gme模型的template和`qwen2-vl`或`qwen2.5-vl`系列的chatml template并不相同,其推理代码最后的结束字符是`<|endoftext|>`而非`<|im_end|>`.
|
||
|
||
## 高级功能
|
||
|
||
- Qwen3-Embedding 自定义 Instruction:
|
||
- 默认无 Instruction,输入模板为:`{Query}<|endoftext|>`。
|
||
- 通过在 system message 中添加 Instruction,可将输入改为:`{Instruction} {Query}<|endoftext|>`。
|
||
- 示例:
|
||
|
||
```json lines
|
||
{"messages": [
|
||
{"role": "system", "content": "请用中文回答,并输出简洁要点"},
|
||
{"role": "user", "content": "介绍一下Qwen3-Embedding"}
|
||
]}
|
||
```
|
||
|
||
> 说明:Qwen3-Embedding 模板会将 system 内容前置拼接到首条 user 消息中,并使用 `<|endoftext|>` 作为结束标记。
|
||
|
||
### 转换前后示例
|
||
|
||
- 不加 Instruction:
|
||
|
||
输入数据(messages):
|
||
|
||
```json lines
|
||
{"messages": [
|
||
{"role": "user", "content": "北京明天天气如何?"}
|
||
]}
|
||
```
|
||
|
||
模板转换后(送入模型的实际文本):
|
||
|
||
```text
|
||
北京明天天气如何?<|endoftext|>
|
||
```
|
||
|
||
- 加 Instruction:
|
||
|
||
输入数据(messages,包含system):
|
||
|
||
```json lines
|
||
{"messages": [
|
||
{"role": "system", "content": "请使用中文、精炼输出要点"},
|
||
{"role": "user", "content": "北京明天天气如何?"}
|
||
]}
|
||
```
|
||
|
||
模板转换后(送入模型的实际文本):
|
||
|
||
```text
|
||
请使用中文、精炼输出要点 北京明天天气如何?<|endoftext|>
|
||
```
|
||
|
||
- positive/negative 同理:
|
||
|
||
若在某个 positive/negative 的消息序列中提供 system,则会将该 system 内容前置到该序列首条 user 内容之前;未提供 system 则不前置。
|
||
|
||
输入数据(包含一个 positive 带 system,和一个 negative 无 system):
|
||
|
||
```json lines
|
||
{
|
||
"messages": [
|
||
{"role": "user", "content": "Anchor"}
|
||
],
|
||
"positive_messages": [[
|
||
{"role": "system", "content": "指令"},
|
||
{"role": "user", "content": "Positive"}
|
||
]],
|
||
"negative_messages": [[
|
||
{"role": "user", "content": "Negative"}
|
||
]]
|
||
}
|
||
```
|
||
|
||
模板转换后(送入模型的实际文本):
|
||
|
||
```text
|
||
Anchor<|endoftext|>
|
||
指令 Positive<|endoftext|>
|
||
Negative<|endoftext|>
|
||
```
|