浮言易逝,唯有文字长存。
(今天继续聊聊AI技术)
约两周前,我用两篇文章介绍了DSPy的原理:
今天是第三篇,算是来完结一下这个话题。虽然DSPy在实用性上还存在非常多挑战,但是它的设计思想非常超前,也非常有意思。因此,现在我们结合AI领域的一些重要概念,做个思考总结。
技术方面的讨论
Training-time / Inference-time / Pre-inference time
在OpenAI的o1发布之后,业界开始关注
Inference-time Compute
。o1在inference的阶段,通过生成大量的reasoning tokens,来获得对于复杂问题的更强的推理能力(reasoning)。
Inference-time Compute这种说法,是相对于Training-time Compute来说的。并不是说o1不需要训练的算力,而是说,相对于以前的LLM来说,o1在inference阶段投入了更多的计算。这体现在,当用户输入一个问题之后,o1要花费更长的时间进行「思考」,其实也就是在生成最终答案之间,先生成了很多reasoning tokens。
显然,o1在进行Inference-time Compute时不会更新模型的weights。这跟Training-time Compute有本质的不同。
通过前面的两篇文章,大家应该已经知道了,DSPy的优化过程,一般来说是不会更新模型weights的。不过DSPy有个例外,它有专门用来做fine-tune的优化器 (Optimizer) ,确实会更新模型的weights。这和DSPy的工程设计思路有关,我们下面再讨论这个,现在暂时先忽略这种特殊情况。可以说,一般情况下DSPy的优化器是不会更新模型weights的。
那么,DSPy的优化器不更新模型weights,那它优化什么呢?其实是优化prompt。具体细分的话,这里面又主要包含两部分:一个是优化instruction,一个是优化few-shot(也被称为exemplar selection[1])。
那DSPy的优化过程,算是Inference-time Compute吗?也算也不算。它确实不会更新模型weights,从这个角度来说,它确实是inference time的。但这个优化过程是发生在用户输入问题之前,而且是基于一个数据集批量做优化。从这个角度来说,它又跟模型训练过程很像。因此,DSPy官方有一个不一样的归类,称为
Pre-inference time Compute
[2]。这个命名表示,DSPy的优化过程通常发生在模型训练阶段之后,但却在真正的inference阶段之前。
稍微总结一下,从LLM整个生命周期来看,按照算力的投入阶段,细分成了三类:
我们在上一篇讨论时提到的自动化提示词工程 (APE) [1],显然也是属于Pre-inference time Compute。
最后,很重要的一点是,以上这三类投入算力的方法并不是互斥的,它们相互之间可以结合。
DSPy与APE的区别
先说APE,它的运行需要5个元素:
DSPy包含了APE工程的大部分元素,但它不仅仅是APE,它是个很重的AI编程框架。DSPy的设计重心在于,它所关注的是一个由多个可自动优化的Module所组成的系统。当然,DSPy在具体优化每个Module的时候,可以使用任何现成的APE优化算法,比如可以调用OPRO。但DSPy的重心在于对系统进行整体优化。从这个角度来看,DSPy与TextGrad[4]的思路比较像。
在
上一篇文章
中,我们已经看到,DSPy通过执行Bootstrap策略,自动化地生成中间结果数据,这就是它着眼于整体系统优化的一个体现。
Modular / Multi-Stage / CoT
在DSPy的论文中[5],我们可以看到,它所关注的是类似
multi-stage pipelines
或
agents
这样的系统。这些系统试图将复杂的任务拆解成小的、易解决的任务,再分别交由LLM来完成。这些系统天然就是模块化的 (Modular)。
Modular
是从空间维度来讲的,系统是由多个Module组成的。
Multi-stage
则是从时间维度来讲的,一个复杂任务可以先完成第一步,然后再完成下一步,依此类推。
既然DSPy关注的是对整个系统进行自动优化,但系统是复杂的,你就需要知道在一个系统的组成部分中,哪些是可优化的,哪些是不可优化的。因此,DSPy为了解决这个问题,它借鉴了pytorch中的
nn.Module
的概念抽象。据此,你可以很容易地将系统中那些可优化的Module识别出来。当然,这也包含那些Module的子Module,一层一层都识别出来。
一个包含多个Module的系统,它的执行过程大概率也是Multi-stage的。但Multi-stage的执行过程,却未必涉及到多个Module。比如,仅针对一个LLM,就可以做多步的reflection。
抽象来看,不同执行步骤之间,可能有依赖关系,也可能没有依赖关系。在复杂的情况下,它们会组成一个DAG (Directed Acyclic Graph)。
现在大家都很关注的o1,在给出答案之前,会给出一段很长的CoT思考过程。从
逻辑层面
来看,CoT的思考过程也可以说是Multi-stage的,因为这个过程包含多个步骤,而且思考步骤之间存在很强的依赖关系(因果关系)。但从
物理层面
来看,o1的CoT是在
一个
inference过程中生成出来的。
虽然在更细的粒度上,o1的reasoning tokens也是一个token一个token生成的,可以分为很多步,但在DSPy所关注的系统粒度上,这个CoT思考的过程不会被认为是Multi-stage的。按照业界的猜测和分析,现在大家已经知道,o1是一个model,而不是一个system。如果我们在微观的层面去看,model和system的界限有时候其实有点模糊。特别是对于一个auto-regressive model来说,在inference阶段其实有相当多的「system」层面的微观操作在执行。而且,估计o1在inference time生成大量reasoning tokens的过程中,还有一个经RL训练过的policy同时也在运行。但总体来说,按照惯常的说法,o1是一个model而不是一个system,是更合理的说法。
所以说,在DSPy的语境下,假设以DSPy的方式来集成o1,即使它的CoT思考过程再长,o1也是作为DSPy的一个Module而存在的(不会成为多个Module)。
关于RL和Reasoning
RL是一种trial-and-error的方法,它通过不断试错来搜索未知空间的更多解。不管是DSPy还是其它APE方法,与这个过程有一点点相似之处。APE通过尝试更多的prompt来执行trial-and-error的过程;而DSPy在更大的尺度上去自动化地摸索更多执行路径。经典的EE难题 (Exploration–Exploitation) 在这里也会出现,是需要更好的APE或DSPy的优化器去解决的。