【finetuning】路由器微调案例分析
1. 案例目标
本案例的主要目标是探索如何通过微调大型语言模型(LLM)驱动的路由器来提高查询路由的准确性。具体来说,案例尝试了两种不同的微调方法,使用查询和真实"选择"作为训练信号:
- 微调嵌入模型
- 微调交叉编码器
案例使用不同城市的维基百科文章作为数据集,并为每种方法生成合成数据集进行微调,最后进行基本评估以验证微调效果。
2. 技术栈与核心依赖
本案例使用了以下主要技术栈和依赖:
- LlamaIndex: 用于构建和评估检索增强生成(RAG)系统的核心框架
- llama-index-finetuning: LlamaIndex的微调模块,提供模型微调功能
- llama-index-llms-openai: LlamaIndex的OpenAI语言模型集成
- SentenceTransformers: 用于微调嵌入模型的库
- BAAI/bge-small-en: 作为基础嵌入模型
- OpenAI GPT-3.5-turbo: 用于生成问题和评估的语言模型
- Wikipedia API: 用于获取城市维基百科文章数据
- pandas, numpy: 用于数据处理和评估结果分析
3. 环境配置
本案例的环境配置包括以下步骤:
- 安装必要的Python包:
%pip install llama-index-finetuning %pip install llama-index-llms-openai !pip install llama-index !pip install spacy - 导入必要的库并设置异步环境:
import nest_asyncio nest_asyncio.apply() - 配置OpenAI API密钥(未在代码中显示,但需要设置)
- 初始化语言模型:
from llama_index.llms.openai import OpenAI llm = OpenAI(model="gpt-3.5-turbo", temperature=0.3)
4. 案例实现
案例实现主要分为以下几个步骤:
4.1 数据准备
- 定义城市列表:
wiki_titles = [ "Toronto", "Seattle", "Chicago", "Boston", "Houston", "Tokyo", "Berlin", "Lisbon" ] - 从维基百科API获取每个城市的文章内容并保存到本地文件
- 使用SimpleDirectoryReader加载所有城市文档:
city_docs = {} for wiki_title in wiki_titles: city_docs[wiki_title] = SimpleDirectoryReader( input_files=[f"data/{wiki_title}.txt"] ).load_data()
4.2 定义路由选择器选项
为每个城市创建两种工具描述:向量搜索工具和摘要工具:
for idx, wiki_title in enumerate(wiki_titles): vector_desc = ( "Useful for questions related to specific aspects of" f" {wiki_title} (e.g. the history, arts and culture," " sports, demographics, or more)." ) summary_desc = ( "Useful for any requests that require a holistic summary" f" of EVERYTHING about {wiki_title}. For questions about" " more specific sections, please use the vector_tool." ) # ... 存储描述和选择映射4.3 生成训练数据集
使用DatasetGenerator为每个城市生成两种类型的问题:
- 向量搜索问题:基于文档节点生成具体问题
- 摘要问题:使用LLM生成城市摘要的变体问题
4.4 数据集分割
将数据集按查询分割为训练集和评估集:
train_dataset, eval_dataset = split_train_val_by_query(dataset, split=0.7)4.5 微调嵌入模型
使用SentenceTransformersFinetuneEngine微调嵌入模型:
finetune_engine = SentenceTransformersFinetuneEngine( train_dataset, model_id="BAAI/bge-small-en", model_output_path="test_model3", val_dataset=eval_dataset, epochs=30, ) finetune_engine.finetune() ft_embed_model = finetune_engine.get_finetuned_model()4.6 评估微调效果
比较基础嵌入模型、GPT-3.5和微调嵌入模型的路由准确性:
ft_matches = run_evals(eval_dataset, ft_selector, choices, choice_to_id_dict) base_matches = run_evals(eval_dataset, base_selector, choices, choice_to_id_dict) llm_matches = run_evals(eval_dataset, llm_selector, choices, choice_to_id_dict)5. 案例效果
案例的评估结果显示了不同模型在路由选择任务上的性能差异:
| 模型 | 匹配率 |
|---|---|
| 基础嵌入模型 | 0.128492 (12.85%) |
| GPT-3.5 | 0.659218 (65.92%) |
| 微调嵌入模型 | 0.994413 (99.44%) |
从结果可以看出,微调后的嵌入模型在路由选择任务上表现最佳,准确率接近99%,远高于基础嵌入模型(12.85%)和GPT-3.5(65.92%)。这表明针对特定任务进行微调可以显著提高模型性能。
案例还展示了如何将微调后的选择器集成到RouterQueryEngine中:
router_query_engine = RouterQueryEngine.from_defaults( selector=ft_selector.from_defaults(), query_engine_tools=tools )并演示了查询示例:"Tell me more about the sports teams in Toronto",系统正确选择了Toronto_vector工具并返回了相关结果。
6. 案例实现思路
本案例的核心实现思路可以总结为以下几点:
6.1 问题定义
将路由选择问题定义为:给定用户查询,从多个预定义的工具/选项中选择最合适的一个。这是一个典型的分类或匹配问题。
6.2 数据生成策略
使用合成数据生成方法创建训练数据:
- 对于向量搜索工具:基于文档内容生成具体问题
- 对于摘要工具:使用LLM生成摘要问题的变体
6.3 微调方法
选择微调嵌入模型而非直接微调LLM的原因:
- 嵌入模型更适合语义相似度计算
- 微调成本更低,效率更高
- 可以更好地捕捉特定领域的语义关系
6.4 评估方法
使用准确率作为评估指标,比较不同模型在路由选择任务上的表现。同时,将微调后的模型集成到实际的路由查询引擎中,验证其实际应用效果。
7. 扩展建议
基于本案例的实现和结果,以下是一些可能的扩展方向:
7.1 扩展数据集
- 增加更多城市和更丰富的主题类别
- 引入真实用户查询而非仅使用合成数据
- 添加更多样化的工具类型,如比较、分析等
7.2 改进微调策略
- 尝试不同的微调参数,如学习率、批大小、训练轮数等
- 探索其他基础嵌入模型作为微调起点
- 尝试多任务学习,同时微调多个相关任务
7.3 高级路由策略
- 实现多级路由,先进行粗分类再细分类
- 探索混合路由方法,结合嵌入模型和LLM的优势
- 添加上下文感知的路由决策,考虑对话历史
7.4 评估与优化
- 引入更多评估指标,如延迟、资源消耗等
- 进行A/B测试,比较不同路由策略在实际应用中的表现
- 开发自动化的路由性能监控和反馈机制
8. 总结
本案例展示了如何通过微调嵌入模型来显著提高路由选择的准确性。主要贡献和发现包括:
- 方法有效性:微调嵌入模型在路由选择任务上表现出色,准确率接近99%,远高于基础模型和通用LLM。
- 数据生成策略:使用合成数据生成方法可以有效创建训练数据,避免了手动标注的需要。
- 集成应用:微调后的选择器可以无缝集成到LlamaIndex的RouterQueryEngine中,提高整体系统性能。
- 成本效益:相比微调大型语言模型,微调嵌入模型成本更低,效率更高。
这个案例为构建更智能、更准确的路由系统提供了实用的方法和技术路线。通过针对特定任务进行微调,可以显著提高系统性能,同时保持较低的计算成本。这种方法可以扩展到其他需要智能路由决策的应用场景,如多模态内容处理、复杂查询分解等。
总的来说,本案例证明了微调嵌入模型是提高路由选择准确性的有效方法,为构建更智能的RAG系统提供了有价值的参考。