马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
前言
现在 LLM(Large Language Model)从文本补全到内容创作,都展示出了强大的天生本领。然而通过 LLM 天生布局化的数据如 JSON 格式的输出,却仍旧是一个有寻衅性的使命。
天生布局化的数据不但要求模子输出符合特定的语法规则,还须要确保数据的精确性和划一性。
固然通过 prompt 工程大概可以实现指定格式的布局化数据天生,但是这也很大水平取决于模子的本领。
本文将探究怎样联合人工规则让 LLM 输出符合 JSON 格式的数据。
布局化天生原理
本文紧张是联合 lm-format-enforcer ( https://github.com/noamgat/lm-format-enforcer ) 这个库来讲授怎样让 LLM 天生指定格式的 JSON 数据。
现在该库也是被 vllm 作为 JSON 格式输出的后端之一:https://github.com/vllm-project/vllm/blob/main/vllm/model_executor/guided_decoding/lm_format_enforcer_decoding.py
布局化数据天生的原理用一句话概括就是:
每个 step 拿到当前 model 给出的 logits 之后,在采样下一个 token 之前,通过人工设定的规则可以得到当前 step 只答应采样的 token 聚集,接着通过加 bias 的方式克制其他不答应采样的 token,从而实现指定的布局化数据天生。
那么怎么得到当前 step 可答应采样的 token 聚集,就是本文重点讲授的内容了。
lm-format-enforcer 这个库包罗两个焦点模块,分别是 tokenizer 前缀树 和 字符级别的分析器,通过这两个模块就可以实现上述的功能。
构造 tokenizer 前缀树
lm-format-enforcer 这个库在初始化阶段,起首会根据 tokenizer 给出的词表,初始化一个字符级别的前缀树,这个前缀树怎么明白呢?
通过 tokenizer 给出的词表,我们可以得到一个词表中的 字符串 和 对应 token id 的映射。通过这些映射,就可以来构造这个前缀树。
树上每个节点对应词表中某个字符串的此中一个字符,每个节点的子节点就是连着的下一个字符,当字符串中的字符已经遍历完了,这时间就是填入该字符串对应的 token id。
现在通过具体的例子表明一下,这个前缀树是怎样构造的。
我们用 llama2 模子的词表来解读,假设就取词表中的一个小子集:
下面用图展示树的构造过程:
遍历第1个映射:
遍历第2个映射:
遍历第3个映射:
遍历第4个映射:
遍历第5个映射:
遍历第6个映射:
遍历第7个映射:
通过上面图示,展示了怎样通过词表子集构造前缀树,现实的前缀树比这个大多了,整个词表中的 字符串 和 token id 的映射都会通过如许的方式插入到前缀树中。
束缚每个 step 可答应采样 token 范围
构造好前缀树之后,接下来就是讲授怎么得到每个 step 可答应采样的 token 聚集。
lm-format-enforcer 另有另一个紧张的模块就是 字符级别的分析器。
这个分析器的作用简单来明白就是,在初始化的时间,会吸收用户指定的 json schema,接着在后续每一步天生过程中,会根据之宿世成的内容,判定现在处于什么状态,然后根据当前所处的状态直接给出限定的字符聚集。
下面举个简单的例子,好比用户指定的 json schema 是:
想要 LLM 天生一个 JSON object ,内容是包罗一个 city 属性,该属性的内容是一个字符串,表现一个都会的名字,同时该 city 必须要在结果中出现。
分析器的作用就是,好比现在已经天生好的内容是 :那么下一步肯定是要天生 city 这个字符串,分析器的作用就是根据现在的状态,会给出限定的字符聚集 ['c', 'i', 't', 'y'] 。
然后接下来好比天生到了:那么接下就是要 LLM 天生一个都会的名字,但是实在对于分析器来说,他只知道接下来要天生的内容是字符串,而且内容只须要符合 JSON 格式就行了,以是这时间给出的限定字符聚集就非常大了,词表中的 token 对应的字符串只要符合 JSON 格式的都可以。
末了具体能天生什么都会名字,另有这个都会是否真实存在,就得看 LLM 的本领了。
下面用一个具体的例子讲授一下,怎么联合 前缀树 和 分析器,获取每个 step 限定的 token 聚集。
假设用户的输入 prompt 和指定的 json schema 是:- prompt = "Please output a JSON segment representing the name of a city, including fields for city name."
- JSON_SCHEMA = {
- "type": "object",
- "properties": {
- "city": {
- "type": "string",
- "description": "Name of the city."
- }
- },
- "required": ["city"]
- }
复制代码 第 1 个采样 step
有一点须要注意,获取可答应采样 token 聚集在 lm-format-enforcer 库中是通过递归的方式实现的,下面为相识说方便,会给每一层递归编个号:
第 0 层递归
起首分析器给出的限定字符聚集就是- [' ', '\t', '\n', '\r', '{']
复制代码 包罗空格和大括号在内的5个字符。
然后将这个5个字符和前缀树根节点的全部第一层子节点对应的字符聚集做一个交集。
获取得到的字符交集照旧这 5 个字符:- [' ', '\t', '\n', '\r', '{']
复制代码 接着遍历这个字符交集。
遍历每个字符的时间会假设现在已经天生了该字符,好比一开始遍历空格字符 ' ',会将空格当作已经天生的内容参加到分析器中,这时间分析器内部状态会厘革,同时取前缀树中空格字符节点对应的全部子节点,进入下一轮递归。
下一轮递归开始的时间,起首将会该子节点包罗的全部 token id 参加到当前 step 的候选 token 列表中,然后继承重复上述流程。
第 1 层递归
起首看现在遍历到的前缀树节点包罗的 token id 聚集是分别对应 llama2 词表中的字符串此中, 0x20 表现 ASCII 编码表中的空格字符,以是 在 llam2 的词表中,空格对应的 token 有两个。
接着继承看第 1 层的递归,分析器在上一层添加了空格字符之后,给出的限定字符聚集仍旧是- [' ', '\t', '\n', '\r', '{']
复制代码 由于假设前面天生的是空格的环境下,接下来的可天生的字符实在照旧可以是之前的 5 个中选一个。
然后前缀树当前节点下的全部第一层子节点的字符聚集:- [' ', 't', 'a', 's', 'd', 'c', 'w', 'p', 'f', 'm', 'o', 'b', 'i', 'h', 'l', 'n', 'I', '(', 'C', 'S', 'u', 'A', '\\', 'e', 'T', 'v', 'g', '*', 'r', 'M', 'y', 'P', 'B', '=', 'D', 'L', '"', 'H', 'E', 'F', 'R', '$', '#', 'W', 'G', 'N', 'k', '`', '{', 'j', 'J', 'O', 'q', '-', 'п', 'K', 'V', 'в', '}', 'U', 'z', '[', "'", '<', 'с', ':', 'и', 'Y', 'о', 'Q', 'д', 'н', '&', '+', '@', 'з', 'м', '–', 'Z', '—', 'à', 'б', '/', 'С', '«', 'у', '.', '|', '_', 'é', 'x', 'В', 'П', 'к', 'X', 'К', 'г', 'а', 'М', '%', 'А', 'р', '“', 'Б', 'Н', '>', 'Д', 'Р', '?', 'ф', 'Г', 'О', 'е', 'Т', 'т', ')', '!', '„', 'Л', 'і', ',', 'У', '»', ';', 'è', 'И', 'ä', 'я', 'э', 'З', 'ч', 'ü', 'Ф', 'ј', '·', 'î', 'Х', 'É', 'Е', 'ш', 'č', 'л', 'Ч', '~', 'ц', 'ú', 'ö', 'á', 'Ш', 'ș', 'х', 'ж', ']', 'Э', '‘', 'І', 'Ц', 'щ', 'Я', 'ž', 'ś', '^', 'Ö', 'š', '†', '°', '\r', 'Ю', 'Ж', 'Ü', 'Á', 'й', 'Č', 'ê', 'ю', 'À', '№', 'Š', 'å', 'є', '•', '→', 'Ś', 'Å', 'ї', 'Ä', 'Î', '│', '×', 'ż', 'Ž', '−', 'È', 'Ł', 'Є', 'í', 'Ż', 'Й', '£', 'Ј', '…', '’', '§', 'ó', 'Ú', '¿', 'ř', 'â', 'α', '\xa0', 'ő', 'њ', 'ا', '€', '”', 'Ó', 'Щ', 'ł', 'Í', '¡']
复制代码 实在对应的都是词表中起始字符是空格的 token ,然后两者的交集是:实在就是对应词表中以空格起始的三个 token :- "▁▁": 259
- "▁\r": 6756
- "▁{
- "
- : 426
复制代码 接着遍历交集 [' ', '\r', '{']
,进入第 2 层递归。
由于 llama2 词表中包罗一连空格的 token 最长的有15个一连空格 token :但是递归最多只会深入到 12 层,由于 lm-format-enforcer 库中默认限定了最长一连的空格数量是 12 个,以是一连探索空格到达 12 层递归之后就会制止探索,接着回溯到第 1 层,继承那一层其他剩下还没探索的交集字符的递归过程。
不绝重复直到全部层 前缀树 和 分析器 的全部字符交集都探索完毕。
终极第一个 step 得到的可答应采样的 token 聚集是:
第 6 个采样 step
然后我们直接跳到第 6 个 step,假设现在 LLM 已经天生的内容是,前面每个 step 天生的内容按序次是 ['\n', '\n', '\n', '{', '\n', '"']:
然后根据用户设定的 json schema,接下来实在就是要限定采样必须天生 city 这个字符串,我们来看下递归的过程。
第 0 层递归
起首分析器给出的限定字符聚集就是 ['c']
然后前缀树根节点全部第一层子节点的交集就只有 'c' 字符,然后将 c 参加分析器,同时取根节点下 c 对应的全部子节点进入
第 1 层递归
而由于上一层天生了字符 c ,那么对于分析器来说,接下来的字符肯定要是 i ,以是给出的限定字符聚集就是 ['i'],和当前树节点的第一层子节点的交集自然也就是只有字符 'i',然后继承递归。
以此类推,可得当前 step 的限定 token 聚集为:
第 9 个采样 step
接着跳到第 9 个 step,假设到现在为止已经天生了:那么这时间,根据分析器的判定,接下来实在就是可以自由天生恣意符合 json 格式的字符,以是这时间返回的 token 聚集会非常大,靠近词表巨细。
lm-format-enforcer 中对这个环境做了优化,就是这些 token 聚集是可以在天生前缀树的过程中拿到。
以是假如当前是自由天生字符模式,则不会进入递归过程,直接返回这些 token 聚集即可。
怎样在采样过程中克制特定 token
在拿到可答应采样的 token 聚集之后,接下来的利用就简单了,只须要给 logits tensor 加一个偏置即可,伪代码实现:
通过给不答应采样的 token 加一个负无穷的方式来克制这些 token 不会被采样得到。
总结
实在除了 lm-format-enforcer 的实现方式之外,另有其他人工规则的布局化天生库好比 github 上 star 更多的 outlines 库。感爱好的读者可以进一步对比两者的实现有什么差别。
怎样学习AI大模子?
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给各人。 至于能学习到多少就看你的学习毅力和本领了 。我已将紧张的AI大模子资料包罗AI大模子入门学习头脑导图、佳构AI大模子学习册本手册、视频教程、实战学习等录播视频免费分享出来。
一、全套AGI大模子学习蹊径
AI大模子期间的学习之旅:从根本到前沿,把握人工智能的焦点技能!
二、640套AI大模子陈诉合集
这套包罗640份陈诉的合集,涵盖了AI大模子的理论研究、技能实现、行业应用等多个方面。无论您是科研职员、工程师,照旧对AI大模子感爱好的爱好者,这套陈诉合集都将为您提供宝贵的信息和启示。
三、AI大模子经典PDF籍
随着人工智能技能的飞速发展,AI大模子已经成为了当今科技范畴的一大热门。这些大型预练习模子,如GPT-3、BERT、XLNet等,以其强大的语言明白和天生本领,正在改变我们对人工智能的熟悉。 那以下这些PDF籍就黑白常不错的学习资源。
四、AI大模子贸易化落地方案
作为平凡人,入局大模子期间须要一连学习和实践,不绝进步本身的技能和认知水平,同时也须要有责任感和伦理意识,为人工智能的康健发展贡献力气。
|