Blame view

docs/reranker-共享前缀批量推理.md 17.9 KB
cd6d887e   tangwang   reranker doc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  
  ## 共享前缀+批量后缀
  
  **"共享前缀批量推理"**(Shared-Prefix Batch Inference):
  - 输入结构:`[Prefix A] + [Suffix B1]`、`[Prefix A] + [Suffix B2]`、`[Prefix A] + [Suffix B3]`...
  - 痛点:Prefix A被重复计算成千上万次,浪费算力和时间
  - 理想方案:Prefix A只推理一次,KV Cache复用给所有后缀
  
  ---
  
  ## 一、顶级推荐方案
  
  ### 1. **FlashInfer + Cascade Attention** ⭐ 最强技术
  - **核心创新**:Cascade Inference(级联推理)
  - **性能提升**:相比vLLM的PageAttention,在32K token共享前缀、batch size 256场景下可达 **31倍加速** 
  - **原理**
    - 将Attention计算分解为两个阶段:
      1. **多查询阶段**:对共享前缀使用Multi-Query Kernel,只计算一次,结果存入Shared Memory
      2. **批量解码阶段**:对每个唯一后缀使用Batch Decode Kernel
    - 使用结合律算子合并部分Attention状态,类似FlashAttention的分块策略
  - **适用场景**:文档QA、系统提示词复用、RAG批量检索
  - **集成**:已集成到SGLang和vLLM中作为后端
  - **GitHub**: https://github.com/flashinfer-ai/flashinfer
  
  ### 2. **SGLang + RadixAttention** ⭐ 最实用框架
  - **核心创新**:RadixTree(基数树)管理KV Cache 
  - **自动前缀复用**:无需手动配置,自动识别共享前缀并复用KV Cache
  - **性能**:相比vLLM、LMQL等基线系统,结构化工作负载上可达 **6.4倍吞吐量提升****3.7倍延迟降低** 
  - **关键特性**
    - **In-Batch Prefix Caching**:同一batch内自动共享前缀(如你的A+B1, A+B2场景)
    - **Multi-Item Scoring (MIS)**:LinkedIn用于推荐排序的优化,将多个候选项合并为单次前向传播
    - **Zero-Overhead CPU Scheduler**:GPU计算时CPU并行准备下一batch,利用率接近100%
  - **特别适合**:Agent系统、工具链、RAG应用 
  - **GitHub**: https://github.com/sgl-project/sglang
  
  ### 3. **vLLM + Automatic Prefix Caching** ⭐ 最成熟稳定
  - **核心机制**:基于哈希表的块级前缀缓存 
  - **工作原理**
    - 将KV Cache按块(默认16 tokens)哈希
    - 新请求先查哈希表,命中则直接复用,只计算新tokens
    - 使用LRU策略管理缓存 eviction
  - **使用方式**
    ```python
    from vllm import LLM, SamplingParams
    # 启用prefix caching
    llm = LLM(model="your-model", enable_prefix_caching=True)
    
    # 第一次调用缓存前缀
    outputs = llm.generate(long_prefix + prompt_1, sampling_params)
    # 第二次调用自动命中缓存,prefix部分零计算
    outputs = llm.generate(long_prefix + prompt_2, sampling_params)
    ```
  - **注意事项**:vLLM 0.6.3之前调度器未考虑缓存命中率,高并发长序列场景可能性能下降,建议升级到0.6.5+ 
  
  ---
  
  ## 二、其他重要方案
  
  ### 4. **TensorRT-LLM + In-Flight Batching**
  - **优势**:NVIDIA官方优化,与FlashInfer深度集成 
  - **特性**
    - 支持Prefix Caching(具体实现闭源,但概念类似vLLM)
    - XQA Kernel(Flash Attention 3变体)优化内存访问
    - 层融合技术减少中间结果存储
  - **性能**:在共享前缀数据集上,吞吐量提升 **~34.7%**,TPOT降低 **~20.9%** 
  - **适用**:NVIDIA GPU生产环境,追求极致性能
  
  ### 5. **LMDeploy + TurboMind**
  - **定位**:纯C++引擎,消除Python开销 
  - **性能**:与SGLang相当,在H100上可达 **~16,200 tok/s**(vLLM为12,553 tok/s)
  - **优化**:支持KV Cache量化(8-bit)、Continuous Batching
  - **适用**:高吞吐生产部署,对延迟敏感的场景
  
  ### 6. **Daft + Dynamic Prefix Bucketing**(大数据场景)
  - **创新点**:动态前缀分桶 + 流式Continuous Batching 
  - **解决痛点**
    - 全局排序分组会导致GPU空闲
    - 动态分桶在推理同时进行前缀分组,实现流水线
  - **性能**:128 GPU集群上,20万prompts(1.28亿tokens)处理速度提升 **50.7%** 
  - **适用**:大规模离线批处理(如数据标注、合成数据生成)
  
  ---
  
  ## 三、针对你的具体场景建议
  
  ### 场景1:在线服务(RAG检索、实时重排序)
  **推荐栈****SGLang****vLLM + FlashInfer后端**
  
  ```python
  # SGLang示例:自动前缀复用
  import sglang as sgl
  
  @sgl.function
  def rerank(s, query, docs):
      # query是共享前缀,docs是批量后缀
      s += "Query: " + query + "\n"
      s += "Document: " + sgl.arg(docs) + "\nRelevance:"
      s += sgl.gen("score", max_tokens=5)
  
  # 批量执行,自动共享query部分的KV Cache
  docs = ["doc1 content", "doc2 content", "doc3 content", ...]  # 成千上万个
  state = rerank.run_batch(
      [{"query": "user query", "docs": d} for d in docs],
      max_new_tokens=5
  )
  ```
  
  ### 场景2:离线批量处理(数据标注、索引构建)
  **推荐栈****Daft****FlashInfer原生API**
  
  ```python
  # Daft示例:动态前缀分桶
  import daft
  from daft.functions import prompt
  
  df = daft.from_pydict({
      "query": ["shared query"] * 10000,
      "doc": ["doc1", "doc2", ...]  # 不同后缀
  })
  
  df = df.with_column("score", 
      prompt(
          df["query"] + "\n" + df["doc"],
          provider="vllm-prefix-caching",  # 利用前缀缓存
          model="your-model"
      )
  )
  ```
  
  ### 场景3:Embedding/Reranker模型(Bi-Encoder/Cross-Encoder)
  **推荐栈****Sentence-Transformers优化** + **ONNX/TensorRT** 
  
  ```python
  # Cross-Encoder批量重排序优化
  from sentence_transformers import CrossEncoder
  import numpy as np
  
  class OptimizedReranker:
      def __init__(self, model_name="cross-encoder/ms-marco-MiniLM-L-6-v2"):
          self.model = CrossEncoder(model_name, max_length=512, device="cuda")
      
      def rerank_batch(self, query, documents, batch_size=32):
          # 构建所有pairs:[query, doc1], [query, doc2], ...
          pairs = [[query, doc] for doc in documents]
          
          # 单次批量推理,自动共享query的编码计算
          scores = self.model.predict(
              pairs, 
              batch_size=batch_size,
              convert_to_numpy=True
          )
          return np.argsort(scores)[::-1]
  ```
  
  ---
  
  ## 四、性能优化关键技巧
  
  ### 1. **Prompt构造策略**
  - **对齐块边界**:确保共享前缀长度是KV Cache块大小(通常是16或32)的整数倍,避免部分块重计算 
  - **静态前缀前置**:将不变的指令、系统提示放在最前面
  
  ### 2. **Batch构造策略**
  - **前缀分桶(Prefix Bucketing)**:将相同前缀的请求分到同一batch,最大化缓存命中率 
  - **长度排序**:同batch内按后缀长度排序,减少padding浪费 
  
  ### 3. **硬件/精度优化**
  - **FP16/BF16**:混合精度推理,吞吐量提升1.5-2倍 
  - **KV Cache量化**:8-bit量化减少内存带宽压力,提升20%吞吐 
  - **CUDA Graphs**:捕获静态计算图,减少CPU开销(适合固定batch size场景)
  
  ---
  
  ## 五、方案对比总结
  
  | 方案 | 前缀复用方式 | 适用场景 | 性能提升 | 易用性 | 成熟度 |
  |------|-------------|---------|---------|--------|--------|
  | **FlashInfer** | Cascade Attention | 超长前缀(32K+) | 31x | 中(需集成) | 高 |
  | **SGLang** | RadixTree自动缓存 | Agent/RAG/工具链 | 6.4x | 高 | 高 |
  | **vLLM** | 哈希块缓存 | 通用在线服务 | 2-5x | 高 | 极高 |
  | **TensorRT-LLM** | 内部优化 | NVIDIA生产环境 | 1.3-2x | 中 | 高 |
  | **LMDeploy** | TurboMind C++引擎 | 低延迟高吞吐 | 1.3x | 中 | 高 |
  | **Daft** | 动态分桶 | 大规模离线批处理 | 50%+ | 高 | 中 |
  
  ---
  
  ## 六、推荐实施路径
  
  1. **快速验证**:先用 **vLLM** 开启`enable_prefix_caching=True`验证效果,零代码改动
  2. **性能突破**:切换到 **SGLang**,利用RadixAttention和自动batch优化,获得显著性能提升
  3. **极致优化**:对超长前缀场景,使用 **FlashInfer** 的Cascade Attention内核,或集成到自定义推理引擎中
  
  需要针对具体框架的详细配置代码或部署架构建议吗?
  
  
  
  ## 天然支持Prefix Caching的Reranker模型清单
  
  最新版本 (v2 系列):混合架构
  在 BGE-Reranker-v2 中,BAAI 为了提升模型上限,引入了不同的底座:
  
  BGE-Reranker-v2-m3: 依然保持了 Encoder-only 架构(基于 XLM-RoBERTa),支持多语言和长文本,具有极高的性价比。
  
  BGE-Reranker-v2-Gemma/Llama: 采用了 Decoder-only 架构。
  
  这些模型是基于 Gemma-2b 或 Llama-3-8B 等大语言模型(LLM)微调而来的。
  
  虽然原生 LLM 是单向(Causal)注意力的,但在作为 Reranker 使用时,模型通常会通过特殊的 Prompt 引导,并取最后一个 Token 的输出经过一个线性层来计算相关性分数。
  
  
  ### 1. **BGE-Reranker-V2/V2.5 系列** ⭐ 强烈推荐
  基于Gemma/MiniCPM等Decoder-only架构,FlagEmbedding官方实现已优化 
  
  | 模型 | 架构 | 参数量 | 特点 |
  |------|------|--------|------|
  | `BAAI/bge-reranker-v2-gemma` | Gemma-2B (Decoder-only) | 2B | 多语言强,基础版 |
  | `BAAI/bge-reranker-v2-minicpm-layerwise` | MiniCPM-2B (Decoder-only) | 2B | **支持层选择**,可截断到第24层加速 |
  | `BAAI/bge-reranker-v2.5-gemma2-lightweight` | Gemma2-9B (Decoder-only) | 9B | **Token压缩+层选择**,极致效率 |
  
  **Prefix Caching友好特性**
  - 输入格式:`[Query A] + [SEP] + [Document B] + [Prompt]`
  - Query部分作为前缀,可被所有Document共享
  - 官方代码中已实现`compute_score_single_gpu`的batch处理,自动对齐长度排序减少padding
  
  **使用示例**
  ```python
  from FlagEmbedding import FlagAutoReranker
  
  # 启用vLLM后端 + Prefix Caching
  reranker = FlagAutoReranker.from_finetuned(
      'BAAI/bge-reranker-v2-gemma',
      model_class='decoder-only-base',
      use_fp16=True,
      devices=['cuda:0']
  )
  
  # 批量推理:Query自动复用KV Cache
  pairs = [
      ('what is panda?', 'The giant panda is a bear species...'),  # A+B1
      ('what is panda?', 'Pandas are popular zoo animals.'),       # A+B2 (Query复用)
      ('what is panda?', 'Pandas eat bamboo and live in China.'),  # A+B3 (Query复用)
  ]
  scores = reranker.compute_score(pairs, batch_size=32)
  ```
  
  ### 2. **Qwen3-Reranker 系列** ⭐ 国产最强
  基于Qwen3 Decoder-only架构,阿里云官方支持 
  
  | 模型 | 架构 | 参数量 | 特点 |
  |------|------|--------|------|
  | `Qwen/Qwen3-Reranker-0.6B` | Qwen3-0.6B (Decoder-only) | 0.6B | 超轻量,快速 |
  | `Qwen/Qwen3-Reranker-4B` | Qwen3-4B (Decoder-only) | 4B | 性能均衡 |
  | `Qwen/Qwen3-Reranker-8B` | Qwen3-8B (Decoder-only) | 8B | 精度最高 |
  
  **架构细节**
  - **纯Decoder-only架构**:使用因果注意力,天然支持Prefix Caching
  - **输入模板**
    ```
    <|im_start|>system
    You are a helpful assistant.
    <|im_end|>
    <|im_start|>user
    Query: {query}
    Document: {document}
    Does the document answer the query? Please answer Yes or No.
    <|im_end|>
    <|im_start|>assistant
    Yes
    ```
  - **输出**:只生成"Yes"或"No"的logits,作为相关性分数
  
  **vLLM部署优化**
  ```bash
  # 启动vLLM服务,启用Prefix Caching
  python -m vllm.entrypoints.openai.api_server \
      --model Qwen/Qwen3-Reranker-8B \
      --tensor-parallel-size 1 \
      --dtype half \
      --max-model-len 32768 \
      --enable-prefix-caching  # 关键参数!提速40%
  ```
  
  ### 3. **Jina-Reranker-V3** ⭐ 创新架构
  基于Qwen3-0.6B的Listwise重排序器,支持跨Document注意力 
  
  | 模型 | 架构 | 参数量 | 特点 |
  |------|------|--------|------|
  | `jinaai/jina-reranker-v3` | Qwen3-0.6B (Decoder-only) | 0.6B | **Listwise**,单次处理64个docs |
  
  **独特优势**
  - **"Last but Not Late"交互**:在单个context window中同时处理Query+所有Documents
  - **跨Document注意力**:通过因果注意力实现Document间交互,捕捉相对相关性
  - **Prefix Caching优化**:Query放在序列开头,可被所有Document共享
  
  **输入格式**
  ```xml
  <|im_start|>system
  You are a search relevance expert...
  <|im_end|>
  <|im_start|>user
  Rank the passages based on their relevance to query: [QUERY]
  
  <passage id="1">[DOC_1]<|doc_emb|></passage>
  <passage id="2">[DOC_2]<|doc_emb|></passage>
  ...
  <passage id="k">[DOC_k]<|doc_emb|></passage>
  
  <query>[QUERY]<|query_emb|></query>
  <|im_end|>
  ```
  
  **性能**:BEIR nDCG@10达61.94,超过Qwen3-Reranker-4B,体积小6倍 
  
  ### 4. **E5-Mistral / NV-Embed-v2 / SFR-Embedding-Mistral**
  基于Mistral-7B Decoder-only架构的Embedding/Reranker 
  
  | 模型 | 架构 | 用途 | 特点 |
  |------|------|------|------|
  | `intfloat/e5-mistral-7b-instruct` | Mistral-7B (Decoder-only) | Embedding | 指令微调,支持多任务 |
  | `nvidia/NV-Embed-v2` | Mistral-7B (Decoder-only) | Embedding | 潜在注意力层优化 |
  | `Salesforce/SFR-Embedding-Mistral` | Mistral-7B (Decoder-only) | Embedding | 长上下文优化 |
  
  **注意**:这些主要是**Embedding模型**(Bi-encoder),但可作为Reranker使用(如通过余弦相似度)。若需Cross-encoder式重排序,需配合其他技术。
  
  ### 5. **RankGPT / RankZephyr / RankLLaMA**
  基于LLM的生成式重排序器 
  
  | 模型 | 架构 | 特点 |
  |------|------|------|
  | `RankGPT` (GPT-3.5/4) | Decoder-only API | 通过Prompt让LLM判断相关性 |
  | `RankZephyr` | Zephyr-7B (Decoder-only) | 蒸馏自RankGPT,开源可部署 |
  | `RankLLaMA` | LLaMA-2/3 (Decoder-only) | 本地部署,隐私友好 |
  
  **实现方式**
  - 使用LLM的logits或生成"Yes/No"判断相关性
  - 完全基于Decoder-only架构,天然支持Prefix Caching
  
  ---
  
  ## 不支持Prefix Caching的Reranker(双向架构)
  
  ### ❌ **BGE-Reranker-V1 / BGE-Reranker-Base/Large**
  - **架构**:基于XLM-RoBERTa(Encoder-only,双向注意力)
  - **问题**:Query和Document拼接后`[CLS]` token的表示依赖于整个序列,无法分离缓存
  - **适用场景**:轻量级、短文本,对延迟不敏感
  
  ### ❌ **Cross-Encoder (BERT-based)**
  - **架构**:BERT/RoBERTa等Encoder-only模型 
  - **问题**
    - 每个Query-Document对必须**联合编码**
    - 前缀的KV Cache与后续token强耦合,无法复用
    - 计算复杂度O((|Q|+|D|)²),无法分解为O(|Q|²) + O(|D|²)
  
  ### ❌ **ColBERT / ColPali / Late Interaction模型**
  - **架构**:基于BERT的双向编码 + 后期MaxSim交互 
  - **问题**
    - **独立编码,但双向注意力**:Query和Document分别编码,但各自内部仍是双向
    - **无法Prefix Cache**:虽然Query可独立编码,但Document的编码不依赖于Query,所以不存在"共享前缀"场景
    - **优化点**:Document可**预计算并离线存储**,Query实时编码,然后做MaxSim
    - **本质区别**:这是"离线预计算"而非"Prefix Caching",适用于Document固定、Query变化的场景
  
  **ColBERT的优化策略**
  ```python
  # ColBERT流程:Document预计算(离线) + Query实时编码(在线)
  document_embeddings = encode_documents(docs)  # 离线,一次性
  query_embedding = encode_query(query)         # 在线,每次查询
  scores = maxsim(query_embedding, document_embeddings)  # 轻量级交互
  ```
  
  ---
  
  ## 实际部署建议
  
  ### 场景1:高并发在线服务(Query多变,Document固定)
  **推荐****Jina-Reranker-V3****Qwen3-Reranker + vLLM Prefix Caching**
  
  ```python
  # vLLM配置优化
  from vllm import LLM, SamplingParams
  
  llm = LLM(
      model="Qwen/Qwen3-Reranker-8B",
      enable_prefix_caching=True,  # 关键!
      max_num_seqs=256,            # 高并发
      max_model_len=32768
  )
  
  # 批量构造Prompts:共享Query前缀
  query = "What is the capital of France?"
  docs = ["Paris is the capital...", "France is a country...", "Berlin is the capital of Germany..."]
  
  prompts = [
      f"Query: {query}\nDocument: {doc}\nRelevant:" 
      for doc in docs
  ]
  
  # vLLM自动识别共享前缀,只计算一次Query的KV Cache
  sampling_params = SamplingParams(max_tokens=1, temperature=0)
  outputs = llm.generate(prompts, sampling_params)
  ```
  
  ### 场景2:离线批量重排序(Query固定,Document多变)
  **推荐****ColBERT / ColPali**(Document预计算策略)
  
  ```python
  from rankify.models.reranking import Reranking
  from rankify.dataset.dataset import Document, Question, Context
  
  # ColBERT:Document预计算,Query实时编码
  model = Reranking(method='colbert_ranker', model_name='Colbert')
  
  # Documents已预计算并存储
  document = Document(
      question=Question("What is RAG?"),
      contexts=[Context(text=doc, id=i) for i, doc in enumerate(docs)]
  )
  
  # 只需编码Query,然后MaxSim计算
  model.rank([document])
  ```
  
  ### 场景3:极致性能 + 高精度
  **推荐****BGE-Reranker-V2.5-Gemma2-Lightweight**(Token压缩 + 层选择)
  
  ```python
  from FlagEmbedding import LightWeightFlagLLMReranker
  
  reranker = LightWeightFlagLLMReranker(
      'BAAI/bge-reranker-v2.5-gemma2-lightweight',
      devices=["cuda:0"],
      use_fp16=True
  )
  
  # 综合优化:层截断 + Token压缩
  scores = reranker.compute_score(
      pairs,
      cutoff_layers=[28],      # 只用前28层
      compress_ratio=4,        # Token压缩4倍
      compress_layers=[24, 40] # 特定层压缩
  )
  ```
  
  ---
  
  ## 总结对比表
  
  | 模型 | 架构 | Prefix Caching | 适用场景 | 性能/效率 |
  |------|------|---------------|---------|----------|
  | **BGE-Reranker-V2/V2.5** | Decoder-only ✅ | ✅ 原生支持 | 多语言、生产环境 | 高 |
  | **Qwen3-Reranker** | Decoder-only ✅ | ✅ vLLM支持 | 中文优先、高精度 | 极高 |
  | **Jina-Reranker-V3** | Decoder-only ✅ | ✅ Listwise优化 | 跨Doc交互、Top-K排序 | 极高 |
  | **E5-Mistral/NV-Embed** | Decoder-only ✅ | ✅ 需配合框架 | Embedding+轻量重排 | 高 |
  | **RankGPT/Zephyr** | Decoder-only ✅ | ✅ API/本地 | 生成式判断 | 中 |
  | **BGE-Reranker-V1** | Encoder-only ❌ | ❌ 不支持 | 轻量、短文本 | 低 |
  | **ColBERT/ColPali** | 双向+后期交互 | ❌ 不适用(预计算替代) | Document固定场景 | 中 |
  
  **最终建议**
  - 如果追求**Prefix Caching加速** + **高精度**:选择 **Qwen3-Reranker-8B****BGE-Reranker-V2.5**,配合 **vLLM + FlashInfer** 部署
  - 如果需要**跨Document比较**(Listwise):选择 **Jina-Reranker-V3**
  - 如果**Document固定且量大**:选择 **ColBERT** 预计算策略(虽非Prefix Caching,但效率类似)