资讯动态

大模型批量推理实战:vLLM、Ray Data 与 Kubernetes 高吞吐优化指南

发布时间:2026/9/28 15:23:01 来源:尧图企业网站定制
1. 批量推理到底在解决什么问题先把场景摆出来。你手里有一个已经跑通的推理服务可能是 vLLM可能是 SGLang单条请求响应很快交互式对话体验也不错。现在业务方找过来说有一批离线数据要处理几万条 prompt 需要生成结果或者几百万条文本需要过一遍 embedding 模型。你第一反应可能是写个脚本循环调用在线接口一条一条发。跑起来才发现GPU 利用率低得可怜吞吐量惨不忍睹原本在线服务能跑到几千 tokens/s批量场景下连十分之一都发挥不出来。这就是批量推理要解决的核心矛盾。在线推理和离线批量推理用的是同一个引擎、同一份权重、同一套算子但目标几乎是相反的。在线推理追求的是低延迟用户发一条请求希望几百毫秒内看到第一个 token所以调度器会把请求拆得尽量细优先保证单条请求的响应时间。批量推理追求的是高吞吐没人关心第 37 条数据什么时候出结果只关心这一批数据总共花了多少时间、用了多少 GPU 小时。目标不同调度策略、批处理方式、资源分配逻辑就完全不同。我见过太多团队在这个地方踩坑拿在线服务的配置直接跑离线任务结果 GPU 利用率长期在 20% 以下成本翻了好几倍。反过来也有人把批量推理的配置搬到在线服务上延迟直接爆炸用户体验崩盘。所以理解这两者的差异不是学术问题是实打实的成本问题。这篇文章面向的是已经接触过大模型推理、正在或准备做批量推理任务的工程师。不管你是用 Kubernetes 做集群调度还是单机多卡跑 Ray Data或者是直接用 vLLM 的离线接口这里面的思路和坑都是相通的。我会从整体设计思路讲起拆解核心细节给出可复现的实操流程最后把常见问题和排查技巧整理出来。2. 批量推理的整体设计与思路拆解2.1 在线推理和批量推理的目标差异要理解批量推理怎么设计先得把在线推理的机制说清楚。在线推理服务通常长这样一个 HTTP 服务器接收请求请求进入调度队列调度器根据当前 GPU 上的显存占用、正在运行的序列数量决定是否把新请求加入当前批次。这里的关键机制是连续批处理continuous batching也叫迭代级调度。传统批处理是等一批请求凑齐了一起跑跑完再跑下一批中间 GPU 会空转。连续批处理则是每生成一个 token 就重新检查队列有完成的序列就踢出去有新请求就加进来让 GPU 尽可能不空闲。这个机制对在线场景非常友好因为请求是流式到达的延迟敏感。但它有一个隐含假设请求的到达是随机的、稀疏的调度器需要频繁做决策来维持 GPU 利用率。到了批量推理场景这个假设不成立了。你手里有十万条数据全部已知不需要等请求到达也不需要保证任何单条请求的延迟。这时候连续批处理的频繁调度反而成了开销调度器每步都在做决策但这些决策本可以提前规划好。所以批量推理的设计思路就变成了把调度开销摊薄把批大小拉满把显存用到极致。具体来说有几个方向静态批处理把数据分成固定大小的批次每批一次性喂给引擎跑完再跑下一批。实现简单但批次内序列长度不一时会有 padding 浪费。动态批处理根据序列长度动态组批把长度相近的序列放在一起减少 padding。vLLM 的离线接口内部就做了这件事。分块预填充把长 prompt 的预填充阶段切成小块和 decode 阶段交错执行避免长 prompt 阻塞其他序列。流水线并行在 Ray Data 这类框架里把数据加载、tokenize、推理、后处理拆成不同阶段用流水线的方式重叠执行让 GPU 不等待数据。这些手段的目标是一致的让 GPU 的算力利用率尽可能接近理论上限。在线推理里GPU 利用率能到 40% 就算不错了批量推理里目标是 80% 以上。2.2 为什么选 vLLM 作为批量推理引擎市面上推理引擎不少TensorRT-LLM、SGLang、vLLM、TGI 各有千秋。批量推理场景下vLLM 的优势比较明显原因有几个。第一PagedAttention 对显存的利用效率高。传统注意力机制的 KV Cache 需要连续显存空间序列长度不一的时候碎片化严重。PagedAttention 把 KV Cache 分成固定大小的块像操作系统管理内存页一样管理显存浪费能压到 4% 以下。批量推理里序列长度差异大这个机制带来的收益非常直接。第二离线接口设计得干净。vLLM 提供了LLM.generate()这样的离线批量接口不需要起 HTTP 服务直接在 Python 进程里调用。数据准备好之后一次性传进去引擎内部自动做动态组批和调度。相比自己写循环调在线接口省掉了网络开销和序列化开销。第三和 Ray Data 的集成成熟。Ray Data 是分布式数据处理框架vLLM 官方提供了ray.data.llm模块可以把 vLLM 引擎包装成 Ray 的算子自动做数据分片、多卡并行、流水线重叠。数据量大的时候这个组合比单机跑要省心得多。第四Kubernetes 生态支持好。vLLM 有官方 Docker 镜像部署到 K8s 集群里配合 GPU 调度器比如 HAMi 做 GPU 虚拟化或者直接用 NVIDIA device plugin可以比较方便地做多副本批量任务。当然SGLang 在某些场景下吞吐更高特别是结构化输出和前缀缓存做得好的时候。但批量推理的通用性上vLLM 的生态和文档更成熟踩坑成本更低。我个人的选择是如果任务里有大量共享前缀比如 few-shot 模板SGLang 值得试如果是通用批量生成或 embeddingvLLM 更稳。2.3 Kubernetes 在批量推理里的角色单机跑批量推理数据量不大的时候够用。但数据量上去之后单机显存和算力都有上限这时候就需要集群。Kubernetes 在这里的角色是资源编排不是推理本身。具体来说K8s 负责几件事GPU 资源分配通过 device plugin 把节点上的 GPU 暴露给 PodPod 申请nvidia.com/gpu: 2就能拿到两张卡。多副本调度一个批量任务可以起多个 Pod每个 Pod 跑一部分数据并行处理。故障恢复Pod 挂了自动重启配合 Ray 的容错机制任务不会因为单点故障全盘重来。弹性伸缩根据队列长度动态调整 Pod 数量闲时缩容省钱。但 K8s 不负责推理引擎内部的调度那是 vLLM 的事。很多人会把这两层搞混以为 K8s 能优化推理吞吐其实 K8s 只管把 Pod 放到合适的节点上Pod 内部的批处理、显存管理、调度策略全是 vLLM 在管。一个典型的批量推理架构是这样的Ray Data 做数据流水线vLLM 做推理引擎K8s 做资源编排。数据从对象存储读进来Ray Data 分片后分发给各个 vLLM 实例每个实例内部做动态组批和推理结果写回存储。这个架构的好处是每一层职责清晰扩展的时候只需要加 Pod 或者加节点。3. 核心细节解析与实操要点3.1 vLLM 离线接口的关键参数vLLM 的离线批量接口核心是LLM类和SamplingParams。先看引擎初始化from vllm import LLM, SamplingParams llm LLM( modelQwen/Qwen2.5-7B-Instruct, tensor_parallel_size2, gpu_memory_utilization0.9, max_model_len8192, swap_space16, enforce_eagerFalse, )这几个参数每一个都影响批量推理的性能逐个说。tensor_parallel_size是张量并行度也就是用几张卡跑一个模型。7B 模型单卡 24G 显存够用但如果是 70B 模型至少需要 4 张 A100 80G。这里有个经验公式模型参数量乘以 2 字节FP16得到权重显存再加上 KV Cache 和激活值大概需要权重显存的 1.5 到 2 倍。70B 模型 FP16 权重约 140G4 张 80G 卡总共 320G留出 KV Cache 空间后比较稳妥。gpu_memory_utilization控制 vLLM 预分配的显存比例默认 0.9。批量推理场景下可以调到 0.92 到 0.95把更多显存留给 KV Cache批大小能开得更大。但别调到 0.98系统本身和其他进程还需要显存调太高容易 OOM。max_model_len是模型支持的最大序列长度。这个值直接影响 KV Cache 的预分配大小。如果你实际数据里最长序列只有 2048但设成 8192会浪费大量显存。批量推理前一定要统计一下数据的长度分布把max_model_len设成实际最大长度加上一点余量。swap_space是 CPU 交换空间单位 GB。当 GPU 显存不够时vLLM 会把部分 KV Cache 换到 CPU 内存。批量推理里如果序列长度波动大适当调大这个值能避免 OOM但换入换出有开销能不用尽量不用。enforce_eager控制是否禁用 CUDA Graph。CUDA Graph 能把一系列 kernel 调用录制成图减少启动开销对 decode 阶段提升明显。批量推理里建议保持False让 vLLM 用 CUDA Graph。但如果遇到奇怪的崩溃可以设成True排查。3.2 批大小的选择逻辑批大小是批量推理里最关键的调优参数。设太小GPU 利用率上不去设太大显存 OOM 或者延迟飙升。vLLM 内部会自动做动态组批但你可以通过max_num_seqs和max_num_batched_tokens来间接控制。max_num_seqs是单批次最大序列数默认 256。这个值决定了同时有多少条序列在跑。对于短序列任务比如分类、embedding可以调到 512 甚至 1024。对于长序列生成任务256 已经比较激进再大容易 OOM。max_num_batched_tokens是单批次最大 token 数默认 8192。这个参数控制的是预填充阶段的批大小。预填充是计算密集型的token 数越多GPU 利用率越高。但设太大单次预填充时间变长会影响 decode 阶段的交错执行。一般建议设成max_model_len的 2 到 4 倍。实际调优的时候我的做法是先用默认值跑一小批数据看 vLLM 的日志输出。日志里会打印每次迭代的 batch size、GPU 利用率、KV Cache 使用率。如果 KV Cache 使用率长期低于 50%说明批大小可以往上调如果接近 100% 且出现 preemption抢占说明批大小太大了需要往下调。这里有个容易忽略的点序列长度分布对批大小的影响。如果所有序列长度都是 512那批大小可以开得很大如果长度从 128 到 8192 都有那批大小受最长序列限制实际利用率会低很多。解决办法是按长度分桶把长度相近的序列放在一起跑每个桶用不同的批大小。Ray Data 里可以自定义分桶逻辑vLLM 离线接口也可以手动分批。3.3 Ray Data 的数据流水线设计数据量大的时候瓶颈往往不在 GPU而在数据加载和预处理。我见过一个案例GPU 利用率只有 30%排查发现是 tokenize 阶段太慢GPU 一直在等数据。Ray Data 的价值就在这里它能把数据加载、预处理、推理、后处理拆成流水线让 CPU 和 GPU 重叠工作。一个典型的 Ray Data 批量推理流水线import ray from ray.data.llm import vLLMEngineProcessorConfig, build_llm_processor config vLLMEngineProcessorConfig( modelQwen/Qwen2.5-7B-Instruct, engine_kwargs{ tensor_parallel_size: 1, gpu_memory_utilization: 0.9, max_model_len: 4096, }, concurrency4, batch_size64, ) processor build_llm_processor( config, preprocesslambda row: { prompt: row[instruction], sampling_params: {max_tokens: 512, temperature: 0.7}, }, postprocesslambda row: { response: row[generated_text], input: row[prompt], }, ) ds ray.data.read_parquet(s3://bucket/input/) ds ds.map_batches(processor, batch_size64) ds.write_parquet(s3://bucket/output/)这里的关键参数是concurrency和batch_size。concurrency是并行运行的 vLLM 实例数每个实例占一张卡。batch_size是每次传给 vLLM 的行数。这两个值决定了流水线的并行度和批处理效率。concurrency的设置要考虑 GPU 数量。如果集群里有 8 张卡concurrency设成 8每个实例用一张卡这样能跑满所有 GPU。如果模型太大单卡放不下tensor_parallel_size设成 2concurrency设成 4总共还是 8 张卡。batch_size的设置要考虑显存和序列长度。batch_size 太大单次推理显存不够太小GPU 利用率低。一般从 32 或 64 开始试根据日志调整。Ray Data 还有一个好处是自动处理数据分片和故障恢复。如果某个 vLLM 实例挂了Ray 会把它的数据分片重新分配给其他实例任务不会中断。这在长时间运行的批量任务里非常重要。3.4 Kubernetes 部署的资源配置把批量推理部署到 K8s 上资源申请有几个要点。GPU 资源申请resources: limits: nvidia.com/gpu: 2这表示 Pod 需要 2 张 GPU。注意limits和requests要一致GPU 不支持超卖。CPU 和内存也要给够。数据加载和 tokenize 是 CPU 密集型的如果 CPU 不够GPU 会等数据。经验值是每张 GPU 配 8 到 16 核 CPU内存至少是 GPU 显存的 2 倍。比如 2 张 80G 的 A100配 32 核 CPU 和 320G 内存。共享内存也要调大。vLLM 用共享内存做进程间通信默认的 64M 不够用设成 16G 以上volumes: - name: shm emptyDir: medium: Memory sizeLimit: 16Gi volumeMounts: - name: shm mountPath: /dev/shm镜像选择上vLLM 官方提供了vllm/vllm-openai镜像但那是给在线服务用的。批量推理建议基于vllm/vllm-openai自己构建加上 Ray 和数据处理依赖。或者直接用rayproject/ray-ml镜像里面已经包含了 Ray 和常用 ML 库再装 vLLM 就行。节点选择上用 nodeSelector 或 affinity 把 Pod 调度到有 GPU 的节点nodeSelector: nvidia.com/gpu.product: NVIDIA-A100-SXM4-80GB这样可以确保 Pod 跑到指定型号的 GPU 上避免不同型号混跑导致的兼容性问题。4. 实操过程与核心环节实现4.1 单机批量推理的完整流程先从单机场景说起这是最基础的也是排查问题的起点。第一步准备数据。假设数据是 JSONL 格式每行一个样本{id: 1, prompt: 解释一下什么是机器学习} {id: 2, prompt: 写一段 Python 快速排序}第二步统计序列长度分布。这一步很多人跳过但它直接决定了max_model_len和批大小的设置import json from transformers import AutoTokenizer tokenizer AutoTokenizer.from_pretrained(Qwen/Qwen2.5-7B-Instruct) lengths [] with open(data.jsonl) as f: for line in f: item json.loads(line) tokens tokenizer.encode(item[prompt]) lengths.append(len(tokens)) import numpy as np print(fmax: {max(lengths)}, p99: {np.percentile(lengths, 99)}, p50: {np.percentile(lengths, 50)})如果 p99 是 1024max 是 4096那max_model_len设成 4096 就行但大部分序列在 1024 以内可以考虑分桶。第三步初始化 vLLM 引擎并跑推理from vllm import LLM, SamplingParams llm LLM( modelQwen/Qwen2.5-7B-Instruct, tensor_parallel_size1, gpu_memory_utilization0.92, max_model_len4096, ) sampling_params SamplingParams( temperature0.7, top_p0.9, max_tokens512, ) prompts [] with open(data.jsonl) as f: for line in f: prompts.append(json.loads(line)[prompt]) outputs llm.generate(prompts, sampling_params) with open(output.jsonl, w) as f: for i, output in enumerate(outputs): result { id: i, prompt: prompts[i], response: output.outputs[0].text, } f.write(json.dumps(result, ensure_asciiFalse) \n)这段代码看起来简单但有几个细节要注意。llm.generate()是阻塞的所有数据一次性传进去引擎内部自动分批。如果数据量特别大比如几百万条一次性加载到内存可能爆掉。这时候要手动分批batch_size 10000 for i in range(0, len(prompts), batch_size): batch prompts[i:ibatch_size] outputs llm.generate(batch, sampling_params) # 写入结果SamplingParams里的max_tokens要设合理。如果设成 512但实际输出平均只有 100 token那有 80% 的 decode 步骤是浪费的。可以先跑一小批统计输出长度分布再调整。第四步监控 GPU 利用率。跑的时候开另一个终端watch -n 1 nvidia-smi如果 GPU 利用率长期低于 60%说明批大小不够或者数据加载是瓶颈。如果显存接近 100% 且出现 OOM说明批大小太大。4.2 Ray Data 多卡并行的实现单机跑通之后数据量上去就需要多卡并行。Ray Data 的配置前面提过这里补充几个实操细节。启动 Ray 集群ray start --head --num-gpus8这表示当前节点有 8 张 GPU。如果是多节点其他节点用ray start --addresshead-address --num-gpus8加入。然后运行批量推理脚本。Ray Data 会自动把数据分片分发给各个 vLLM 实例。每个实例占一张卡或者多张卡取决于tensor_parallel_size。这里有个坑Ray Data 的默认分片策略可能不均匀。如果数据文件大小差异大某些实例分到的数据多某些少导致负载不均。解决办法是设置override_num_blocksds ds.repartition(num_blocks64)把数据重分成 64 块每块大小相近这样并行度更均匀。另一个坑是结果写入的并发冲突。多个实例同时写同一个文件会出问题。Ray Data 的write_parquet会自动处理每个实例写自己的分片最后合并。但如果自己写文件要注意加锁或者用不同的文件名。4.3 Kubernetes 上的批量任务编排K8s 上跑批量推理有两种方式一种是每个 Pod 跑一个 Ray worker用 Ray 的集群模式另一种是每个 Pod 独立跑一个批量任务用 K8s 的 Job 编排。Ray 集群模式适合数据量大、需要弹性伸缩的场景。部署 Ray head 和 workerapiVersion: apps/v1 kind: Deployment metadata: name: ray-head spec: replicas: 1 template: spec: containers: - name: ray image: rayproject/ray-ml:latest-gpu command: [ray, start, --head, --num-gpus0, --dashboard-host0.0.0.0] resources: limits: nvidia.com/gpu: 0 --- apiVersion: apps/v1 kind: Deployment metadata: name: ray-worker spec: replicas: 4 template: spec: containers: - name: ray image: rayproject/ray-ml:latest-gpu command: [ray, start, --addressray-head:6379, --num-gpus2] resources: limits: nvidia.com/gpu: 2这样有 4 个 worker每个 2 张卡总共 8 张卡。Ray head 不占 GPU只做调度。K8s Job 模式适合一次性任务跑完就释放资源apiVersion: batch/v1 kind: Job metadata: name: batch-inference spec: parallelism: 4 completions: 4 template: spec: containers: - name: inference image: your-vllm-image:latest command: [python, batch_infer.py, --shard-id, $(JOB_COMPLETION_INDEX)] resources: limits: nvidia.com/gpu: 2 restartPolicy: OnFailureparallelism是并行 Pod 数completions是总完成数。每个 Pod 通过JOB_COMPLETION_INDEX拿到自己的编号处理对应的数据分片。两种方式各有优劣。Ray 集群模式更灵活支持动态扩缩容和故障恢复K8s Job 模式更简单适合确定性任务。我一般数据量超过 100 万条用 Ray小规模用 Job。4.4 性能调优的参数计算批量推理的性能调优核心是找到显存和吞吐的平衡点。这里给一个粗略的计算方法。假设模型是 7BFP16 权重约 14G。KV Cache 每 token 占用显存KV Cache per token 2 * num_layers * hidden_size * 2 bytes以 Qwen2.5-7B 为例28 层hidden_size 35842 * 28 * 3584 * 2 401,408 bytes ≈ 0.4 MB per token如果max_model_len是 4096单条序列的 KV Cache 约 1.6G。GPU 显存 80G权重占 14G激活值和其他开销约 10G剩余 56G 给 KV Cache。理论上可以同时跑 35 条 4096 长度的序列。但实际因为碎片和调度开销能跑 25 到 30 条就不错了。如果序列平均长度只有 512单条 KV Cache 约 0.2G那可以同时跑 280 条。这就是为什么长度分布对批大小影响这么大。调优的时候先用小批量数据跑观察 vLLM 日志里的GPU KV cache usage。如果这个值在 80% 到 95% 之间说明批大小合适。低于 80%可以调大max_num_seqs高于 95% 且出现 preemption需要调小。5. 常见问题与排查技巧实录5.1 GPU 利用率上不去的排查思路这是批量推理最常见的问题。GPU 利用率低意味着钱白花了。排查顺序如下。先看数据加载。用iostat或者nvidia-smi dmon看磁盘 IO 和 GPU 利用率的关系。如果 GPU 利用率周期性掉到 0说明数据加载跟不上。解决办法是用 Ray Data 做预取或者把数据放到更快的存储上本地 SSD 比网络存储快很多。再看 tokenize。tokenize 是 CPU 操作如果 CPU 核数不够tokenize 会成为瓶颈。用htop看 CPU 利用率如果某个核跑满说明 tokenize 是单线程的需要并行化。Ray Data 的map_batches可以设置num_cpus来并行 tokenize。然后看批大小。如果数据加载和 tokenize 都没问题GPU 利用率还是低那就是批大小不够。调大max_num_seqs和max_num_batched_tokens观察显存和利用率的变化。最后看模型并行。如果用了 tensor_parallel_size 1卡间通信可能成为瓶颈。用nvidia-smi topo -m看卡间连接方式NVLink 比 PCIe 快很多。如果卡间是 PCIe 且通信量大考虑换用 pipeline parallel 或者减少并行度。5.2 OOM 的常见原因和解决OOM 在批量推理里很常见原因通常有几个。一是max_model_len设太大。实际数据最长只有 2048设成 8192KV Cache 预分配多了 4 倍。解决办法是统计实际长度分布设成 p99 加一点余量。二是批大小太大。max_num_seqs设成 512但显存只够跑 256 条。解决办法是调小或者用gpu_memory_utilization控制预分配比例。三是碎片化。长时间运行后显存碎片化导致没有连续空间分配。PagedAttention 已经很大程度上解决了这个问题但如果还是 OOM可以尝试重启引擎或者调小block_size。四是其他进程占用显存。Jupyter、TensorBoard 这些进程可能占着显存不放。用nvidia-smi看哪些进程在占杀掉不必要的。5.3 输出质量不稳定的处理批量推理里输出质量不稳定通常和采样参数有关。temperature设太高输出随机性大设太低输出重复。批量任务里如果任务需要确定性输出把temperature设成 0top_p设成 1。如果需要多样性temperature设 0.7 到 0.9top_p设 0.9 到 0.95。另一个原因是max_tokens设太小输出被截断。统计一下输出长度分布把max_tokens设成 p99 加一点余量。还有一个容易忽略的点是prompt 格式。批量推理里 prompt 是拼接好的如果格式和模型训练时不一致输出质量会下降。比如 ChatML 格式需要|im_start|和|im_end|标记漏了的话模型可能输出奇怪的内容。建议用 tokenizer 的apply_chat_template方法生成 prompt确保格式正确。5.4 常见问题速查表问题现象可能原因排查方法解决办法GPU 利用率低于 50%数据加载慢看磁盘 IO 和 GPU 利用率关系用 Ray Data 预取换本地 SSDGPU 利用率低于 50%tokenize 慢看 CPU 利用率并行 tokenize增加 CPU 核数GPU 利用率低于 50%批大小不够看 KV Cache 使用率调大 max_num_seqsOOMmax_model_len 太大统计实际序列长度调小 max_model_lenOOM批大小太大看显存使用曲线调小 max_num_seqsOOM显存碎片看 nvidia-smi 显存分布重启引擎调小 block_size输出被截断max_tokens 太小统计输出长度调大 max_tokens输出质量差prompt 格式不对检查 prompt 模板用 apply_chat_template输出质量差采样参数不当检查 temperature/top_p调整采样参数多卡并行慢卡间通信瓶颈nvidia-smi topo -m换 NVLink减少并行度任务中途失败单点故障看 Pod 日志用 Ray 容错加 restartPolicy结果写入冲突并发写同一文件看文件锁分片写入最后合并5.5 几个踩过的坑第一个坑是镜像里的 vLLM 版本和模型不匹配。有次用vllm/vllm-openai:v0.27.1加载 Qwen3-Embedding-0.6B报了一堆奇怪的错误排查半天发现是版本太老不支持这个模型架构。解决办法是看 vLLM 的 release notes确认版本支持目标模型。或者直接用最新版镜像但要注意新版本可能有性能回退上线前先小批量测试。第二个坑是K8s 的 GPU 配额不够。有次提交任务Pod 一直 Pending看事件发现是根组织的云原生开发-gpu配额已不够预冻结。这是集群的 GPU 配额限制需要找管理员扩容或者等之前的任务释放资源。批量任务最好设置priorityClass低优先级的任务可以被高优先级的抢占。第三个坑是共享内存不够。vLLM 用共享内存做进程间通信默认 64M 在批量推理里完全不够。表现是引擎启动时报Bus error或者shared memory allocation failed。解决办法是在 K8s 里挂载大容量的/dev/shm或者用--shm-size参数起 Docker 容器。第四个坑是数据格式不一致。输入数据里有的字段是字符串有的是列表tokenize 的时候报错。批量推理前一定要做数据校验确保所有样本的格式一致。可以用 Ray Data 的map做校验把不合格的样本过滤掉或者修正。第五个坑是结果写入太慢。推理跑完了写结果花了几个小时。原因是结果文件太多小文件或者写入的存储带宽不够。解决办法是合并小文件用 Parquet 格式而不是 JSONL或者写到更快的存储上。6. 批量推理的扩展方向批量推理跑通之后还有几个方向可以继续优化。一是前缀缓存。如果大量 prompt 共享相同的前缀比如 few-shot 模板vLLM 的自动前缀缓存能大幅减少预填充计算。开启方式是设置enable_prefix_cachingTrue。实测在 few-shot 场景下吞吐能提升 2 到 3 倍。二是量化。FP16 推理显存占用大用 AWQ 或 GPTQ 量化到 4bit显存占用降到四分之一批大小能开大 4 倍。代价是精度略有下降但批量任务里通常可以接受。vLLM 支持加载量化模型只需要在LLM初始化时指定quantizationawq。三是投机解码。用一个小模型做 draft大模型做 verify能在保持输出质量的前提下提升 decode 速度。vLLM 支持speculative_model参数但配置起来比较复杂适合对吞吐有极致要求的场景。四是多模态批量推理。如果任务涉及图片或音频vLLM 也支持多模态模型。数据准备和单模态类似只是 prompt 里要包含图片路径或 base64 编码。注意多模态模型的显存占用更大批大小要相应调小。五是和向量数据库集成。批量 embedding 任务跑完后结果通常要写入向量数据库Milvus、Qdrant、pgvector。Ray Data 可以直接对接这些数据库的写入接口省掉中间落盘再导入的步骤。这些扩展方向不是必须的但了解它们能帮你在遇到性能瓶颈时知道往哪个方向优化。我个人的经验是先把基础流程跑通把 GPU 利用率做到 70% 以上再考虑这些高级特性。基础没打好就上高级特性往往事倍功半。最后分享一个我在实际项目里总结的小技巧批量推理任务开始前先用 1000 条数据跑一个 mini batch记录吞吐、显存、GPU 利用率。然后根据这个结果估算全量数据的时间和成本。如果估算结果和预期差距大先调优再跑全量别直接上全量数据跑了一半发现有问题时间和钱都浪费了。这个 mini batch 测试花不了几分钟但能省下大量返工时间。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑