从图11的架构图中可见,一个RAG服务,至少需要三个模型:语义文本分割模型(text-splitter)、嵌入模型(embedding)、大语言模型(LLM)。RAG的实现可以有不同的层次,这超出本篇的范围。这里只以图11-企业知识库服务架构展示的RAG方案为例介绍ModelHub。
这里需要文本分割模型和嵌入模型不像大语言模型有公共API提供,需要自行解决调用问题。
在初次实现RAG服务时,我们通过本地模型文件加载,推理的方式使用。从上文可以看出来,这种使用方式有以下几种痛点:
代码费时:加载本地模型文件,就需要配置模型文件地址。这对修改模型调试需要修改代码增加了复杂度。而开发时的文件地址,和部署生产环境的文件地址又有不同,这又增加了服务代码和构建部署代码的复杂度。
部署费力:在云上部署服务,AI应用的单个模型文件都是几百MB到GB不等,将模型文件放到代码中,再构建部署是不合适的。这就需要额外的挂载存储资源的配置工作。
资源浪费(CPU、Memory):模型占用资源较多,但是每个模型使用量不同。
无法复用:其它AI应用无法复用模型的使用经验。上述的问题,在其它AI应用上,会继续重复问题。
我们可以通过将模型推理功能和RAG拆分开来,将模型推理功能服务化,通过APIs使用模型推理,将模型推理服务集中管理,即模型服务中心应用(ModelHub)。
一个典型的AI应用开发兼顾模型服务开发的流程如下图所示:
为了更好地理解这个流程,我们以一个开发者希望使用某个模型来构建一个应用程序为例。
对于AI应用开发人员,工作流程如下:
选择模型:开发者首先在ModelHub上选择一个适合其需求的模型。每个模型都有一个唯一的ID,方便开发者进行标识和调用。
如果模型不在ModelHub上,则进入在ModelHub部署模型的流程。如果存在,则进入下一步。
使用模型:通过ModelHub的SDK,可以调用ModelHub上部署的模型服务,推理得到模型调用结果。
开发应用:开发者使用模型功能开发自己的应用程序。在这个过程中,应用程序会调用之前部署的API服务,从而实现模型的预测功能。
对于部署模型服务人员(可以和AI应用开发人员是同一人),工作流程如下:
将选择的模型,或训练的模型,部署到ModelHub上成为模型服务。
在AI平台上(一般是JupyterHub/JupyterLab)使用的模型加载、推理代码,整理成ModelHub格式的模型预测脚本。
将模型文件和预测脚本按ModelHub的方式,发布到ModelHub平台上。发布成功后,就可以根据对应的ID和ModelHub SDK调用该模型执行推理得到结果。
通过是否使用ModelHub的各项对比,可以评估研发ModelHub的价值:
|
直接使用(本地)模型 |
使用ModelHub模型 |
| 学习成本 |
无
|
中等 |
| 代码难度 |
低 |
中等 |
| 部署难度 |
中上 |
低 |
| 资源利用 |
一般 |
较高 |
| 模型复用 |
否 |
是 |
| 代码复用 |
否 |
是 |
| 模型对比实验 |
麻烦 |
较易 |
在对比直接使用模型和通过ModelHub使用模型的优缺点后,我们可以更清晰地看到ModelHub带来的好处和可能的挑战。
首先,通过ModelHub使用模型的学习成本相对于直接使用模型会稍微提高一些。原因是用户不仅需要理解模型本身,还需要理解ModelHub的使用方式和流程。然而,一旦掌握了ModelHub的使用,这种投入将会带来长远的回报。
其次,直接使用模型的代码难度相对较低,因为用户只需要关注如何加载和使用模型。但是,通过ModelHub使用模型,用户需要额外掌握如何将模型上传到ModelHub,以及如何通过ModelHub的API进行调用。然而,这也意味着用户可以通过ModelHub提供的API,更方便地在不同的项目中复用模型和代码。
部署难度方面,ModelHub显著优于直接使用模型。直接使用模型需要解决模型文件的存储和加载问题,而ModelHub为用户提供了一个中心化的解决方案,简化了部署的复杂性。
在资源利用方面,ModelHub也比直接使用模型更高效。因为ModelHub可以根据需要配置模型服务资源,避免了直接使用模型可能存在的资源浪费问题。
然后,ModelHub的另一个显著优点是支持模型和代码的复用。这意味着一旦一个模型被上传并配置到ModelHub,其他AI应用可以轻松地复用这个模型,无需再次进行模型加载和配置。这极大地提高了模型的使用效率和便利性。
最后,ModelHub提供了方便的模型对比实验功能。用户可以轻松地在ModelHub中上传和测试不同的模型,然后通过比较它们的性能来选择最合适的模型。而在没有ModelHub的情况下,进行模型对比实验会更加麻烦。
总的来说,尽管通过ModelHub使用模型的学习成本和代码难度略高于直接使用模型,但ModelHub的便利性、资源效率、以及对模型和代码复用的支持,使其成为一个非常值得投资的工具。
根据调研的模型服务和预期的模型服务中心用户故事,模型服务中心的系统可以设计成客户端(SDK)、应用UI、APIs、后台管理UI、服务管理、部署模型服务、模型文件存储等七个组件,如下图所示:
上述组件是综合前面调研的设计。在实际的实施过程中,是可以根据开发阶段先只实现其中部分组件。后台管理部分完全是可选的。ModelHub UI部分是提供用户使用,可以通过UI上传模型文件、模型推理脚本,实现自动化部署模型服务,这部分也是可选。而模型存储组件可以通过固定的挂载存储方式解决。
一天找开源项目,一天部署开源项目,问题就这样解决了?我们做了如下调研分析:
1. Replicate
Replicate是一个闭源商业大模型服务供应商,Replicate提供了用户友好的接口(SDK),是一个理想的模型服务中心方案。但是Replicate只是提供了生成式AI模型[8]的支持,不能满足需求。
2.HuggingFace inference endpoints
Inference Endpoints提供了一种安全的生产解决方案,可以轻松地将Hub中的任何Transformers、Sentence Transformers和扩散器模型部署到由Hugging Face管理的专用和自动扩展的基础设施上[9]。但是不支持modelscope上的模型,这使AI应用对模型的使用有限制。不能完全满足需要。
3. Sagemaker
SageMaker通过完全托管的基础设施、工具和工作流程为任何用例构建、训练和部署机器学习(ML)模型。SageMaker 提供对数百个预训练模型的访问权限,包括公开的基础模型,您只需点击几下即可部署这些模型[10]。
我们没有使用SageMaker的经验,它应该可以满足部署模型服务的需求,但是使用SageMaker需要引入一套不同的技术栈,在这个需求上不合适。
4. Ollama
ollama是大模型的容器化本地执行方案,一开始并不支持Embedding模型,在后来版本支持。且只局限于提供大模型服务和几个有数的Embedding模型。不能满足需求。
5. LiteLLM
LiteLLM可以通过类似反向代理的方式,将许多的大模型供应商的服务都统一成部署LiteLLM地址的OpenAI兼容(OpenAI Compatible)的API[11]。
LiteLLM在大模型服务上提供了完美的解决方案,它可以自行创建API KEY,统计每个KEY的调用量、tokens使用花费,多个大模型部署负载均衡,大模型输入输出日志,还有后台管理UI等等功能。
LiteLLM也提供了Embedding模型、文生图、STT、TTS等API,但都是通过类似反向代理的方式,调用这些实际部署的服务,而不是提供部署模型的方案。且不支持其它种类的模型服务。
所以在LiteLLM提供的模型服务管理功能和统一API有很好的参考意义,但不能直接使用解决需求。
从上面调研的数个产品可知,业界没有一个开箱即用的ModelHub,那么需要自行设计并实现一个ModelHub产品。
部署模型服务可有以下几个主要选择:
存储挂载+BetterCDS[12]
单体单机部署/单体多机部署/单体K8s部署
微服务+K8s部署
其中BetterCDS是数禾自研的一站式DevOps平台[12]。它有自己的一套上线服务流程,在本文的需求场景并不适用,但是未来可以探讨立项基于BetterCDS开发一个ModelHub产品。
单体ModelHub应用,在软件实施上是较容易的,但是拓展性(Scaling)较差,长期迭代很容易遇到困难。其中显而易见的是各个AI模型的Python包依赖冲突难题。
微服务+K8s部署的方案,其中微服务可简化为各个模型应用隔离,且与ModelHub服务隔离。ModelHub服务的独立,且通过ModelHub部署提供服务的多个模型服务各自独立为微服务。这通过架构方式而非软件方式直接解决了AI模型的Python包依赖冲突问题。
增删模型服务的拓展、一个模型提供服务容量的拓展,都是可以通过微服务的独立部署解决。
直接采用K8s部署应用服务,在容错性(Failovers)、副本(Replication)等方面,直接使用K8s提供的原生特性,可以省去重复造轮子的拙劣工作。
K8s之外的部署方案选择、对比,超出了本文范围,不再展开。
ModelHub产品必须提供一个有规则的APIs方案,不然与每个模型服务手动部署、配置也没有差别。
APIs可分成调用模型服务推理的API、ModelHub应用服务的API。这里探讨模型服务推理的API设计:
服务地址
因为前文探讨的部署方式,每个模型服务各自部署,所以每个模型服务地址自然不同。根据前文的用户故事(User Story)设计,每个模型服务自然需要对应ModelHub的模型服务ID,模型服务地址的确定便可以通过该ID确定。
因为选定K8s技术部署模型服务,通过应用LoadBalancer类型的Service资源,访问模型服务地址,即实现负载均衡。
模型服务在K8s部署时,将K8s服务名称直接设为ModelHub的模型服务ID,那么无需额外实现索引功能,直接利用K8s客户端,即可通过ModelHub的模型服务ID查询模型服务地址。
路由
对于AI应用使用的模型,在前文提供的结构设计图中,我们可以做出一些分类。其中大模型服务的路由(router or endpoints),业界已有约定俗成的写法,即”/completions”、”/chat/completions”。而embedding模型,亦有”/embeddings”。这些有约定俗成写法的模型,大多在许多AI框架中也有封装。所以这类模型服务的路由,可以通过服务代码、客户端SDK固定下来,以便提供各种AI框架的客制化封装。
其它比如文本分割模型,则可以用户自行定义。
协议
在机器学习社区有多个框架实现了将模型封装成服务应用的功能,它们通常既实现了服务端代码的封装,也实现了客户端封装,既实现了HTTP协议,也可以通过配置以gRPC协议提供模型服务。所以这里无需对比选择,两者都实现的情况,只需要按情况配置使用即可。
缓存、消息队列等,对于ModelHub产品不是必须实现的功能或组件。
数据库、认证与鉴权等,根据ModelHub的使用范围和本文实现版本,相关功能不需实现或未来版本再实现。
我们不准备从零编写每一行代码实现ModelHub,在机器学习社区有多个框架实现了将模型封装成服务应用的功能,这部分功能对于ModelHub的封装模型推理、部署推理服务实现是有帮助。不过大多数框架是MLOps框架,或做机器学习模型训练的框架,且一并实现了模型推理服务、部署的功能。在模型推理服务和部署这部分支持的相对完善、符合需求的框架,我们选择了Jina[13]。
对于ModelHub上的模型服务,我们约定一个固定的挂载路径,和一个固定的对象存储服务地址。这样使用的模型都上传到这个对象存储服务地址,在模型推理程序中,使用这个挂载地址和模型名称加载模型。
一个嵌入模型服务的模型加载代码示例如下:
图20: 开发ModelHub上的嵌入模型服务的加载模型代码
使用Jina框架实现服务:使用Jina提供的Executor类包装模型推理功能即可通过Jina提供AI服务。
如前文所言,对于嵌入模型服务,有”/embeddings”路由端点是业界约定俗成的。所以我们对此做了一定程度的封装,如此一个嵌入模型推理代码示例如下:
图21: 开发ModelHub上的嵌入模型服务的推理代码
根据前文的系统设计可知,其中”langchain-hf-embeddings-bge-base-zh”即是模型服务ID。在下文可以看到使用该ID客户端定位到模型服务地址。
使用K8s部署,需要先构建镜像,然后编K8s的YAML配置文件部署。
构建镜像
构建镜像的过程和一般服务应用构建镜像的过程无异。编写应用依赖、编写Dockerfile、构建和上传镜像即可。
K8s配置文件
参考Jina文档的云原生Kubernetes部署支持,结合前面几步,使用Jina框架提供的to_kubernetes_yaml方法,可以自动生成K8s配置文件。
其中使用ModelHub的ID作为生成配置文件中的服务资源名称。配置LoadBalancer类型的Service资源示例代码如下:
这样ModelHub客户端可以使用ID通过K8s客户端查询服务地址,参见下文SDK客户端。
K8s部署
使用K8s的kubectl apply命令可以直接部署上述模型服务。通过将多个模型服务的配置文件生成在同一文件夹下的不同路径,模型服务的增删、更新,都可以使用相同的命令部署。该配置文件夹即ModelHub上的所有模型服务的部署集合。
SDK客户端
将模型服务部署之后,我们需要定位模型服务地址以调用服务。Client通过模型ID获取模型服务地址的代码示例如下:
Jina框架提供了Client类,其封装的功能对AI应用常用的embeddings和tensors数据嵌入AI应用服务特别有用。
参考Jina文档,使用图中获取的模型服务地址,和前文实现模型服务路由端点,即可实现Client访问前文实现的模型推理服务。
在AI应用中,我们使用ModelHub的SDK,配置想要使用的模型服务ID,即可调用。在企业知识库服务的使用代码示例如下:
图25: RAG应用使用ModelHub上的嵌入模型服务
从图25中代码可以看出,AI应用不再依赖本地模型文件,这意味着部署AI应用不再需要挂载文件对象储存等步骤。对于模型的更换、新的AI应用开发上线,都简化了这个部署步骤。
因为AI应用不再使用本地模型执行推理,所以AI应用使用的资源和一般应用消耗资源无异。
图26: 使用ModelHub后的RAG应用CPU消耗
对比图15-RAG应用资源消耗,可以看到原本需要6C配置,现在下降使用不到1C。
图27: 使用ModelHub后的RAG应用Memory消耗
对比图15-RAG应用资源消耗,可以看到原本需要7G的配置,现在下降到一般使用不到1.5G。
从图25中代码可以看出,AI应用不再依赖本地模型文件,这对AI应用开发有如下好处:
减少模型推理代码,因为推理代码在图21的模型服务中。
对多种不同的模型做效果评估实验等,变得容易。
更换更好效果的模型只需要修改模型服务ID即可。
本文从RAG(Retrieval-Augmented Generation,即检索增强生成)定义开始,先通过介绍LLM(Large Language Model, 即大语言模型)是什么、有什么问题、解释了RAG技术如何解决LLM问题。然后展示了将RAG用做生产应用过程中的问题,揭示了RAG应用的产品细节,暴露出来其从实验室到生产应用遇到的挑战,进而引入ModelHub(模型服务中心)解决方案。介绍了模型服务中心产品的设计、调研、开发到应用全链路过程。ModelHub为AI时代到来即将产生的更多AI应用提供一套可拓展(Scaling)的解决方案。
本文重点介绍了客户端SDK、APIs、部署模型服务三个组件,对于ModelHub产品,还有数个组件是可以完善的。在未来或可以先实现用户UI使部署模型服务流程更简化。实现存储方案对模型文件的管理和使用也对现存问题有所解决。最后后台管理UI涉及的相关功能,可以进一步简化模型服务的监控,解决权限、计费管理等等问题。
目前我们是在企业内部使用ModelHub,可以省略众多组件、功能实现。根据图17-ModelHub架构图,有些组件又可以分阶段逐步实现,这为未来的产品迭代保留空间。
注意,本文的ModelHub应用在2024年上半年完成开发和上线。因为AI社区正在快速变化,所以本文的部分内容(比如对ModelHub应用的调研)可能已经过时,请自行甄别。
引用
[1] How I Use "AI", Nicholas Carlini
[2] LLM tokenization解释:《为什么AI数不清Strawberry里有几个 r?Karpathy:我用表情包给你解释一下》, 微信公众号文章
[3] Hallucination (artificial intelligence), wikipedia
[4] ChatGPT, 百度百科词条
[5] RAG Paper: Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks
[6] “In simple terms, RAG is to LLMs what an open-book exam is to humans.”, Retrieval-Augmented Generation (RAG): From Theory to LangChain Implementation, Leonie Monigatti.
[7] 《基于大模型构建本地知识库》, 微信公众号数禾技术文章
[8] Replicate官网:replicate.com
[9] HuggingFace推理端点:huggingface.co/inference-endpoints/dedicated
[10] Amazon SageMaker: aws.amazon.com/cn/sagemaker
[11] LiteLLM项目:github.com/BerriAI/litellm
[12] 《数禾AI模型持续交付实践》, 微信公众号数禾技术文章
[13] Jina Docs: docs.jina.ai