优化整体流程
在代码和系统架构层面,可以配合多种策略来进一步提升效率。
从“精排”到“粗排+精排”的两阶段检索:这是最重要的一步。先让
SimilarityPostprocessor开闸,调大VectorIndexRetriever和BM25Retriever的retrieval_top_k,让引擎召回足够多的候选(比如20-50个),再让SentenceTransformerRerank只对其中的top_n(比如5-10个)进行精排。这能让重排的计算量减少50%以上,同时保证高质量的召回。参数对照:
retrieval_top_k: 粗排召回量 → 增加 20-50rerank_top_k: 精排输出量 → 减少 3-5
异步非阻塞重排:重排序的过程可以不阻塞用户。当RAG处理请求时,可以先返回一个基于纯检索的“快速答案”,同时在后台启动重排任务,任务完成后通知前端更新。这样能提升界面响应的流畅度。
差异化的条件触发:并非所有查询都需要重排。你可以通过简单的规则(如查询长度<3个词)或一个轻量级分类器来判断查询的复杂度,仅在处理“难”问题时才启用重排步骤,从而节省宝贵的计算资源
维度 BGE-Reranker-v2-M3 Jina-Reranker-v3 BGE-Reranker-Base 中文精度 ⭐⭐⭐⭐⭐ (最强) ⭐⭐⭐⭐ ⭐⭐⭐⭐ CPU推理速度 中等 (建议开启Batch) 快 (架构优势) 极快 内存占用 低 (~2GB) 低 (~2GB) 极低 (~1.5GB) 适用场景 追求高精度、纯中文环境 追求速度、中英混合环境 实时性要求极高、配置较低 推荐指数 第一选择 第二选择 备选方案
pip install FlagEmbedding optimum
1、下载模型 cmd下
pip show huggingface_hub Name: huggingface_hub Version: 0.36.2 set HF_ENDPOINT=https://hf-mirror.com
huggingface-cli download BAAI/bge-reranker-v2-m3 --local-dir bge-reranker-v2-m3 --local-dir-use-symlinks False huggingface-cli download BAAI/bge-reranker-base --local-dir ./bge-reranker-base --local-dir-use-symlinks False
融合排序 vs 重排:
查询融合(
QueryFusionRetriever)是一种检索阶段的分数合并方法(例如倒数排名融合、平均分数等),它在多个检索器之间做第一次排序。重排(Cross-Encoder Rerank)是后处理阶段的独立步骤,它对检索器已经输出的节点列表进行重新打分和排序,通常可以弥补向量/BM25召回的不足。
---------------------------------------------------------------------------------------------------------
对模型进行量化
使用optimum-cli将模型导出为 ONNX 格式并进行 INT8 量化,然后让FlagReranker自动加载 ONNX 模型(因为 FlagEmbedding 新版支持从 ONNX 目录加载)。
下面给出具体操作步骤:
步骤 1:导出量化模型
打开命令行,进入到你的模型目录(或任意目录),执行以下脚本:
方案一
import torch import os import json from transformers import AutoTokenizer, AutoConfig, AutoModelForSequenceClassification # --- 配置区域 --- ORIGINAL_MODEL_PATH = r"D:\project\llama\python_knowlage_rag_api\models\bge-reranker-v2-m3" QUANTIZED_MODEL_PATH = r"D:\project\llama\python_knowlage_rag_api\models\bge-reranker-v2-m3-int8-cpu" print("🚀 开始 CPU 量化 (PyTorch 原生方案)...") # 1. 检查源路径是否存在 if not os.path.exists(ORIGINAL_MODEL_PATH): print(f"❌ 错误:找不到源模型目录 {ORIGINAL_MODEL_PATH}") exit() # 2. 创建输出目录 os.makedirs(QUANTIZED_MODEL_PATH, exist_ok=True) # 3. 加载 Tokenizer 和 Config print("⬇️ 加载分词器和配置...") try: tokenizer = AutoTokenizer.from_pretrained(ORIGINAL_MODEL_PATH, trust_remote_code=True) config = AutoConfig.from_pretrained(ORIGINAL_MODEL_PATH, trust_remote_code=True) # 保存基础文件 tokenizer.save_pretrained(QUANTIZED_MODEL_PATH) config.save_pretrained(QUANTIZED_MODEL_PATH) except Exception as e: print(f"❌ 加载配置失败: {e}") exit() # 4. 智能加载模型权重 print("⚙️ 正在加载原始模型权重...") model_file_path = None # 优先查找 model.safetensors (新标准),其次找 pytorch_model.bin (旧标准) candidates = ["model.safetensors", "pytorch_model.bin"] for candidate in candidates: path = os.path.join(ORIGINAL_MODEL_PATH, candidate) if os.path.exists(path): model_file_path = path print(f" 发现权重文件: {candidate}") break if not model_file_path: print(f"❌ 错误:在目录中未找到 'model.safetensors' 或 'pytorch_model.bin'") print(f" 请检查目录: {ORIGINAL_MODEL_PATH}") exit() # 5. 执行动态量化 # 注意:必须用 AutoModel... 来加载 safetensors,不能直接用 torch.load print("⚙️ 正在进行 INT8 动态量化 (这可能需要几分钟)...") try: # 使用 from_pretrained 可以自动处理 safetensors 和 bin 的加载 model = AutoModelForSequenceClassification.from_pretrained( ORIGINAL_MODEL_PATH, trust_remote_code=True, torch_dtype=torch.float32, # 确保以 float32 加载以便量化 device_map="cpu" ) # 执行量化 quantized_model = torch.quantization.quantize_dynamic( model, {torch.nn.Linear}, dtype=torch.qint8 ) # 6. 手动保存权重 print("💾 正在保存量化后的模型...") # 无论原文件是什么,我们都保存为 pytorch_model.bin 以保持兼容 output_bin_path = os.path.join(QUANTIZED_MODEL_PATH, "pytorch_model.bin") torch.save(quantized_model.state_dict(), output_bin_path) print("✅ 量化完成!") print(f"✨ 模型已保存至: {QUANTIZED_MODEL_PATH}") except Exception as e: print(f"❌ 量化过程出错: {e}") import traceback traceback.print_exc()方案二
先导出 ONNX,再进行量化
第一步:先导出标准 ONNX 模型
请运行以下命令,这一步只负责把 PyTorch 模型转换成 ONNX 格式(不量化):
optimum-cli export onnx -m "D:\project\llama\python_knowlage_rag_api\models\bge-reranker-v2-m3" --task text-classification --opset 17 --no-post-process "D:\project\llama\python_knowlage_rag_api\models\bge-reranker-v2-m3-onnx"第二步:对 ONNX 模型进行 INT8 量化
import os import time import threading from optimum.onnxruntime import ORTQuantizer from optimum.onnxruntime.configuration import AutoQuantizationConfig # --- 配置路径 --- ONNX_MODEL_PATH = r"D:\project\llama\python_knowlage_rag_api\models\bge-reranker-v2-m3-onnx" QUANTIZED_MODEL_PATH = r"D:\project\llama\python_knowlage_rag_api\models\bge-reranker-v2-m3-int8-onnx" def monitor_progress(target_dir): """后台监控函数:显示心跳和文件大小变化""" last_size = 0 counter = 0 while True: time.sleep(2) # 每2秒检查一次 counter += 1 # 简单的“心跳”动画 print(f"\r💓 正在处理中... {'. ' * (counter % 5)}", end="", flush=True) if os.path.exists(target_dir): # 计算目录下所有文件总大小 (MB) total_size = sum(os.path.getsize(os.path.join(dp, f)) for dp, dn, filenames in os.walk(target_dir) for f in [os.path.join(dp, fn) for fn in filenames]) / (1024 * 1024) if total_size > last_size: print(f"\r📊 当前输出体积: {total_size:.1f} MB (写入中...)", flush=True) last_size = total_size def main(): print("🚀 开始 ONNX INT8 量化...") # 1. 创建配置 qconfig = AutoQuantizationConfig.avx2(is_static=False, per_channel=False) # 2. 启动监控线程 monitor_thread = threading.Thread(target=monitor_progress, args=(QUANTIZED_MODEL_PATH,), daemon=True) monitor_thread.start() try: # 3. 加载量化器 print(f"🔧 正在从 {ONNX_MODEL_PATH} 加载模型...") quantizer = ORTQuantizer.from_pretrained(ONNX_MODEL_PATH) # 4. 执行量化 (这里会阻塞一段时间) print("⚙️ 开始量化计算 (CPU满载是正常的)...") quantizer.quantize( quantization_config=qconfig, save_dir=QUANTIZED_MODEL_PATH ) print("\n✅ 量化成功!") print(f"💾 模型已保存至: {QUANTIZED_MODEL_PATH}") except Exception as e: print(f"\n❌ 量化失败: {e}") if __name__ == "__main__": main()