Home / 大语言模型 / PAL:让模型写出解法,让程序执行解法

063 · PAL:让模型写出解法,让程序执行解法

返回目录 · 上一篇:HELM · 下一篇:投机解码

论文:Luyu Gao等,PAL: Program-aided Language Models,2022年公开,ICML 2023。原始论文 · v2正文 · 官方项目。

图表来源:Luyu Gao等,2023,原文;CC0 1.0。本篇展示做了图形区域裁切,图中数据与标签未改动。

先接住前作的问题

CoT把算术步骤写出来,却仍由语言模型预测计算结果。本篇组合Codex式程序生成与解释器,把确定性运算交给执行环境;这与ReAct多轮工具闭环有教学联系,但原主实验是一次生成后执行。

五个前置概念

五个前置概念:少样本提示用上下文示例指定输出方式;语义解析把语言映射为结构化表示;解释器按程序语义执行指令;执行正确性只保证按程序运行;答案投票聚合多个候选的结果而非程序文本。

一、问题设定:题意分解正确时,怎样避免再由token预测执行算术

一道应用题既要识别相关数量、确定依赖关系,也要执行运算。自然语言过程混合这三步,PAL改用可执行程序承载中间关系,使诊断能够区分模型建模错误与算术执行错误。

PAL把后两个环节部分分开。模型仍然必须判断应该乘还是除,程序不会替它理解题意;但一旦关系正确,执行器可以避免依赖语言模型记忆多位数乘法。对日期、列表索引、对象计数等任务,也可以利用已有程序语义处理确定性的状态变化。

这种设定是冻结模型的少样本提示,不是强化学习,也没有新价值函数。上下文中放入题目与解题程序,模型生成新程序,外部运行得到答案。论文实验采用一次生成后执行;虽然正文提到可以逐段执行并反馈,但不能把后来的交互调试循环当成它的主要实验流程。

关键假设是模型已有足够的代码生成能力,任务能够映射到程序,且输入提供了必要信息。解释器不能补出问题里不存在的事实,也无法自动判断一个看似合理的变量是否对应了正确实体。

二、把程序分布经执行器映射成答案分布

设问题为 xx,示例提示为 pp,程序为 zz,执行函数为 EE。单次推理可写成

z∼Pθ(z∣p,x),y^=E(z).z\sim P_\theta(z\mid p,x),\qquad \widehat y=E(z).

这里 θ\theta 不更新;EE 也不是训练出来的奖励模型。语言模型输出的是程序,最终答案来自执行。若要描述系统的答案概率,则可写为

P(y^=y∣x,p)=∑z:E(z)=yPθ(z∣x,p).P(\widehat y=y\mid x,p) =\sum_{z:E(z)=y}P_\theta(z\mid x,p).

这是理解系统的概率表达,不是论文额外提出的训练损失。它揭示一个有用事实:语法不同、变量名不同的程序可能得到同一答案。多采样后可以在执行结果上投票,不必要求所有程序文本一致。

但这个求和也有危险的一面:多种错误程序可能碰巧输出相同数字。答案一致不构成语义正确的证明。若题目只有少量可能答案,偶然一致尤其需要注意。

执行器保证算对表达式,不保证表达式代表题意

设z为模型生成的程序、Exec(z)为执行结果,答案概率是 p(a∣x)=∑z1[Exec⁡(z)=a]p(z∣x)p(a\mid x)=\sum_z\mathbf1[\operatorname{Exec}(z)=a]p(z\mid x),其中 xx 为问题、aa 为答案,1\mathbf1 为匹配指示量。执行失败也应作为一种结果记录。PAL通常生成有限候选再执行,并没有完整计算这个求和。把计算交给解释器减少算术错误,却留下变量对应、单位、边界条件和算法选择等建模错误。

教学算例。 “每箱12件,3箱退掉5件”正确程序是 12×3−5=3112\times3-5=31;生成 (12−5)×3=21(12-5)\times3=21 时,解释器会非常可靠地给出错误任务答案。诊断应分别记录代码可运行率、表达式语义正确率和最终正确率,而非以“没有报错”替代验证。

用单位断言定位执行前的语义错误

把“每箱24件、每件2元、共3箱”写成3×24×2,结果单位才是元。若模型漏乘24,Python仍可正常返回6,却是把箱当成件。可在程序旁标出每个变量的单位,再用数量级和边界输入检查;这些检查针对语义映射,不是重新让语言模型口算一遍原程序。

教法联系:Lilian Weng的LLM Powered Autonomous Agents。算例为本文自构。

三、生成程序、记录失败并按约定读取执行结果

输入:问题x,包含问题与程序的少样本提示p,冻结模型M
program = M.generate(p + x)
提取约定的程序主体
在受限执行环境中运行program
若语法错误、超时或没有约定结果:记录执行失败
否则:读取结果变量或函数返回值,规范化为答案
输出:答案、生成程序、执行状态

可选多样本版本:
    重复生成和执行k次
    按规范化后的执行答案投票

受限执行环境和完整失败记录是面向实际实现的教学补充,不应被理解成论文已经证明了任意生成程序的安全性。原实验的核心干预是执行程序,而不是让模型在代码后面继续猜一个数。

变量命名也有特殊作用。Python并不在意一个数量叫 a 还是 remaining_money,但模型会利用名字把程序状态与题目实体对应起来。对于生成器,有意义的名字是语义线索;对于解释器,它只是标识符。这种双重角色解释了为什么代码可执行性一样,提示效果却可能不同。

题目:仓库有17箱零件,每箱24个,发出125个后还剩多少?可写成:

# 自构教学例子
boxes = 17
parts_per_box = 24
parts_sent = 125
parts_initial = boxes * parts_per_box
answer = parts_initial - parts_sent  # 283

模型负责把“每箱”映射为乘法,把“发出”映射为减法。解释器负责算出283。如果题目改成“发出125箱”,原程序仍可执行,却不再表达题意。检验这种变化,比只看代码有没有报错更能判断理解是否扎实。

四、固定后端检验执行器与变量语义的贡献

先固定同一个代码后端比较真实执行与模型模拟,再读变量命名消融;跨模型最高分只说明系统能力。

项目 原文设置
后端 主要为code-davinci-002,比较Direct、CoT和PAL;另测其他代码与文本模型
任务 八个数学任务、三个符号任务、两个算法任务,共13个
例子 尽可能沿用既有CoT题目,改写为程序;一般3—6个固定示例
单样本 温度0的贪心生成;数学任务比较三种随机提示顺序
多样本 40个候选,温度0.7,nucleus阈值0.95,按答案多数投票
测试规模 附录H列GSM8K 1319题、SVAMP 1000题等,各数据集分别报告
资源边界 无参数训练;增加程序生成与执行成本,主表不是同墙钟预算的吞吐实验

与PaLM-540B的比较能说明系统效果,但并未控制预训练数据、模型规模和代码训练量。因果解释更应优先看同一Codex后端下的CoT与PAL,以及有无真实解释器的消融。

GSM-hard把原问题中的一个数替换为更大的整数,重点测试计算难度的变化。它不是新领域知识测试。更值得关注的是标签生成过程:附录H.1使用PAL程序帮助重算答案,再辅以人工抽查和处理剩余样本。这使构造成本可控,也带来方法参与基准标注的依赖,不能把新标签视为全部独立手工验证。

证据一:表1,同模型的基本收益。 GSM8K上CoT为65.6%,PAL为72.0%,绝对提高6.4个百分点;相对提升约 6.4/65.6=9.8%6.4/65.6=9.8\%,这是自行计算。与PaLM的56.9%相比则高15.1个百分点,两个差值的对照条件不同。

证据二:表6,执行器是否关键。 让模型生成代码后自己模拟运行,正确率为23.2%,远低于真正执行的72.0%。把程序压成单行表达式为47.8%,也低于逐步程序,但并没有降到Direct的19.7%。正文“接近直接提示”的概括不如具体表值精确。

证据三:图9,语义线索是否重要。 删除注释后,保留有意义变量名仍能保持较好表现;继续把名字换成随机字符会进一步下降。它支持变量名帮助生成器建立对应关系,不代表解释器本身理解这些名字。 变量名消融比较生成提示,而非对已生成程序做等价重命名;另一个采样曲线中部分基线由1与40样本端点插值,并非每个中间点都实测。

PAL移除代码注释和变量语义后在三个任务上的准确率
原实验图摘引:Luyu Gao等,2022,图9,PDF第8页;核对原文。原版权保留。

证据四:附录C,适用边界。 text-davinci-001上CoT为26.5%、PAL仅8.6%;更强后端才体现优势。工具不是给任何模型加上就必然有益,错误程序生成可能抵消执行优势。

证据五:表3与表7,预算和波动。 40次采样PAL达到80.4%,对应CoT为78.0%;单样本PAL三种提示顺序的标准差为0.16个百分点。前者增加推理预算,后者只反映有限提示顺序变化,不是对所有输入分布的置信保证。

GSM-hard的CoT数字还存在版本内表格差异:表1为23.1%,表7均值为23.3%,PAL均为61.2%。引用时应保留出处,不能把两个基线混合后声称精确提升到小数点后一位。图12中的部分基线曲线由端点插值得到,也不应读成每个采样数都直接运行过。

PAL和基线随每题候选数量变化的解题率及插值基线
原文图形摘引:Luyu Gao等,2023,图12,PDF第16页;核对原文。

五、语法成功仍不保证单位、实体和边界建模正确

它继承了少样本提示和程序生成能力,没有证明语言模型获得了新的内部算术算法。

收益有条件:程序正确表达问题时,执行能可靠处理确定性步骤;程序漏掉条件、用错单位或错误分类时,运行成功仍然会答错。语法错误通常容易检测,语义错误更隐蔽。例如把“每箱”价格当成“每件”价格,代码可以毫无异常地得到错误金额。

我的分析是,程序中间表示提高了可检查性,却没有自动完成检查。可以加入单位断言、边界测试和独立算例,但这些需要额外设计,也会改变系统预算。论文没有系统证明这类工程措施能消除所有错误。

此外,多次采样的执行费用通常低于大模型生成费用,但具体比例取决于程序长度、执行任务和后端延迟。不能从“Python算术很快”推断整个系统比直接回答更快。对开放式解释、主观评价或缺少事实的问题,程序并不天然提供合适求解器。

六、从程序执行接到一般调用训练

一次生成程序后执行,仍未覆盖交互调试与一般API选择。ReAct提供闭环参照,Toolformer研究调用监督;应分别验证题意建模、执行和后续反馈是否可靠。

自测:能够回答,才算理解

1. PAL解决了计算错误,是否也解决了逻辑错误?

答案:只解决其中可交给正确程序执行的部分。模型仍决定条件、变量和操作关系;这些选择错误时,解释器会忠实执行错误逻辑。程序输出正确与程序在所有同类输入上正确,也不是同一个命题。

2. 为什么变量名对Python无意义,对PAL却有意义?

答案:执行器把名字当作引用标识,生成模型却从词义和训练分布中利用名字理解变量角色。重命名不改变已写好程序的语义,但可能改变模型生成下一步的概率,因此消融要区分生成阶段与执行阶段。

3. 如何设计实验,证明收益主要来自执行而不是换了更强模型?

答案:固定后端、题目、示例选择和采样预算,比较自然语言CoT、程序加真实执行、程序加模型模拟执行,并记录语法失败、语义错误和计算错误。跨模型最高分只能补充系统能力比较,不能替代这一受控对照。