执行器 API
TensorRT-LLM 包含一个高级的 C++ API,称为 Executor API,它允许您异步执行请求,支持在飞行中进行批处理,并且不需要定义回调函数。
一个软件组件(在文本中称为“客户端”)可以使用在executor.h文件中定义的API与执行器进行交互。有关API的详细信息,请参阅_cpp_gen/executor.rst。
以下部分提供了Executor API中定义的主要类的概述。
API
执行器类
Executor 类负责接收来自客户端的请求,并为这些请求提供响应。执行器通过提供一个包含 TensorRT-LLM 引擎的目录路径或包含引擎和模型 JSON 配置的缓冲区来构建。客户端可以创建请求,并使用 Executor 类的 enqueueRequest 或 enqueueRequests 方法将这些请求加入队列以执行。加入队列的请求将由执行器调度执行,并且多个独立的请求可以在主执行循环的每次迭代中批量处理(这一过程通常称为连续批处理或迭代级批处理)。可以通过调用 awaitResponses 方法并提供请求 ID 来等待特定请求的响应。或者,在调用 awaitResponses 时省略请求 ID,可以等待任何请求的响应。Executor 类还允许使用 cancelRequest 方法取消请求,并使用 getLatestIterationStats 方法获取每次迭代和每个请求的统计信息。
请求类
Request 类用于定义请求的属性,例如输入令牌ID和要生成的最大令牌数。streaming 参数可用于指示请求是否应为每个新生成的令牌生成响应(streaming = true)或仅在生成所有令牌后生成响应(streaming = false)。请求的其他必需参数包括采样配置(由SamplingConfig类定义),其中包含控制解码过程的参数,以及输出配置(由OutputConfig类定义),该配置控制特定响应中应包含在Result中的信息。
在构建请求时,还可以提供可选参数,例如不良词汇列表、停用词列表、客户端ID,或用于提示调优、LoRA或推测性解码的配置对象,或者要生成的序列数量等。
响应类
Executor 类的 awaitResponses 方法返回一个响应向量。每个响应包含与此响应关联的请求ID,并且还包含一个错误或一个 Result。在尝试使用 getResult 方法获取与此响应关联的 Result 之前,请使用 hasError 方法检查响应是否有错误。
结果类
Result 类保存了给定请求的结果。它包含一个名为 isFinal 的布尔参数,用于指示这是否是给定请求 ID 返回的最后一个 Result。它还包含生成的令牌。如果请求配置为 streaming = false 和 numReturnSequences = 1,将返回单个响应,isFinal 布尔值将设置为 true,并且所有生成的令牌将包含在 outputTokenIds 中。如果使用 streaming = true 和 numReturnSequences = 1,Result 将包含一个或多个令牌(取决于请求的 returnAllGeneratedTokens 参数),除了最后一个结果,并且与请求关联的最后一个结果的 isFinal 标志将设置为 true。
The request numReturnSequences parameter controls the number of output sequences to generate for each prompt. When this option is used, the Executor will return at least numReturnSequences responses for each request, each containing one Result. In beam search (beamWidth > 1), the number of beams to be returned will be limited by numReturnSequences and the sequenceIndex attribute of the Result class will always be zero. Otherwise, in sampling (beamWidth = 1), the sequenceIndex attribute indicates the index of the generated sequence in the result (0 <= sequenceIndex < numReturnSequences). It contains a Boolean parameter called isSequenceFinal that indicates if this is the last result for the sequence and also contains a Boolean parameter isFinal that indicates when all sequences for the request have been generated. When numReturnSequences = 1, isFinal is identical to isSequenceFinal.
以下是一个示例,展示了当numReturnSequences = 3时,3个响应的子集可能看起来是什么样子:
Response 1: requestId = 1, Result with sequenceIndex = 0, isSequenceFinal = false, isFinal = false
Response 2: requestId = 1, Result with sequenceIndex = 1, isSequenceFinal = true, isFinal = false
Response 3: requestId = 1, Result with sequenceIndex = 2, isSequenceFinal = false, isFinal = false
在这个例子中,每个响应包含不同序列的一个结果。第二个结果的isSequenceFinal标志被设置为true,表示这是sequenceIndex = 1的最后一个结果,然而,每个响应的isFinal标志被设置为false,因为序列0和2尚未完成。
使用不同波束宽度发送请求
如果满足以下条件,执行器可以处理具有不同波束宽度的请求:
模型是使用
max_beam_width > 1构建的。执行器配置了一个
maxBeamWidth > 1(配置的maxBeamWidth必须小于或等于模型的max_beam_width)。请求的光束宽度小于或等于配置的
maxBeamWidth。对于具有两种不同波束宽度的请求,
x和y,具有波束宽度y的请求在所有具有波束宽度x的请求的响应都被等待之前不会被加入队列。
执行器的请求队列必须为空,以允许其重新配置自身以适应新的波束宽度。当具有新波束宽度的请求入队时,此重新配置将自动发生。如果同时入队具有不同波束宽度的请求,执行器将遇到错误并提前终止所有请求。
使用Logits后处理器控制输出
可选地,您可以通过提供一个Executor::LogitsPostProcessorConfig的实例来修改网络生成的logits。例如,此功能可用于生成JSON格式的输出。Executor::LogitsPostProcessorConfig指定了以下形式的命名回调映射
std::unordered_map<std::string, function<Tensor(IdType, Tensor&, BeamTokens const&, StreamPtr const&, std::optional<IdType>)>>
地图键是与该logits后处理回调相关联的名称。然后,每个请求可以指定用于该特定请求的logits后处理器的名称(如果有的话)。
回调函数的第一个参数是请求ID,第二个是logits张量,第三个是到目前为止由请求生成的tokens,第四个是logits张量使用的操作流,最后一个是可选的客户端ID。回调函数返回一个修改后的logits张量。多个请求可以共享相同的客户端ID,回调函数可以根据客户端ID使用不同的逻辑。
你必须使用流来访问logits张量。例如,要执行与偏置张量的加法操作,加法操作会在该流上排队。或者,你可以调用stream->synchronize(),但这会减慢整个执行管道的速度。
执行器还包括一个LogitsPostProcessorBatched方法,该方法可以批量修改多个请求的logits。批量方法允许进一步的优化并减少回调开销。
std::function<void(std::vector<IdType> const&, std::vector<Tensor>&, std::vector<std::reference_wrapper<BeamTokens const>> const&, StreamPtr const&, std::vector<std::optional<IdType>> const&)>
可以在LogitsPostProcessorConfig中指定一个单一的批量回调。每个请求可以通过指定Request::kBatchedPostProcessorName作为logits后处理器的名称来选择应用此回调。
注意:目前,STATIC 批处理类型不支持任何回调变体。
在多GPU运行中,默认情况下,回调函数会在第一个张量并行组中的所有等级上调用。为了确保正确执行,请在这些等级上复制由回调函数访问的客户端状态。如果复制成本高或不可行,请使用LogitsPostProcessorConfig::setReplicate(false)仅在等级0上调用回调函数。执行器内部会广播采样的令牌以确保正确执行。
结构化输出与引导解码
引导解码控制生成输出,使其符合预定义的结构化格式,例如JSON或XML。目前,引导解码支持XGrammar后端。
要启用引导解码,必须在构建Executor时提供GuidedDecodingConfig的有效实例。GuidedDecodingConfig应使用一些分词器信息构建,包括encodedVocab、tokenizerStr(可选)和stopTokenIds(可选)。给定一个Hugging Face分词器,可以通过以下方式提取这些信息:
encoded_vocab = tokenizer.get_vocab()
encoded_vocab = [token for token, _ in sorted(encoded_vocab.items(), key=lambda x: x[1])]
tokenizer_str = tokenizer.backend_tokenizer.to_str()
stop_token_ids = [tokenizer.eos_token_id]
请参考tensorrt_llm/llmapi/tokenizer.py获取更多详细信息。您可以将这些材料转储到磁盘,并重新加载到C++运行时中使用。
每个请求可以选择性地指定一个GuidedDecodingParams,它定义了所需的结构化格式。目前,它支持四种类型:
GuidedDecodingParams::GuideType::kJSON: 生成的文本符合JSON格式;GuidedDecodingParams::GuideType::kJSON_SCHEMA: 生成的文本符合JSON格式并带有额外的限制;GuidedDecodingParams::GuideType::kREGEX: 生成的文本符合正则表达式;GuidedDecodingParams::GuideType::kEBNF_GRAMMAR: 生成的文本符合扩展巴科斯-瑙尔形式(EBNF)语法。
后三种类型应与提供给GuidedDecodingParams的模式/正则表达式/语法一起使用。
C++ 执行器 API 示例
提供了两个C++示例,展示了如何使用Executor API,可以在examples/cpp/executor文件夹中找到。
Executor API 的 Python 绑定
Python 绑定也可用于从 Python 使用 Executor API。Python 绑定定义在 bindings.cpp 中,一旦构建完成,可以在包 tensorrt_llm.bindings.executor 中使用。在 Python 解释器中运行 'help('tensorrt_llm.bindings.executor') 将提供可用类的概述。
此外,提供了三个Python示例,以演示如何使用Python绑定到Executor API来处理单GPU和多GPU模型。它们可以在examples/bindings中找到。
使用Triton推理服务器进行飞行批处理
一个Triton推理服务器C++ 后端与TensorRT-LLM一起提供,它包括使用飞行中批处理服务模型所需的机制。该后端也是一个很好的示例,展示了如何使用TensorRT-LLM C++执行器API实现飞行中批处理。