一、前言
大型语言模型在诸如对话问答、代码生成等广泛任务上表现出了出色的性能。
在较高的层次上,给定一段输入,大语言模型可用于按照概率统计方式自动补全序列。在此基础上,用户用指令(instructions)或示例(examples)去提示(prompt)大语言模型,以实施各种下游任务。
本质上,提示(prompt)方法是语言模型、用户和外部工具(例如计算器)之间的交互规范。
然而,要获得最先进的性能,或针对特定任务适配的语言模型,需要进行大量复杂的针对特定任务/特定模型的临时交互调整,这非常消耗时间,也不利于持续迭代LLM程序的整体表现效果。
基于这个背景,论文提出了语言模型编程(Language Model Programming,LMP)的思想。 LMP主要包括以下几个方面,
- 纯文本提示(text prompting)
- 文本提示(text prompting)和编程脚本(scripting)组合
- 语言模型输出约束(output constraints),这个特性使得LMP能够轻松适应许多任务,同时抽象语言模型内部结构并提供高级语义。
为了启用 LMP,论文实现了 LMQL(Language Model Query Language),它利用来自 LMP prompt的约束和控制流,以生成有效的推理过程,最大限度地减少对底层语言模型的昂贵调用的数量。
论文通过实验证明 LMQL 可以以直观的方式追上各种最先进的提示方法,特别是促进使用现有高级 API 难以实现的交互流程。
论文实验的评估表明我们保持或提高了几个下游任务的准确性,同时也显着减少使用付费 API 所需的计算量或成本(节省 26-85% 的成本)。
参考资料:
https://arxiv.org/pdf/2212.06094.pdf
二、INTRODUCTION
大型语言模型(large LMM)已被证明在各种基于语言的任务上取得了成功,例如机器翻译、文本摘要、问答、推理、从文本生成代码等等。基于这些令人惊艳的效果,LMM 已经超过机器学习社区,变得越来越流行,并正在慢慢集成到许多应用程序中。
大语言模型的本质是一种概率预测模型,它在内部最小操作单元是token,这种token预测方式不同于人类感知语言的方式。本质上,LLM的训练过程是在极大似然拟合一个“pre-token -> next-token”的条件概率函数。
虽然大语言模型可以用直观概念的指令(instructions)或者示例(examples)进行提示(prompt),但使用大语言模型依然存在很多挑战,例如,
- 首先,因为LMM是在token级别进行操作,因此很难将解码过程限制在合法单词或短语中
- 此外,许多prompt提示技术可能需要用户和LLM之间的来回交互(例如像 ChatGPT 这样的聊天机器人)或特定于任务的接口(例如执行与外部控制逻辑的算术计算)。为了实现这些prompt提示,需要大量的手动操作(与模型的解码过程进行交互),这限制了completions生成的通用性
- 最后,由于 LLM 一次仅生成一个(sub-word)token,完成一个序列可能需要多次调用。 此外,随着前缀、提示和迄今为止生成的响应的增长,解码变得越来越昂贵
同时由于语言模型通常是非常大的神经网络,因此实际推理中对计算成本存在较高的要求,同时也存在明显地延迟。对于付费使用的 API(例如 OpenAI 的 GPT 模型),这会导致每个回答的查询的使用成本很高。
在这项工作中,我们提出了通过LMQL进行语言模型编程的想法,通过在自然语言提示的基础上扩展了两大核心特性
- lightweight scripting(轻量级脚本)
- constraining of outputs(输出约束)
这种设计有利于在进行LLM prompting中实现前端/后端分离,即允许用户指定复杂的交互(interactions)、控制流(control flow)和约束(constraints),而无需了解 LLM 的内部结构,例如向量化(tokenization)、实现(implementation)和模型架构(architecture)。
此外,LMQL构建的程序屏蔽了底层LLM的细节,这大大提高了LLM迁移性(底层LLM可插拔)。
总体而言,语言模型编程(LMP)保留了简单的自然语言驱动的 LM 接口,还可以实现精确的约束、脚本编写、以及高效的解码。
为了启用 LMP,我们提出了一种称为语言模型查询的新颖语言和运行时语言 (LMQL)。 LMQL 是一种高级语言,具有类似 SQL 的声明性元素和脚本的命令式语法。 底层运行时与现有的LM语言模型兼容。
LMQL 可被用于表达种类丰富地prompt方法(prompt交互范式),而仅仅使用简单、简洁且与供应商无关的代码。
此外,专门设计的评估语法(evaluation semantics),支持部分评估(partial evaluation )和前瞻(lookahead),使我们能够对查询进行端到端的优化:LMQL 利用输出约束和脚本化提示,修剪了 LM 的搜索空间,带来了最高80%的推理成本降低。
我们在图 1 中展示了两个简单 LMQL 程序的示例。
Fig1. Two LMQL programs that demonstrate core features like scripted prompting, eager output constraining and validation, and prompting with control flow.
总结来说,论文的核心贡献是:
- 引入了语言模型编程的新范式,制定并解决了最近的语言模型提示技术带来的几个挑战。
- 提出了 LMQL,一种高效、高级的 LM 查询语言,支持脚本化提示和输出约束。
- 展示了如何用简单、简洁的 LMQL 程序,表达广泛和先进的prompt提示技术。不仅如此,LMQL 将推理成本和延迟降低了 26-80%,同时保持或提升了任务准确性。
笔者认为,LMQL的主要价值在于,提供了一套可编程的prompt开发框架,使得开发者可以更有效、更精确地构建和LLM的信息交互通道,LMQL为广大开发者开启LLM编程时代提供了有力的工具,类似在software1.0时代IDE的作用。
三、OVERVIEW: LANGUAGE MODEL PROGRAMMING
0x1:Background: (Large) Language Models
1、Few-Shot Prompting
Few-shot prompt 指的是语言模型不需要针对下游任务(例如分类、问题回答等)进行定制化地训练。 相反,使用广泛的文本序列预测数据集进行预训练,并在调用它们时以示例的形式提供上下文即可达到不错的效果。
我们在下图中展示了一个例子,
Fig. 3. Example of few-shot promptin
其中,我们的目标是将“奶酪”从英语翻译成法语。为此,我们提供了几个示例,然后要求语言模型以相同的语法完成“cheese”的翻译,
这样,翻译和其他任务就可以被重新定义为简单的序列补全任务,这使得 LM 成为强大的多任务推理机,甚至是一个通用计算机。
2、Multi-Part Prompting
由于其强大的推理能力,LM 不再只是被用于简单的prompt-completions,也可作为“组合推理引擎”被集成到更大的程序中。
LM 编程方案有很多不同的编程范式,例如,
- 迭代分解(Iterated Decompositions),典型地项目如 langchain,它更关注按顺序使用的多个提示的组成。
- 元提示(meta prompting)
- 工具使用( tool use)
- 思维链(Chain-Of-Thought)
- 键值存储(Key-Value Memory)
- LM 级联框架(LM cascades frame):在概率编程环境中使用。
0x2:Key Challenges
在本节中,我们先支出 LM 使用中的三个关键挑战,之后会讨论如何用语言模型编程和 LMQL 来克服它们。
挑战一:复杂任务需要和LLM多次交互才能得到最终结果
解码过程中的 LM 交互仍然是一个挑战。
考虑下面图4的一个例子,该方法讨论了元提示(meta prompt)的想法,为了获得特定问题的答案,首先要求语言模型扩展prompt提示,然后再次将扩展后的prompt输入到同一模型以获得答案。 在图4的例子中可以看到,我们的目标是找到“什么是地球的周长?”。在元提示(meta prompt)中,我们首先向语言模型询问能否回答这个问题的专家名字,然后询问该专家如何回答这个问题。
如果使用传统的人工 LM 界面,需要人工输入prompt提示的第一部分,手动调用 LM 获得专家名称,得到第一个completions序列,然后从该completions序列中提取专家名称,并手动将其输入到模板的其余部分,然后再次将其提供给LM以获得最终的回答。 这种方法需要通过 API 进行大量的手动交互,甚至人必须参与到整个循环(HITL)中。
这里需要注意的是,一旦某个值固定(例如专家姓名),解码算法将假定它是提示的固定部分,并且不会与提示的其余部分一起,优化最终的答案。在 HITL 循环流程中,这使用户能够手动尝试多个专家名称,并选择他们最喜欢的名称,完成completions的生成。 然而,它排除了自动联合优化所有模板参数,以此最大化总体似然的可能性,也排除了可能会产生更好的结果的可能。
挑战二:Constraints & Token Representation
下图示例中展示的另一个挑战问题是输出约束。
有时,LM 会在生成过程中偏离主题并产生太长的连续文本序列。虽然有些答案可以很好地被用于下一阶段的prompt提示,但大多数产生的结果是错误且无用的。如果这些生成的结果被用于下游另一个信息处理系统,这个问题就尤其严重,因为下游系统可能只能处理固定格式的输入数据。
为了规避这个问题,开发者需要对生成的文本进行限制,因为 LM 本质上是一个概率预测模型,它并不天然地遵守这些限制。
理想情况下,这些约束应该可以用人类可理解的概念和逻辑来表达,因为用户只能基于单词、句子和实体进行推理,而不是像 LM 那样在token级别上进行推理。
挑战三:Efficiency and Cost
最后,效率和性能仍然是巨大的挑战。 虽然人们已经投入了很大精力致力于使 LM 中的推理步骤更加高效,但它们仍然需要昂贵的、高端 GPU 以合理的性能运行。
正因为如此,许多实际用户求助于到在云上运行的托管模型,但即使托管到云上,依然需要使用付费 API 进行调用,LM 查询在计算和财务方面都可能变得非常昂贵。
然而,当引入语言模型编程和约束时,新的优化机会得以出现,因为“预定义行为”和“搜索空间限制”可以减少 LM 被调用的次数。 在这种设置下,验证、解析的成本与单个 LM 调用的巨大成本相比可以大幅减少。
Fig.4 Example of a meta prompt for the circumference of the earth and its scripted prompting counterpart
0x3:Language Model Programming in LMQL
这章我们讨论 LMQL 如何帮助克服传统LLM面临的挑战。
如下图(c)所示,我们编写与之前的人工promot-completions交互相同的查询LMQL 语法。
![](/i/l/?n=23&i=blog/532548/202307/532548-20230719092130388-795997629.png)
在LMQL query中,一切变量被输入到 LM 之前,以及通过LM预测得到的答案,都使用 [VAR] 进行了占位。大括号中的变量名 [VAR] 只是调用先前定义的变量。这大大简化了prompt提示并消除手动交互的需要。
此外在本例中,它还允许联合考虑专家姓名(LLM生成的临时中间变量结果)和答案的解码过程(LLM生成的最终结果)。
此外在本例中,为了解决运行时间较长的句子的问题,LMQL 允许对专家姓名(LLM生成的临时中间变量结果)施加约束。在上图中可以看到,约束强制执行 EXPERT 的解码标记最多为三个单词,并且序列需要以“.”结尾。 虽然可以使用当前的查询 API 指定最大长度,但它们通常作用在token级别上,因此无法精确控制最终序列的词/句生成结果。相比之下,LMQL 支持声明性高级约束,这些约束需要在解码期间强制执行。
总体而言,语言模型编程概括并自动化了许多 multi-part prompting方法,与用户必须手动尝试 EXPERT 的多个值然后选择最好的一个传统手工方式相比,LMQL 允许用户提前施加一组经过考虑的专家限制到prompt-completions生成过程中,并使该选择过程完全自动化。 一旦完成开发和测试,LMQL 查询(和约束)可以应用于无监督的许多不同输入。
LMQL 约束强制输出符合提示模板,并避免生成结果过长等故障情况。这可以提高的下游任务的准确性。
最后,LMQL 也比手动交互更加高效,不需要多次 LM 调用。
四、THE LMQL LANGUAGE
在接下来讨论运行时和语法语义之前,我们在这里提供一份 LMQL 语法的宏观解释。对于具体示例,请考虑图 1 中给出的 LMQL 程序。
Fig. 5. Syntax of LMQL. Brackets denote optional elements. Syntax is generally python based.
LMQL程序有5个部分:- 解码器(decoder):⟨decoder⟩ 表示 LMQL 运行时在求解问题时采用的解码过程。LMQL 支持 argmax、sample 和 beam。
- 实际查询(actual query):它符合和model进行交互,可以通俗将<query>理解为python函数体,但区别是:i)不允许声明内部函数(但是可以导入);ii) 每个top-level字符串都被视为对 LM 的直接查询。 这些查询字符串允许两个特殊转义的子字段,类似于 python f-strings1: 1) "{varname}" 调用 a 的值当前范围内的变量。 2.),“[varname]”代表将由LM生成的短语,也成为占位词。
- 指定查询模型的 from 子句(from):⟨model⟩ 表示要使用哪个 LM,标识来自 Hugging Face Model 存储库的文本生成模型,或通过 OpenAI API 提供的模型(例如 GPT 系列)。然而,这也可以扩展到其他本地模型或 API 后端。
- 指定约束的 where 子句(where):当LM为占位词(hole)生成值时,它们将受查询的 where 子句中定义的约束条件加以约束。其中 ⟨condition⟩ 对 [varname] 占位词变量施加约束,从而约束语言模型的生成结果。约束可以是任意的合取或析取 ⟨cond_expr⟩ 或成员资格(in)检查表达式。 在这些限制条件下,将使用⟨decoder⟩指定的解码过程。解码完成后,相应的变量将在查询程序的范围内创建并赋值为解码结果。如果同名变量已经存在,它将被覆盖。
- 分配指令(distribution instruction):在 ⟨python_expression⟩ 中,distribute ⟨var⟩ 是一个可选指令,可以被用来增加返回结果。 这里,⟨var⟩必须引用查询和python表达式集合(概率分布集合)中的最后一个变量。
解码器(decoder)和模型(model)两者都由字符串指定,而查询(query)和约束(where)则以 python 语法给出。
LMQL程序运行完成后,查询程序的结果由许多内容组成,包括:- 交互过程(interaction trace):即 LMQL 查询的整个文本记录,以及被 LM 的答案替换的占位词。
- 占位词(hole)词典:所有占位词变量的集合都是可访问的,允许客户直接访问 LM 响应的特定部分。
对于sample和beam,参数
标签:prompt,LLM,Language,Prompting,Models,模型,LM,LMQL,解码 From: https://www.cnblogs.com/LittleHann/p/17563479.html