【finetuning】交叉编码器微调案例
本案例演示如何使用LlamaIndex微调交叉编码器,以提高检索系统的重排序效果。通过使用QASPER数据集,我们将展示从数据准备、模型微调到评估的完整流程。
1. 案例目标
本案例的主要目标是:
- 学习交叉编码器微调:了解如何使用LlamaIndex对交叉编码器进行微调,以适应特定领域的重排序任务。
- 提高检索效果:通过微调交叉编码器,提升检索系统的重排序能力,从而提高检索结果的准确性。
- 评估微调效果:学习如何使用多种评估方法(如Hits指标、pairwise比较)来量化微调前后模型性能的差异。
2. 技术栈与核心依赖
- LlamaIndex:用于构建检索增强生成(RAG)系统的核心框架
- llama-index-finetuning-cross-encoders:LlamaIndex的交叉编码器微调模块
- sentence-transformers:用于加载和使用预训练的句子嵌入模型
- datasets:Hugging Face数据集库,用于加载和处理QASPER数据集
- OpenAI API:用于生成评估数据
- Hugging Face Hub:用于保存和分享微调后的模型
3. 环境配置
在开始之前,需要安装以下依赖包:
!pip install llama-index-finetuning-cross-encoders !pip install datasets !pip install sentence-transformers !pip install openai !pip install huggingface_hub此外,还需要设置以下环境变量:
import os os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY"4. 案例实现
4.1 数据准备
本案例使用QASPER数据集,这是一个包含学术论文及其相关问答的数据集。我们首先加载数据集并进行预处理:
from datasets import load_dataset # 加载QASPER数据集 qasper_dataset = load_dataset("allenai/qasper") # 抽取部分数据用于训练和测试 train_dataset = qasper_dataset["train"].shuffle(seed=42).select(range(800)) test_dataset = qasper_dataset["test"].shuffle(seed=42).select(range(80))接下来,我们定义一些辅助函数来处理论文文本和问题提取:
def get_full_text(paper): """获取论文的全文""" full_text = "" if "title" in paper: full_text += paper["title"] + "\n\n" if "abstract" in paper: full_text += paper["abstract"] + "\n\n" # 添加全文内容 for section in paper["full_text"]: section_name = section["section_name"] section_text = " ".join(section["paragraphs"]) full_text += f"{section_name}\n{section_text}\n\n" return full_text def get_questions(paper): """获取论文中的所有问题""" return [q["question"] for q in paper["qas"]]4.2 生成微调数据
我们使用LlamaIndex的generate_finetuning_dataset函数来生成微调数据:
from llama_index.finetuning.cross_encoders import generate_finetuning_dataset # 生成微调数据 finetuning_dataset = generate_finetuning_dataset( train_dataset, get_full_text, get_questions, max_chunk_length=256, top_k=5, output_path="fine_tuning.csv" )这个过程会生成11674条微调样本,每条样本包含一个查询和相关文档的片段,以及它们的相关性标签。
4.3 微调交叉编码器
接下来,我们使用生成的微调数据来微调交叉编码器:
from llama_index.finetuning.cross_encoders import CrossEncoderFinetuneEngine # 初始化微调引擎 finetune_engine = CrossEncoderFinetuneEngine( model_name="cross-encoder/ms-marco-MiniLM-L-12-v2", train_dataset_path="fine_tuning.csv", model_output_path="finetuned_cross_encoder", epochs=3, batch_size=16, learning_rate=2e-5, warmup_steps=50, save_steps=200, eval_steps=200, logging_steps=50, ) # 开始微调 finetune_engine.finetune()微调完成后,我们可以将模型推送到Hugging Face Hub:
from huggingface_hub import HfApi, notebook_login # 登录Hugging Face notebook_login() # 推送模型 finetune_engine.push_to_hub("your-username/finetuned-cross-encoder")4.4 评估微调效果
我们使用多种方法来评估微调后的交叉编码器效果:
4.4.1 使用Hits指标评估
from llama_index.core.evaluation import RetrieverEvaluator from llama_index.core.schema import TextNode, NodeWithScore # 创建评估数据 eval_data = [] for paper in test_dataset: full_text = get_full_text(paper) questions = get_questions(paper) # 创建节点 node = TextNode(text=full_text) # 为每个问题创建查询和期望结果 for question in questions: eval_data.append({ "query": question, "expected_ids": [node.id], "mode": "default" }) # 评估原始模型 original_retriever = VectorIndexRetriever(index=index, similarity_top_k=5) original_evaluator = RetrieverEvaluator.from_metric_names( ["hit_rate"], retriever=original_retriever ) original_results = original_evaluator.evaluate(eval_data) # 评估微调后的模型 finetuned_retriever = VectorIndexRetriever( index=index, similarity_top_k=5, reranker=CrossEncoderReranker(model_name="your-username/finetuned-cross-encoder") ) finetuned_evaluator = RetrieverEvaluator.from_metric_names( ["hit_rate"], retriever=finetuned_retriever ) finetuned_results = finetuned_evaluator.evaluate(eval_data)4.4.2 使用Pairwise比较评估
from llama_index.core.evaluation import PairwiseComparisonEvaluator # 创建比较评估器 pairwise_evaluator = PairwiseComparisonEvaluator( llm=OpenAI(model="gpt-4") ) # 比较原始模型和微调后模型的结果 pairwise_results = [] for query in test_queries: # 获取原始模型结果 original_response = original_query_engine.query(query) # 获取微调后模型结果 finetuned_response = finetuned_query_engine.query(query) # 进行比较评估 result = pairwise_evaluator.evaluate( query=query, response=original_response, reference=finetuned_response ) pairwise_results.append(result)5. 案例效果
通过实验,我们观察到以下结果:
5.1 Hits指标对比
- 无重排序器:Hits@5 = 30
- 基础交叉编码器:Hits@5 = 34
- 微调交叉编码器:Hits@5 = 37
5.2 Pairwise比较结果
- 无重排序器:平均pairwise得分 = 0.553788
- 基础交叉编码器:平均pairwise得分 = 0.556818
- 微调交叉编码器:平均pairwise得分 = 0.6
这些结果表明,微调后的交叉编码器在重排序任务上表现更好,能够更准确地识别与查询最相关的文档。
6. 案例实现思路
6.1 数据准备
我们选择QASPER数据集是因为它包含了学术论文及其相关问题,非常适合用于学术检索系统的微调。通过提取论文全文和问题,我们创建了高质量的查询-文档对。
6.2 微调策略
我们选择了"cross-encoder/ms-marco-MiniLM-L-12-v2"作为基础模型,因为它在重排序任务上表现良好且模型大小适中。微调过程中,我们使用了较小的学习率(2e-5)和适当的批次大小(16),以确保模型能够稳定学习。
6.3 评估方法
我们采用了多种评估方法来全面衡量模型性能:
- Hits指标:衡量正确答案出现在前K个结果中的频率
- Pairwise比较:使用GPT-4作为评估器,比较不同模型的响应质量
7. 扩展建议
- 使用更大的数据集:增加训练数据量可能会进一步提升模型性能
- 尝试不同的基础模型:实验不同的预训练交叉编码器作为基础模型
- 调整超参数:优化学习率、批次大小等超参数
- 领域自适应微调:针对特定领域(如医学、法律)进行微调
- 集成到RAG系统:将微调后的交叉编码器集成到完整的RAG系统中
8. 总结
本案例展示了如何使用LlamaIndex微调交叉编码器,以提高检索系统的重排序效果。通过QASPER数据集的实验,我们证明了微调后的交叉编码器在Hits指标和Pairwise比较上都优于基础模型。这种方法可以应用于各种领域,以提升检索系统的性能。
交叉编码器微调的关键在于:
- 高质量的数据:需要准备与目标领域相关的查询-文档对
- 合适的微调策略:选择适当的基础模型和超参数
- 全面的评估:使用多种评估方法来衡量模型性能
通过本案例的学习,您应该能够掌握交叉编码器微调的基本流程,并将其应用到自己的检索系统中。