跳到内容

多模态数据处理

为了在 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. 对文本进行分词
  2. 处理多模态输入
  3. 执行提示更新

并且我们要求

  • 对于文本 + 多模态输入,应用所有步骤 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 和多模态数据彼此一致。