专栏算法工具链【大模型】Prompt 长度对齐 之 padding 与 truncation

【大模型】Prompt 长度对齐 之 padding 与 truncation

no_name2026-07-26
19
0

声明:本文主要参考开源资料进行学习整理,如有错漏,欢迎评论交流~

引言

把一个 LLM 部署到端侧跑推理,输入永远是一条长度任意的 prompt

但端侧推理引擎拿到这条 prompt 时,会立刻撞上一堵“静态”墙:

导出/编译期定形墙:端侧模型在导出静态计算图时就把 prefill 的输入维度焊死了,比如 (1, 1216, hidden)。你喂 8 个 token,形状对不上;喂 2037 个 token,又塞不进去。引擎必须先把它对齐到固定长度 L
本文专门讲这堵墙怎么翻——也就是 padding / truncation。至于另一堵"显存峰值墙"(长 prompt 一次性算不动),那是 chunk prefill 的事,见下一篇《Chunk Prefill——把一次算不动的事拆成多次》。两件事正交,本文先只聚焦长度对齐。

1. 为什么要对齐到固定长度

端侧推理有一个铁律:导出/编译期固定的输入形状,运行期不能变
原因在于端侧工具链在导出静态计算图时,需要喂入一组确定的 example_inputs,据此把所有中间张量的形状、算子的排布、L2/DDR 的访存策略全部固化下来。这张静态图的输入维度是多少,板子运行时就只认多少:
于是引擎在把真实 prompt 喂进这张静态图之前,必须先做一道长度归一化:把任意长度的 token 序列,变成恰好长度为 L 的序列。这道工序里只有两种动作——短了补、长了砍

2. 对齐逻辑长什么样

抛开框架差异,对齐的核心就一个函数。下面是一段通用实现:

代码不长,但里面藏着三个关键设计选择,下面分别来看一下!

3. 三个关键设计

3.1 左 padding

注意上面 left=True——pad 补在序列左端,真实 token 全部堆在右端:
为什么不补在右边(像训练时常见的 padding_side='right')?因为解码阶段要对齐 KV cache 的尾部
prefill 之后立刻进入 decode(一次吐一个 token)。decode 阶段每生成一个新 token,都要让它去 attend(关注)已经写进 KV cache 的历史位置。如果真实 token 在 KV cache 的右端(高位),那么:
  • 第一个生成 token 的位置就是 L,往后递增;
  • KV cache 里有效位置是 [L-5, L-4, ..., L-1],正好连续贴在右端;
  • causal mask(因果掩码)右对齐,新 token 永远 attend(关注)左边,逻辑干净。

反过来如果是右 padding,真实 token 在左端、pad 在右端,那么 decode 时新 token 得插在"真实 token 和 pad 之间",KV cache 的有效区间被 pad 割裂,mask 和 position 都要额外处理,容易出错。左 padding 是推理(而非训练)的标准做法。

补充一句:这条"右对齐"约定,正是chunk prefill 里 KV cache 滚动写入和 mask 切分都坚持右对齐的源头。

3.2 attention_mask 同步补 0

光把 input_ids 补上 pad 还不够,必须同步把 attention_mask 在对应位置补 0:
attention_mask=0 的位置告诉注意力层:"这些是 pad,不要 attend(关注)它们,也不让它们被 attend(关注)"。最终体现在 causal mask(因果掩码)上,就是把这些位置对应的注意力分数压成 $$-\inft$$(或量化友好的大负数,如 -32768),softmax(归一化指数函数)后权重为 0。

这样即便序列里塞了 pad token,模型的行为也等价于"只看见真实 token"。(以后会详细介绍这块)

3.3 截断而不报错

当 seq_len > max_lm_input_len 时,上面的实现选择左截断——保留最后 L 个 token,丢掉最前面的:
为什么砍前面留后面?因为生成任务里末尾的 instruction 更重要——用户给的指令、最近上下文都在序列尾部,砍掉前面冗长的文档正文,比砍掉末尾的"请总结成三点"要安全得多。
但要注意:截断是有损的妥协,不是正常路径。生产实现里一定会打 warning 提示信息丢失,并建议把 max_lm_input_len 调大。对 VLM 而言这尤其危险——图像 token 通常由 chat template 插在序列前部,左截断可能直接把图像信息砍没,导致"瞎答图"。所以正确做法是把 max_lm_input_len 配到能覆盖最长 prompt 的值,让截断分支永远不触发

4. 三个长度参数的关系

对齐牵涉三个长度,它们的关系是推理引擎的命脉,务必理清:

参数

含义

角色

max_lm_input_len (L)

prefill 单次输入长度

导出期焊死,padding/truncation 的目标
max_kvcache_len (C)

KV cache 总容量

decode 能用到多少历史

seq_len (s)

真实 prompt 长度

运行时变量,被对齐到 L

约束关系:

一个常见配置陷阱:把 max_lm_input_len 和 max_kvcache_len 设成相等。这时表面看 L 没超限,但 C - L = 0,decode 没有空间生成任何新 token——引擎能"读"不能"写"。校准/编译时这两个值要留出 gap(典型如 L=1216, C=2048),给 decode 留出生成空间。
这里出现的 max_kvcache_len (C) 和 KV cache,它是 prefill 和 decode 共享的核心数据结构,本文仅介绍它的容量约束,后面会介绍它如何在 chunk 之间滚动。

5. 总结

对齐解决的是"形状匹配"问题。但形状匹配了,显存不一定够——一条几千/万 token 的 prompt,即便形状对齐了,单次 prefill 的 attention 峰值仍可能把板端显存顶爆。后面会介绍:如何把这条已经对齐的长序列,切成 chunk 分批喂进去。

后续

端侧 LLM 推理的第一道工序是长度对齐
  • 为什么:导出静态图时输入维度焊死成固定 L,运行期任意长度的 prompt 必须对齐到 L。
  • 怎么做:短了左 padding(pad token 补左端、attention_mask 同步补 0);长了左截断(取尾部 L 个)。
  • 为什么左对齐:贴合 decode 时 KV cache 的右对齐,新 token 从尾部往后递增,mask 和 position 逻辑干净。
  • 三个长度:max_lm_input_len (L) ≤ max_kvcache_len (C),C - L 是 decode 可生成空间,二者相等会"能读不能写"。
形状对齐只是开始。对齐后的序列若仍因过长而算不动,就要靠 chunk prefill 分批化解。
算法工具链
社区征文杂谈前沿技术
评论0
0/600