一句话总结

生成更少 token 的大模型,可以比生成更多 token 的小模型更快、更准——这个反直觉结论重新定义了 VLM 推理效率的优化方向。


为什么 Token 数量是真正的瓶颈?

在 Vision-Language Model (VLM) 的推理链路中,有一个常被忽视的事实:推理延迟主要由输出 token 数量决定,而不是模型参数量

这和大多数人的直觉相反。直觉上,”70B 模型比 7B 模型慢 10 倍”。但真实情况是:

VLM 推理分两个阶段:

  • Prefill(预填充):并行处理所有输入 token,一次性完成,计算量大但时间固定
  • Decode(自回归解码):每次只生成一个 token,必须串行,时间与输出长度线性正相关

Decode 阶段是典型的内存带宽瓶颈(memory-bandwidth-bound),每生成一个 token 都需要把模型权重从 HBM 加载到 SRAM。因此:

\[\text{Decode 吞吐} \approx \frac{\text{内存带宽}}{2 \times \text{参数量} \times \text{字节数/参数}}\]

对 A100 (2TB/s 带宽) 上的 fp16 模型:

  • 7B 模型:$2 \times 10^{12} / (2 \times 7 \times 10^9 \times 2) \approx 71\ \text{tokens/sec}$
  • 70B 模型(8×A100):吞吐约降至 $\approx 14\ \text{tokens/sec}$

大模型吞吐约为小模型的 1/5,但如果大模型输出 token 数少 10 倍,它反而更快。


背景:Chain-of-Thought 的代价

现代 VLM 和 LLM 大量依赖 Chain-of-Thought(CoT)推理——让模型在给出答案之前先”想清楚”。

问题在于:小模型能力弱,需要更长的推理链才能达到可接受的准确率。大模型能力强,更短的推理链就能达到同等甚至更好的效果。

这制造了一个效率悖论:

  • 小模型 × 长 CoT = 低质量 + 高延迟
  • 大模型 × 短 CoT = 高质量 + 低延迟(在 token 数差距够大时)

核心方法:多智能体推理框架

直觉

论文的核心洞见是:大模型不需要自己想清楚每一步,可以借用小模型的推理过程

传统做法:
  小模型 → [长推理链 + 答案]  ← 慢且准确率低
  大模型 → [长推理链 + 答案]  ← 准确率高但也很慢

多智能体框架:
  小模型 → [长推理链]
       ↓ 提取关键推理 token
  大模型 → [借用推理] → [短答案]  ← 准确率接近大模型,延迟更低

效率分析

设:

  • $T_L, T_S$:大/小模型的 token 吞吐(tokens/sec)
  • $N_L, N_S$:大/小模型的输出 token 数
  • $N_{key}$:从小模型借用的关键推理 token 数(加入 prefill,不影响 decode)

大模型在多智能体框架下的延迟:

\[\text{Latency}_{\text{multi}} = \underbrace{\frac{N_S}{T_S}}_{\text{小模型生成推理}} + \underbrace{\frac{N_L'}{T_L}}_{\text{大模型生成短答案}}\]

其中 $N_L’ \ll N_L$,因为大模型不再需要自己推理。

效率收益条件(相比单独运行大模型):

\[\frac{N_S}{T_S} + \frac{N_L'}{T_L} < \frac{N_L}{T_L}\]

即:小模型生成推理的代价 < 大模型节省的推理代价。


实现

延迟模型:量化效率拐点

先用代码验证什么时候大模型真的更高效:

import numpy as np
import matplotlib.pyplot as plt

def estimate_throughput(params_B: float, bandwidth_TBs: float = 2.0,
                        bytes_per_param: int = 2) -> float:
    """基于内存带宽估算 decode 吞吐 (tokens/sec)"""
    model_size_bytes = params_B * 1e9 * bytes_per_param
    return bandwidth_TBs * 1e12 / model_size_bytes

def compute_latency(params_B: float, output_tokens: int, 
                    prefill_tokens: int = 1000) -> dict:
    """计算推理延迟 (秒)"""
    throughput = estimate_throughput(params_B)
    # Prefill 延迟近似(compute-bound,简化估算)
    prefill_latency = prefill_tokens / (throughput * 10)
    decode_latency = output_tokens / throughput
    return {
        "throughput": throughput,
        "prefill": prefill_latency,
        "decode": decode_latency,
        "total": prefill_latency + decode_latency
    }

# 对比:小模型(7B)长CoT vs 大模型(70B)短CoT
configs = {
    "7B × 500 tokens":  (7,  500),
    "7B × 200 tokens":  (7,  200),
    "70B × 50 tokens":  (70, 50),
    "70B × 100 tokens": (70, 100),
    "70B × 200 tokens": (70, 200),
}

print(f"{'配置':<25} {'吞吐(tok/s)':<15} {'Decode延迟(s)':<15} {'总延迟(s)'}")
print("-" * 70)
for name, (params, tokens) in configs.items():
    r = compute_latency(params, tokens)
    print(f"{name:<25} {r['throughput']:<15.1f} {r['decode']:<15.2f} {r['total']:.2f}")

典型输出(单 A100):

配置                      吞吐(tok/s)     Decode延迟(s)   总延迟(s)
----------------------------------------------------------------------
7B × 500 tokens           71.4            7.00            7.10
7B × 200 tokens           71.4            2.80            2.90
70B × 50 tokens           7.1             7.00            7.07
70B × 200 tokens          7.1             28.09           28.16

注意:7B 生成 500 tokens 和 70B 生成 50 tokens 总延迟几乎相同,但 70B 的答案质量更高。这就是论文的核心发现。

多智能体推理框架核心实现

from dataclasses import dataclass
from typing import Optional
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

@dataclass
class AgentConfig:
    model_name: str
    max_new_tokens: int
    is_large: bool = False

class MultiAgentInference:
    """多智能体推理:小模型生成推理链,大模型借用并生成短答案"""
    
    def __init__(self, small_cfg: AgentConfig, large_cfg: AgentConfig):
        self.small_model = AutoModelForCausalLM.from_pretrained(
            small_cfg.model_name, torch_dtype=torch.float16, device_map="auto"
        )
        self.large_model = AutoModelForCausalLM.from_pretrained(
            large_cfg.model_name, torch_dtype=torch.float16, device_map="auto"
        )
        self.small_tok = AutoTokenizer.from_pretrained(small_cfg.model_name)
        self.large_tok = AutoTokenizer.from_pretrained(large_cfg.model_name)
        self.small_cfg = small_cfg
        self.large_cfg = large_cfg
    
    def _generate(self, model, tokenizer, prompt: str, max_new_tokens: int) -> str:
        inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
        with torch.no_grad():
            outputs = model.generate(
                **inputs, max_new_tokens=max_new_tokens,
                do_sample=False, temperature=1.0
            )
        # 只返回新生成的部分
        new_tokens = outputs[0][inputs["input_ids"].shape[1]:]
        return tokenizer.decode(new_tokens, skip_special_tokens=True)
    
    def extract_key_reasoning(self, reasoning: str, max_tokens: int = 200) -> str:
        """提取关键推理 token(简单版:截取前 N 个词)"""
        words = reasoning.split()
        # 实际工作中可以用更复杂的摘要或重要性评分
        return " ".join(words[:max_tokens])
    
    def infer(self, question: str) -> dict:
        # Step 1: 小模型生成完整推理链
        small_prompt = f"Question: {question}\nLet me think step by step:\n"
        reasoning = self._generate(
            self.small_model, self.small_tok, small_prompt,
            self.small_cfg.max_new_tokens
        )
        
        # Step 2: 提取关键推理 token
        key_reasoning = self.extract_key_reasoning(reasoning)
        
        # Step 3: 大模型借用推理,生成短答案
        large_prompt = (
            f"Question: {question}\n"
            f"Reasoning context: {key_reasoning}\n"  # 关键:注入推理 token
            f"Based on the above reasoning, the concise answer is:"
        )
        answer = self._generate(
            self.large_model, self.large_tok, large_prompt,
            self.large_cfg.max_new_tokens  # 大模型只需生成短答案
        )
        
        return {
            "reasoning_tokens": len(reasoning.split()),
            "answer_tokens": len(answer.split()),
            "reasoning": reasoning,
            "answer": answer
        }

效率对比实验

import time

def benchmark_inference(agent: MultiAgentInference, questions: list) -> dict:
    """对比直接用大模型 vs 多智能体框架"""
    results = {"multi_agent": [], "large_only": []}
    
    for q in questions:
        # 多智能体方案
        t0 = time.perf_counter()
        ma_result = agent.infer(q)
        results["multi_agent"].append({
            "latency": time.perf_counter() - t0,
            "answer_tokens": ma_result["answer_tokens"]
        })
        
        # 仅大模型(生成完整 CoT)
        large_prompt = f"Question: {q}\nLet me think step by step:\n"
        t0 = time.perf_counter()
        answer = agent._generate(
            agent.large_model, agent.large_tok, large_prompt, max_new_tokens=500
        )
        results["large_only"].append({
            "latency": time.perf_counter() - t0,
            "answer_tokens": len(answer.split())
        })
    
    # 汇总统计
    for key in results:
        latencies = [r["latency"] for r in results[key]]
        tokens = [r["answer_tokens"] for r in results[key]]
        print(f"{key}: 平均延迟 {np.mean(latencies):.2f}s, 平均输出 {np.mean(tokens):.0f} tokens")
    
    return results

工程实践

关键推理 token 的选择策略

论文中提到”transfer key reasoning tokens”,但具体如何选择是工程核心:

def select_key_tokens_tfidf(reasoning: str, query: str, top_k: int = 5) -> str:
    """用 TF-IDF 相关性选择关键句子"""
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.metrics.pairwise import cosine_similarity
    
    sentences = reasoning.split(".")
    sentences = [s.strip() for s in sentences if len(s.strip()) > 10]
    
    if not sentences:
        return reasoning[:500]
    
    vectorizer = TfidfVectorizer()
    # 用查询和每个句子计算相关性
    corpus = [query] + sentences
    tfidf_matrix = vectorizer.fit_transform(corpus)
    scores = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:]).flatten()
    
    top_indices = scores.argsort()[-top_k:][::-1]
    return ". ".join([sentences[i] for i in sorted(top_indices)])

什么时候触发小模型推理?

不是所有问题都需要多智能体推理,简单问题直接用大模型:

def needs_reasoning(question: str, confidence_threshold: float = 0.8) -> bool:
    """判断是否需要小模型辅助推理(基于大模型置信度)"""
    # 用大模型的 log-prob 估计置信度
    # 低置信度 → 触发小模型推理
    # 高置信度 → 大模型直接回答
    pass  # 实际实现需要接入模型的 logits

常见坑

坑 1:小模型推理质量太差,大模型被带偏

小模型的错误推理可能误导大模型。解决方案:在 prompt 中明确告诉大模型”以下推理供参考,请自行判断正确性”:

large_prompt = (
    f"Question: {question}\n"
    f"Reference reasoning (may contain errors): {key_reasoning}\n"
    f"Provide your own concise answer:"  # 不是"基于以上推理"
)

坑 2:两次推理的延迟叠加超过预期

多智能体框架实际跑两个模型,若不并行化则总延迟更高。关键在于小模型和大模型的 prefill 可以流水线化

坑 3:不同 tokenizer 的 token 边界不对齐

小模型的 token 不能直接喂给大模型(词表不同),需要先 decode 成文本再 encode。这会引入微小的语义损失。


适用边界

适用场景 不适用场景
推理链较长的复杂问题(数学、代码) 简单问答(直接用大模型更快)
有现成小模型可用的场景 实时交互(额外延迟不可接受)
批量推理,可流水线化 小模型推理频繁出错的领域
追求准确率同时控制成本 两个模型都要塞进同一 GPU 时

与相关工作对比

方法 核心思路 优点 缺点
Speculative Decoding 小模型猜测,大模型验证 无质量损失,延迟低 两模型词表需兼容
Mixture of Experts 路由不同问题给不同专家 单次推理 训练成本高
本文方法 小模型推理 → 大模型借用 复用现有模型,无需重训 两次推理的调度开销
Chain-of-Thought 单模型长推理 简单 小模型效果差,大模型慢

我的观点

这篇论文揭示了一个重要的系统级优化机会:大家都在优化模型结构,却忽视了输出 token 数对延迟的决定性影响。

但有几个开放问题值得关注:

  1. 小模型推理质量的下限:当小模型在某个领域能力极差时,借用其推理反而有害。需要一个可靠的”推理质量估计器”,但这本身就是个难题。

  2. 两阶段延迟叠加问题:论文的效率分析在某些场景下可能过于乐观——如果小模型和大模型不能并行运行(例如单 GPU 内存不足),总延迟反而增加。

  3. 离实际部署还差什么:论文用的是”模拟数据”做延迟分析,真实部署中 KV cache、批处理、网络通信都会影响结论。工业级落地需要在具体推理框架(vLLM、TensorRT-LLM)上验证。

这个方向的本质是把推理时计算的分配问题重新形式化,值得关注,但不要被”大模型反而更快”这个标题忽悠——前提是大模型真的能用更少 token 达到同等效果,而这个前提在开放域任务中并不总是成立。