ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Outlines Logits Processors 完全指南:通过自定义概率分布约束控制文本生成

Outlines Logits Processors 完全指南:通过自定义概率分布约束控制文本生成 Outlines Logits Processors 完全指南通过自定义概率分布约束控制文本生成【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlinesLogits processors 是 Outlines 中控制文本生成的关键机制它们通过在每一步生成时修改模型原始输出的 logits即下一个 token 的概率分布从而对模型的选择施加约束。本文围绕docs/features/advanced/logits_processors.md展开系统讲解 Logits Processor 的原理、三种可驾驭steerable模型的接入方式、如何使用内置处理器如RegexLogitsProcessor以及如何继承OutlinesLogitsProcessor编写自己的处理器读完你将能够用任意自定义约束正则、词表白名单、业务规则等直接驱动本地模型的采样过程。什么是 Logits ProcessorLogits Processor 是控制文本生成的对象。大语言模型在每个生成步骤输出一个 logits 向量形状为[batch_size, vocab_size]词表上每个 token 对应一个未归一化的分数采样器基于该向量挑选下一个 token。Logits Processor 的作用就是在采样发生之前修改这份 logits将不允许出现的 token 的 logits 置为-inf使其概率归零、永远不会被选中或提高/压低某些 token 的分数从而偏向特定的采样路径。据此Processor 可以实现三类典型能力原文档列举生成结构化输出例如生成符合特定 JSON Schema 的内容JSON、正则、CFG 等输出类型在内部都被编译成对应的 logits processor禁止模型生成特定词或 token例如屏蔽敏感词、禁用某个 token 序列实现自定义 token 采样策略例如只允许二进制字符、限定枚举值集合等。在 Outlines 中当你为模型指定一个output_typePython 类型、正则或 CFG时框架会在后台把它翻译成一个 logits processor 并传给推理引擎见 generator.py 中python_types_to_terms到get_cfg_logits_processor/get_json_schema_logits_processor/get_regex_logits_processor的分支。而 Logits Processor 机制则允许你跳过这一层编译直接把自己的处理器注入生成流程实现输出类型无法表达的任意约束。适用范围哪些模型支持 Logits ProcessorsOutlines 只有在使用**可驾驭模型steerable models**时才使用 logits processors——即本地运行、允许对生成过程进行细粒度控制的模型。当使用这类模型时你提供的输出类型会被转换成 logits processor 再传给推理引擎。支持 logits processors 的模型有三种原文档明确列出LlamaCppMLXLMTransformers从源码看这一点与SteerableModel的定义严格对应在 models/base.py 中可驾驭模型除了要满足通用模型接口外还必须声明一个tensor_library_name属性——它决定了处理器内部用哪种张量库来操作 logits。三种模型的实际调用也印证了这一点transformers.py的generate/generate_batch通过self.type_adapter.format_output_type(output_type)得到最终注入generate的 logits processor见 transformers.pyllamacpp.py与mlxlm.py也以同样的方式把处理器传给各自的推理入口llamacpp.py、mlxlm.py。反过来黑盒模型black-box models如 OpenAI、Anthropic 等远程 API不支持 logits processors。在 generator.py 中如果为非SteerableModel传入processor参数会直接抛出NotImplementedError(This model does not support logits processors)。把 Logits Processor 接入生成流程Generator 的 processor 参数与原文档强调的一致不能直接用模型调用 logits processor必须创建Generator实例并通过processor关键字参数传入。import transformers from outlines import Generator, from_transformers from outlines.processors import RegexLogitsProcessor # Create a model model from_transformers( transformers.AutoModelForCausalLM.from_pretrained(NousResearch/Hermes-2-Pro-Llama-3-8B), transformers.AutoTokenizer.from_pretrained(NousResearch/Hermes-2-Pro-Llama-3-8B) ) # Create a regex logits processor that only returns hex unicode notations logits_processor RegexLogitsProcessor(rU\[0-9A-Fa-f]{4,6}, model.tokenizer, model.tensor_library_name) # Create a generator with the logits processor and use it to generate text generator Generator(model, processorlogits_processor) response generator(Whats the unicode for the hugging face emoji) print(response) # U1F917关于Generator的processor参数源码给出了更精确的语义见 generator.pyoutput_type与processor互斥Generator内部会统计两个参数同时非None的个数若同时提供则抛出ValueError(At most one of output_type or processor can be provided)processor仅对SteerableModel有效传入处理器时走SteerableGenerator.from_processor(model, processor)分支其余模型类型一律抛错from_processor是类方法它跳过__init__中基于输出类型编译处理器的逻辑SteerableGenerator的__init__只处理output_type直接构造一个已绑定好处理器的实例见 generator.py。另外值得注意的是SteerableGenerator在每次__call__、batch、stream生成前都会调用self.logits_processor.reset()见 generator.py因此处理器可以借助reset()维护跨生成步骤的状态例如OutlinesCoreLogitsProcessor用is_first_token标志控制首 token 的特殊初始化逻辑见 outlines_core.py。说明RegexLogitsProcessor属于底层后端处理器实际由outlines_core、xgrammar、llguidance三个后端实现对应 backends/outlines_core.py、backends/xgrammar.py、backends/llguidance.py 中的OutlinesCoreLogitsProcessor、XGrammarLogitsProcessor、LLGuidanceLogitsProcessor。它们都继承自下文介绍的OutlinesLogitsProcessor即输出类型 → 后端处理器的统一基类。编写自定义 Logits Processor原文档给出的核心路径是继承OutlinesLogitsProcessor实现process_logits方法。process_logits的契约如下两个参数均为二维张量input_ids当前已有序列的 token id形状为 2D 张量logits当前生成步的 logits形状为 2D 张量返回值处理后的 logits2D 张量。下面的例子完整继承自原文档创建了一个只允许输出二进制字符token ID 15 和 16 对应 0 和 1的自定义处理器强制模型只用 0/1 作答from outlines.processors.base_logits_processor import OutlinesLogitsProcessor, TensorType from outlines import Generator, from_transformers import transformers ALLOWED_TOKENS [15, 16] # token IDs corresponding to 0 and 1 in the models vocabulary # Subclass OutlinesLogitsProcessor class BinaryLogitsProcessor(OutlinesLogitsProcessor): def process_logits(self, input_ids: TensorType, logits: TensorType) - TensorType: # Create a mask for all tokens mask self.tensor_adapter.boolean_ones_like(logits) # Set mask to False for the allowed tokens for token_id in ALLOWED_TOKENS: mask[:, token_id] False # Set non-allowed tokens to -inf so they are not selected logits[mask] float(-inf) return logits # Create a regular model tf_tokenizer transformers.AutoTokenizer.from_pretrained(NousResearch/Hermes-2-Pro-Llama-3-8B) tf_model transformers.AutoModelForCausalLM.from_pretrained(NousResearch/Hermes-2-Pro-Llama-3-8B) model from_transformers(tf_model, tf_tokenizer) # Instantiate your custom logits processor logits_processor BinaryLogitsProcessor(model.tensor_library_name) prompt Write the number 47 in binary. For example, 1010 is the binary representation of 10. Answer just with the binary number composed of 0s and 1s. formatted_prompt tf_tokenizer.apply_chat_template( [{role: user, content: prompt}], tokenizeFalse ) # Create a generator with the custom logits processor instance and use it to generate text generator Generator(model, processorlogits_processor) response generator(formatted_prompt) print(response) # 101111基类内部机制__call__与process_logits的分工OutlinesLogitsProcessor的设计是模板方法模式见 base_logits_processor.py__call__是模型调用的统一入口base_logits_processor.py。由于不同模型存储input_ids和logits的结构不同例如 mlx-lm 可能传入 1D 的input_ids__call__会在调用process_logits前把输入标准化为 2D 张量处理完成后再还原为原始形状返回若input_ids为 1D 且logits为单序列 2D则先对input_ids做unsqueeze断言logits与input_ids除最后一维外的形状一致logits为 2D 时直接调用process_logits为 1D 时先unsqueeze、处理后squeeze还原其他维度直接抛ValueError。process_logits是子类需要实现的核心方法base_logits_processor.py基类中标注为abstractmethod。其 docstring 还给出了编写通用处理器的三条重要提醒logits processor 只被使用一次不会为新的序列生成器重复应用有些模型只传output_ids而 llamacpp、transformers 等模型会以input_ids作为前缀传入某些采样方法如 beam search在 vLLM 这类模型中会导致序列顺序不稳定。reset()钩子base_logits_processor.py默认空实现仅在处理器需要为新一轮生成重置内部状态时由子类覆写——正如前文所述Generator每次生成前都会调用它。构造参数tensor_library_name与 Tensor AdapterOutlinesLogitsProcessor的构造签名是__init__(self, tensor_library_name: str)base_logits_processor.py合法取值有mlx、numpy、torch必须与模型使用的张量库一致可以从model.tensor_library_name直接取得。初始化时基类会从tensor_adapters注册表中查找对应的适配器类并实例化tensor_adapters/init.py若传入未知库名则抛出NotImplementedError(fLibrary {tensor_library_name} is not available)。另外当库名为torch时基类会做一次特殊处理导入torch._dynamo并设置suppress_errors True以规避 Python 3.12 下 torch 警告可能引发的错误。每个处理器实例都持有self.tensor_adapter它屏蔽了不同张量库的 API 差异。TensorAdapter抽象基类tensor_adapters/base.py定义了以下方法你的process_logits内部应该通过这些方法而不是直接调用torch/numpy/mlx的 API 来操作张量方法作用shape(tensor)返回张量形状unsqueeze(tensor)/squeeze(tensor)在 axis 0 上增/减一维to_list(tensor)/to_scalar(tensor)转 Python list / 标量full_like(tensor, fill_value)生成同形状填充张量注意无论何种库入参可能为 torch 张量concatenate(tensors)沿 axis 0 拼接get_device(tensor)/to_device(tensor, device)查询 / 迁移设备boolean_ones_like(tensor)生成同形状的布尔全 True 张量apply_mask(tensor, mask, value)将 mask 为 True 的位置填充为指定值argsort_descending(tensor)沿最后一维返回降序排序索引三个具体实现分别位于 tensor_adapters/torch.py、tensor_adapters/numpy.py、tensor_adapters/mlx.py。例如文档示例中的boolean_ones_like在 torch 下对应torch.ones_like(tensor, dtypetorch.bool)在 numpy 下对应numpy.ones_like(tensor, dtypebool)apply_mask则分别对应torch.masked_fill与numpy.where。形状契约与测试验证process_logits接收 2D 张量并非形式约定而是由基类的形状标准化逻辑保证并经过测试固化下来的。在 tests/processors/test_base_processor.py 中针对numpy、torch以及可选安装的mlx三种库各测试了六种输入组合其中合法的有三种input_ids与logits均为 1D两者均为 2Dinput_ids为 1D、logits为 2D 且只含单条序列此时会自动unsqueeze归一。非法组合则必须报错input_ids为 2D 而logits为 1D → 断言失败input_ids为 1D、logits为 2D 但含多条序列 → 断言失败两者均为 3D →ValueError。测试还验证了三个关键行为构造时未知库名抛NotImplementedError、reset()默认可用、以及调用后 logits 形状保持不变见 test_base_processor.py。这些用例是你编写自定义处理器时可以对照的形状契约避免因模型传参习惯不同而踩坑。端到端视角一个处理器从创建到生效的完整链路结合 generator.py 与各模型实现一个自定义 processor 的完整调用链可以概括为你在Generator(model, processorxxx)中传入处理器Generator检测到model是SteerableModel且processor非空调用SteerableGenerator.from_processor(model, processor)generator.py生成时SteerableGenerator.__call__先调用processor.reset()再把处理器作为output_type参数传给model.generate(prompt, logits_processor)generator.py模型侧以 transformers 为例通过self.type_adapter.format_output_type(output_type)包装后注入 transformers 的generate(..., logits_processor...)transformers.py推理库在每一步采样前调用处理器的__call____call__标准化形状后调用你的process_logits把修改后的 logits 返回给采样器。从OutlinesCoreLogitsProcessor的实现outlines_core.py可以看到生产级处理器的典型工作方式首 token 时按batch_size与vocab_size分配 bitmask通过Guide/Index维护约束状态机每步用fill_next_token_bitmask计算允许的 token 集合再用apply_token_bitmasktorch 下为apply_token_bitmask_inplace就地改写 logits。这与你手写的置-inf在原理上完全一致——约束最终都是通过对 logits 的掩码操作实现的。其他用法与注意事项与 llama.cpp 的原生接口配合仓库中的 examples/llamacpp_processor.py 展示了另一种使用方式——直接用llama_cpp的Llama.create_completion(..., logits_processorLogitsProcessorList([...]))传入JSONLogitsProcessor(Character, tokenizer, tensor_library_namenumpy)其中Character是 Pydantic 模型tokenizer 为LlamaCppTokenizer。这说明处理器对象本身与模型解耦既可以经由 Outlines 的Generator使用也可以作为标准 HF/llama.cpp logits processor 组合进原生推理流程。处理器是一次性的不要假设同一个处理器实例可以在多个独立的序列生成任务间复用而不做状态清理Generator会在每次生成前调用reset()你的自定义子类若维护了内部状态如已生成字符计数、阶段标志应当覆写reset()重置它。黑盒模型不可用processor参数只接受SteerableModel本地模型远程 API 模型会抛NotImplementedError此时应使用output_type让服务端处理约束。output_type与processor二选一两者同时提供会抛ValueError如果你有自定义处理器就不需要再传output_type。延伸阅读基类与形状标准化实现src/outlines/processors/base_logits_processor.py处理器公开导出src/outlines/processors/init.pyTensor Adapter 抽象与三种实现src/outlines/processors/tensor_adapters/Generator/SteerableGenerator的processor参数语义src/outlines/generator.py三种后端处理器实现src/outlines/backends/形状契约与错误分支测试tests/processors/test_base_processor.pyllama.cpp 原生集成示例examples/llamacpp_processor.py输出类型如何被编译为处理器的统一入口output_type路径src/outlines/backends/base.py【免费下载链接】outlinesStructured Outputs项目地址: https://gitcode.com/GitHub_Trending/ou/outlines创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表