从FasterTransformer源码解读开始了解大模型(2.1)代码通读02

2024-06-17 03:28

本文主要是介绍从FasterTransformer源码解读开始了解大模型(2.1)代码通读02,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

从FasterTransformer源码解读开始了解大模型(2.0)代码解读02-初始化和forward

写在前面的话

本篇的内容主要是介绍ParallelGpt.cc中的代码内容,首先介绍一些初始化和工具函数,然后会从forward主函数开始介绍一部分。

零、初始化initialize和allocateBuffer

打开src/fastertransformer/models/multi_gpu_gpt/ParallelGpt.cc文件,这里是GPT的真正的处理推理请求的功能函数。在这个文件的fastertransformer namespace中,第一个函数是用于做一些初始化的函数initialize,从31到87行,主要是创建了三个对象,gpt_context_decoder是用于做ContextDecoder或者说Encoder部分的,gpt_decoder是用于做Decoder的,而进行采样和结果生成的是DynamicDecodeLayer部分。这三个部分我们会在后续的代码解读中展开说明。

第95行到202行是allocateBuffer函数,在每次处理一个推理请求时,都会使用allocateBuffer进行显存的分配和内存的分配。这里挑出几个比较有特点的buffer进行简单讲解。

在109行,计算了一个变量为self_cache_size,大小是*(num_layer / pipeline_para_.world_size) * batchxbeam * memory_len * hidden_units_ / tensor_para.world_size_,这个实际上就是计算KV Cache的大小。而134和135行就用该数值的大小进行了KV Cache的分配。

const size_t self_cache_size =(num_layer_ / pipeline_para_.world_size_) * batchxbeam * memory_len * hidden_units_ / tensor_para_.world_size_;

llm小知识-KV Cache:我们知道,在Attention注意力得分的计算过程中,对于当前的token i,需要先计算出查询结果Qi,Ki和Vi,然后使用Qi与token i 之前的所有token的K结果和V结果来进行注意力得分计算,就是拿Qi与所有的K(0-i)点积求和,再与V(0-i)进行加权求和,最终求得Attention注意力得分。那么在这个过程中,可以通过将之前所有计算过的Ki和Vi进行存储的方式,来减少计算量(用显存空间换时间),那么这部分用于存储KV的就是KVCache。目前有一些量化算法也会关注于KV的量化以减少存储空间

在163行的context_decoder_input_buf_,这块buff被分配的大小为sizeof(T) * batchxbeam * max_input_len * hidden_units,这块buff大小为每一个输入token的大小乘以隐藏状态的大小,同样大小的buff还有context_decoder_output_buf,context_decoder_normed_input_buf等,这个大小是在ContextDecoder进行计算流程时真正的隐藏状态的大小(可以参考前几章中的decoder-only模型结构)所以会多次出现。

context_decoder_input_buf_  = (T*)(allocator_->reMalloc(context_decoder_input_buf_, sizeof(T) * batchxbeam * max_input_len * hidden_units_, false));

类似的还有120行的decoder_input_buf变量,大小为sizeof(T) * batchxbeam * hidden_units,相比较之下少了一个max_input_len大小维度,由于Decoder每一步只生成一个token所以相当于长度始终为1,少了一个输入长度的维度。与decoder_input_buf大小相同的还有decoder_normed_input_buf,decoder_output_buf等等。

decoder_input_buf_ = (T*)(allocator_->reMalloc(decoder_input_buf_, sizeof(T) * batchxbeam * hidden_units_, false));decoder_normed_input_buf_ =(T*)(allocator_->reMalloc(decoder_normed_input_buf_, sizeof(T) * batchxbeam * hidden_units_, false));decoder_output_buf_ =(T*)(allocator_->reMalloc(decoder_output_buf_, sizeof(T) * batchxbeam * hidden_units_, false));

在204到271行,是与allocateBuffer对应的freeBuffer函数,对指针中分配了的空间进行释放,这一段没有特别值得讲解的地方。

一、forward函数-起始检查

ParallelGpt.cc文件中拥有两个forward函数,我们主要看574行开始的forward函数。

进入forward函数后,首先通过FT_CHECK的多个宏定义检查了输入tensor的数量以及对几个比较重要的输入tensor进行了输入形状检查。

		FT_CHECK_WITH_INFO(input_tensors->size() >= 3, "input_tensors->size() >= 3");FT_CHECK_WITH_INFO(output_tensors->size() >= 2, "output_tensors->size() >= 2");FT_CHECK(input_tensors->at("input_ids").shape.size() == 2);FT_CHECK(input_tensors->at("input_lengths").shape.size() == 1);FT_CHECK(input_tensors->find("output_seq_len") != input_tensors->end()&& input_tensors->at("output_seq_len").shape.size() == 1);FT_CHECK(output_tensors->at("output_ids").shape.size() == 3);FT_CHECK(output_tensors->at("sequence_length").shape.size() == 2);FT_CHECK_WITH_INFO(input_tensors->at("input_ids").shape[0] == output_tensors->at("output_ids").shape[0],"input_tensors->at(\"input_ids\").shape[0] == output_tensors->at(\"output_ids\").shape[0]");// Used when inputs do not contain random_seedconst size_t batch_size = output_tensors->at("output_ids").shape[0];const size_t beam_width = output_tensors->at("output_ids").shape[1];FT_CHECK_WITH_INFO(output_tensors->count("cum_log_probs") == 0|| output_tensors->at("cum_log_probs").size() == batch_size * beam_width,"The shape of cum_log_probs should match with batch_size x beam_width if provided.");

对于输入input_tensors来说,必须要有input_ids,input_lengths,output_seq_len这三个必备的输入,而对于output_tensors来说,则必须要有output_ids和sequence_length这两个必备的输出。这几个tensor的含义和形状如下所示(B指的是batch size,S指的是Sequence length, bz指的是beam width)

tensor名称形状含义
input_idsB x S需要推理的所有token ids,总共batch size个句子
input_lengthsB长度为batch size的数组,每个位置标记着对应位置的token ids长度为多少
output_seq_lenB长度为batch size的数组,每个位置标记着对应位置句子的输入最长到多少
output_idsB x bw x S推理完成的所有token ids,总共batch size个句子
sequence_lengthB x bw推理完成后所有句子的长度,每个位置标记着对应位置的句子长度

llm小知识-batch size:大部分的推理引擎都会将多个请求(可能每个请求只包含一个句子)打包为一个batch来进行推理,在推理过程中同一batch的句子之间是完全可以做到互不干扰的,打batch的一个非常明显的优点是可以充分发挥硬件的算力,同时处理多个请求。另外需要注意的一点是,在batch中,可能会出现长短不一的情况,这个时候是需要在短的句子后面做padding的,这一步往往是会在客户端或者server侧前端就完成好,在推理侧拿到的数据往往是已经做好padding的

如图是一个batch_size为4的推理请求,其中除了最长的句子以外其他的句子都做了padding
在这里插入图片描述

完成了输入形状的检查之后,根据不同的输出要求,还需要对cum_log_probs的tensor进行检查。这一步是对完成推理后是否要返回生成的logits进行开关设置检查。

在645行可以看见,max_input_length的设置是取出input_ids的第二个维度长度,这也是建立在短的输入是做了padding的基础认知之上的。

在647行可以看见,这里对continue gen进行了一个取出,这个参数是用于控制是否进行多轮持续生成的,但实际使用情况中并不会经常使用到continues gen,会对整体的推理服务使用产生较多的限制。

下一回预告

下一回继续讲解forward函数中的多个步骤,在代码解读中会跳过一些不常用的和并不是很重要的参数,按照顺序对比较重要的部分进行分析

这篇关于从FasterTransformer源码解读开始了解大模型(2.1)代码通读02的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1068356

相关文章

Spring Boot 3.4.3 基于 Spring WebFlux 实现 SSE 功能(代码示例)

《SpringBoot3.4.3基于SpringWebFlux实现SSE功能(代码示例)》SpringBoot3.4.3结合SpringWebFlux实现SSE功能,为实时数据推送提供... 目录1. SSE 简介1.1 什么是 SSE?1.2 SSE 的优点1.3 适用场景2. Spring WebFlu

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

基于Flask框架添加多个AI模型的API并进行交互

《基于Flask框架添加多个AI模型的API并进行交互》:本文主要介绍如何基于Flask框架开发AI模型API管理系统,允许用户添加、删除不同AI模型的API密钥,感兴趣的可以了解下... 目录1. 概述2. 后端代码说明2.1 依赖库导入2.2 应用初始化2.3 API 存储字典2.4 路由函数2.5 应

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Java中&和&&以及|和||的区别、应用场景和代码示例

《Java中&和&&以及|和||的区别、应用场景和代码示例》:本文主要介绍Java中的逻辑运算符&、&&、|和||的区别,包括它们在布尔和整数类型上的应用,文中通过代码介绍的非常详细,需要的朋友可... 目录前言1. & 和 &&代码示例2. | 和 ||代码示例3. 为什么要使用 & 和 | 而不是总是使