改进RAG准确率:微调领域知识嵌入模型(第1部分)

2025年02月28日 由 alex 发表 4444 0

本文探讨了如何通过优化嵌入来提高检索增强生成 (RAG) 系统的准确性。它涵盖了嵌入的基础知识,以及在特定领域的数据集上微调嵌入以增强相关性的好处。我们可以概括为,通过微调嵌入模型可以实现更高效、更准确的 RAG 应用程序,从而提高检索和生成性能。


1


简介

嵌入是数据的数值表示形式,通常指单词、短语甚至整篇文档,它们被映射到一个连续的向量空间中。这些向量基于上下文相似性捕捉单词之间的语义关系,使得相似的概念在嵌入空间中位置更接近。在自然语言处理(NLP)中,嵌入有助于将原始文本转换为机器学习模型(包括大型语言模型(LLM))能够高效处理的格式。流行的嵌入技术包括Word2Vec、GloVe以及基于transformer的上下文嵌入,如BERT或OpenAI模型生成的嵌入。


嵌入在LLM中的重要性

嵌入对于LLM至关重要,因为它们是理解和生成类似人类文本的基础。由于LLM不处理原始单词,而是依赖于数值表示,嵌入使它们能够学习复杂关系、捕捉上下文含义并在不同任务中泛化知识。先进的基于transformer的模型生成动态嵌入,这意味着同一个单词可以根据其周围上下文具有不同的向量表示。这种能力使LLM能够理解细微差别、消除歧义并产生更连贯和相关的响应。


嵌入在检索增强生成(RAG)中的作用

在检索增强生成(RAG)中,嵌入在检索相关信息以增强模型输出方面发挥着关键作用。RAG将预训练的LLM与外部知识源相结合,使用嵌入从大型数据库中高效搜索相关文档或段落。当输入查询时,它首先被转换为嵌入,然后与存储的文档嵌入集合进行比较,使用相似性搜索技术,如余弦相似性或近似最近邻(ANN)方法。检索到的文档在语义上与查询相关,然后被反馈到LLM中以生成有根据的响应。这种方法使LLM能够克服知识过时等限制,提高其事实准确性,并使响应基于外部来源。


微调阶段:

第一步是选择适当的数据集,该数据集应代表目标领域或任务。高质量和多样化的数据集有助于模型学习能够良好泛化的稳健嵌入。数据集通常由将被转换为嵌入以供进一步处理的文本数据组成。


一旦数据集准备好,下一步是选择合适的预训练模型。预训练模型,如BERT、RoBERTa或句子变换器,通过利用来自大规模文本语料库的先验知识提供了坚实的基础。这些模型已经学习了通用语言表示,可以通过根据新数据调整其嵌入来进一步微调以适应特定应用。


2


损失函数的选择在微调过程中至关重要,因为它决定了模型学习数据中关系的效果。根据具体任务的不同,嵌入模型常用的损失函数包括对比损失、三元组损失和交叉熵损失。例如,对比损失有助于学习相似和不相似的对,而三元组损失则确保相似项目的嵌入保持接近,同时将不相似的项目推开。


微调过程涉及在新数据集上训练模型并更新其参数。这一步需要配置超参数,如学习率、批量大小和优化器设置,以优化性能并防止过拟合。微调有助于模型适应特定领域的语言,捕捉上下文细微差别,并提高其生成相关嵌入的能力。


最后,根据使用场景,使用余弦相似度、检索准确率或聚类性能等指标对微调后的模型进行评估。评估模型可以确保微调过程提高了嵌入的质量,并且模型能够有效区分语义上相似和不相似的输入。如果性能不令人满意,可能需要通过数据集优化、超参数调整或更换损失函数等进一步迭代来改进结果。


实现:


from datasets import load_dataset
ds = load_dataset('vblagoje/PubMedQA_instruction')
df = ds['train'].to_pandas()
df = df[['instruction', 'context']][:10000]


# Find the similarities in the dataset
from sentence_transformers import SentenceTransformer
import numpy as np
# Load an embedding model
model = SentenceTransformer("all-mpnet-base-v2")
# Encode all job descriptions
pubmed_context_embeddings = model.encode(df['context'].to_list())
# compute similarities
similarities = model.similarity(pubmed_context_embeddings, pubmed_context_embeddings)


# match least context with least similar to positive match as the negative match
# get sorted indexes of simiarlities
similarities_argsorted = np.argsort(similarities.numpy(), axis=1)
# initialize list to store negative pairs
negative_pair_index_list = []
for i in range(len(similarities)):
    # Start with the smallest similarity index for the current row
    j = 0
    index = int(similarities_argsorted[i][j])
    # Ensure the index is unique
    while index in negative_pair_index_list:
        j += 1  # Move to the next smallest index
        index = int(similarities_argsorted[i][j])  # Fetch next smallest index
    negative_pair_index_list.append(index)
# add negative pairs to df
df['context_neg'] = df['context'].iloc[negative_pair_index_list].values


上述代码片段的总结:

  1. 加载数据 → 从PubMedQA中检索上下文。
  2. 计算句子嵌入 → 将上下文转换为数值向量。
  3. 计算相似度 → 衡量每个上下文与其他上下文的相似程度。
  4. 查找最不相似的对 → 为每个上下文分配一个负匹配(最不相似的上下文)。
  5. 更新数据框 → 添加context_neg列。


# Prepare the dataset for our finetuning
# Shuffle the dataset
df = df.sample(frac=1, random_state=42).reset_index(drop=True)
# Split into train, validation, and test sets (e.g., 80% train, 10% validation, 10% test)
train_frac = 0.8
valid_frac = 0.1
test_frac = 0.1
# define train and validation size
train_size = int(train_frac * len(df))
valid_size = int(valid_frac * len(df))
# create train, validation, and test datasets
df_train = df[:train_size]
df_valid = df[train_size:train_size + valid_size]
df_test = df[train_size + valid_size:]
from datasets import DatasetDict, Dataset
# Convert the pandas DataFrames back to Hugging Face Datasets
train_ds = Dataset.from_pandas(df_train)
valid_ds = Dataset.from_pandas(df_valid)
test_ds = Dataset.from_pandas(df_test)
# Combine into a DatasetDict
dataset_dict = DatasetDict({
    'train': train_ds,
    'validation': valid_ds,
    'test': test_ds
})
# push data to hub
dataset_dict.push_to_hub("pavanmantha/pumed-finetuning", token='<your hf token>')


from sentence_transformers import SentenceTransformer
from sentence_transformers.evaluation import TripletEvaluator
dataset_label = 'pavanmantha/pumed-finetuning'
# importing data
dataset = load_dataset(dataset_label)

# import model
model_name = "sentence-transformers/all-distilroberta-v1"
model = SentenceTransformer(model_name)
# create evaluator
evaluator_valid = TripletEvaluator(
    anchors=dataset["validation"]["instruction"],
    positives=dataset["validation"]["context"],
    negatives=dataset["validation"]["context_neg"],
    name="ai-pubmed-validation",
)
evaluator_valid(model)


上述代码片段的总结:

  1. 打乱并分割数据 → 80%训练集,10%验证集,10%测试集。
  2. 将数据转换为Hugging Face格式 → DatasetDict。
  3. 将数据集上传到Hugging Face Hub → 可共享且可重用。
  4. 加载句子转换器模型 → all-distilroberta-v1。
  5. 使用三元组损失评估模型 → 检查排序质量。


from sentence_transformers.losses import MultipleNegativesRankingLoss
from sentence_transformers import SentenceTransformerTrainingArguments
loss = MultipleNegativesRankingLoss(model)
num_epochs = 1
batch_size = 16
lr = 2e-5
finetuned_model_name = "distilroberta-pubmed-embeddings"
train_args = SentenceTransformerTrainingArguments(
    output_dir=f"models/{finetuned_model_name}",
    num_train_epochs=num_epochs,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    learning_rate=lr,
    warmup_ratio=0.1,
    batch_sampler="no_duplicates",  # MultipleNegativesRankingLoss benefits from no duplicate samples in a batch
    eval_strategy="steps",
    eval_steps=100,
    logging_steps=100,
)


from sentence_transformers import SentenceTransformerTrainer
trainer = SentenceTransformerTrainer(
    model=model,
    args=train_args,
    train_dataset=dataset["train"],
    eval_dataset=dataset["validation"],
    loss=loss,
    evaluator=evaluator_valid,
)
trainer.train()


# Validate the newly created model
# import model
model = SentenceTransformer("pavanmantha/distilroberta-pubmed-embeddings")
# new query
query = "Does age significantly influence how growth hormone responds to brief, high-intensity exercise among healthy young men of normal weight?"
query_embedding = model.encode(query)
# encode context
test_pubmed__embeddings = model.encode(dataset["test"]["context"])
# compute similarities
similarities = model.similarity(query_embedding, test_pubmed__embeddings)


上述代码片段的总结:

  1. 设置微调环境 → 使用预训练模型和多重负样本排序损失
  2. 配置训练参数 → 1个训练周期,批量大小为16,学习率为2e-5
  3. 初始化并执行训练 → 使用训练数据集进行微调,并定期验证
  4. 加载并测试微调后的模型 → 使用新模型对生物医学查询进行编码,并与测试上下文进行比较


结论:

微调嵌入模型在特定领域的自然语言处理(NLP)应用中具有显著优势,能够提高准确性、相关性和整体性能。虽然预训练模型提供了坚实的基础,但它们往往在处理专业术语、任务特定细微差别以及区分微妙语义差异方面存在困难。


通过微调,我们可以使嵌入适应我们的数据集,从而确保为搜索、检索、分类和推荐系统提供更好的文本表示。它还增强了负采样的有效性,使模型在排名相关结果时更加精确,优于不相关的结果。此外,与从头开始训练相比,微调需要的标注数据更少,使其成为医学、法律、金融和客户支持等实际NLP应用中的一种高效方法。


最终,微调将通用语言模型转变为功能强大、任务优化的AI工具,充分释放其解决复杂和特定领域挑战的潜力。

文章来源:https://medium.com/@manthapavankumar11/boosting-rag-accuracy-part1-the-role-of-fine-tuning-an-embedding-model-for-domain-knowledge-279261b1bb22
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消