Created
Jul 29, 2024 05:47 AM
Favorite
Favorite
Priority
备注
推荐
🌟🌟🌟🌟🌟
类型
模型应用
在我们日常大模型应用开发中,经常需要输出固定内容或者结构化JSON的结果,这样有助于我们输出或者开展下一步流程。但是大模型往往不按套路出牌,这样我们有写不完的后处理和调不完的prompt,有没有好的解决方案呢?
今天我们来介绍一个在GitHub上拥有7.4k的工具outlines. 首先我们介绍他能干什么,然后介绍它是怎么干的为什么能提速,最后介绍在VLLM中如何使用它。
notion image
环境搭建

本篇文章用到以下安装包:

大体上是这几个包,如果运行有问题根据提示调整。
outlines怎么用

模型加载:

模型加载非常容易,只需一行代码就能加载,与transformers类似:
指定输出类型:
选择题输出
当我们做分类任务时,这个方法非常有用,再也不会因为模型输出无关答案而苦恼了。
正则表达式输出
当我们有固定格式的答案时可以使用。
结构化输出:
直接限制模型BaseModel格式的输出,这对于模型后续进一步操作省时省力:
还有更多有用的用法,待大家去进一步发掘。还支持JSON Schema、func等约束,大家有需要去试试吧。
outlines约束结构化输出和加速

outlines是如何保证输出json格式的呢,我们来一步步揭晓谜底,知道了过程就知道了为什么它可以加速结果输出。

生成json格式
首先,我们使用pydantic的BaseModel定义一个json的格式,它是包含两个字段Name和Age,他们分别有两个候选值:
我们使用outlines来约束模型,看生成的结果是什么:
下面我们来剖析,outlines是怎么做到的:
第一步:将json转化为正则表达式
保证结构化的输出第一步就是先将Json转换为正则表达式的形式,正则表达式是加速结构化输出的一个很重要的部分,当向Outlines传入Pydantic定义的Model时, Outlines首先会将Model转换成Json格式:
notion image
然后JSON进一步转换为正则表达式。如果大模型生成的字符串与这个正则表达式匹配上,那么我们就可以知道它符合JSON的格式,就能够被Pydantic解析。代码如下:
现在就可以利用这个正则表达式来控制大模型的输出结构化了。PS:从原理上讲,不是所有的JSON格式都能用正则表达式表示,但在大多数情况下,用正则表达式近似表示已经足够了。
第二步:转化为有限状态机
Outline 中结构化生成速度背后的秘密是正则表达式和有限状态机 (FSM) 之间众所周知的等价性。为了理解它是如何工作的,我们需要将我们的 JSON 正则表达式转换为 FSM。
我们使用该库从表示 JSON 模式的正则表达式到有限状态机执行此转换。以下是此过程输出的 FSM 的可视化效果。
notion image
然后就可以根据这个有限状态机进行Json结构的生成了,下面是具体过程:
1、从状态0开始。
2、随机生成允许的转换字符之一。
3、按照相应的转换到达新状态。
4、重复上述步骤,直到达到有限状态机的最终状态之一(在这种情况下,只有状态27)。
就这样按照根据状态自动机去生成,最后生成出来的肯定是符合要求的Json格式的。
notion image
第三步:状态合并
现在可以尝试改进结构化生成的技术,这是通向Coalescence的第一步。如果我们看一下上述FSM(有限状态机)的部分,可以很明显的得到一个结论:大多数的状态只有一个可转移的状态,而这里状态的转移就限制了我们从模型中抽样的选择,一个状态的可转移状态代表了我们可以从大模型的输出概率中抽样的可能值。如果只有一个状态可以转移,就没有必要抽样了。
由此我们就可以得到一个很显而易见的优化方法:如果我们把只有一个转移状态的节点进行合并,我们就可以跳过一些采样过程了(大模型调用次数减少了),合并之后,上面的有限状态机就变成了下面这样:
notion image
看起来我们已经大大简化了我们的模型,并发现了一种加快生成速度的好方法!不幸的是,在使用 LLM 时,我们遗漏了一个重要部分:LLM 不使用单个字符,而是使用标记。事实证明,这引入了更多的细微差别,可能会对合并产生巨大影响。

LLM Works with tokens

到目前为止,上面所有的示例都是使用单个字符的形式来举例的。但是大模型却不接受像上面单个字符的输出, 而是用token(包括整个单词和单个字符)来训练和推理的。所以,当我们尝试使用LLM生成文本时,上面单个字符的有限状态机不太行,得变成token形式的有限状态机。
幸运的是,Outlines可以将基于字符的有限状态机转换成基于Token的有限状态机。下面是代码示例:
转换之后,有限状态机变成了下面的形式:
notion image
也可以用下面的表示形式,和上面的有限状态机状态转移图是等价的, 数字代表有限状态机的所处状态,字符串代表大模型词表中的token:
看起来虽然很复杂,但是和上面单个字符的生成过程是一样的,只不过是把单个字符组合为token输入进大模型。
notion image
因此,我们在生成的时候,可以在候选集里面进行选择,比如生成最新的这个字符是"na",我们可以直接连接后续的token "na_me", 这样大模型就可以少计算几个步,直接从状态2到状态6了。这就是outlines能约束结果和加速的原因。简单总结:使用outlines后,模型在预测新token时,不再从全词表选择token,而是在outlines建立的满足约束条件的有限状态机中选择;并且在计算的过程中,合并转移状态为1的分支,这样就就可以一次预测多个token,达到提速的目的。
VLLM中使用

目前VLLM已经支持了outlines,因此我们可以很简单就使用它:

extra_body包含的选项
加入json约束例如我们之前定义的Character类,简单处理就可以输入了:
加入选项约束
其它方法让我们自己去探索吧,outlines确实是一个非常有用的工具,用起来吧。
如果对内容有什么疑问和建议可以私信和留言,也可以添加我加入大模型交流群,一起讨论大模型在创作、RAG和agent中的应用。
好了,这就是我今天想分享的内容。如果你对大模型应用感兴趣,别忘了点赞、关注噢~
往期推荐
参考资料:
https://blog.dottxt.co/coalescence.html
Loading...