AI 模型量化格式介绍,LangChain Agent 原理解析,HugggingFace 推理 API、推理端点和推理空间使用介绍。
在 HuggingFace 上下载模型时,经常会看到模型的名称会带有fp16、GPTQ,GGML等字样,对不熟悉模型量化的同学来说,这些字样可能会让人摸不着头脑,我开始也是一头雾水,后来通过查阅资料,总算有了一些了解,本文将介绍一些常见的模型量化格式,因为我也不是机器学习专家,所以本文只是对这些格式进行简单的介绍,如果有错误的地方,欢迎指正。
What 量化
量化在 AI 模型中,特别是在深度学习模型中,通常指的是将模型中的参数(例如权重和偏置)从浮点数转换为低位宽度的整数,例如从 32 位的浮点数转换为 8 位整数。通俗地说,量化就像是把一本详细的、用高级词汇写的书简化为一个简短的摘要或儿童版故事。这个摘要或儿童版故事占用的空间更小,更容易传播,但可能会丢失一些原始书中的细节。
Why 量化
量化的目的主要有以下几点:
减少存储需求:量化后的模型大小会显著减小,这使得模型更容易部署在存储资源有限的设备上,如移动设备或嵌入式系统。
加速计算:整数运算通常比浮点运算更快,尤其在没有专门的浮点硬件支持的设备上。
减少能耗:在某些硬件上,整数运算消耗的能量更少。
但是,量化也有一个缺点:它可能会导致模型的精度下降。因为你实际上是在用较低的精度来表示原始的浮点数,可能会损失一些信息,这意味着模型的能力会变差。为了平衡这种精度损失,研究者们开发了各种量化策略和技术,如动态量化、权重共享等,可以在尽量少降低模型能力的情况下,尽可能多地降低模型所需的损耗。打个比方,如果我们一个模型的完整能力是 100,模型大小和推理所需内存也是 100,我们将这个模型量化后,模型的能力可能会降低到 90,但模型大小和推理所需内存可能会降低到 50,这个就是量化的目的。
FP16/INT8/INT4
HuggingFace 上模型名称如果没有特别标识,比如Llama-2-7b-chat、chatglm2-6b,那么说明这些模型一般是全精度的(FP32,但也有些是半精度 FP16),而如果模型名称中带有fp16、int8、int4等字样,比如Llama-2-7B-fp16、chatglm-6b-int8、chatglm2-6b-int4,那么说明这些模型是量化后的模型,其中fp16、int8、int4字样表示模型的量化精度。
量化精度从高到低排列顺序是:fp16>int8>int4,量化的精度越低,模型的大小和推理所需的显存就越小,但模型的能力也会越差。
以ChatGLM2-6B为例,该模型全精度版本(FP32)的大小为 12G,推理所需用到的显存为 12~13G,而量化后的 INT4 版本模型大小为 3.7G,推理所需显存为 5G,可以看到量化后的模型大小和显存需求都大大减小了。
FP32 和 FP16 精度的模型需要在 GPU 服务器上运行,而 INT8 和 INT4 精度的模型可以在 CPU 上运行。
GPTQ
GPTQ[1] 是一种模型量化的方法,可以将语言模型量化成 INT8、INT4、INT3 甚至 INT2 的精度而不会出现较大的性能损失,在 HuggingFace 上如果看到模型名称带有GPTQ字样的,比如Llama-2-13B-chat-GPTQ,说明这些模型是经过 GPTQ 量化的。以Llama-2-13B-chat为例,该模型全精度版本的大小为 26G,使用 GPTQ 进行量化成 INT4 精度后的模型大小为 7.26G。
如果你用的是开源模型LLama,可以使用GPTQ-for-LLaMA[2]这个库来进行 GPTQ 量化,它可以将相关的Llama模型量化成 INT4 精度的模型。
但现在更流行的一个 GPTQ 量化工具是AutoGPTQ[3],它可以量化任何 Transformer 模型而不仅仅是Llama,现在 Huggingface 已经将 AutoGPTQ 集成到了 Transformers 中,具体的使用方法可以参考这里[4]。
GGML
讲 GGML 之前要先说下llama-cpp[5]这个项目,它是开发者 Georgi Gerganov 基于 Llama 模型手撸的纯 C/C++ 版本,它最大的优势是可以在 CPU 上快速地进行推理而不需要 GPU。然后作者将该项目中模型量化的部分提取出来做成了一个模型量化工具:GGML[6],项目名称中的GG其实就是作者的名字首字母。
在 HuggingFace 上,如果看到模型名称带有GGML字样的,比如Llama-2-13B-chat-GGML,说明这些模型是经过 GGML 量化的。有些 GGML 模型的名字除了带有GGML字样外,还带有q4、q4_0、q5等,比如Chinese-Llama-2-7b-ggml-q4,这里面的q4其实指的是 GGML 的量化方法,从q4_0开始往后扩展,有q4_0、q4_1、q5_0、q5_1和q8_0,在这里[7]可以看到各种方法量化后的数据。
GGUF
最近在 HuggingFace 上的模型还发现了一些带有GGUF字样的模型,比如Llama-2-13B-chat-GGUF,GGUF其实是 GGML 团队增加的一个新功能,GGUF 与 GGML 相比,GGUF 可以在模型中添加额外的信息,而原来的 GGML 模型是不可以的,同时 GGUF 被设计成可扩展,这样以后有新功能就可以添加到模型中,而不会破坏与旧模型的兼容性。
但这个功能是Breaking Change,也就是说 GGML 新版本以后量化出来的模型都是 GGUF 格式的,这意味着旧的 GGML 格式以后会慢慢被 GGUF 格式取代,而且也不能将老的 GGML 格式直接转成 GGUF 格式。
关于 GGUF 更多的信息可以参考这里[8]。
GPTQ vs GGML
GPTQ 和 GGML 是现在模型量化的两种主要方式,但他们之间有什么区别呢?我们又应该选择哪种量化方式呢?
两者有以下几点异同:
GPTQ 在 GPU 上运行较快,而 GGML 在 CPU 上运行较快
同等精度的量化模型,GGML 的模型要比 GPTQ 的稍微大一些,但是两者的推理性能基本一致
两者都可以量化 HuggingFace 上的 Transformer 模型
因此,如果你的模型是在 GPU 上运行,那么建议使用 GPTQ 进行量化,如果你的模型是在 CPU 上运行,那么建议使用 GGML 进行量化。
Groupsize
在 HuggingFace 上,不管是什么格式的量化模型,模型名称中还经常出现一些32g、128g字样,比如pygmalion-13b-4bit-128g,这些又是表示什么意思呢?
128g中的g其实表示的是 groupsize 的意思,在量化技术中,权重可能会被分成大小为 groupsize 的组,并对每组应用特定的量化策略,这样的策略可能有助于提高量化的效果或保持模型的性能。
groupsize 的值有:1024、128、32,GPTQ 默认的 groupsize 值是 1024。如果 groupsize 没有值,那么 groupsize 就为-1( 注意不是 0)。groupsize 会影响模型的准确性和推理显存大小,groupsize 根据同等精度模型准确性和推理显存从高到底的排列顺序是:32 > 128 > 1024 > None(-1),也就是说 None(-1) 是准确性和显存占用最低的,而 32 是最高的。
总结
本文总结了 HuggingFace 上模型的常见量化格式,量化技术是 AI 模型部署的重要技术,它可以大大减小模型的大小和推理所需的显存。想要让大语言模型真正地走进普通人的生活,在每个人的手机上能运行起来,做到真正意义上的“普及”,那么量化技术以后肯定是必不可少的,因此掌握一些量化技术是非常有必要的。
关注我,一起学习各种人工智能和 AIGC 新技术,欢迎交流,如果你有什么想问想说的,欢迎在评论区留言。
参考:
[1] GPTQ: https://github.com/IST-DASLab/gptq
[2] GPTQ-for-LLaMA: https://github.com/qwopqwop200/GPTQ-for-LLaMa
[3] AutoGPTQ: https://github.com/PanQiWei/AutoGPTQ
[4] 这里: https://huggingface.co/blog/zh/gptq-integration
[5] llama-cpp: https://github.com/ggerganov/llama.cpp
[6] GGML: https://github.com/ggerganov/ggml
[7] 这里: https://github.com/ggerganov/llama.cpp#quantization
[8] 这里: https://github.com/ggerganov/llama.cpp/pull/2398#issuecomment-1682837610
LangChain Agent 原理解析
LangChain 是一个基于 LLM(大型语言模型)的编程框架,旨在帮助开发人员使用 LLM 构建端到端的应用程序。它提供了一套工具、组件和接口,可以简化创建由 LLM 和聊天模型提供支持的应用程序的过程。LangChain 由几大组件构成,包括 Models,prompts,Chains,Memory 和 Agent 等,而 Agent 是其中重要的组成部分,如果把 LLM 比做大脑的话,那 Agent 就是给大脑加上手和脚。今天就来带大家重点了解一下 Agent 以及它的工作原理。
什么是 LangChain Agent
在 LangChain 中,Agent 是一个代理,接收用户的输入,采取相应的行动然后返回行动的结果。Agent 可以看作是一个自带路由消费 Chains 的代理,基于 MRKL 和 ReAct 的基本原理,Agent 可以使用工具和自然语言处理问题。官方也提供了对应的 Agent,包括 OpenAI Functions Agent、Plan-and-execute Agent、Self Ask With Search 类 AutoGPT 的 Agent 等。Agent 的作用是代表用户或其他系统完成任务,例如数据收集、数据处理、决策支持等。Agent 可以是自主的,具备一定程度的智能和自适应性,以便在不同的情境中执行任务。我们今天主要了解基于 ReAct 原理来实现的 Agent。
ReAct
ReAct 是一个结合了推理和行动的语言模型。虽然 LLM 在语言理解和交互决策制定方面展现出了令人印象深刻的能力,但它们的推理(例如链式思考提示)和行动(例如行动计划生成)的能力主要被视为两个独立的主题。ReAct 的目标是探索如何使用 LLM 以交错的方式生成推理痕迹和特定任务的行动,从而在两者之间实现更大的协同作用。
想象一下,你有一个智能助手机器人,名叫小明。你给小明一个任务:去厨房为你做一杯咖啡。小明不仅要完成这个任务,还要告诉你他是如何一步步完成的。
没有 ReAct 的小明:
小明直接跑到厨房。
你听到了一些声音,但不知道小明在做什么。
过了一会儿,小明回来给你一杯咖啡。
这样的问题是,你不知道小明是怎么做咖啡的,他是否加了糖或奶,或者他是否在过程中遇到了任何问题。
有 ReAct 的小明:
小明告诉你:“我现在去厨房。”
小明再说:“我找到了咖啡粉和咖啡机。”
“我现在开始煮咖啡。”
“咖啡煮好了,我要加点糖和奶。”
“好了,咖啡做好了,我现在给你拿过去。”
这次,你完全知道小明是怎么做咖啡的,知道他的每一个步骤和决策。
ReAct 就是这样的原理。它不仅执行任务(行动),还会告诉你它是如何思考和决策的(推理)。这样,你不仅知道任务完成了,还知道为什么这样做,如果有问题,也更容易找出原因。
更多关于 ReAct 的内容可以查看这篇文章[1]。
自定义 LLM Agent
LangChain 在官方网站上提供了关于如何创建自定义 LLM Agent[2]的例子,在官网的示例中,我们除了看到自定义 LLM Agent 外,还有一个自定义 Agent,这两者的区别就是自定义 LLM Agent 使用了 LLM 来解析用户输入,判断使用何种工具,而自定义 Agent 则是直接自行判断工具的使用,这种方式只能用于简单的场景,而自定义 LLM Agent 可以用于更复杂的场景。
在官方示例中,对其实现原理做了一些简单的描述,介绍了其组成部分和流程等,但如果是刚开始了解 Agent 的同学看起来可能会一知半解,所以我们今天主要结合其中的示例代码来了解自定义 LLM Agent 的实现原理。
提示词模板
要实现 Agent,我们需要先定义一套基于 ReAct 的提示词模板,示例中的 Agent 就是基于 ReAct 原理来实现的,为了方便理解,我们将官方的英文提示词模板换成中文和去掉一些没必要的内容,修改后的提示词模板内容如下:
简单介绍一下这个提示词模板,首先提示词模板中会引用到一些工具,可以看到模板中有 2 个变量:tools和tool_names,tools 变量是一个列表,包含了所有的工具,列表中的每个元素包含了工具的名称和描述,而 tool_names 变量是工具名称的列表,传入具体的工具后,会生成对应的工具列表,比如我们有如下 2 个工具,解析后如下所示:
这样 LLM 在执行任务时就知道要使用哪些工具,以及在提取信息时可以提取到正确的工具名。
模板中下面的思考/行动/行动输入/观察就是标准的 ReAct 流程,思考是指思考如何解决问题,行动是具体的工具,行动输入是工具用到的参数,观察是工具执行完成后得到的结果,这个流程可以重复多次,直到最终得到最终答案。
模板最后还有 2 个变量:input和agent_scratchpad,input 是用户输入的问题,agent_scratchpad 是之前的思考过程(下面解析代码时会讲),包括了思考、行动、行动输入和观察等,这个变量在 Agent 执行过程中会被更新,代入具体的值后,模板会生成如下的提示词:
从问题下面一句到最后结束就是agent_scratchpad的值。
构造提示词
准备好提示词模板后,我们就可以构造提示词了,构造提示词的官方示例代码如下:
首先我们需要定义一个类,继承自StringpromptTemplate,然后实现format方法,这个方法的作用是将提示词模板中的变量代入具体的值,然后返回提示词。其中intermediate_steps是中间 Agent 思考的步骤,每个步骤是一个元组,包含了AgentAction(行为和行为输入)和Observation(观察)的值,这个变量不会直接传递到 LLM,所以它不会在提示词中出现,但提示词模板会将它转换为agent_scratchpad变量,这个变量也就是我们在上面提到的agent_scratchpad,这个变量的值是 Agent 思考的过程,包括了思考、行动、行动输入和观察等。
format 方法最后会设置模板中的tools和tool_names变量,这两个变量的值是提示词模板中的工具列表,这个列表是根据tools变量生成的,包含了所有的工具,列表中的每个元素包含了工具的名称和描述,而tool_names变量是工具名称的列表。
最后我们需要创建一个CustompromptTemplate对象,传入提示词模板和工具列表,这个对象就是我们最终要传入 LLM 的提示词。
工具解析
接下来是输出结果的解析,其中分为 2 个部分,一个是工具的解析,一个是结果的解析,我们来看下官方的示例代码:
parse 方法前半部分是关于结果解析的,我们待会再讲,我们先看后面的代码,这是对工具的解析。
代码使用了一个正则表达式来解析 LLM 关于思考过程的输出结果,一般思考过程的输出结果是这样的格式:
根据我们的提示词模板,LLM 会智能地返回我们制定好的格式,有思考、行动、行动输入几项内容,我们主要通过这个正则表达式来获取到行动和行动输入这两项的值(也就是工具名称和工具所需参数),这样 Agent 就知道该如何使用工具了。可以看到代码中 action(工具)获取的是正则结果中的第一个分组的值,而 action_input(工具参数)获取的是第二个分组的值。最后将这 2 个值封装成一个 AgentAction 对象返回。
但需要注意的是,虽然我们设置好了提示词模板,但如果 LLM 不够智能的话,返回的结果可能会和我们预期的不一样,比如 LLM 可能返回这样的结果:
可以看到行动的值不是我们预期的 search,而是一句话,这样通过正则表达式获取的工具名就是我需要使用search工具来查询,然后 Agent 会拿这个工具名去匹配工具,然后去调用,发现没有这个工具,就会报错。所以如果我们发现 LLM 有时候返回的结果不符合我们预期时,我们需要通过其他方式来解析出工具的名称,比如去掉一些无用的内容,只保留工具名称,这样才能保证 Agent 能够正常使用工具。
结果解析
我们再说下结果的解析,就是上面代码中 parse方法的前半部分:
示例代码中判断 LLM 的输出是否有包含最终答案:,如果有的话,就说明 LLM 已经得到了最终的答案,这时就可以返回最终的答案了,这个格式也是我们在提示词模板中定义的:
这个最终的答案是 LLM 输出结果中的最后一句话,我们可以通过split方法来获取到最后一句话,然后返回一个ActionFinish对象,这个对象包含了最终的答案,解析出来的结果如下:
跟工具解析一样,我们在结果解析时也需要注意 LLM 返回的结果是否符合我们预期,比如有时候 LLM 会输出这样的结果:
可以看到 LLM 的输出没有包含最终结果:关键字,这样示例代码中的 if 逻辑就不生效了,会导致解析逻辑进到工具解析那一部分去,然后引发错误。所以我们在解析结果时,要提高我们解析程序的健壮性,以满足不同的 LLM 输出结果,或者调整我们的提示词模板,让 LLM 返回的结果更加准确。
中断提示
最后一步是创建 Agent,示例代码如下:
这里重点关注的是 Agent 中的stop参数,我们知道一般 LLM 都会长篇大论,说一大堆废话,我们希望 LLM 在返回了我们需要的信息后就停止输出,这里就需要用到stop参数,这个参数是一个列表,列表中的每个元素都是一个字符串,代表了 LLM 输出中的某一句话,当 LLM 输出中包含了这句话时,LLM 就会停止输出,这样我们就可以只获取到我们需要的信息了,这里我们使用观察关键字来停止 LLM 的输出。
总结
自定义 LLM Agent 的示例代码我们已经介绍完了,最后我们再讲下来 Agent 中使用的 LLM。在官方示例中,LLM 用的是 OpenAI,也就是gpt-3.5这个模型,但如果想达到更好的效果的话,推荐使用 OpenAI 的gpt-4模型,它是目前最好的 LLM,如果使用的 LLM 比较差,就容易出现刚才我们提到 LLM 返回的结果不符合我们预期的情况。
有人希望通过一些开源的 LLM 来实现 ReAct Agent,但实际开发过程中会发现开源低参数(比如一些 6B、7B 的 LLM)的 LLM 对于提示词的理解会非常差,根本不会按照提示词模板的格式来输出,这样就会导致我们的 Agent 无法正常工作,所以如果想要实现一个好的 Agent,还是需要使用好的 LLM,目前看来使用gpt-3.5模型是最低要求。
参考:
[1] 这篇文章: https://react-lm.github.io/
[2] 创建自定义 LLM Agent: https://python.langchain.com/docs/modules/agents/how_to/custom_llm_agent
HugggingFace 推理 API、推理端点和推理空间使用介绍
接触 AI 的同学肯定对HuggingFace[1]有所耳闻,它凭借一个开源的 Transformers 库迅速在机器学习社区大火,为研究者和开发者提供了大量的预训练模型,成为机器学习界的 GitHub。在 HuggingFace 上我们不仅可以托管模型,还可以方便地使用各种模型的 API 进行测试和验证,部署属于自己的模型 API 服务,创建自己的模型空间,分享自己的模型。本文将介绍 HuggingFace 的推理 API、推理端点和推理空间的使用方法。
HuggingFace 推理 API
在 HuggingFace 托管的模型中,有些模型托管之后会提供推理 API,如果我们想快速验证模型是否可以满足我们的需求,可以使用这些 API 进行测试,下面以这个模型为例Salesforce/blip-image-captioning-base进行介绍,该模型可以通过图片生成英文描述。
页面小组件
推理 API 有两种使用方式,一种是在模型页面的右侧找到推理 API 的小组件页面,初始界面如下图所示:
我们可以在这个页面中上传图片,然后就可以看到模型进行推理运行,等一会后推理结果就出来了,如下图所示:
推理结果为:“a dog wearing a santa hat and a red scarf”(一只狗戴着圣诞老人的帽子和红色的围巾)
页面小组件的方式是 HuggingFace 自动帮助模型创建的,具体的信息可以参考这里[2]。
代码调用
另外一种方式是通过代码对推理 API 进行调用,在右侧的Deploy菜单中选择Inference API,如下图所示:
打开菜单后可以看到几种代码调用方式,分别有 Python, Javascript 和 Curl:
这里我们选择 Curl 方式来进行调用,我们可以直接复制界面上的 Curl 命令,注意其中包含了我们的 API token,所以不要随意分享出去,然后在终端上执行命令,就可以看到预测结果了:
HuggingFace 推理端点(Endpoint)
推理 API 虽然方便,但推理 API 一般用于测试和验证,由于速率限制,官方不推荐在生产环境中使用,而且也不是所有模型都有提供推理 API。如果想要在生产环境部署一个专属的推理 API 服务,我们可以使用 HuggingFace 的推理端点(Endpoint)。
推理端点的部署也比较简单,首先在Deploy菜单中选择Inference Endpoints,如下图所示:
打开菜单后可以看到新建推理端点的界面,如下图所示:
首先是服务器的选择,先选择云服务厂商,目前只有 AWS 和 Azure 两种,再选择机器区域节点。
然后是服务器的配置,HuggingFace 默认会给出模型的最低推理配置,如果我们想要更高的配置,可以点击2中的下拉框进行选择。
接着是推理端点的安全等级,有 3 种选择,分别是Protected、Public和Privaate
Pubulic:推理端点运行在公共的 HuggingFace 子网中,互联网上的任何人都可以访问,无需任何认证。
Protected:推理端点运行在公共的 HuggingFace 子网,互联网上任何拥有合适 HuggingFace Token 的人都可以访问它。
Privacy:推理端点运行在私有的 HuggingFace 子网,不能通过互联网访问,只能通过你的 AWS 或 Azure 账户中的一个私有连接来使用,可以满足最严格的合规要求。
最后显示的是服务器的价格,按小时算,根据配置的不同,价格也会有所不同。HuggingFace API 是免费的,但 HuggingFace 的推理端点是要收费的,毕竟是自己专属的 API 服务。因为推理端点部署是收费的,所以在部署之前需要在 HuggginFace 中添加付款方法,一般使用国内的 Visa 或 Master 卡就可以了。
信息确认无误后点击Create Endpoint按钮创建推理端点,创建成功后可以进入推理端点的详情页面看到如下信息:
其中Endpoint URL就是部署好的推理端点地址,我们可以跟调用推理 API 一样的方式来使用它,示例代码如下:
HuggingFace 模型空间(Space)
HuggingFace 推理端点是部署 API 服务,但是如果我们想要分享自己的模型,让别人可以直接在浏览器中使用模型的功能,这时候就需要使用 HuggingFace 的模型空间(Space)了。
要部署一个模型空间,首先在模型的Deploy菜单中选择Spaces,如下图所示:
选择菜单后可以看到空间创建的引导界面,如下图所示:
界面中显示了启动模型的 Python 脚本,然后我们点击Create new Space按钮进入空间的创建页面,如下图所示:
在模型创建页面中,我们需要设置以下信息:
首先要指定空间的名称,一般以模型的名称命名。
然后选择空间的 SDK,目前有Streamlit、Gradio、Docker和Static 四种。
Streamlit:Streamlit 是一个可以帮助我们快速创建数据应用的 Python 库,可以在浏览器中直接使用模型,它相比Gradio可以支持更加丰富的页面组件,界面也更加美观。
Gradio:Gradio 也是一个编写 GUI 界面的 Python 库,相对Streamlit来说,它的 GUI 功能虽然比较少,但它的优势在于简单易用,一般演示的 Demo 用它就足够了。
Docker:推理空间也可以使用 Docker 容器进行部署,它内部支持了 10 种模版。
Static:静态页面,我理解是包括 Html、Js、Css 等前端资源来作为页面展示。
然后选择空间硬件,HuggingFace 为每个空间提供了一个免费的配置:
2 核 CPU 16G 内存,用这个配置部署推理空间是免费的,如果你想要更高的配置,也可以选择付费的配置。
最后是安全等级,有Public和Private两种,Public 是公开的,任何人都可以访问,但只有你的组织成员可以修改,Private 是私有的,只有你的组织成员可以访问。
设置完后点击Create Space按钮就开始创建推理空间了,创建完成后会自动跳转到空间的页面,如下图所示:
如果推理空间的安全等级设置为 Public,你就可以将空间的 URL 分享给其他人使用了。想查看 HuggingFace 推理空间更多的信息,可以参考这里[3]。