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

news2024/9/22 19:30:47

从FasterTransformer源码解读开始了解大模型(2.4)代码解读05-ContextDecoder的前向01

写在前面的话

ContextDecoder部分是用于处理输入部分的组件层,在这一层中,会对所有输入的input ids进行处理,计算Attention(在此过程中还会生成KV Cache),计算FFN,在完成所有输入部分计算之后,会生成输出部分的第一个token

零、ContextDecoder的buffer和功能函数

在src/fastertransformer/models/multi_gpu_gpt/ParallelGptContextDecoder.cc这个文件中包含了整个gpt的ContextDecoder的函数和组成结构。从25到85行的initial函数中,可以初步看见整个ContextDecoder的整体结构:

template<typename T>
void ParallelGptContextDecoder<T>::initialize()
{
    FT_LOG_DEBUG(__PRETTY_FUNCTION__);
    self_attention_layer_ = new TensorParallelGptContextAttentionLayer<T>(max_batch_size_,
                                                                          max_seq_len_,
                                                                          head_num_,
                                                                          size_per_head_,
                                                                          tensor_para_,
                                                                          stream_,
                                                                          cublas_wrapper_,
                                                                          allocator_,
                                                                          true,
                                                                          is_free_buffer_after_forward_,
                                                                          is_qk_buf_float_,
                                                                          sparse_,
                                                                          int8_mode_,
                                                                          custom_all_reduce_comm_,
                                                                          enable_custom_all_reduce_);

    bool use_gated_activation = activation_type_ == ActivationType::GeGLU || activation_type_ == ActivationType::ReGLU;
    size_t max_inter_size     = has_adapters_ ? std::max(inter_size_, adapter_inter_size_) : inter_size_;
    if (activation_type_ == ActivationType::Gelu || activation_type_ == ActivationType::GeGLU) {
        ffn_layer_ = new TensorParallelGeluFfnLayer<T>(max_batch_size_,
                                                       max_seq_len_,
                                                       head_num_,
                                                       size_per_head_,
                                                       expert_num_,  // expert_num
                                                       max_inter_size,
                                                       tensor_para_,
                                                       stream_,
                                                       cublas_wrapper_,
                                                       allocator_,
                                                       true,
                                                       is_free_buffer_after_forward_,
                                                       sparse_,
                                                       int8_mode_,
                                                       use_gated_activation,
                                                       custom_all_reduce_comm_,
                                                       enable_custom_all_reduce_);
    }
    else if (activation_type_ == ActivationType::Relu || activation_type_ == ActivationType::ReGLU) {
        ffn_layer_ = new TensorParallelReluFfnLayer<T>(max_batch_size_,
                                                       max_seq_len_,
                                                       head_num_,
                                                       size_per_head_,
                                                       expert_num_,  // expert_num
                                                       max_inter_size,
                                                       tensor_para_,
                                                       stream_,
                                                       cublas_wrapper_,
                                                       allocator_,
                                                       true,
                                                       is_free_buffer_after_forward_,
                                                       sparse_,
                                                       int8_mode_,
                                                       use_gated_activation,
                                                       custom_all_reduce_comm_,
                                                       enable_custom_all_reduce_);
    }
}

主要由一个Attention层和一个ffn层组成,Attention层主要负责进行注意力得分计算,而FFN层则主要负责进行矩阵乘进行升降维,并在高维时进行激活。在initial函数中,由于根据模型配置可能会调用不同的激活函数,所以这里留了不同激活函数的FFN。

在93到147行,则是对ContextDecoder中用到的buffer进行专门的分配。其中一些buffer可以从变量名看出它的具体用途,比如decoder_normed_input,用于存储归一化后的input输入,normed_self_attn_output用于存储归一化后的attention模块输出。而149到183,则是对上面allocate后的buffer进行释放的freebuffer函数。

在185到212行,是一系列用于layer id判断的函数。为什么要这么做?我们之前有介绍过PP架构,即Pipeline Parallel流水线并行,会将一个完整模型的多个层划分给不同的机器节点(假设我们这里有一个80层的llama2-70b,那么我们可以考虑部署4台gpu机器,每个机器负责20层,这样就可以将单卡上放不下的模型放在多卡上执行了),在185~212行的这些模型,就是判断当前节点所需要运行的模型实际层数的。

在215到300行,则是函数的构造函数和析构函数,这里不进行赘述。

一、forward前向部分之共享上下文

从303行开始,则是真正的前向推理部分。

首先,我们计算所需要的输出输入,都按照tensor的格式在output_tensors/input_tensors里写好了,从327行到349行,将所需要的decoder输入,mask输入,输入长度,输出buffer,以及kvcache等等buffer给取出来。

有一个很值得注意的技术在358和344行,叫做共享context,解释起来也比较简单,在一些对话模型中,用户的输入往往会有一个固定前缀,那么这些前缀在计算注意力时其实共享前缀的部分都是重复计算,那么就可以利用类似前缀树的方式进行管理,每当有共享前缀的输入进入时,就只计算前缀树的叶子的部分,主干部分就可以利用之前已经计算好的部分了
在这里插入图片描述

在359行是一个处理前缀的kernel,其具体实现在gpt_kernels.cc的736到770行

template<typename T>
__global__ void compact_inputs(T*         compact_input,
                               T*         compact_attention_mask,
                               int*       compact_input_lengths,
                               const T*   decoder_input,
                               const T*   decoder_mask,
                               const int* input_lengths,
                               const int* compact_idx,
                               size_t     compact_size,
                               size_t     seq_len,
                               size_t     hidden_dimension)
{
    const int global_idx = blockIdx.x * blockDim.x + threadIdx.x;

    if (global_idx < compact_size * seq_len * hidden_dimension) {
        const int h_id     = global_idx % hidden_dimension;
        const int seq_id   = (global_idx / hidden_dimension) % seq_len;
        const int batch_id = global_idx / (hidden_dimension * seq_len);

        compact_input[global_idx] = decoder_input[(compact_idx[batch_id] * seq_len + seq_id) * hidden_dimension + h_id];
    }

    if (global_idx < compact_size * seq_len * seq_len) {
        const int seq1_id  = global_idx % seq_len;
        const int seq2_id  = (global_idx / seq_len) % seq_len;
        const int batch_id = global_idx / (seq_len * seq_len);

        compact_attention_mask[global_idx] =
            decoder_mask[(compact_idx[batch_id] * seq_len + seq2_id) * seq_len + seq1_id];
    }

    if (global_idx < compact_size) {
        compact_input_lengths[global_idx] = input_lengths[compact_idx[global_idx]];
    }
}

可以看见,主要的目的就是为了从输入的tensor中取出并不属于前缀部分的input以及mask等,并存储在compat buffer中,这是一个纯IO类kernel

二、forward前向部分之attention计算前的准备

让我们回到ContextDecoder中,我们可以简化思考,考虑不存在前缀树的情况,继续看forward函数。

在一系列做好kvcache和attention参数的计算后,在406行进入了一个整体ite的循环(这里是因为如果batch太大,每次处理的max_batch又有限的话,需要拆开batch多次循环)。在409行,如果有padding的存在,由于attention计算是和位置息息相关的,所以需要考虑padding的影响,处理好pad位置后,421行再开始整个layers层数循环。428行到455行,为了考虑到当前层数是否是第一层或最后一层,需要对buffer进行不同的设置,在457行,如果当前节点是PP并行的非节点,还需要通过nccl通信获取上一个节点的计算结果。当然,如果还有tp划分的话,还需要做AllReduce。

在496行,是真正为attention层做输入参数的配置,包含一些必要的输入以及mask,attention类型,还有用于调试信息的layer_id信息等等。在523行,如果配置了alibi那么还需要对输入插入alibi参数。

TensorMap self_attention_input_tensors{
                {"input_query",
                 Tensor{MEMORY_GPU,
                        activation_in_type,
                        {h_token_num, hidden_units_},
                        layernorm_type_ == LayerNormType::pre_layernorm ? decoder_normed_input_ : decoder_input}},
                {"attention_mask",
                 Tensor{MEMORY_GPU,
                        data_type,
                        {local_batch_size, 1, seq_len, seq_len},
                        attention_ptr + local_batch_size * ite * seq_len * seq_len}},
                {"attention_type", Tensor{MEMORY_CPU, TYPE_VOID, {1}, &attention_type}},
                {"is_final_layer", Tensor{MEMORY_CPU, TYPE_BOOL, {1}, &is_final}},
                {"layer_id", Tensor{MEMORY_CPU, TYPE_INT32, {(size_t)1}, &l}}};

            if (is_unpadded_mha) {
                self_attention_input_tensors.insert("padding_offset",
                                                    Tensor{MEMORY_GPU, TYPE_INT32, {h_token_num}, padding_offset_});
                self_attention_input_tensors.insert(
                    "cu_seqlens", Tensor{MEMORY_GPU, TYPE_INT32, {size_t(local_batch_size + 1)}, cu_seqlens_});
            }

            /* if (dynamic_quant_) { */
            /*     self_attention_input_tensors.insert("attention_query_dynamic_scale", */
            /*         Tensor{MEMORY_GPU, TYPE_FP32, {h_token_num}, attention_query_dynamic_scale_}); */
            /* } */

            if (input_tensors->isExist("linear_bias_slopes")) {
                self_attention_input_tensors.insert("linear_bias_slopes", input_tensors->at("linear_bias_slopes"));
            }

在539行,真正需要获取的输出其实很少,一个用于接下来做add_bias_norm的主要输出,以及attention计算所产生的的kv cache,之后,直接调用attention层进行了前向计算推理。

TensorMap self_attention_output_tensors{
                {"hidden_features",
                 Tensor{MEMORY_GPU, activation_out_type, {h_token_num, hidden_units_}, self_attn_output_}},
                {"key_cache", Tensor{MEMORY_GPU, data_type, self_k_cache_size, k_cache_ptr}},
                {"value_cache", Tensor{MEMORY_GPU, data_type, self_v_cache_size, v_cache_ptr}}};

            self_attention_layer_->forward(
                &self_attention_output_tensors, &self_attention_input_tensors, &layer_weight->self_attention_weights);

下一回预告

下一回我们会继续介绍在ContextDecoder中,attention计算完成之后,还需要做哪些工作,会对layernorm以及ffn的调用流程进行一下讲解

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全国大学生数学建模比赛——关联规则

一、问题背景与关联规则适用性 在数学建模比赛中&#xff0c;常常会遇到需要分析大量数据以寻找变量之间潜在关系的问题。关联规则分析作为一种数据挖掘技术&#xff0c;特别适用于这种场景。例如&#xff0c;在一些实际问题中&#xff0c;可能需要从众多的因素中找出哪些因素之…

基于Python的量化交易回测框架Backtrader初识记录(一)

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;基于Python的量化交易回测框架Backtrader初识记录&#xff08;一&#xff09;-CSDN博客 前言&#xff1a;近期以来&#xff0c;对股市数据获取及预处理算是告一段落&#xff0c;下一步…

Java-多线程机制

上篇我提到一些多线程的基本使用方法&#xff0c;但并没有说到底层原理&#xff0c;你或许会有一些疑问&#xff0c;为什么不直接调用Run方法而是要用start方法启动线程&#xff1f;多线程到底是怎样执行的&#xff1f;接下来我会详细带你了解多线程的机制原理。 多线程的底层…

Leetcode114将二叉树转换为链表(java实现)

来看下本题的题目描述&#xff1a; 本题想让我们将二叉树转换为单链表&#xff0c;可以发现&#xff0c;左边的二叉树转换为右边的链表是使用的中序遍历&#xff0c;根 左 右 所以本道题的思路就是可以先用一个集合以中序遍历收集元素&#xff0c;然后再构建单链表。 class S…

[pytorch] --- pytorch基础之tensorboard使用

0 tensorboard介绍 TensorBoard是一个用于可视化机器学习实验结果的工具&#xff0c;可以帮助我们更好地理解和调试训练过程中的模型。 在PyTorch中&#xff0c;我们可以使用TensorBoardX库来与TensorBoard进行交互。TensorBoardX 是一个PyTorch的扩展&#xff0c;它允许我们…

Android Codec2 CCodec (七)IConfigurable

上一篇文章我们了解了接口参数的定义&#xff0c;这一节我们简单梳理一下参数获取、配置以及参数反射过程。 1、IConfigurable 通过之前的介绍我们了解到&#xff0c;Codec2模块的功能实现与配置实现是相互分离的&#xff0c;Codec2框架设计了一组API用于获取与模块关联的配置…

2.4 定时器与TIM中断

文章目录 时钟与时钟树stm32时钟树可以手动把系统时钟72mhz改成其他的吗&#xff1f;ST公司给的外围设备配置文件 的 默认配置说明 定时器什么是定时器定时器的类型 时钟与时钟树 频率&#xff1a;如72Mhz即为每秒72M个脉冲 时钟的源头 晶振 时钟树的分支 分频器&#xff1a;…

超越在线翻译百度!揭秘3款工具,工作学习快人一步

在如今这个信息爆炸的时代&#xff0c;我们经常需要用到不同的语言。无论是看外国文章、写邮件给国外朋友&#xff0c;还是和外国客户聊天&#xff0c;语言不通都是个大问题。不过&#xff0c;科技的进步带来了很多在线翻译工具&#xff0c;百度翻译就是其中很受欢迎的一个。但…

U盘文件目录损坏难读?数据重生方案

在数字化时代的洪流中&#xff0c;U盘作为便携的存储介质&#xff0c;承载着无数人的重要数据与回忆。然而&#xff0c;当U盘不幸遭遇文件或目录损坏&#xff0c;导致数据无法读取时&#xff0c;这份便携与便捷瞬间化为乌有&#xff0c;留下的只有无尽的焦虑与困扰。本文将深入…

爆改YOLOv8|利用可改变核卷积AKConv改进yolov8-轻量涨点

1&#xff0c;本文介绍 AKConv&#xff08;可改变核卷积&#xff09;是一种改进的卷积操作方法&#xff0c;其核心在于动态调整卷积核的形状和大小。与传统卷积层固定核大小不同&#xff0c;AKConv 通过引入可学习的机制&#xff0c;使卷积核在训练过程中能够自适应地调整&…

优雅谈大模型:白话ZeRO 下

机器学习模型的复杂性和规模不断增长&#xff0c;分布式训练变得比以往任何时候都更加重要。训练具有数千亿参数的大型语言模型&#xff08; LLMs &#xff09;将是机器学习基础设施面临的挑战。与传统的分布式计算框架不同的地方在于GPU的分布式训练需要将数据传递给GPU芯片等…

JAVAEE初阶第二节——多线程基础(下)

系列文章目录 JAVAEE初阶第二节——多线程基础(下) 多线程基础(下) 单例模式阻塞式队列定时器线程池 文章目录 系列文章目录JAVAEE初阶第二节——多线程基础(下) 多线程基础(下) 一.多线程案例 1.单例模式1.1 饿汉模式 1.2 懒汉模式 1.2.1 懒汉模式-单线程版1.2.3 懒汉模式…

[Tools: LoRA] Diffusers中Stable Diffusion的实现

实现底层原理 Diffusers中的Attention操作实现在AttnProcessor类&#xff08;diffusers.models.attention_processor.py&#xff09;&#xff0c;里面定义了单次Attention操作。添加LoRA&#xff0c;本质上是用LoRAAttnProcessor类替换AttnProcessor类。LoRAAttnProcessor中新…

github和gitlab的区别是什么

区别&#xff1a;github如果使用私有仓库&#xff0c;是需要付费的&#xff1b;而gitlab可以在上面搭建私人的免费仓库。gitlab让开发团队对他们的代码仓库拥有更多的控制&#xff0c;相对于github&#xff0c;它有不少的特色&#xff1a;允许免费设置仓库权限&#xff1b;可以…

自然语言处理-词向量转换

文章目录 一、简介1.含义2.基本原理3.常见转换方法1&#xff09;. 独热编码&#xff08;One-Hot Encoding&#xff09;2&#xff09;. 词袋模型&#xff08;Bag of Words, BoW&#xff09;3&#xff09;. TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xf…

网络工程师学习笔记——局域网和城域网

传统局域网&#xff08;LAN&#xff09; 局域网的主要特征&#xff1a;由网络拓扑结构所采用的协议类型以及介质访问的控制方法 分组广播式网络&#xff0c;所有的工作站都连接到共享的传输介质上&#xff0c;共享信道的分配技术是局域网的核心技术 局域网常见的设备&#x…

Centos Stream9网卡驱动重置无法找到网卡解决办法

1.问题原因 使用Centos Stream9系统时&#xff0c;我们正常在/etc/NetworkManager/system-connections目录下修改网络配置文件保存后&#xff0c;重置网卡会发现提示无法连接或没有找到该网卡&#xff0c;此问题有以下几点原因&#xff1a; linux系统重管理网络连接的有netwo…

巧妙的数(逐倍数判断)

cin>>s; 若s串=1236 lens=s.size(),pd=1,ys=0,p[10]={} 0<=i< l 开始运算: P[1]=p[2]=p[3]=p[6]=true; //下标做标记 若 p[6]=ture,则p[2]=p[3]=ture,p[6]=false pd=1 9>=k>1 若pd%k!=0&&p[k]=ture时,则pd*=k;

开学季老师如何发布分班?

开学啦&#xff0c;老师们又要开始忙碌了。但是&#xff0c;别担心&#xff0c;现在有个超方便的工具&#xff0c;让分班这件事变得简单又快速。以前分班可是个大工程&#xff0c;得一个个手动处理&#xff0c;现在不一样了&#xff0c;有了易查分这个小程序&#xff0c;一切都…

不可思议!分享6款AI论文大纲提纲自动生成器,导师直夸好

在当今学术研究和写作领域&#xff0c;人工智能&#xff08;AI&#xff09;技术的迅速发展为论文写作带来了革命性的变化。AI论文大纲生成器作为其中的重要工具&#xff0c;能够显著提高论文撰写效率和质量。本文将介绍六款AI论文大纲生成器&#xff0c;这些工具不仅能够帮助学…