探索LLM推理全阶段的JSON格式输出限制方法

文章详细讨论了如何确保大型语言模型(LLMs)输出结构化的JSON格式,这对于提高数据处理的自动化程度和系统的互操作性至关重要。

一、引言

1.1 JSON结构化输出的意义

对于基于大型语言模型(LLMs)的应用而言,确保输出能直接以结构化的JSON格式呈现,对于提升数据处理的自动化程度、增强系统的互操作性具有重要意义。例如,客户需要对LLM的输出进行信息提取时,若输出是一个JSON格式则会大大方便工程链路上的后处理;例如,LLM在调用工具(或其它智能体)时,需要按照工具要求传入正确的参数,若能保证LLM的输出是结构化的JSON,则能保证传参正确,从而正确调用工具。

然而,在实践中,即使我们在提示词中反复告诉模型要输出JSON结构,LLM还是偶尔出错。虽然“偶尔”出错的概率很低,但对于工程链路的设计来说,是致命且麻烦的。

1.2 LLM为何不能严格输出JSON

LLM在推理时,基于已经输出的句子,从词汇表中预测下一个词。预测时,为词汇表中的每个词分配一个概率,通过采样得到预测输出,如图1所示。例如,模型在输出"My name is"后,仅有0.62的概率输出自己的名字"Tang",即使我们在提示词中告诉了模型自己的名字是"Tang",模型也有0.38的概率输出别的名字。

图1. LLM推理的预测概率示意

因此,这个依概率采样的推理过程决定了LLM不可能100%按要求输出JSON格式。错误的JSON输出导致了我们在工程链路上无法作后续的解析,因此,能100%严格限制JSON格式输出的方法非常重要。

1.3 友商方案

  • OpenAI JSON Mode

推出于2023年12月份,基于提示词优化,用户仍需要在提示词中给出JSON示例,不能保证严格100%输出JSON。

  • Kimi JSON Mode

近期推出。类似OpenAI的 JSON Mode,用户仍需要在提示词中给出JSON示例,不能保证严格100%输出JSON。

  • OpenAI Structured Outputs

推出于2024年8月份,根据用户给出的JSON示例,严格保证100%输出JSON格式。

https://openai.com/index/introducing-structured-outputs-in-the-api/

图2. OpenAI输出JSON格式的方法,橙、黄、绿分别代表提示词优化、微调、动态限制解码法的JSON输出准确率

(动态限制解码法准确率为100%)

二、前中后三阶段的优化策略

Motivation: 在一个基于通义千问的AI教评项目场景中,JSON格式输出对客户十分重要。因此,我们在该项目实践中由浅入深,从LLM推理的前、中、后三个阶段探索了限制输出JSON格式的方法。其中,“推理前”和“推理后”这两个阶段的方法用在了项目实践中,大大提高了AI教评任务中JSON格式的输出概率。为了进一步研究如何100%输出JSON格式,我们借他山之石,研究了OpenAI的Structured Outputs方法,在“推理中”这一阶段探索并验证了基于动态限制解码的100%输出JSON格式方法。

在分析相关工作基础上,我们将深入讨论每阶段的方法、优劣及其实现方式,以期帮助读者掌握提升JSON输出概率的办法,并应用在实践中。

2.1 推理“前”:Prompt Engineering

(以下提示词来自大量项目实践验证)

在提示词中加入这句话“The JSON object:json”可提高JSON输出概率。(别问,问就是大量实践总结的经验~)

在提示词中给出"##输出格式规范",并给出JSON示例```json … ```

## 输出格式规范:`` ```json ``[{`  `"name":"<评价维度>",`  `"mentions":"< 提及次数 >",`  `"references":[{`    `"time":"<发言时间>",`    `"text":"<原文内容>"}]}]`` ```````The JSON object:json
【实践】

在利用Qwen-long作AI教评的一个项目中,我们需要从教师的课堂录音文本中提取结构化的教学维度信息。采用本节中的prompt加上2.3中的JSON后处理方法后,输出样本基本是符合预期的结构化JSON。JSON正确概率从50%左右上升到了95%。可见仅靠prompt和后处理,已经能以很高的概率使得大模型按照JSON格式输出。然而,在一些需要严谨输出JSON格式的场景,100%严格输出JSON格式的方法仍值得研究。

【优势】

实施简便,无需模型架构调整,可以大幅提高输出JSON的概率。

【不足】

高度依赖于人工设计的prompt,灵活性受限。不能100%输出JSON

2.2 推理“中”:基于动态限制解码实现100%输出JSON

【原理】

LLM依据已输出的词,从词汇表中预测下一个词,可以在词汇表中将不符合JSON规范的词概率置零,从而防止输出不符合JSON规范。(原理偏复杂,可跳过本节直接看结论)。假设我们想让LLM的输出为一个城市的如下信息:

city_info_schema=[{`  `"name":"城市名",`  `"country":"城市所属国家",`  `"latitude":"城市纬度",`  `"population":"城市人口(千万)",`  `"top 3 landmarks":["知名景点1","知名景点2","知名景点3"]``}]

如上代码块所示,在内存中定义JSON输出的模式city_info_schema。LLM每轮逐个单词输出"response",对于JSON的"key"值,如"name",我们直接从内存拼接到输出字符串"response_str"中;对于JSON的"value",则让LLM通过推理产生。当用户提出问题“请填写杭州的城市信息”后,动态限制解码流程如下:

图3. 动态限制解码法示意图。其中只有绿色词是LLM的推理产生。

上图展示了动态限制解码的工作流程,每一轮推理过程我们给定了JSON的“键”,仅让模型推理“值”。可以进一步用正则式(Python re库)限制我们想要的输出格式:

city_regex = (`    `r"""\{\n"""`    `+ r"""  "name": "[\w\d\s]{1,16}",\n"""`    `+ r"""  "country": "[\w\d\s]{1,16}",\n"""`    `+ r"""  "latitude": [-+]?[0-9]*\.?[0-9]{0,2},\n"""`    `+ r"""  "population": [-+]?[0-9]{1,9},\n"""`    `+ r"""  "top 3 landmarks": \["[\w\d\s]{1,16}", "[\w\d\s]{1,16}", "[\w\d\s]{1,16}"\]\n"""`    `+ r"""\}"""``)

在推理过程中,根据正则式限制输出格式的流程如下:

图4. 动态限制解码法的”推理-限制-采样-拼接”流程

如第一个键"key"对应的"name",我们用正则式限制其必须输出16个字以内的英文,则"杭"的概率由于不符合正则式要求,预测概率置零,模型一定会按照我们的要求输出。

由于动态限制解码技术需要我们有冻结模型解码过程、改变词汇表采样概率、改变模型输入的权限,目前在线的API接口。

不支持编写动态限制解码算法。但是可以在本地部署模型以实现动态限制解码。

【实践】

在PAI平台的免费体验DSW(NVIDIA A10)上本地部署Qwen2-7B-Instruct实现动态限制解码。基于开源的sglang库,可快速部署动态限制解码算法。

pip install --upgrade pip``pip install "sglang[all]"``# Install FlashInfer CUDA kernels``wget "https://modelscope.oss-cn-beijing.aliyuncs.com/resource/flashinfer-0.1.2%2Bcu121torch2.3-cp310-cp310-linux_x86_64.whl"``pip install flashinfer-0.1.2+cu121torch2.3-cp310-cp310-linux_x86_64.whl
modelscope download --model=qwen/Qwen2-7B-Instruct --local_dir ./Qwen2-7B-Instruct
python3 -m sglang.launch_server --model-path Qwen2-7B-Instruct --port 30000

图5. sglang框架下的千问模型本地部署成功示意图

显示上图即部署成功。

###导入库``import json``import time``from sglang import set_default_backend, RuntimeEndpoint``import sglang as sgl``from sglang.test.test_utils import (`    `add_common_sglang_args_and_parse,`    `select_sglang_backend,``)``from sglang.utils import dump_state_text, read_jsonl``##定义“限制模型输出的正则式”``city_regex = (`    `r"""\{\n"""`    `+ r"""  "name": "[\w\d\s]{1,16}",\n"""`    `+ r"""  "country": "[\w\d\s]{1,16}",\n"""`    `+ r"""  "latitude": [-+]?[0-9]*\.?[0-9]{0,2},\n"""`    `+ r"""  "population": [-+]?[0-9]{1,9},\n"""`    `+ r"""  "top 3 landmarks": \["[\w\d\s]{1,16}", "[\w\d\s]{1,16}", "[\w\d\s]{1,16}"\]\n"""`    `+ r"""\}"""``)``## 将正则式应用在输出范式中``@sgl.function``def chat_example(s,question):`    `s += sgl.system("You are a helpful assistant.")`    `# Same as: s += s.system("You are a helpful assistant.")``   `    `with s.user():`        `s += question``   `    `s += sgl.assistant_begin()`    `s += "Answer: " + sgl.gen("json_output", max_tokens=256, regex=city_regex)`    `s += sgl.assistant_end()``## 设置Qwen2的本地通信端口,上图设置为port30000``set_default_backend(RuntimeEndpoint("http://localhost:30000"))``## 捕捉用户输入``state = chat_example.run(`    `question=input("请输入城市名:"),`    `# temperature=0.1,`    `stream=True``)``## 打印必然的JSON输出结果``for out in state.text_iter():`    `print(out, end="", flush=True)

运行效果:试输入“杭州”和“纽约”两个城市。输出严格按照了正则式的限制。

图6. 基于动态限制解码的JSON格式输出结果。

【优势】
  • 100%严格输出JSON格式,甚至是任意正则式可以定义的格式。

  • 在输出的JSON中,节省了输出"key"值的token:因为"key"值是内存中定义好的,不需要由LLM推理而得。因此,相对于prompt的方式让模型输出全JSON的方式,节省了输出的token数量。(这也是为什么OpenAI的JSON 模式每token价格有30%的折扣的原因)

【不足】
  • 必须本地部署LLM。

2.3 推理“后”:JSON数据后处理

在模型返回response后,也可以利用后处理的技术,校正JSON结构以提高JSON输出的概率。

  • JSON Repair库

Python 的json_repair库,可以解决一部分模型输出JSON格式不规范的问题。

from json_repair import loads #pip install json_repair``import json``   ``if __name__ == '__main__':`    `    bad_string= '''``[`            `{`                `"foo": "Foo bar baz",`                `"tag": "foo-bar-baz"`            `},`            `{`                `"中文": "foo bar foobar foo bar baz.",`                `"标签": "foo-bar-foobar"`            `}`        `]``'''`    `    parsed_json = loads(bad_string)`    `json_str = json.dumps(parsed_json,ensure_ascii=False)`    `print(json_str)

经实践验证,json_repair可以解决输出的JSON中缺少"},],"的问题。

  • 随机种子控制:可改变LLM推理的seed, 在不同的seed下输出以减少出错概率。

三、总结与展望

以上介绍的三种类型的方法,可以同时使用,但需要注意不同的场景限制:

【前、中、后三阶段方法总结】

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值