跳到内容

多模态数据处理

为了在 vLLM 中启用各种优化(如 分块预填充 (chunked prefill)前缀缓存 (prefix caching)),我们使用 BaseMultiModalProcessor,根据 HF 处理器的输出,提供占位符特征标记(如 <image>)与多模态输入(如原始输入图像)之间的对应关系。

以下是 BaseMultiModalProcessor 的主要特性:

提示词更新检测

HF 处理器的主要职责之一是用占位符标记更新提示词。例如:

  • 在字符串开头插入特征占位符标记(例如 <image><image>...<image>,其数量等于特征维度大小)。
  • 将现有的输入占位符标记(例如单个图像的 <image>)替换为特征占位符标记(例如 <image><image>...<image>,其数量等于特征维度大小)。

关于哪些标记已被更新的信息,是寻找占位符特征标记与多模态输入之间对应关系的关键。

在 vLLM 中,此信息通过 _get_prompt_updates 中的 PromptUpdate 指定。我们可以通过检查更新后的标记是否存在,来自动检测 HF 是否更新了提示词。

分词后的提示词输入

为了支持在独立进程中进行分词,我们支持将输入 Token ID 与多模态数据一起传递。

问题所在

考虑到 HF 处理器遵循以下主要步骤:

  1. 1. 对文本进行分词
  2. 2. 处理多模态输入
  3. 3. 执行提示词更新

而我们的要求是:

  • 对于“文本 + 多模态输入”,应用所有步骤 1-3。
  • 对于“分词后内容 + 多模态输入”,仅应用步骤 2-3。

如何在不重写 HF 处理器的前提下实现这一点?我们可以尝试对不同的输入多次调用 HF 处理器:

  • 对于“文本 + 多模态输入”,直接调用 HF 处理器即可。
  • 对于“分词后内容 + 多模态输入”,仅对多模态输入调用处理器。

虽然 HF 处理器原生支持“文本 + 多模态输入”,但对于“分词后内容 + 多模态输入”并非如此:如果输入占位符标记的数量与多模态输入的数量不一致,则会抛出错误。

此外,由于分词后的文本没有经过 HF 处理器,我们必须自行应用“步骤 3”,以保持输出标记与多模态数据的一致性。

哑文本 (Dummy text)

我们通过要求每个模型通过 get_dummy_text 定义如何根据多模态输入的数量生成哑文本,从而解决了第一个问题。这使我们能够生成对应于多模态输入的哑文本,并将它们一起输入,以获得处理后的多模态数据。

自动提示词更新

我们通过在 _apply_prompt_updates 中实现模型无关的代码来解决第二个问题,该代码根据 _get_prompt_updates 输出的规范自动用特征占位符标记更新提示词。

摘要

借助于哑文本和自动提示词更新,我们的多模态处理器终于可以同时接收文本或分词后的提示词以及多模态数据。详细逻辑见 _apply_hf_processor_main

处理器输出缓存

一些 HF 处理器(例如 Qwen2-VL 的处理器) 非常缓慢。为了缓解这个问题,我们缓存了 HF 处理器的多模态输出,以避免再次处理相同的多模态输入(如图像)。

当新数据传入时,我们首先检查哪些项在缓存中,哪些缺失。缺失的项会在单个批次中传递给 HF 处理器并进行缓存,然后再与缓存中的现有项合并。

由于我们只处理缺失的多模态数据项,输入占位符标记的数量不再对应于多模态输入的数量,因此它们不能与文本提示词一起传递给 HF 处理器。因此,我们分别处理文本和多模态输入,使用 哑文本 来避免 HF 报错。由于这跳过了 HF 的提示词更新代码,我们之后会应用 自动提示词更新,以保持输出标记和多模态数据之间的一致性。