大模型推理框架 RTP-LLM 架构解析

大模型推理框架RTP-LLM架构解析

RTP-LLM 是阿里巴巴智能引擎团队推出的大模型推理框架,支持了包括淘宝、天猫、闲鱼、菜鸟、高德、饿了么、AE、Lazada 等多个业务的大模型推理场景。RTP-LLM 与当前广泛使用的多种主流模型兼容,使用高性能的 CUDA kernel, 包括 PagedAttention、FlashAttention、FlashDecoding 等,支持多模态、LoRA、P-Tuning、以及 WeightOnly 动态量化等先进功能,已在众多 LLM 场景中得到实际应用与检验。

本篇文章介绍了 RTP-LLM 的整体架构,并着重分析了模型加载过程中的核心部分:模型的权重和配置文件。本文主要由社区用户 mingming 贡献,特此感谢其对项目的支持。

一、架构介绍

我们首先对 RTP-LLM,该系统由多个层次组成,从最外层的 InferenceApp 到最内层的 AsyncModel,每一层都有特定的职责。

1.1 InferenceApp 和 FastAPI 服务

最外层是 InferenceApp,其核心是一个基于 FastAPI 的异步服务。这一层负责接受外部的 HTTP 请求,并将请求传递给内部的推理服务器。

1.2 InferenceServer 和 InferenceWorker

InferenceServer 是实际处理推理请求的服务器,内部有多个 InferenceWorker 负责具体的任务。每个 InferenceWorker 包含一个 Pipeline,包含了用于处理请求的异步模型 AsyncModel

二、异步模型 AsyncModel 的工作流程

AsyncModel 是整个系统的核心组件,负责具体的推理任务。它包含两个主要部分:Scheduler 和 ModelExecutor

2.1 接收请求

当 HTTP 请求到达时,系统会依次进行以下处理:

  1. Prompt 构建:根据请求内容,构建推理所需的 prompt。

  2. Tokenize:将 prompt 转换为模型可以理解的 token。

2.2 任务排队

处理完请求后,系统会将任务通过 model.enqueue 放入队列中,准备进行后续处理。

2.3 Decoder Engine

Decoder Engine 是异步推理的核心,包含两个部分:Scheduler 和 ModelExecutor

Scheduler

Scheduler 负责调度任务,它会从 waiting_streams 中选择需要处理的任务,并安排执行。在后台,Scheduler 会不断运行 step 函数:

  1. Scheduler.schedule 获取 batch_query。

  2. ModelExecutor.process 执行推理任务。

ModelExecutor

ModelExecutor 负责实际的模型执行过程:

  1. Process BatchQuery:处理 batch_query。

  2. Token Embedding:将 img token 转换为 img embedding。

  3. CUDA 预测:将输入数据送入 CUDA 引擎进行预测。

2.4 执行推理

Decoder 函数会调用 step 函数,将输入数据放入 batch_query,然后开始等待 step 函数完成推理,并将结果返回。

三、模型配置加载

该框架在模型的加载过程中,通常包含两个核心部分:模型的权重和配置文件。本小节将通过一个具体案例——添加对新模型(如 OPT 模型)的支持,来详细探讨如何加载模型的配置文件。

3.1 完整实现

首先给出一个最终的配置加载的实现如下, 具体逻辑为将模型的配置对应到框架的配置上, 因为每个模型的配置文件的变量的名字都不尽相同,比如表示 transformer 模型多头注意力机制的 head num 的变量名,有的模型可能叫 num_attention_heads(opt),有的模型可能叫 n_head(starcoder),所以需要对应到框架统一的变量体系下。

class Opt(GPT):    """Opt"""    @classmethod    def _create_config(cls, ckpt_path: str):        offset = 2        config_dict = get_config_from_path(ckpt_path)        config = GptInitModelParameters(            head_num=config_dict['num_attention_heads'],            size_per_head=config_dict['hidden_size'] // config_dict['num_attention_heads'],            layer_num=config_dict.get('num_hidden_layers', 12),            vocab_size=config_dict['vocab_size'],            max_seq_len=config_dict['max_position_embeddings'] + offset        )        config.layernorm_type = 'pre_layernorm'        config.norm_type = "layernorm"        config.has_post_decoder_layernorm = True        config.hidden_size = config_dict['hidden_size']        config.inter_size = config_dict["ffn_dim"]        config.has_positional_encoding = True        config.activation_type = 'relu'        config.add_special_tokens = True        config.special_tokens.eos_token_id = config_dict.get('eos_token_id', 2)        config.special_tokens.pad_token_id = config_dict.get('pad_token_id', 1)        config.special_tokens.bos_token_id = config_dict.get('bos_token_id', 2)        config.head_num_kv = config.head_num        return config

3.2 实现解析

在这个框架中,常用的 config 变量名及其对应的含义如下:

  • head_num: 在 Transformer 模型的自注意力(Self-Attention)机制中,表示注意力机制被分割成的头的数量。每个头负责学习输入数据的不同表示子空间。

  • head_num_kv: 指定了在自注意力机制中用于处理键(Key)和值(Value)的注意力头的数量。在标准的 Transformer 模型中,查询(Query)、键和值通常使用相同数量的注意力头进行处理。然而,某些模型(如 llama2 70B)可能设计为使用不同数量的头来处理键和值。

  • size_per_head: 表示每个注意力头的维度大小。通常,模型的总隐藏层维度(hidden_size)被平均分配给所有的头。例如,如果hidden_size是 512 且head_num是 8,那么size_per_head将是 64。

  • layer_norm_type: 指定了模型中使用的层归一化(Layer Normalization)的类型或位置。例如,'pre_layernorm'表示归一化操作在每个子层(如注意力层或前馈网络层)之前进行;而'post_layernorm'则表示归一化操作在每个子层之后进行。

  • vocab_size: 表示词表的大小。

  • max_seq_len: 表示模型能够处理的最长序列长度。

  • norm_type: 指定模型使用的归一化技术,如“Layernorm”或“RMSNorm”。

  • has_post_decoder_layernorm: 表示模型是否在解码器的最后添加了额外的层归一化层。

  • hidden_size: 表示隐藏层的维度。

  • inter_size: 表示 FFN 中间层的维度。在 LLM 的 FFN 中,会先进行 up_proj,然后进行 down_proj,这里的inter_size就是 up_proj 后的维度。

  • has_positional_encoding: 表示模型是否使用位置编码,且位置编码是否通过nn.embedding得到

  • activation_type: 指定激活函数的类型,如 relu、gelu 或 swiglu。

  • add_special_tokens: 表示是否在模型的词汇表中添加特殊标记,如 EOS(结束标记)、PAD(填充标记)和 BOS(开始标记)。

  • special_tokens: 包含模型使用的特殊标记的 ID,如eos_token_idpad_token_idbos_token_id

在加载配置文件后,框架会根据这些参数去加载权重等。例如,如果has_positional_encoding为 True,框架会初始化nn.embedding(config.max_seq_len, config.hidden_dim)。当然,还有很多其他操作,这里就不展开了。如果要添加新模型,可以通过对比框架参数和 Hugging Face 模型文件下的config.json参数来进行编写。

3.3 注意事项

1)在编写完新模型的支持代码并加载了模型文件配置之后, 需要确认一下模型是不是按照参数的正常含义去使用的。OPT 模型就不是,它有着自己独特的位置编码(Positional Encoding)实现方式, 无论是position_ids还是nn.embedding,它都统一增加了一个偏移量,让人好一通 debug,一直以为是模型权重没加载对,没想到是位置编码出了问题。

因此,当我们要为一个大模型添加新的支持时,要注意他的独特之处,比如位置编码的实现方式、权重的初始化方法、或者是某些特定的层结构。

class OPTLearnedPositionalEmbedding(nn.Embedding):"""This module learns positional embeddings up to a fixed maximum size."""def __init__(self, num_embeddings: int, embedding_dim: int):# OPT is set up so that if padding_idx is specified then offset the embedding ids by 2# and adjust num_embeddings appropriately. Other models don't have this hackself.offset = 2super().__init__(num_embeddings + self.offset, embedding_dim)def forward(self, attention_mask: torch.LongTensor, past_key_values_length: int = 0):"""`input_ids_shape` is expected to be [bsz x seqlen]."""attention_mask = attention_mask.long()# create positions depending on attention_maskpositions = (torch.cumsum(attention_mask, dim=1).type_as(attention_mask) * attention_mask).long() - 1# cut positions if `past_key_values_length` is > 0positions = positions[:, past_key_values_length:]return super().forward(positions + self.offset)

2)inter_size是一个必须传递的参数,因为它在底层 CUDA GEMM 算子的执行中起着关键作用。如果忽略了这个参数,底层 CUDA GEMM 算子将会报错。

四、模型权重加载

在本小节中,我们以相同的案例(添加对新模型如的支持),来展开如何加载模型的权重。

4.1 完整实现

首先给出一个最终的模型权重加载的实现如下, 和模型的配置是差不多的,具体逻辑就是将模型的权重参数名对应到框架的权重参数名上, 因为每个模型的权重参数名定义每个都不一样,所以需要对应到框架统一的变量体系下。

class OptWeightInfo(ModelDeployWeightInfo):def _get_weight_info(self):layer_weights = [# * Attention之前 layer_norm部分WeightInfo(W.pre_ln_gamma, [CkptWeightInfo('model.decoder.layers.{i}.self_attn_layer_norm.weight', identity)], identity),WeightInfo(W.pre_ln_beta, [CkptWeightInfo('model.decoder.layers.{i}.self_attn_layer_norm.bias', identity)], identity),# *  AttentionWeightInfo(W.attn_qkv_w, [CkptWeightInfo('model.decoder.layers.{i}.self_attn.q_proj.weight', identity),CkptWeightInfo('model.decoder.layers.{i}.self_attn.k_proj.weight', identity),CkptWeightInfo('model.decoder.layers.{i}.self_attn.v_proj.weight', identity),], functools.partial(merge_qkv_hf)),WeightInfo(W.attn_qkv_b,[CkptWeightInfo('model.decoder.layers.{i}.self_attn.q_proj.bias', identity),CkptWeightInfo('model.decoder.layers.{i}.self_attn.k_proj.bias', identity),CkptWeightInfo('model.decoder.layers.{i}.self_attn.v_proj.bias', identity),], functools.partial(merge_qkv_b)),WeightInfo(W.attn_o_w,[CkptWeightInfo('model.decoder.layers.{i}.self_attn.out_proj.weight', identity)], transpose),WeightInfo(W.attn_o_b,[CkptWeightInfo('model.decoder.layers.{i}.self_attn.out_proj.bias', identity)], identity),# * Attention之后 layer_norm部分 FFN之前WeightInfo(W.post_ln_gamma,[CkptWeightInfo('model.decoder.layers.{i}.final_layer_norm.weight', identity)], identity),WeightInfo(W.post_ln_beta,[CkptWeightInfo('model.decoder.layers.{i}.final_layer_norm.bias', identity)], identity),# * FFN部分 WeightInfo(W.ffn_w3,[CkptWeightInfo('model.decoder.layers.{i}.fc1.weight', identity)], transpose),WeightInfo(W.ffn_b3,[CkptWeightInfo('model.decoder.layers.{i}.fc1.bias', identity)], identity),WeightInfo(W.ffn_w2,[CkptWeightInfo('model.decoder.layers.{i}.fc2.weight', identity)], transpose),WeightInfo(W.ffn_b2,[CkptWeightInfo('model.decoder.layers.{i}.fc2.bias', identity)], identity),]weights = [WeightInfo(W.embedding, [CkptWeightInfo('model.decoder.embed_tokens.weight', concat_0)], identity),WeightInfo(W.positional_embedding,[CkptWeightInfo('model.decoder.embed_positions.weight', identity)], identity),WeightInfo(W.final_ln_gamma,[CkptWeightInfo('model.decoder.final_layer_norm.weight', identity)], identity),WeightInfo(W.final_ln_beta,[CkptWeightInfo('model.decoder.final_layer_norm.bias', identity)], identity),WeightInfo(W.lm_head,[CkptWeightInfo('lm_head.weight', identity)], identity),]return ModelWeightInfo(layer_weights=layer_weights, weights=weights)#, tp_strategy=self._get_gpt_style_tp_strategy())class OPT_125M(GPT):@staticmethoddef get_weight_cls():return OptWeightInfo

框架中对于权重信息分为了 layer weights(对应的是 decoder layer 的权重参数)和 weights(对应的是 decoder layer 之外的权重参数,比如 lm_head、token embedding 部分 ), 上图中是框架的参数和模型结构的一个对应,首先我们需要看一下 pytorch.bin.index 里面的参数名称,然后和框架中的参数名对应上,比如:

WeightInfo(W.pre_ln_gamma, [CkptWeightInfo('model.decoder.layers.{i}.self_attn_layer_norm.weight', identity)], identity),

这里 W.pre_ln_gamma 表示的是 Attention 和 FFN 层之前的 Layer Norm 层,而在 OPT 模型中,参数名称为 model.decoder.layers.

WeightInfo(W.embedding, [CkptWeightInfo('model.decoder.embed_tokens.weight', concat_0)], identity),

这里 W.embedding 表示的是 token 的 embedding 层,在 OPT 模型中参数名 model.decoder.embed_tokens.weight。

参考链接

[01] RTP-LLM 项目地址:

https://github.com/alibaba/rtp-llm

[02] 原作者博客:

https://www.zhihu.com/people/66-34-28-46-64/posts

[03] OPT 模型:

https://link.zhihu.com/?target=https%3A//github.com/huggingface/transformers/blob/main/src/transformers/models/opt/modeling_opt.py%23L566

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/418488.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot-自定义banner

在 Spring Boot 应用中,你可以自定义启动时显示的 banner。这些 banner 可以包括图形、文字或者其他形式的标识。如图所示: 1. 使用 banner.txt 文件 默认情况下,Spring Boot 使用项目的 banner.txt 文件中的内容作为启动时的 banner。你可以…

会员营销如何利用JSON发送短信

在当今这个数字化时代,企业间的竞争日益激烈,如何高效地触达并维护用户群体,提升用户粘性和忠诚度,成为了每个企业都必须面对的重要课题。在众多营销手段中,会员营销因其精准性和个性化而备受青睐。而在会员营销的策略…

Vue学习笔记 二

4、Vue基础扩展 4.1 插槽 组件的最大特性就是复用性,而用好插槽能大大提高组件的可复用能力在Vue中插槽是很重要的存在,通过插槽,我们可以把父组件中指定的DOM作用到子组件的任意位置,后面我们坐项目用到的组件库比如element-ui…

ctfshow-nodejs

什么是nodejs Node.js 是一个基于 Chrome V8 引擎的 Javascript 运行环境。可以说nodejs是一个运行环境,或者说是一个 JS 语言解释器 Nodejs 是基于 Chrome 的 V8 引擎开发的一个 C 程序,目的是提供一个 JS 的运行环境。最早 Nodejs 主要是安装在服务器…

C语言 | Leetcode C语言题解之第391题完美矩形

题目&#xff1a; 题解&#xff1a; bool isSubsequence(char* s, char* t) {int mstrlen(s); int nstrlen(t);int k0; int j0;if(mn&&m0) return true;for(int i0;i<n;i){if(s[j]t[i]){j;}if(jm) return true;}return false; }

Mac使用Elasticsearch

下载 Past Releases of Elastic Stack Software | Elastic 解压tar -xzvf elasticsearch-8.15.1-darwin-x86_64.tar.gz 修改配置文件config/elasticsearch.yml xpack.security.enabled: false xpack.security.http.ssl: enabled: false 切换目录 cd elasticsearch-8.15.1/…

ArcGIS中怎么合并多个点图层并删除重复点?

最近&#xff0c;我接到了一个怎么合并多个点图层并删除其中的重复点的咨询。 下面是我对这个问题的解决思路&#xff1a; 1、合并图层 在地理处理工具里面 选择合并 并设置好要合并的图层即可 2、接下来在 数据管理工具→常规→删除相同项 即可 希望这些建议能对大家有所帮…

【PPT学习笔记】使用PPT制作动画/手书/视频等作品的适配性和可能性?

【PPT学习笔记】使用PPT制作动画/手书等作品的可能性&#xff1f; 背景前摇&#xff1a;&#xff08;省流可不看&#xff09; 最近找到另外一份新的实习工作&#xff0c;有很多需要用到PPT动画的地方。 然而&#xff0c;我们之前制作的理工科PPT全是摒弃了形式主义的艰苦朴素…

【LeetCode】08.字符串转换整数

题目要求 解题思路 本题没有难点&#xff0c;只需注意最大整数的比较时要切换成long long 代码实现 class Solution { public:int myAtoi(string s) {//标记正负号int flag1;long long ret0;int ns.size();int i0;//去除空格while(s[i] ) i;//识别符号if(s[i]-) flag-1;i…

链动2+1模式AI智能名片S2B2C商城小程序源码在社群商业价值构建中的应用探索

摘要&#xff1a;在数字经济浪潮的推动下&#xff0c;社群作为商业生态的核心组成部分&#xff0c;其商业价值正以前所未有的速度增长。本文深入探讨了如何通过“链动21模式AI智能名片S2B2C商城小程序源码”这一前沿技术工具&#xff0c;深度挖掘并优化社群的商业价值。通过详细…

LED显示屏维修技巧与常见问题

LED显示屏作为现代显示技术的重要组成部分&#xff0c;广泛应用于广告、信息发布、公共显示等多个领域。然而&#xff0c;随着使用时间的增长&#xff0c;LED显示屏难免会出现各种问题。本文将探讨LED显示屏维修的一些小技巧以及常见的问题&#xff0c;帮助用户更好地维护和延长…

docker进入容器运行命令

前言 Docker是一种流行的容器化平台&#xff0c;它能够快速构建、交付和运行应用程序。在使用Docker时&#xff0c;我们经常需要进入容器进行调试、管理和运行命令等操作。 进入 docker 容器需要执行以下步骤&#xff1a;打开终端窗口。使用 docker ps 命令查看正在运行的容器…

集合及映射

1、集合类图 1&#xff09;ArrayList与LinkedList 区别 LinkedList 实现了双向队列的接口&#xff0c;对于数据的插入速度较快&#xff0c;只需要修改前后的指向即可&#xff1b;ArrayList对于特定位置插入数据&#xff0c;需要移动特定位置后面的数据&#xff0c;有额外开销 …

时序预测及模型简介

1. 时序预测 时序预测是一种统计或机器学习方法&#xff0c;它尝试对历史的时序数据建模&#xff0c;以预测未来的时间点。比如股价、商超销售额、航空乘客量等。本文主要介绍时序预测的基本概念以及常用方法介绍&#xff0c;但不做展开介绍&#xff0c;后续会针对方法、模型做…

找到字符串中所有字母异位词问题

欢迎跳转我的主页&#xff1a;羑悻的小杀马特-CSDN博客 目录&#xff1a; 一题目简述&#xff1a; 二思路汇总&#xff1a; 三解答代码&#xff1a; 一题目简述&#xff1a; leetcode题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二思路汇总&#xff1a; …

基于微信小程序在线订餐系统

微信小程序在线订餐系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了微信小程序在线订餐系统的开发全过程。通过分析微信小程序在线订餐系统管理的不足&#xff0c;创建了一个计算机管理微信小程序在线订…

【原创】java+swing+mysql简易员工管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 文末有本人名片&#xff0c;希望和大家…

远程桌面 Rust Desk 自建服务器

因为某些原因(诈骗)&#xff0c;Rush Desk 服务已暂停国内访问&#xff0c;今天我们介绍如何利用自己的服务器搭建 Rust Desk 远程桌面&#xff0c;低延迟电脑远程手机&#xff0c;手机远程电脑等 一、准备工作 准备一台服务器&#xff0c;我用的腾讯云服务器&#xff0c;一年…

Gitlab-ce upgrade 16.0.1 to 17.3.1【Gitlab-ce 16.0.1 升级 17.3.1】

文章目录 背景gitlab-ce 16.0.1 升级 17.3.1 失败gitlab-ce 16.0.1 升级 16.11.8 失败gitlab-ce 16.0.1 升级 16.7.9 失败gitlab-ce 16.0.1 升级 16.3.8 成功gitlab-ce 16.3.8 升级 16.11.8 失败gitlab-ce 16.3.8 升级 16.7.9 成功gitlab-ce 16.7.9 升级 16.11.8 成功gitlab-ce…

JavaScript练手小技巧:利用鼠标滚轮控制图片轮播

近日&#xff0c;在浏览网站的时候&#xff0c;发现了一个有意思的效果&#xff1a;一个图片轮播&#xff0c;通过上下滚动鼠标滚轮控制图片的上下切换。 于是就有了自己做一个的想法&#xff0c;顺带复习下鼠标滚轮事件。 鼠标滚轮事件&#xff0c;参考这篇文章&#xff1a;…