多模态数据处理¶
为了在 vLLM 中实现各种优化,例如分块预填充和前缀缓存,我们使用BaseMultiModalProcessor来根据 HF 处理器的输出,提供占位符特征 token(例如<image>
)与多模态输入(例如原始输入图像)之间的对应关系。
以下是BaseMultiModalProcessor的主要功能
提示更新检测¶
HF 处理器的一个主要职责是用占位符 token 更新提示。例如
- 在字符串开头插入特征占位符 token(例如
<image><image>...<image>
,数量等于特征大小)。 - 将现有输入占位符 token(例如用于单张图像的
<image>
)替换为特征占位符 token(例如<image><image>...<image>
,数量等于特征大小)。
关于哪些 token 已被更新的信息是查找占位符特征 token 与多模态输入之间对应关系的关键。
在 vLLM 中,此信息通过PromptUpdate在_get_prompt_updates中指定。我们可以通过检查已更新 token 的存在性来自动检测 HF 是否已更新提示。
分词后的提示输入¶
为了在单独的进程中实现分词,我们支持在多模态数据之外传递输入 token ID。
问题¶
考虑 HF 处理器遵循以下主要步骤
- 对文本进行分词
- 处理多模态输入
- 执行提示更新
并且我们要求
- 对于文本 + 多模态输入,应用所有步骤 1-3。
- 对于分词 + 多模态输入,仅应用步骤 2-3。
我们如何在不重写 HF 处理器的情况下实现这一点?我们可以尝试在不同的输入上多次调用 HF 处理器
- 对于文本 + 多模态输入,直接调用 HF 处理器即可。
- 对于分词 + 多模态输入,仅在多模态输入上调用处理器。
虽然 HF 处理器原生支持文本 + 多模态输入,但对分词 + 多模态输入则不然:如果输入占位符 token 的数量与多模态输入的数量不对应,则会抛出错误。
此外,由于分词后的文本未通过 HF 处理器处理,我们必须自行应用步骤 3,以保持输出 token 和多模态数据彼此一致。
占位文本¶
我们通过要求每个模型通过get_dummy_text定义如何根据多模态输入的数量生成占位文本来解决第一个问题。这使我们能够生成与多模态输入对应的占位文本,并将它们一起输入以获得处理后的多模态数据。
自动提示更新¶
我们通过在_apply_prompt_updates中实现与模型无关的代码来解决第二个问题,该代码根据_get_prompt_updates输出的规范,自动使用特征占位符 token 更新提示。
总结¶
借助占位文本和自动提示更新,我们的多模态处理器最终可以接受带有多模态数据的文本和 token 提示。详细逻辑如_apply_hf_processor_main所示。
处理器输出缓存¶
一些 HF 处理器,例如 Qwen2-VL 的处理器,速度非常慢。为了缓解这个问题,我们缓存了 HF 处理器对多模态输出的处理结果,以避免再次处理相同的多模态输入(例如图像)。
当传入新数据时,我们首先检查哪些项在缓存中,哪些项缺失。缺失的项会以单个批次的形式传递给 HF 处理器并缓存起来,然后与缓存中已有的项合并。
由于我们只处理缺失的多模态数据项,输入占位符 token 的数量不再与多模态输入的数量对应,因此它们不能与文本提示一起传递给 HF 处理器。因此,我们分别处理文本和多模态输入,使用占位文本来避免 HF 错误。由于这跳过了 HF 的提示更新代码,我们随后会应用自动提示更新,以保持输出 token 和多模态数据彼此一致。