我的经验:做代码生成时,repetition_penalty设到1.05-1.15就够了。设太高(比如1.5),模型会开始胡言乱语。我曾经踩过这个坑,生成了一堆语法错误但「绝不重复」的代码。
3.2 批量处理中的性能调优
离线批处理的核心优势就是吞吐量。但很多人一上来就塞几千条数据,结果发现速度反而慢了。为什么会这样?因为vLLM的批处理不是简单的「把数据堆在一起」。它背后有一套复杂的调度机制。
我总结了三个调优方向:
- 批大小(batch size)不是越大越好
vLLM内部有动态batching。你设的batch size只是「最大允许值」。实际运行时,vLLM会根据GPU显存自动调整。我建议从32开始,逐步增加到128或256,观察显存占用和吞吐量。
- max_num_seqs 和 max_num_batched_tokens
这两个参数直接控制vLLM的调度行为。max_num_seqs控制同时处理的序列数,max_num_batched_tokens控制一次前向传播能处理的最大token数。
- 使用异步提交
别用同步循环。用asyncio或者多线程来提交请求,让vLLM的调度器自己决定怎么合并批次。
来看一个性能调优的配置示例:
from vllm import LLM
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
max_num_seqs=256, # 同时处理256个序列
max_num_batched_tokens=8192, # 每次前向传播最多处理8192个token
gpu_memory_utilization=0.9, # 显存利用率
enable_prefix_caching=True, # 开启前缀缓存,长文本场景很有用
)
# 异步提交请求
async def batch_generate(prompts):
tasks = [llm.generate_async(p) for p in prompts]
results = await asyncio.gather(*tasks)
return results
注意:enable_prefix_caching在vLLM 0.4.0之后才稳定。如果你用的是旧版本,建议升级。我曾经在生产环境里因为没开这个,处理长文本时显存直接爆了。
3.3 理解vLLM的异步处理机制
vLLM的异步机制,说白了就是「一边生成,一边调度」。它不像传统推理框架那样,等一个请求完全结束才处理下一个。vLLM用的是连续批处理(continuous batching)。
我画了一张图,帮你理解这个流程:
这个机制的好处是:当一个请求生成了第一个token,它就可以被「踢出」当前批次,让新请求进来。这样GPU的利用率始终是满的。我在项目中测试过,相比传统批处理,连续批处理能把吞吐量提升2-3倍。
3.4 处理长文本输入
长文本是离线批处理的噩梦。你想想看,一个请求的输入是10k tokens,另一个是100 tokens。如果硬塞在一起,短的请求要等长的跑完,效率极低。
vLLM提供了几个解决方案:
- 动态序列长度分组:vLLM会自动把长度相近的请求分到同一批次。你不需要手动排序。
- 前缀缓存(Prefix Caching):如果多个请求共享相同的前缀(比如系统提示词),vLLM会缓存这部分计算结果。我建议在长文本场景下一定要开启。
- 分块预填充(Chunked Prefill):把长文本的预填充阶段拆成多个小块,避免一次性占用太多显存。
实战建议:如果你要处理大量长文本(比如论文摘要、法律文档),我建议先做预处理:把输入截断到模型的最大上下文长度。vLLM虽然支持长文本,但超过上下文长度后,生成质量会断崖式下降。
来看一个处理长文本的完整示例:
from vllm import LLM, SamplingParams
llm = LLM(
model="Qwen/Qwen2.5-7B-Instruct",
max_model_len=32768, # 设置模型最大上下文
enable_prefix_caching=True, # 开启前缀缓存
gpu_memory_utilization=0.85, # 给长文本留点余量
)
# 长文本输入
long_prompts = [
"请总结以下论文:" + "A" * 15000, # 15k tokens
"请总结以下论文:" + "B" * 12000, # 12k tokens
]
params = SamplingParams(
temperature=0.3,
max_tokens=2048,
)
# 批量生成
outputs = llm.generate(long_prompts, params)
for output in outputs:
print(output.outputs[0].text[:100])
避坑指南:我曾经在处理一批法律文档时,发现显存占用忽高忽低。后来排查发现,是因为文档长度差异太大(从500到20000 tokens)。解决方案是:按长度分组,分批处理。vLLM虽然能自动调度,但极端长度差异还是会拖慢整体速度。
好了,这一章的内容就到这里。离线批处理的进阶技巧,说白了就是理解vLLM的调度机制,然后根据你的场景去调参。下一章我们会进入在线服务部分,到时候你会看到,同样的模型,部署方式一变,调优思路又完全不同了。
公众号:蓝海资料掘金营,微信deep3321