Embedding 模型的选择和微调

目录

引言

向量模型在 RAG 系统中的作用

1. 对 query 和 私域知识 进行向量化表示

2. 动态更新知识库

3. 数据隐私和安全

有哪些性能不错的向量模型

OpenAI Embedding

JinaAI Embedding

BAAI/bge Embedding

模型评测

MTEB 排行榜:https://huggingface.co/spaces/mteb/leaderboard

如何 Finetune 向量模型

1. 安装 FlagEmbedding

2. 数据准备

3. Hard Negatives 挖掘(可选)

4. 训练

5. 模型合并(可选)


引言

万物皆可 Embedding。Embedding 用一个多维稠密向量来表示事物的多维特征,从而在一个连续的向量空间中刻画事物之间的相似性和差异性。这种表示方式不仅提高了计算效率,还增强了模型对数据内在结构和关系的理解能力。

向量模型在 RAG 系统中的作用

1. 对 query 和 私域知识 进行向量化表示

2. 动态更新知识库

3. 数据隐私和安全

Embedding 在 RAG 系统中扮演着至关重要的角色:如果 Embedding 模型在对私域知识进行向量化表示的过程中表现不佳,那么即使 RAG 系统在其他方面设计得当,最终效果也难以达到预期水平 。

为了让私域知识能在问答中被检索到,我们可以基于倒排和基于向量的方式构建知识库索引。倒排索引是一种基于关键词的精确性检索,但语义理解能力弱,而向量索引是基于文本向量的语义检索,可以捕捉文本的语义信息。一般情况下,我们会同时使用这两种检索方式。

有哪些性能不错的向量模型

OpenAI Embedding

JinaAI Embedding

https://huggingface.co/jinaai/jina-embeddings-v2-base-zh

BAAI/bge Embedding

https://huggingface.co/BAAI/bge-large-zh-v1.5

模型评测

面对这么多向量模型,我们如何衡量一种 Embedding 模型相对于其他模型的有效性呢?Hugging Face 推出了 MTEB(Massive Text Embedding Benchmark 大规模文本嵌入基准)测试框架,旨在评估文本 Embedding 模型在多种任务上的性能。它覆盖了 8 类任务和 58 个数据集,涉及 112 种语言,是目前最全面的文本嵌入评估基准之一。MTEB 提供了一个公开的排行榜,用于展示各个模型在不同任务上的表现。

MTEB 排行榜:https://huggingface.co/spaces/mteb/leaderboard

MTEB 包含以下任务类别,每个类别对应不同的评估指标和数据集:

  1. 1. 文本分类(Classification):如情感分析、意图分类等。

  2. 2. 聚类(Clustering):如将相似文本分为同一类。

  3. 3. 成对分类(Pair Classification):判断两个文本是否重复或具有相似含义。

  4. 4. 重排序(Reranking):根据查询重新排序相关和不相关的参考文本。

  5. 5. 检索(Retrieval):从大量文档中找到与查询相关的文档。

  6. 6. 语义文本相似性(STS):评估句子对之间的相似性。

  7. 7. 摘要(Summarization):评估机器生成摘要的质量。

如何 Finetune 向量模型

在特定领域,对向量模型进行 Finetune 的主要目标是提高 Recall@N (前 N 个检索结果中包含相关文档的比例)的准确率和优化正例与负例的 similarity 值域分布

下面,我以 BAAI/bge-large-zh-v1.5 为例,看看如何基于私有领域数据进行 Finetune。

1. 安装 FlagEmbedding

首先,安装 FlagEmbedding 库:

pip install -U FlagEmbedding

2. 数据准备

训练数据是一个 json 文件,其中每一行都是一个独立的 json 对象,如下所示:

{"query": "如何提高机器学习模型的准确性?", "pos": ["通过交叉验证和调参可以提高模型准确性。"], "neg": ["机器学习是人工智能的一个分支。"]}
{"query": "什么是深度学习?", "pos": ["深度学习是机器学习的一个子领域,涉及多层神经网络。"], "neg": ["数据科学是一门交叉学科。"]}

其中,query 是问题,pos 是正样本列表,neg 是负样本列表,如果没有现成的负样本,可以考虑从整个语料库中随机抽取一些文本作为 neg。

将数据保存为 jsonl 文件,例如 finetune_data.jsonl 。

3. Hard Negatives 挖掘(可选)

Hard Negatives 是指那些在向量空间中与查询较为接近但实际上并不相关的样本。挖掘这些样本可以提高模型的辨别能力,提供 Embedding 质量。具体方法可以参考以下代码:

python -m FlagEmbedding.baai_general_embedding.finetune.hn_mine \
--model_name_or_path BAAI/bge-large-zh-v1.5 \
--input_file finetune_data.jsonl \
--output_file finetune_data_minedHN.jsonl \
--range_for_sampling 2-200 \
--negative_number 15

其中,range_for_sampling 表示从哪些文档采样,例如 2-200 表示从 top2-top200 文档中采样 negative_number 个负样本 。

4. 训练

微调 Embedding 模型的命令如下:

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 ./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 "" 

以上命令指定了训练参数,包括学习率、批次大小、训练轮次等,需要根据实际情况进行调整。

5. 模型合并(可选)

对通用模型进行微调可以提高其在目标任务上的性能,但可能会导致模型在目标域之外的一般能力退化。通过合并微调模型和通用模型,不仅可以提高下游任务的性能,同时保持其他不相关任务的性能。

为了将微调后的模型和原来的 bge 模型进行合并,我们需要先安装 LM_Cocktail,如下所示:

pip install -U LM_Cocktail

合并代码参考如下:

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-zh-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_embedding_model')

拓展阅读

  • • https://github.com/FlagOpen/FlagEmbedding/tree/master/examples/finetune

  • • https://github.com/FlagOpen/FlagEmbedding/tree/master/LM_Cocktail

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是用于微调pkuseg模型的train函数的源代码: ```python import os import time import torch import argparse from tqdm import tqdm from pkuseg.models import BiLSTM_CRF from pkuseg.utils import load_model, load_data, batch_yield, evaluate def train(args): # 加载训练数据和字典 train_data, word2id, tag2id = load_data(args.data_dir, data_type='train') # 加载验证数据 dev_data = load_data(args.data_dir, data_type='dev', word2id=word2id, tag2id=tag2id) # 创建模型 model = BiLSTM_CRF(len(word2id), len(tag2id), args.embed_size, args.hidden_size, dropout=args.dropout, init_embedding=None) # 加载预训练的模型 if args.pretrained_model_path: print('Loading pretrained model from {}'.format(args.pretrained_model_path)) model = load_model(model, args.pretrained_model_path) # 将模型移动到GPU上 if torch.cuda.is_available(): model.cuda() # 设置优化器和学习率 optimizer = torch.optim.Adam(model.parameters(), lr=args.lr) # 开始训练 print('Start training...') best_dev_f1 = 0. for epoch in range(args.num_epochs): start_time = time.time() model.train() total_loss = 0. for i, (words, tags) in tqdm(enumerate(batch_yield(train_data, args.batch_size, word2id, tag2id))): # 将数据移动到GPU上 if torch.cuda.is_available(): words = words.cuda() tags = tags.cuda() # 前向传播和计算损失 loss = model.neg_log_likelihood(words, tags) total_loss += loss.item() # 反向传播和更新参数 optimizer.zero_grad() loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), args.clip_grad) optimizer.step() # 计算验证集的F1值 dev_f1 = evaluate(model, dev_data, word2id, tag2id, batch_size=args.batch_size) # 保存最好的模型 if dev_f1 > best_dev_f1: best_dev_f1 = dev_f1 if not os.path.exists(args.save_dir): os.makedirs(args.save_dir) torch.save(model.state_dict(), os.path.join(args.save_dir, 'best_model.pth')) # 打印训练结果 print('Epoch: {}, Loss: {:.4f}, Dev F1: {:.4f}, Time: {:.2f}s'.format(epoch+1, total_loss, dev_f1, time.time()-start_time)) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_dir', type=str, default='./data', help='Directory of training data.') parser.add_argument('--pretrained_model_path', type=str, default=None, help='Path of pretrained model.') parser.add_argument('--save_dir', type=str, default='./models', help='Directory to save the trained model.') parser.add_argument('--embed_size', type=int, default=100, help='Dimension of word embedding.') parser.add_argument('--hidden_size', type=int, default=128, help='Dimension of hidden layer.') parser.add_argument('--dropout', type=float, default=0.5, help='Dropout rate.') parser.add_argument('--clip_grad', type=float, default=5.0, help='Clip gradient.') parser.add_argument('--lr', type=float, default=0.001, help='Learning rate.') parser.add_argument('--batch_size', type=int, default=64, help='Batch size.') parser.add_argument('--num_epochs', type=int, default=10, help='Number of epochs.') args = parser.parse_args() train(args) ``` 这个函数的作用是用于在训练数据上微调pkuseg模型。它首先加载训练数据和字典,然后创建一个BiLSTM_CRF模型。如果指定了预训练的模型,则加载预训练的模型。接下来,设置优化器和学习率,并开始训练。在每个epoch中,模型进行前向传播和计算损失、反向传播和更新参数。在每个epoch结束时,计算验证集的F1值并保存最好的模型。最后,输出训练结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值