微信扫码
添加专属顾问
我要投稿
阿里云AI团队深度解析大模型训练与推理优化,揭秘性能提升核心技术。核心内容: 1. 大模型训练与推理的核心挑战及优化算法 2. 连续批处理、分块注意力等关键技术解析 3. 实践案例中的显存效率与训练加速效果分析
我们是阿里云公共云 AI 汽车行业大模型技术团队,致力于通过专业的全栈 AI 技术推动 AI 的落地应用。
引言
本文主要总结了大模型落地过程中的训练、推理和性能优化相关算法和实践,并重点分享了大模型在推理与训练中的性能优化方法,针对推理延迟高、显存占用大及计算效率低等核心挑战,介绍包括连续批处理、分块注意力机制、分布式训练等技术,并结合实践分析了优化策略在显存效率、吞吐量及训练加速中的应用效果,以及roofline模型分析和定位瓶颈等相关内容。
本文的整体大纲如下:
从语言模型到大模型
语言模型(LM)是一种基于自然文本预测词(Token)的机器学习方法,常见的架构包括GPT和BERT等。大语言模型通常指的是拥有亿级参数的神经网络语言模型,目前已经出现很多种能力强大的开源、闭源模型,如在下图中Zhao等人整理的大模型发展时间线中的模型。
LLMs_timeline
针对语言模型发展阶段,可以通过Yang等人整理的大语言模型进化树图,可以清楚地了解主流LM之间的关系。[1]
图中,不同的分支上的模型具有不同的基础模型结构,仅有左下方的灰色分支为非Transformer模型,其余颜色的分支均为基于Transformer的模型:其中,蓝色分支为仅解码器模型(Decodr-Only),包括GPT系列模型、ChatGPT模型、LLaMA系列模型等;粉红色分支为仅编码器模型(Encoder-Only),包括Bert系列模型等;绿色分支为编码器-解码器模型(Encoder-Decoder),包括T5模型、GLM系列模型、UL2系列模型等。
图中,实心方块代表开源模型,空心方块代表闭源模型;右下角的条形图显示了各公司和机构的模型数量;纵轴上的垂直位置表示模型的发布日期。可以看出,NLP基础模型及Transformer架构的研究,对大模型的发展至关重要。
因此,本节将从NLP技术角度,简介语言模型的发展历程:
首先,简介自然语言处理领域中的大概发展路线,以理解从语言模型到大语言模型的发展历程;
然后,针对NLP领域中以Transformer技术为基础的两大类预训练模型,即Bert和GPT的原理和训练方式进行简要介绍和对比;
最后,针对大模型技术——对话式GPT模型进行较为详细的介绍。
NLP 技术发展历程
自然语言处理是计算机科学、人工智能和语言学的交叉领域,研究如何让计算机处理、理解和生成人类语言。目标是:能够实现人机交互、自动翻译、信息检索、情感分析等任务。应用领域包括搜索引擎、社交媒体监测、智能客服、新闻生成等。 ### 模型发展阶段
NLP领域主要模型的发展历程可以大致分为如下几个阶段:
1)早期研究阶段:侧重于设计人工编写的规则和语法,如基于规则和知识的方法等;
2)统计方法崛起:引入数学和统计方法,侧重于从大规模语料库中自动学习语言规律,如隐马尔可夫模型(HMM)、条件随机场(CRF)等;
3)深度学习革命:基于神经网络模型的方法,强调自动提取特征和端到端的训练,如循环神经网络(RNN)、长短时记忆网络(LSTM)等;
4)预训练模型兴起:基于大规模数据和深度学习模型的预训练方法,提升了NLP任务的性能,如BERT、GPT、T5等。
可以发现,NLP领域的主要模型,从深度学习阶段开始,经过预训练模型兴起,直到如今的各种聊天大模型的爆发,NLP模型一直在向着参数量更多、通用性更强的方向发展。
基于神经网络模型的方法,进行自动提取特征和端到端的训练,代表性方法有循环神经网络(RNN)、长短时记忆网络(LSTM)等;
RNN模型:
RNN(循环神经网络)是按时间序列的前馈神经网络模型;
训练后可处理顺序数据输入,并将其转换为特定的顺序数据输出;
问题:
训练速度慢:时间序列的处理;
记忆时长短:记住后面的token,容易忘记前面的token;
梯度消失或梯度爆炸;
LSTM模型:
Encoder-Decoder结构模型:
Encoder-Decoder结构模型:
Seq2Seq(序列到序列)是一类特殊的序列建模问题, 主要为了;
Encoder-Decoder结构模型包括编码器和解码器,Encoder将输入序列进行处理,得到向量(thought vector),向量输入到Decoder 转化成 想要的输出;
本节对常见Transformer[2]进行简要分析对比。
Transformer架构的左侧为Encoder结构,右侧为Decoder结构,如下:
从网络结构角度来看,Transformer中Encoder与Decoder的主要区别在于Self-Attention机制的Mask应用。具体而言,Encoder在计算Self-Attention时不使用Mask,而Decoder则引入了Mask操作。
从训练和预测过程来看,训练阶段Encoder和Decoder均可并行计算;但在预测阶段,仅Encoder支持并行计算,Decoder则需顺序处理。
self-attention中最重要的组成部分是Q、K、V三个向量:
Query:查询是当前单词的表示,用于与所有其他单词(使用其关键字)进行评分。我们只关心当前正在处理的标记的查询。
Key:关键向量就像是段中所有单词的标签。我们在搜索相关词语时会与它们进行匹配。
Value:值向量是实际的单词表示,一旦我们对每个单词的相关性进行了评分,这些值就会相加来表示当前的单词。
在Attention模块中的,对当前Q和所有的K计算相似度,然后通过Softmax操作得到权重,最后根据权重与对应的V成绩求和,得到Attention的Value值。
因此,在Attention中的Scaled dot-product attention运算为:
一个比喻是:这个过程就像在文件柜中搜索。Query查询就像写有研究主题的便签,而Key键则好比文件夹上的标签。[3]
当便签与某个标签匹配时,我们就会取出该文件夹里的Value内容,这些内容构成了值向量。不过,在实际应用中寻找的结果往往不是单一的值,而是来自多个不同信息的组合。
通过将Q向量与每一个K向量相乘(使用点积后进行softmax运算),可以为每个文件夹计算出一个得分。
多头注意力机制是进行了多个self-attention的结合,每个head都会学到不同表示空间中的特征。从而较好的表征序列中的关联关系。 运算为:
其他的主要网络结构如下:
1. Norm:对数据归一化,将数据约束到高斯分布上,稳定训练;
2. Add&Residual:残差相加,避免梯度消失,构建更深的网络;
3. Feed Forward:将数据转化为非线性,更好表征复杂关系;
4. Masked Multi Head Attention:在Decoder中,将MHA中的矩阵与右上角进行掩码的矩阵相乘,其他操作与MHA相同;
5. Softmax:在模型的Decoder之后进行归一化,得到概率输出。
本节对基于Transformer结构的常见模型的训练阶段进行简要分析对比。
BERT(Bidirectional Encoder Representations from Transformers)是一种基于transformer的以Encoder为主的双向语言表征模型。
由上图可见,其训练方法包括两个阶段:预训练和微调。
1)在预训练阶段:BERT通过两种无监督任务来学习语言模型:
掩码语言模型(MLM):随机选择文本中的一些单词并将其替换为特殊的掩码标记,然后让模型预测这些被掩码的单词。这使得BERT能够在上下文中学习单词表示。
下一句预测(NSP):给定两个句子,模型需要判断它们是否是原始文本中相邻的句子。这有助于模型学习句子间的关系。
2)在微调阶段:BERT模型通过添加一个任务特定的头部(如分类器)并对整个模型进行端到端的监督训练,从而适应特定的NLP任务。
由于模型结构原因,导致的一个问题是其必须要通过微调才能够适配特定的下游任务。
GPT(Generative Pre-trained Transformer),即生成式预训练Transformer模型,是一种基于Transformer架构的大规模预训练语言模型,它采用单向(从左到右)的自回归语言模型进行训练。
相比Transformer[2]中的Encoder结构其每个Block中减少了一个多头注意力MHA模块。
下图为GPT-1模型[3]的基础Transformer结构(左),以及在不同任务下的模型输入构造方式(右)。从图中可以看到其已经可以进行更多的处理任务。
GPT模型的训练过程同样分为两个主要阶段:预训练和微调。
1)在预训练阶段:与BERT不同,GPT在预训练阶段只使用一个无监督任务:通过给定文本中的前N个单词,让模型预测第N+1个单词。即学习一个概率分布,即给定一系列文本片段(如单词、短语等),预测下一个文本片段的出现概率。这个过程也被称为自回归语言建模(Autoregressive Language Modeling)。
2)在微调阶段:在预训练阶段完成后,GPT模型已经学会了一定程度的语言知识。为了将模型应用于特定的NLP任务(如文本分类、摘要生成等),可以对模型进行进一步微调。 在微调阶段,GPT也会针对特定的NLP任务进行监督训练。完成微调后的GPT模型可应用于特定的NLP任务以获得较高的性能。
从GPT-2、GPT-3模型,开始采用直接在指令中进行样例学习的方式,以下三种直接在指令中进行的上下文小样本学习方式,而没有采取常规的小样本微调学习方式:
Zero-shot,给定任务描述,如 Translate English to French,然后直接给出问题,要求模型给出答案。这种方式与 GPT-2 一致。
One-shot,给定任务描述,然后给一个例子,包括问题和答案
Few-shot,与 One-shot 类似,只是给的示例更多。
如图为 GPT-3 中的 3种学习方式(左)与常规的微调方式(右)的对比:
对话式GPT(Conversational Generative Pre-trained Transformer)是一种专为生成自然、流畅对话而设计的模型。它基于GPT架构,并利用多轮对话数据进行训练。与GPT相似,ChatGPT等对话模型同样采用了自回归语言模型,但在训练期间,它会对话数据的上下文信息进行建模,以生成更符合实际对话场景的回复。
在预训练阶段:模型首先需要在大量无标注文本数据上进行预训练。这些文本数据通常来自于互联网,例如维基百科、书籍、论坛等。预训练的目标是学习语言的一般规律,例如句子结构、语法、常识等。预训练采用的是自回归语言模型,即在给定上文的情况下,预测下一个词,优化模型参数以最大化训练数据上的对数似然。预训练完成后,将得到一个通用的语言模型。
在微调阶段:在预训练后的基础模型上,需要继续进行微调。微调过程通常需要有标注数据,例如人工标注的问答对、对话等,利用问答数据优化模型参数,以最大化给定任务的性能指标。微调可以采用有监督学习和强化学习等方法:通过有监督学习,模型通过正确的输入输出对学习;通过强化学习,模型通过与环境的交互,得到反馈并不断调整。
对话式GPT模型训练流程如下图所示:
针对对话式大模型,详细的训练方式等内容将在下文的模型微调部分具体展开。
针对上述提到的BERT、GPT和对话式GPT的训练方式,可以对比总结如下:
BERT:采用了双向编码器,能够同时考虑一个词左侧和右侧的上下文信息,并通过MLM和NSP任务学习语言表示。这使得BERT在理解句子中的语义关系方面具有明显优势,尤其是在需要深刻理解上下文的任务中表现突出,如问答系统、文本分类等。不过,BERT的生成能力相对较弱,因为它主要用于理解和分析文本,而不是生成新的内容。
GPT:是一个单向解码器模型,其训练目标是预测下一个词,基于当前词之前的上文进行推理。这种设计使得GPT特别擅长于生成连贯且自然的语言文本,比如文章创作、代码编写或对话生成等领域。
Conversational GPT:结合了GPT的基本架构与专门针对对话场景优化的技术。这类模型不仅继承了GPT强大的生成能力,还通过特定的训练数据集(多轮对话)学习到了如何更好地维持会话的一致性和流畅度。其可以产生更加自然、连贯的回复,并且能够记住之前交流的内容,在对话式应用中表现出色。
Embedding:无论是BERT、还是GPT,它们都依赖于高质量的词嵌入来捕捉词汇间的语义关系。这些模型通过将每个词映射到高维空间中的向量,Embedding向量是根据单词在语言上下文中的出现模式进行学习的,在训练过程中自动学习到词语之间的相似性和差异性。对于所有类型的NLP任务而言,良好的词嵌入是提升模型性能的关键因素之一。
GPT模型相比Bert模型可以有更好效果以及更广泛应用,其优势:
1. GPT是单向模型,只需要利用上文,Bert模型是双向模型,训练使用上下文,则更为复杂。
2. GPT是自回归模型,因此可以通过Prompt应用适配众多NLP任务;而Bert采用自编码,必须要先预训练再微调完成NLP任务。
此外,随着技术的发展,也有一些新方法尝试结合这两者的优点,或是采用混合架构,将双向编码器的优势融入到自回归生成框架之中,以求达到更好的性能平衡。
生成式大模型
在自回归生成模型生成sentence文本的过程中,token的生成方式是逐一生成(这也是为什么大模型可以进行流式输出),其中每个token的出现概率由之前tokens计算得到,即:
因此,最朴素的文本生成方式为:
计算 :将前
个token输入到模型,得到第
个token的概率分布
,选择概率最大的token作为
。
计算 :同样的方式,将前
个token输入到模型,得到概率分布
,并生成第个token作为
。
迭代输出直到达到停止条件:重复上述步骤,直到达到停止条件(停止符、停止长度等)。
上述方法可能存在的问题:
重复计算:在生成和
时,都利用了前第
个token,即存在重复计算的情况。
局部最优:当选择概率最大的词作为第个token时,只能说明该token是通过前
个得到的最优解,但不一定是全局最优。
重复输出:如果每次都选择概率最大的词,当存在某些组合概率始终最大时会不断被选择到(这也解释了大模型为什么会有重复输出的情况)。
针对上述可能存在的问题,常规的规避方法如下:
减少计算:针对重复计算问题,可以通过复用历史计算结果,即greedy search策略。
token缓存:针对局部最优问题,可以保留第的可能tokens及其概率分布
,然后选择可以让整个句子概率最大的token序列,即beam search策略。
针对重复输出的问题,可以对生成token和组合模式增加限制,以减少错误组合的出现,即使用repetition penalty等策略。
自2010年以来,随着深度学习技术进步,embedding技术在研究与应用上显著扩展。期间诞生了Word2Vec、GloVe和FastText等重要嵌入算法,通过神经网络训练或矩阵分解等方式学习单词的向量表示,并广泛应用于文本分类、机器翻译及情感分析等多种自然语言处理任务。
近年来,深度学习及自然语言处理领域的快速发展推动了embedding技术的持续改进。如BERT、ELMo和GPT等大模型能够生成基于上下文的词嵌入,更准确地反映词汇的语义及其所处情境。
在机器学习和自然语言处理中,embedding就是一个N维的实值向量,是指将高维度的数据(例如文字、图片、音频)映射到低维度空间的过程。embedding向量通常是一个由实数构成的向量,它将输入的数据表示成一个连续的数值空间中的点,它几乎可以用来表示任何事情。实值向量的embedding可以表示单词的语义,主要是因为这些embedding向量是根据单词在语言上下文中的出现模式进行学习的。
在使用大语言模型时,embedding可以解决大模型的输入限制,即将本地知识进行embedding化,通过embedding匹配获得相关性最高的内容,与query一起构建模型输入,最终获得答案,包含文档拆分、向量化、向量存储、向量检索、基于文档对话等。
Embedding的生成方法有很多,这些方法都有各自的优点和适用场景,选择最适合特定应用程序的嵌入生成方法需要根据具体情况进行评估和测试。几个比较经典的方法和库:
Word2Vec:是一种基于神经网络的模型,用于将单词映射到向量空间中。Word2Vec包括两种架构:CBOW (Continuous Bag-of-Words) 和 Skip-gram。CBOW 通过上下文预测中心单词,而 Skip-gram 通过中心单词预测上下文单词。这些预测任务训练出来的神经网络权重可以用作单词的嵌入。
GloVe:全称为 Global Vectors for Word Representation,是一种基于共现矩阵的模型。该模型使用统计方法来计算单词之间的关联性,然后通过奇异值分解(SVD)来生成嵌入。GloVe 的特点是在计算上比 Word2Vec 更快,并且可以扩展到更大的数据集。
FastText:是由 Facebook AI Research 开发的一种模型,它在 Word2Vec 的基础上添加了一个字符级别的 n-gram 特征。这使得 FastText 可以将未知单词的嵌入表示为已知字符级别 n-gram 特征的平均值。FastText 在处理不规则单词和罕见单词时表现出色。
Qwen、OpenAI的Embeddings:例如text-embedding-ada-002。
Embedding的保存可以考虑使用向量数据库。例如开源向量数据库Milvus、Pinecone、Anthropic VDB等。 ### 随机性控制参数
通过调整随机性控制参,可以用于控制生成文本的多样性,以temperature为例:当 temperature 较高时,概率分布更加平坦,因此采样出的标记更具多样性;当 temperature 较低时,概率分布更加尖锐,因此采样出的标记更倾向于概率最大的那个;当 temperature 等于 0 时,直接选择概率最大的标记,则相同的提示会产生相同的结果(存在部分模型该参数不能设置为0)。
常见的随机性控制参数主要有如下:
temperature(温度):
作用:temperature
参数在文本生成中用于调节输出的随机性。提升温度设置会使得产生的文本更为随机和创新;降低温度则导致文本更加稳定和重复。
常见设置:通常设置在0.7到1之间。较低的温度(如0.7)可以使生成的文本更加连贯和准确,而较高的温度(如1)则使文本更加多样和创造性。
top_k(Top-K Sampling):
作用:top_k
参数限定了模型预测下一个词时的选择范围,限定模型从最可能的前k个词里挑选预测词。随着k值增大,可选词的范围变宽,文本的多样性提升;相反,减小k值将减少可选词的范围,使得生成文本更倾向于出现概率较高的词。
常见设置:一般设置在40到100之间。较小的 k
值可以提高文本的相关性和连贯性,而较大的 k
值则增加了文本的多样性。
top_p(Nucleus Sampling):
作用:top_p
参数限定了从概率累积达到给定的p值时的一组词汇集合中选取下一个词。较低的top_p值使生成的文本更加可预测和相关;较高的值增加了文本的多样性和创造性。其中,这种采样方式的可选词的数量是动态的,在不同的上下文中可能是不同的。
常见设置:通常设置在0.8到0.95之间。较低的 top_p
值(如0.8)使生成的文本更加可预测和相关,而较高的值(如0.95)增加了文本的多样性和创造性。
其中,控制参数top_k与top_p的区别主要为:
从工作方式上看:
top_k
参数限制了模型在生成每个新词时考虑的候选词集合的大小,即模型只从概率最高的 k
个词中选择下一个词。这个 k
是固定的数字,例如如果 top_k
设为50,那么无论上下文如何,模型总是从概率最高的50个词中选择下一个词。
与 top_k
不同,top_p
参数定义了一个概率阈值 p
,模型将从累计概率达到这个阈值的那一组词中选择下一个词。这个过程是动态的,依据每个上下文的不同而变化。例如,如果 top_p
设为0.9,那么模型会从累计概率加起来为90%的那些词中选择下一个词,这些词的数量在不同的上下文中可能是不同的。
从结果影响上看:
使用 top_k
可以有效减少生成不相关或者奇怪的词的可能性,但在某些情况下可能导致生成结果过于保守,缺乏多样性。
使用 top_p
允许模型在更广泛的词汇中进行选择,增加了文本的多样性和创造性,同时仍然限制了选择最不可能的词的风险。
在大模型生成过程中,最主要的问题即为幻觉问题,在论文中认为幻觉主要为两种类型:[5]
1)事实性幻觉(Factuality Hallucination):主要表现为“与事实不一致”或者“捏造”,为生成的内容与现实世界之间存在差异。
2)忠实性幻觉(Faithfulness Hallucination):主要表现为存在生成内容“不自洽”的情况,即生成内容和用户的输入指令及上下文内容存在偏离。
幻觉的出现被认为与训练数据、训练过程、推理过程都存在关系:
有毒的训练数据:由于大模型需要大量的标注数据,因此也说明了其存在了大量的有毒数据。通过质量过滤和质量标注可以发现,无论是手工标注数据还是GPT的生成数据,均存在了大量的垃圾数据,其种类包括但不限于广告数据、重复数据、输出回复不一致数据等多种复杂情况,因此不可避免会将模型训坏。由于大模型等推理方式是通过前文预测下文的形式,因此不可避免会出现当遇到一些不佳的token组合时出现幻觉情况。
训练过程有缺陷:主要有两个训练阶段会存在问题,即学习通用表示和世界知识的预训练阶段、使大模型更好地与人类偏好一致的微调对齐阶段。
预训练阶段的问题:
架构缺陷:缺失上下文信息等情况。
自注意力模块缺陷:token数量持续增加时,很难保证对每个词的注意力,可能导致误解或者信息缺失。
暴露偏差:模型基于之前的预测生成后续单词,因此当前序预测存在错误的时候,就会导致后续输出的累积误差越来越大,偏离预期输出。
微调对齐阶段的问题:
能力错位: 大模型在对齐训练阶段,可能会存在部分任务希望使模型学习到超出其原有能力范围内的知识,因此会极大增加产生幻觉的风险。存在显著的例子是在利用了ChatGPT数据训练开源大模型时,其数据会加剧开源模型的重复幻觉等情况。
信念错位:在对齐阶段,会希望模型输出符合人类偏好的数据,这种偏向性的训练对齐也会导致其输出内容与事实不准确的情况。
推理阶段的随机性:
固有的抽样随机性:由于随机性的存在,即使设置随机性较低,也可能会概率性出现输出与预期偏离较大的情况。
不完美的解码表示:这主要是由于对上下文的关注度不够以及softmax存在的瓶颈问题所引起的。softmax函数用于确定下一个具有最高概率的单词,但由于其表达能力有限,可能会无法精确地选择出最适合的单词,进而导致生成的内容与实际状况不匹配。
训练数据构建和优化
本节主要针对在开源大模型落地过程中,关于模型的有监督调优阶段的相关实验,以及与知识库结合的实践过程中的一些相关总结和分享:首先,介绍了模型微调方法以及微调数据集的准备;其次,介绍了数据和超参数优化以及模型效果评价指标的常见方法;然后,基于之前的试验结果介绍了模型的测试和选型;最后,重点介绍了调优实践及与业务相关的尝试。
训练数据字段说明
模型训练过程中,仅使用预训练数据集的正文字段,因此只需要对正文内容统一处理。
针对模型微调训练,常见的SFT数据集格式存在两种,分别是包含问题和答案两个字段的问答格式数据集,以及带有指令(instruction)的指令微调数据集。本节将对这两种数据集进行说明和对比。
问答格式数据集以问答形式呈现,例如包含prompt、response字段的数据集。该种数据集的核心是要保持两个独立的字段,即包含问题和答案,格式如下:
[{
"prompt": "对作者拍买文稿所得如何计算缴纳个人所得税?",
"response": "根据《国家税务总局关于印发的通知》(国税发〔1994〕89号)第五条的规定,XXX"
}, {
"prompt": "是否可到税务局开销售返利的红字发票?",
"response": "根据《国家税务总局关于商业企业向货物供应方收取的部分费用征收流转税问题的通知》,XXX"
}, {
"prompt": "增值税专票开据问题?",
"response": "根据增值税暂行条例第二十一条的规定,XXX",
}, {
"prompt": "...",
"response": "..."
}]
问答格式可以处理成多种文件格式, 例如JSONL, Excel File, CSV。
通常我们希望模型遵循用户的指令,但只利用问答格式数据集对模型训练时,这种基础模型的语言建模目标不足以让模型学会以有用的方式遵循用户的指令。因此通常需要使用指令微调 (Instruction Fine-Tuning,IFT)方法来达到该目的,该方法除了使用情感分析、文本分类、摘要等经典 NLP 任务来微调模型外,还在非常多样化的任务集上向基础模型示范各种书面指令及其输出,从而实现对基础模型的微调。这些指令示范由三个主要部分组成 —— 指令、输入和输出。
其中,输入是可选的。一些任务只需要指令,如使用LLM做开放式文本生成。当存在输入时,输入和输出组成一个实例 (instance)。给定指令可以有多个输入和输出实例。如下例:
针对带有指令的问答数据集,例如包含 instruction、input、output 字段的数据集,分别用来表示指令、输入,和期望输出。格式示例如下:
[{
"instruction": "比较给出的两个对象并提供简明的比较描述。 ",
"input": "对象1:苹果 对象2:橙子",
"output": "苹果和橙子都是水果,但苹果通常是圆形和甜的,而橙子通常是椭圆形且味道酸甜。苹果的内部为奶白色,而橙子的内部是多汁且鲜艳的颜色。"
}, {
"instruction": "为给定产品生成广告口。",
"input": "亚马逊Prime",
"output": "亚马逊Prime - 享受无限制的无限访问,包括电影,电视节目,音乐,电子书和游戏。"
}, {
"instruction": "...",
"input": "...",
"output": "..."
}]
数据集一致化处理
预训练数据集仅使用数据集的正文字段,因此对正文内容统一处理成’content’列。
SFT数据集的输入输出相关的统一为:instruction,input,output 三个字段。其中:
instruction
和 output
列应当是非空的字符串。
input
列的内容将会和 instruction
列拼接作为模型输入。
history
列应是一个列表,其中每个元素是一个字符串二元组,分别代表用户请求和模型答复。部分数据(如sharegpt_zh_27k)存在历史字段(history),但如果不做多轮对话的训练时可以忽略。
SFT数据集格式批量统一处理方式为,在配置文件中按照如下格式构建数据集列名对应字典,并进行批量处理。配置文件示例如下:
"数据集名称": {
"columns": {
"instruction": "数据集代表提示词的表头名称(默认:instruction)",
"input": "数据集代表请求的表头名称(默认:input)",
"output": "数据集代表回答的表头名称(默认:output)",
"history": "数据集代表历史对话的表头名称(默认:None)"
}
}
进行批量数据集字段检查,发现在部分数据集中,如alpaca_data_zh_51k.json,同一个数据集内部存在字段不一致情况或字段缺失情况,其包括[“instruction”, “input”, “输出”]、[“指示”, “输入”, “输出”]、[“instruction”, “输入”, “输出”]、[“instruction”, “input”] 等多种情况。因此,进行清洗和处理,处理为统一的数据格式和字段。
模型训练数据构造
当使用模型的chat接口时,以Qwen的ChatML Format为例,Chat模型的角色组装格式如下:
<|im_start|>system
{system_prompt}<|im_end|>
<|im_start|>user
{user_prompt} <|im_end|>
<|im_start|>assistant
{output}<|im_end|>
例如:
<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
Hello! <|im_end|>
<|im_start|>assistant
Hello ! How can I assist you today?<|im_end|>
大模型的多轮对话组装格式为:
<assistant>
You are a helpful assistant.
<user>
Hello!
<assistant>
Hello!
<user>
Hello!
<assistant>
输入:
<|im_start|>system
你是可以通过插件调用模型api来回复用户的问题的大语言模型。你需要从可选插件中进行插件选择,并将所选择的插件调用请求按照json标准格式给出且必须包含插件的api_name、url、parameters字段和对应的内容。可选插件信息如下:
{{ tool_list }}
<|im_end|>
<|im_start|>user
{{ query }}
请将所选择的插件调用请求按照json标准格式给出,且必须包含插件的api_name、url、parameters字段和对应的内容:<|im_end|>
<|im_start|>assistant
数据质量优化和增强
针对大模型的训练数据,通常认为AI大模型需要高质量、大规模、多样性的数据集。
因此,考虑从数据规模、数据质量和数据多样性三个角度进行优化:
数据规模:OpenAI 在中提出 LLM 模型遵循“伸缩法则”(scaling law),即独立增加训练数据量、模型参数规模或者延长模型训练时间,预训练模型的效果会越来越好;针对SFT任务,通常1万条左右的精标数据即可发挥较好的效果。
数据质量:高质量数据集能够提高模型精度与可解释性,并且减少收敛到最优解的时间。因此,在进行大模型的SFT训练时,通常认为数据质量相比数据规模更重要。挑选质量较高的数据,并避免出现一些错误,或者无意义的内容,可以有效提高模型的性能。数据质量可以通过reward model,文本质量分类模型等方式进行初步评估,经过人工进行后续筛选。
数据多样性:数据丰富性能够提高模型泛化能力,过于单一的数据会非常容易让模型过于拟合训练数据。因此,在扩充数据规模时需要注意数据多样性,多样性的数据可以提高模型性能。多样性除了从原始数据中获取,也可以通过prompt_template方式构建,对prompt指令进行数据增强,比如中文翻译英文的指令可以拓展为,中译英,翻译中文为英文等相同语义的指令。
互联网语料基本上可以分为3大类:高质量语料(语句通顺且包含一定知识)、低质量语料(语句不通顺)、广告语料(语句通顺但重复率过高,如广告、网站说明、免责声明等)。其中低质量数据主要包括以下内容:
语句不通顺的句子:互联网语料中含有较多的语义不通顺、有特殊符号的语料,为低质量数据。可以使用困惑度(perplexity,简称PPL)来评估语句的合理性。
针对知识表达不充分的句子:互联网语料中存在较多的短文本,如网站导航、目录结构等,为低质量数据。
广告数据:通常为频繁出现的句子,互联网语料中含有大量的网站说明、产品/公司广告介绍等,以及重复的广告、网站信息,会对语言模型产生误导,为广告数据。
答非所问的问答数据:逻辑混乱、答非所问等类型的问答数据。
信息缺失的问答数据:根据问题无法回答答案的文本数据。
指令微调数据多样性方面,需要尽量保障数据的多样性。其中firefly-train-1.1M的数据分布如下图所示:
针对训练行业大模型的主要有如下两种数据构建方式:
开源大模型+行业数据的行业模型训练:这也是最为普遍且资源需求较小的行业大模型构建方式,开源大模型通方面,常为Qwen已经是衍生模型最多的模型;模型训练阶段方面主要为后训练的方式;行业数据集以及行业问答数据的构建方面,通常这一步对模型效果影响较大,需更多经验和实验的部分,这一步可选择对话问答数据集、考试数据,或者借助Teacher模型收集数据等。
通用基座模型后训练+行业数据集微调:一种更为完整的训练方式,首先在通用中文基座模型的基础上扩充领域专有词表、大规模中文行业语料预训练,增强了大模型在领域的基础语义理解能力;然后,在此基础上,构造领域对话问答数据集、领域考试数据集等进行指令精调,提升了模型对行业内容的理解和执行能力。其中,预训练方面的trick有扩充领域词表二次预训练等。
同时,如果仅使用领域数据进行微调,容易导致模型严重的过拟合现象,会导致模型忘掉原有的能力。因此, 需要采用数据组合来丰富模型的知识和能力,例如使用如下数据组合:
通用领域数据:例如,使用大规模的通用领域文本数据集,其中包括不同指令类型、不同领域的文本。通过引入通用领域数据,模型可以更好地理解自然语言和上下文信息,提高对各种问题的处理能力。
行业专业文本:为了增强模型对行业内容的理解,可以引入包括行业参考书籍在内的专业行业文本。这些文本提供了具体的行业规定和解释,帮助模型学习并提供更准确的行业解释和建议。
行业问答数据:收集丰富的行业问答数据,包括常见行业问题和相应的答案。这些问答数据尽量涵盖了多个行业领域。通过使用这些数据,模型可以学习到常见法律问题的回答模式和解释能力。
针对指令数据构建,可以通过如下的优化方式以让模型更好的理解指令以及具备泛化性: 1. 通过数据增强增加训练指令数据的多样性,增强模型的泛化能力。 2. 在训练阶段,可以对于特定下游任务进行相应的指令构建,同时在预测阶段,建议与训练阶段的指令或者特定任务的指令保持一定的一致性和相关性。 3. 适当构建 few shot 及COT(Chain of Thought) 数据加入训练,可以有助于模型的指令理解以及多轮对话能力。
大模型有更好的筛选能力,但在实际业务场景中需要对大批量数据进行快速优化,而小模型或者Bert模型的方案可以进行快速筛选。
因此,可以构建Teacher-student的架构构建低质量模型方案,进行模型训练和分类:
基于生成式大模型构建判别器进行低质量数据的筛选过滤。
训练小模型或者bert模型构建语料质量分类模型,筛选低质量数据和采样高质量数据。
以此实现将大模型的语义能力蒸馏到小模型的目的,实现大批量数据的快速质量优化。
模型推理和性能优化
大型语言模型(LLM)推理性能优化是当前AI应用部署的关键挑战。本节系统分析LLM推理过程中的性能瓶颈,并探讨主流优化技术及其理论基础。
LLM推理中的核心挑战主要体现在三个方面:
1. 推理延迟高:自回归生成模式下单token生成速度难以满足实时交互需求
2. GPU利用率低:内存带宽受限导致计算资源无法充分利用,多数部署中GPU利用率仅为10%-30%
3. 显存需求大:模型参数与KV-Cache共同占用大量显存,特别在长序列场景下容易出现OOM问题
针对这些挑战,业界已发展出多层次优化策略:
服务架构层:Continuous Batching、动态请求调度等技术提升系统整体吞吐量;
计算引擎层:PagedAttention、FlashAttention、算子融合等提高计算和内存效率 ;
模型优化层:量化技术、并行策略等减少资源需求并提升硬件利用率;
接下来将分析LLM推理过程中的性能问题,并逐一介绍各类优化技术的原理与实现。
模型推理分析
由于大模型一般都是采用自回归生成方式,即根据前面的语句预测下一个字的概率,而自回归生成方式上看会存在以下问题:
1. KV Cache占用显存大:
每个请求需要维护独立的KV缓存;
随着请求数量增加,KV缓存呈线性增长;
长序列场景下显存压力更大;
2. 解码算法复杂度:
通常需要提供多种解码策略(贪婪解码、采样解码、束搜索)支持;
不同解码策略对内存管理要求不同,例如并行采样和束搜索需要不同的内存管理机制;
3. 动态序列长度:
输入输出长度未知且不固定,要求内存管理系统能够适应不同长度;
解码过程中序列长度持续增长,KV 缓存内存也会相应增加;
同时,现有系统采用静态连续内存分配策略,存在以下三类内存分配问题:
1. 预留浪费(reserved):需要为每个请求预留最大可能序列长度的内存空间,导致资源利用率低下;
2. 内部碎片(internal fragmentation):内存分配效率低导致已分配内存块内部存在未被充分使用的区域,降低内存的整体利用效率;
3. 外部碎片(external fragmentation):当小内存块无法被有效整合利用时,造成系统可用内存资源的浪费;
分析LLM结构及推理过程,对LLM推理的计算量、IO量和显存占用进行详细计算,并按照 Roofline方法 进行分析,定位推理速度慢、吞吐量低和OOM的原因。
GPT模型推理分为两个阶段:
1. Prefill阶段: 向模型输入context tokens,模型执行Context计算过程,该过程无论输如的context-length有多长,只需计算一次,输出第一个token,称为Prefill阶段。
2. Decode阶段: 之后将生成的token作为input_id,执行自回归生成过程,称为Decode阶段,第二阶段反复进行。
大模型推理过程中的90%以上的时间耗费在第二阶段。单线程生成首个token的延迟一般不超过0.1s,后续每生成一个token的量级大概为50ms,推理时间和生成长度成线性关系。[6]
下图展示了Transformer编码器的核心架构,其中左侧部分呈现了多头注意力(MHA)模块的详细结构,右侧部分则展示了前馈神经网络(FFN)模块的计算流程图。
GPT类模型中解码器中的计算与序列长度的关系图如下:
对单个token生成过程的的计算分析,计算耗时主要集中在以下三个模块:
多头注意力(MHA)计算
层归一化(LayerNorm)
分组矩阵乘法(group gemm)
以GPT-2模型为例,计算耗时与序列长度的关系图如下:[7]
从上图分析可以得出:
1. 序列长度较短时,MHA(多头注意力)和FFN(前馈网络)中的投影层(projection layer)是主要的延迟来源,这主要是因为矩阵乘法运算的开销较大 ;
2. 序列长度较长时: 激活值之间(act-to-act)的延迟变得更加显著,非线性操作(如softmax、gelu等)的延迟超过了编码器(encoder)推理的延迟,这主要是由于序列长度增加导致注意力计算复杂度上升;
因此后续的性能分析和优化也需要重点关注这些计算密集型模块。
Roofline Model是评估和分析高性能计算平台性能的有效工具,通过绘制性能上限与计算强度关系的图,直观展示系统瓶颈。
每个硬件平台都对应一个特定的Roofline图,其中关键参数包括:
硬件平台参数(对应图中折线):
峰值算力(π):计算平台每秒能完成的最大浮点运算次数(Maximum FLOPs Per Second),对应图中水平线的高度 - 单位:FLOPS (FLoating-point Operations Per Second)
内存带宽(β):计算平台每秒能完成的最大内存交换量(Maximum Memory Access Per Second),对应斜线的斜率 - 单位:Byte/s - 计算强度上限(Imax):π/β,表示单位内存交换最多可支持的计算量 - 单位:FLOPs/Byte
算法/Kernel参数(对应图中屋檐下的某一点):
计算强度(I):计算量/访存量,表示每单位内存交换所执行的浮点运算次数 - 单位:FLOPs/Byte - 反映算法的内存利用效率计算强度I越大,内存使用效率越高。对应横轴x值。
理论性能上限(P):模型在特定平台上能达到的每秒浮点运算次数 - 单位:FLOPS - 受平台和算法特性双重限制,对应纵轴y值
根据Roofline模型划分的两个性能瓶颈区域:
内存受限区域(Memory-Bound):当算法计算强度I < Imax时,性能受内存带宽限制
优化策略:提高带宽或增加算法计算强度可使性能呈线性增长
计算受限区域(Compute-Bound):当算法计算强度I > Imax时,性能受算力上限制约
此时算法已充分利用平台算力资源
因此有如下性能优化目标:
1. 提升模型的计算强度(Attainable Performance):通过多核并行等方式提高计算效率 ;
2. 提升计算密度(Operational Intensity):减少内存访问次数,降低带宽占用;
可以通过一些Roofline工具来生成得到相关数据,Roofline模型的核心价值在于揭示硬件资源限制与算法特性之间的关系,帮助开发者理解在特定计算平台约束下可达到的理论性能上限,从而指导性能优化方向。
基于对LLM推理过程的分析,我们发现推理性能主要受内存带宽而非计算能力限制,而服务吞吐量则受限于可支持的最大批量大小。因此,LLM推理优化主要围绕以下三个核心目标展开:
1. 推理速度优化
降低单token生成延迟:通过优化计算和内存访问效率,减少每个token的生成时间
保证批量请求延迟:在增加batch_size的同时,确保单条请求的响应时间不会显著增加
优化长序列处理:针对长文本输入场景,优化KV-Cache管理和内存使用
2. 吞吐量优化
提升GPU资源利用率:通过Continuous Batching等技术提高GPU计算效率
优化请求调度:实现动态请求分配和负载均衡,最大化系统整体吞吐量
支持并发处理:通过并行计算和流水线处理提高并发能力
3. 资源占用优化
解决显存OOM问题:通过量化技术和内存管理优化减少显存占用
提高显存利用效率:优化KV-Cache存储和访问方式,减少内存碎片
支持更大模型部署:通过模型并行和量化技术,使大模型能在有限资源下运行
从性能瓶颈来看,推理性能主要受显存带宽限制,而服务吞吐量则主要受batch_size限制。因此,针对这些限制,优化工作主要从三个维度展开:服务层优化、推理引擎层优化和模型量化技术。服务层优化通过高效的批处理策略和特定场景优化提升系统吞吐量;推理引擎层优化通过创新的算法和并行技术突破计算瓶颈;模型量化技术则通过降低模型精度在保证性能的同时减少资源占用。
在实际应用中,需要根据具体场景和资源约束,选择合适的优化策略组合,才能达到最优的推理性能。
服务层优化
由于LLM推理过程主要受内存带宽限制,在较小batch_size的情况下,模型权重的加载时间占据了主要开销。此时增加batch_size并不会显著增加推理延迟,反而能带来显著的吞吐量提升。
通过合理配置请求的batch策略,可以大幅提升系统吞吐量。因此,为了优化服务吞吐量和提升资源利用效率,batch策略的优化是其中最关键的部分。
Dynamic-batching的效果类似电梯,基于Dynamic-batching的服务主要服务侧维护一个task-queue,需要等够设置的batch size后进行批处理,或者需要超过等待时间。
- 场景:通常用于ResNet、BERT在线服务提取特征等应用场景。
- 局限:GPT类大模型具有自回归执行时间长、序列长等特性,不太适合GPT模型推理,主要表现在:
如果max_queue_wait_time太短,则组成的batch小,GPU利用率很低且后续的请求处于等待状态;
如果queue_wait时间长,则无法及时响应在线服务;
Continuous-Batching(连续批处理)类似自动扶梯,是一种针对自回归生成模型特性设计的高效批处理机制,其核心思想是动态重组批次,而非等待一个完整批次的计算结束后才处理下一批。 在连续批处理中,新请求会立即加入当前批次,仅需等待一个token-step(约20毫秒)即可开始计算,而不需要等待当前批次的计算结束。
如上图,当请求1和2正在处理时,请求3、4、5和6到达系统。基于Continuous-Batching策略,新请求立即加入计算批次,无需等待前序请求完成,极大提高了服务效率。
Continuous-Batching通过动态重组批次的方式,实现了毫秒级响应、提升GPU利用率和吞吐量,同时显著减少了请求等待时间,从而优化了整体用户体验。
为了满足不同场景下LLM服务的需求,服务层提供了流式返回、交互式对话以及continue/cancel等关键功能特性:continue/cancel功能允许系统先返回部分生成结果,用户可以根据结果质量决定是继续生成还是中断生成过程,从而提升整体推理效率。采用web-socket和server-sent events(SSE)等协议来实现客户端的流式返回。
目前主流的服务框架都实现了对这些特性的支持。
LLM推理受限于预设的上下文窗口长度,在很多场景需要扩展模型的有效序列长度,主要解决方案如下表。
方案 |
实现方式 |
优势 |
劣势 |
适用场景 |
从头训练 |
直接训练长序列模型 |
效果最好 |
计算成本高 |
资源充足场景 |
继续训练 |
在基础模型上微调 |
成本适中 |
效果有限 |
快速迭代场景 |
可扩展位置编码 |
修改位置编码机制 |
支持动态扩展 |
需修改架构 |
定制化需求 |
位置编码内插 |
调整scale参数 |
简单易用 |
效果一般 |
通用推理场景 |
推理引擎层
KV-Cache的主要思想是空间换时间,主要因为和CNN、RNN等架构的深度学习网络模型相比,生成式模型的推理过程的特点是,输出一个回答(长度为N)过程中执行了N次推理迭代,具有很高的时间复杂度,因此需要通过缓存来优化推理效率。
由上文中自回归生成模型的token计算公式可知,第个token的概率分布以及概率最大的token 的选择会受到前个token的影响,而不会受到个及之后token的影响:
1. 计算:将前个token输入到模型,得到第个token的概率分布,选择概率最大的token作为。
2. 计算:同样的方式,将前个token输入到模型,得到概率分布,并生成第个token。
3. 迭代输出直到达到停止条件:重复上述步骤,直到达到停止条件(停止符、停止长度等)。
因此,计算时,可以对K和V的值进行缓存复用后,且只需要计算相关序列的值(蓝色框)而不再需要计算之前的单元(绿色框)。
如图所示,在生成第9个token时,模型需要计算Self-Attention。如果不使用KV-Cache,每一层Decoder Layer都需要重新计算之前所有token的key和value,并与当前token进行scale-dot-product计算。通过缓存之前步骤的key和value,在生成新token时,每层mask-MHA计算可以直接使用缓存的key和value,避免了重复计算,从而显著提升了推理效率。[3]
KV-Cache是大模型推理中最有效的优化方法之一,因此目前在transformers库及常用加速框架中通常把该特性已经在config配置中设置为默认使用,在基于FasterTransformer库的GPTNeoX、GPTJ、GPT等模型中,也都分配了key_cache_和value_cache_成员变量以保存推理过程中的key/value。
PagedAttention主要为了解决传统KV-Cache管理机制的局限性。KV-Cache其在自回归生成过程中,系统需要缓存Key和Value值以优化计算效率。以FasterTransformer(FT)为例,在Beam Search场景下,KV-Cache的显存分配公式为:
其中,layer表示Transformer模型的层数,每一层都需要存储自己的KV-Cache;beam_size 表示每个时间步保留的候选序列数量,序列长度seq_len,包括输入序列和已生成的token长度;dim表示每个token的隐藏层维度大小;由于需要同时存储Key和Value两个矩阵所以需要乘2。
这个公式反映了KV-Cache显存占用的几个关键特点:
1)线性增长:显存占用与模型层数、批处理大小、束搜索宽度和序列长度都呈线性关系。
2)双重存储:由于需要同时存储Key和Value,所以需要乘以2。
3)维度依赖:显存占用与模型的隐藏层维度大小直接相关。
这种分配方式存在三个主要问题,导致显存利用率低下:
1. 变长序列对齐冗余:在动态批处理(dynamic-batching)场景下,系统需要按照最长序列长度分配显存空间。大多数推理框架在服务启动时会设定max_total_tokens
参数,为每个请求分配固定长度的显存缓存。当请求序列长度差异较大时,会造成大量显存空间浪费。
2. Beam-Search冗余:在Beam-Search解码过程中,不同候选序列可能共享相同的前缀。传统KV-Cache存储方式会重复存储这些共享前缀,造成显存冗余。
3. 相同Prompt冗余:在实际应用中,同一业务场景下的请求通常使用相同的系统提示词(Prompt),也会造成浪费。
该算法的核心思想是允许在非连续的显存空间中存储逻辑上连续的key和value张量。具体实现方式为:将每个序列的KV-Cache分为固定大小的块(blocks),每块包含固定数量token的key和value张量,这些块在显存中不需要物理连续分布。
PagedAttention将物理空间当做KV Cache,将其划分为固定大小的连续内存块,从左到右存储。PagedAttention允许在非连续内存空间存储K和V,更适合LLM的推理。
类比于在操作系统中的虚拟内存,可以将块比作页面,token比作字节,sequence则相当于进程;通过块表,sequence的逻辑块被映射到非连续的物理块上;当生成新的token时物理块按需分配;基于分块策略每个sequence只在最后一个块中可能会留有未使用的空间。
在逻辑空间与物理空间的映射过程中,vLLM通过维护一个映射表(Block table)来实现这一功能。当处理如“Alan Turing is a computer scientist”这样的输入时,每当生成新的token,系统将执行以下步骤:
1. 查询Block table中的Physical block编号。
2. 定位到相应的物理内存位置进行数据存储,并更新Block table中Filled slots的信息。
3. 若生成新token(例如“renowned”)需要额外的存储空间,则分配新的物理内存块,并相应地更新Block table。
多个Request时同理,物理空间不需要连续,随机选取Block即可。
内存利用率显著提升,浪费空间仅占KV Cache总空间的4%以下,整体内存利用率提升了3-5倍。这种优化效果主要来自两个方面:首先,内部碎片被最小化,因为碎片仅出现在最后一个block中,且大小不超过block size。论文中采用的block size为16或32个token,远小于传统方案中近千量级的tokens长度;其次,通过分块管理机制,完全避免了外部内存碎片的产生。
并行采样(Parallel Sampling)场景:
多个候选输出序列(candidates)通常会共享相同的输入提示(Prompt)。针对这种情况,PagedAttention采用了KV块共享机制(Sharing KV blocks),其工作原理如下:
当两个输出序列共享同一个Prompt时,虽然在逻辑空间上各自维护独立的存储,但在物理空间上只需保存一份KV缓存。具体实现通过以下步骤:
1. 引用计数管理:
系统维护一个引用计数器(Ref count)
对于共享相同Prompt的两个输出序列,初始Ref count=2
2. 序列A生成新token时:
检测到Ref count=2,表明当前block被多个序列共享
创建block的副本,并将Ref count减1
在复制的block中写入新生成的token
3. 序列B生成新token时:
检测到Ref count=1,表明当前block仅被一个序列使用
直接在现有block中写入新token,无需复制
这种机制有效减少了内存占用,提高了系统资源利用率。
Beam search场景:
在一些相对复杂的场景如Beam search时,其每一次迭代只保留 top-k 个候选序列。
PagedAttention 对其处理与 parallel sampling 不同,它不仅会共享 prompt 的 KV cache,还会基于copy-on-write和mapping机制来有效支持动态共享候选块。
在虚线之前的每个候选序列使用了4个Block。其中,Block 0被所有序列共享,Block 1由候选序列0、1、2共享,而Block 2则仅被候选序列3使用。虚线之后,由于候选序列3不再需要Block 2、4和8,这些块被释放。
因此,通过引用计数的方法,beam-search能够及时回收无效分支占用的显存。对于相同的prompt请求,可以通过增加引用计数来重用同一块 prompt kv-cache。
PageAttention的内存共享机制显著减少了复杂采样算法(如并行采样和beam search)的内存开销,内存使用量降低一半左右。实测结果显示,采用PagedAttention优化后,推理速度没有明显下降,可支持的batch_size相比增加两倍以上。目前,TGI等框架已集成了PagedAttention功能。
Sharing prefix场景:
针对system prompt等不会频繁修改的prefix信息存储下来,可以提前计算system prompt 的 KV 值并缓存下来作为共享部分,只需要计算新的token的KV Cache并进行拼接。
自动前缀缓存(Automatic Prefix Caching, APC) 是一种针对大规模语言模型(LLM)推理场景设计的高级内存优化技术。通过智能缓存和复用已计算的键值对(KV)缓存,APC能够显著提升推理效率和系统吞吐量。这一技术特别适用于那些请求间存在共享前缀内容的应用场景,如系统提示、文档内容或对话历史。
在实际应用中,不同请求间经常共享相同的前缀内容。其工作流程如下:
1. 缓存管理:系统缓存现有请求的KV值,并建立高效索引结构。
2. 前缀检测:新请求到达时检测是否与已有请求共享前缀。
3. 计算复用:对共享前缀部分直接复用已缓存的KV值,仅计算新增内容。
通过这种方式,APC能够显著减少重复计算,提高推理速度和系统吞吐量。
APC的核心在于构建高效的映射关系:hash(prefix tokens + block tokens) <--> KV Block
。通过在vLLM的KV缓存管理中引入一层间接映射,所有具有相同哈希值的KV块可以被映射至同一物理块,并共享内存空间,从而避免了重复计算。
当缓存空间满时,系统采取多层次淘汰机制的缓存管理策略:
1. 首先淘汰引用计数为0的KV块(即无活跃请求使用)。
2. 对于多个可淘汰块,依据LRU(最近最少使用)原则进行筛选。
3. 若时间戳相同,则优先淘汰前缀较长的块,以优化内存使用效率。
在如下场景有较大优化:
长文档查询服务:用户对同一长文档(如软件手册或年度报告)使用不同查询进行多次问答。APC使系统只需处理文档内容一次,显著减少了重复计算。
多轮对话应用:APC在聊天会话中允许系统复用历史对话的处理结果,仅计算新增内容。
作为一种典型的“以空间换时间”优化方法,APC特别适用于高并发、低延迟需求且请求间存在前缀相似性的LLM应用场景。
TP(张量并行)和PP(流水线并行)是用于扩展大规模模型训练任务的通用方法:
TP并行是将一个变量分散到多个设备上,共同完成某个或多个计算操作。(适用于单个张量或模型较大的情况)
PP并行则将模型的按层划分,不同部分分配到不同的设备上。
TP并行:把一个变量分散到多个设备并共同完成某个或多个计算操作。(应对单个 Tensor/Op 很大或者模型很大情况)
由于层叠的Transformer Layer中,主要是Self-Attention和MLP,所以主要可分为MHA并行和MLP并行。[8]
MHA并行: Multi-Head-Attention的每个head之间是独立的,可以把multi-head按照TP并行数量进行切分,KV-Cache缓存也被均分;
MLP并行: 具备KV-Cache切分及参数切分的能力,TP推理不仅具备加速的功能,还具备KV-Cache切分及参数切分的能力;
当模型规模增大到一定程度,单张卡放置不下模型,则可以将大模型按层进行切分,放置在多个设备上。
其主要依照模型的运算符的操作将模型的上下游算子分配为不同的流水阶段,每个设备负责其中的一个阶段模型的存储和计算。一个完整请求的计算过程为:首先在gpu0上处理,随后继续在gpu1上进行,此时gpu0可以接受新的请求;依此类推,直到请求完成。
通过PP并行可以实现数据传输量较少、Batch_size可以更大:
数据传输量较少:在并行推理过程中,仅需传输层间的激活值,而层间的KV-Cache是独立的,无需传递。因此,在Decoder自回归阶段,单个样本只需传输激活值,通信量相对较小。
Batch_size可以更大:在参数并行(PP)中,单个GPU仅需承载模型的一部分,剩余显存可用于存储KV-Cache,从而支持更大的batch_size,提升GPU计算效率。
为了提升设备效率,已经提出了一些复杂的多流水并行方法。例如,GPipe借鉴了数据并行的思想,将批次(Batch)拆分为更小的微批次(Micro-Batch),如F0,1、F0,2等。这样,下游设备可以更早地获得可计算的数据,从而减少设备空闲(Bubble)时间,提高整体效率。
在大模型部署过程中,当存在多个单卡可容纳的模型时,可以通过多模型的Pipeline并行(PP)部署来实现负载的自动均衡。根据AlpaServe论文中的原理,假设在两张GPU卡(GPU1和GPU2)上部署了两个模型(Model A和Model B),对以下两种部署方案进行了对比分析:
无协作分配方案:每个模型独占一个GPU卡。当请求到来时,如果所有请求(如R1-R4)都是针对Model A的突发请求,在没有采用PP并行部署的情况下,这些请求需要在GPU1上串行处理,导致GPU2(运行Model B)处于空闲状态。
PP并行协作式部署:采用PP并行方法,模型的不同部分被分配到两块GPU上。这样,当GPU1正在处理Model A的一部分时,GPU2可以同时处理Model B的一部分。这种方法使得来自不同模型的请求更有可能被均衡分配到两块GPU上进行处理。
通过比较可以看出,在多模型请求存在时间错峰的情况下,采用PP并行部署(方案b)能够更好地实现负载均衡,显著减少请求的服务时间,从而提高整体资源利用率。
从通信量的角度来看,大致排序为:TP > DP> PP,由于TP需要在每次迭代中频繁交换被分割的张量,导致通信量较大;DP则在每个epoch结束时进行一次大规模的数据交换。相比之下,PP虽然通信需求较低,但需精细调度与同步以确保正确执行。
选择建议:
TP因其加速性能被广泛应用于模型推理;PP则通过按层划分模型,能够处理单个设备无法容纳的大规模模型。
对于中小型模型,优先考虑TP进行加速;对于超大规模模型,推荐采用PP。
在实际应用中,通常结合多种并行策略优化性能。例如,单个计算节点内采用张量并行(TP)分割模型,跨多个节点时利用数据并行(DP)加速训练。引入流水线并行(PP)至TP架构,进一步降低模型复杂性。选择并行策略时,需综合考虑模型大小、硬件配置及数据集特点。
在深度学习推理引擎中,算子层面的优化是提升计算性能的关键环节。例如,PyTorch 2.0对LayerNorm的引入Welford算法进行均值和方差计算的优化,显著提升并行性能。[9] 优化策略可分为两类:
1. 工程实现优化:针对现有kernel函数进行底层实现层面的优化,主要关注计算资源的利用效率
2. 算法优化:通过数学变换,采用更高效的计算方法替代原有实现,从算法层面提升性能
算子融合(Operator Fusion)是深度学习推理优化的核心方法之一,核心思想是将多个串行执行的算子合并为复合算子,通过减少中间数据传输和Kernel启动次数来提升性能。具体优势包括:
- 显存带宽优化:合并后的算子无需将中间结果写入显存,降低内存访问次数。
- 降低Kernel调用次数:每个算子对应一次Kernel启动,合并后可显著减少启动开销。
- 提升计算密度:复合算子通过扩大并行粒度更充分地利用计算资源。
FasterTransformer和ByteTransformer[10]等优化方法均在此方面进行了优化,以Transformer的非常重要的Add&Normalize操作为例,原始实现与优化后的对比如下: 1. 原始实现(PyTorch)流程:Multi-Head Attention输出后,依次执行逐元素相加(element-wise addition)和LayerNorm。存在的问题是:需启动多个独立Kernel(Add、LayerNorm)。 2. 优化实现(FasterTransformer)的融合策略:将Add与LayerNorm合并为一个算子(如AddBiasResidualLayerNorm),仅需启动1个Kernel。
通过算子融合,Transformer层的对应结构可以简化为下图所示的形式,显著提升了推理性能:
模型量化
大规模语言模型的推理性能受限于显存带宽,而推理吞吐量则受显存容量和最大批次大小(max_batch_size)的限制。在推理过程中,显存溢出(OOM)问题使得无法通过增加批次大小来进一步提升吞吐量。
从上图可以看到,近年来,大型语言模型的模型大小的发展速度比GPU等计算资源更快,从而导致对显存的供应和需求之间存在差距,因此,模型量化技术已逐渐成为优化模型推理性能和服务吞吐量的重要手段。
量化(Quantization)通常指的是定点数量化,即小数点位置固定,这与常见的浮点数表示法形成对比。量化的主要目的是在牺牲少量精度的基础上,压缩模型体积,使其能够在资源受限的环境中运行,例如嵌入式设备,而无需依赖高性能服务器或GPU。
定点数与浮点数这两种数值表示方法主要区别在于小数点位置的处理:定点数的小数点位置固定,整数和小数部分各自占用一定数量的位;浮点数则通过存储有效数字(尾数)和指数来表示数值,因此能够覆盖更广的数值范围。
模型量化的主要优势包括:
1. 降低内存及显存占用:量化技术能够显著减少模型参数的存储需求。例如,将一个具有16B参数的模型从FP16量化到INT4,显存占用可从32GB减少至10GB,使得该模型能够在消费级GPU上运行。
2. 加速计算:在支持低精度计算的硬件上,量化模型可通过INT8 GEMM内核执行计算,通常比使用FP16或FP32格式更快。量化减少了单个数据点所需的比特数,从而减少了计算过程中的I/O通信量,进一步提升了计算效率。
3. 增大批次大小以加快处理速度:由于量化降低了显存需求,可以在相同的硬件条件下增加批次大小,从而提高单次推理处理的样本数量,进而提升并行度和计算速度。
量化目标: 在大规模语言模型(LLM)的推理过程中,量化技术主要应用于权重(Weight)、激活值(Activation)以及键值(KV)缓存三个模块,此外还可以针对模型参数、激活值及梯度进行量化。
通过这三个维度的方法进行量化优化,能显著提升模型的推理吞吐量和执行效率。
Emergent Features问题:
随着模型参数量的增加,参数和激活值中会出现系统性的异常凸出特征,这些异常值在所有层中普遍存在,形成了大尺度的凸出特征。这种特征扩大了量化的数值范围,降低了Tensor级别的量化精度,从而影响了量化后模型的性能指标。
针对大语言模型量化的这种问题,当前的解决方案思路有几种:
- TimDitmmers提出的混合精度量化方案LLM.int8
- MIT团队提出的平滑量化(SmoothQuant)方案 - 高通通过引入截断softmax和门控注意力机制来降低异常值的幅度
前两种方法都旨在解决激活值中的异常值计算问题,第三种方法则从模型结构层面入手,使得传统的W8A8量化算法在LLM中也能保持良好的性能。
在SmoothQuant论文中,对大模型量化的难点进行了总结,指出大模型量化具有以下特点:[11]
1. 激活值量化难度高于参数量化:如图所示,权重参数值的分布相对平稳,而激活值的分布范围非常广泛,存在显著的离群点。
2. 激活值中的离群点导致量化困难:部分激活值的值可能达到其他激活值的100倍左右。在这种情况下,如果直接对Tensor进行量化,异常离群点将主导量化范围,导致有效量化比特位降低,大部分激活值被压缩到0附近,量化误差显著增大。
3. 离群点分布于特定通道:异常激活值的比例较小且集中在固定的通道内。基于这一特性,后续的LLM.int8和SmoothQuant方法得以实现更有效的量化。
模型参数和异常值的分布如下图所示:
SmoothQuant方法对离群点进行了平滑量化:
SmoothQuant中的混合精度方法:
GPTQ是一种专为GPT系列大语言模型设计的后训练量化技术。该技术通过逐个量化模型中每个block的参数,并适当调整未量化的参数来补偿量化带来的精度损失,从而实现模型压缩。执行GPTQ需要准备特定的校准数据集以确保量化过程的准确性。
GPTQ的发展根植于Yann LeCun于1990年提出的OBD算法,之后经过OBS、OBC(或称OBQ)等多种方法的演化,最终形成了当前的GPTQ技术框架。
在GPTQ中,模型的每一层被划分为若干个block,针对每个block,采用逐列量化的方式,并通过最小化平方重建误差优化未量化参数。这样,在保证模型性能的同时,也能实现有效的参数压缩。
以特定计算设备为例,使用fp16格式进行推理时,批量大小(batch size)仅能达到4(最大令牌数max_tokens为2048)。相比之下,GPTQ能够在相同的令牌数限制下支持批量大小达到8。尽管在小批量推理场景下FP16可能提供更快的速度,但GPTQ通过支持更大的批量大小,能够在整体吞吐量(throughput)方面优于FP16。
GPTQ量化程序通过利用Cholesky分解中存储的逆Hessian信息,在给定步骤中对连续列块进行量化(标记为粗体),并在步骤结束时更新剩余权重(标记为蓝色)。此量化过程在每个块内递归执行,当前正在处理的是标记为白色的中间列。
模型训练和性能优化
模型微调整体流程
当前大语言模型from Zero to Hero的完整训练过程,通常认为主要分为基座预训练(Base pretrain)和大模型微调。其中,大预言模型的基座预训练通常指利用无监督或自监督学习方法让模型学习到通用语言能力,这个过程往往需要利用大量语料以及大量的计算资源。
大模型微调阶段中,可以使用更少的资源消耗和时间,提升模型在领域数据集上的效果,目前大家较为公认的LLM微调优化阶段,通常主要指的是如下几个阶段[12]:
1. 监督微调(Supervised Fine-Tuning,SFT):使用精选的标注数据监督微调预训练模型。
2. 奖励模型微调(Reward Modeling):使用一个包含人类对同一问题的多个答案打分的数据集来训练一个单独的奖励模型。
3. 基于人类反馈的强化学习(RLHF):基于更多人类反馈数据集,利用强化学习算法根据模型的奖励反馈进一步调优模型并与人类进行对齐。
可以在训练中采用的一些训练方法有:
1. 微调(Fine-Tuning):在预训练模型的基础上,使用特定任务的数据进行微调;可提高模型在特定任务上的性能。
2. 知识蒸馏(Knowledge Distillation):使用大型教师模型的知识来训练小型学生模型;提高小型模型的性能,同时减少计算资源需求。
3. 多任务学习(Multi-Task Learning):同时训练多个相关任务,共享部分模型参数;提高模型在多个任务上的性能,同时减少过拟合。
4. 迁移学习(Transfer Learning):利用预训练模型在新任务上进行微调;减少训练时间和数据需求,提高模型性能。
大语言模型的基座(Base)通常指的是在大量文本数据上进行预训练的初始模型。这个初始模型作为一个基础框架,具有一定程度的通用语言理解能力,但可能在特定任务上的表现尚不理想。预训练模型的目的是让模型学习到语言的基本结构、语法规则和一般知识,从而为后续的任务定向训练打下基础。
这个基座模型在许多自然语言处理任务上具有较强的表现,但为了在特定任务上取得更好的性能,通常需要对其进行进一步的微调。通过对基座模型进行微调,可以使其更好地适应特定任务的需求,从而实现更高的性能。
Prompt Tuning是大模型微调方法中的一种常用方法,包括Prefix-Tuning、P-Tuning、Parameter-Efficient Prompt Tuning、P-Tuning v2、LoRA等方法。[13、14、15、16、17]
模型的监督微调,是一种在深度学习预训练模型中最常被使用到的模型调优方式,其通常指对预先训练好的神经网络模型,针对下游任务,在少量的监督数据上完成参数重新训练的技术。
同样,LLM的SFT微调是指对预先训练好的大型语言模型(如GPT系列)进行监督式微调。通过使用大量的人工标注数据,根据特定任务需求,进一步优化模型的性能。这些数据通常包括输入与对应的期望输出,让模型学会如何从输入得出正确的输出。微调的过程可以看作是在原始预训练模型的基础上,为其适应特定任务场景而进行的个性化训练。
SFT在LLM训练过程中的应用有以下重要原因:
1)针对特定任务提升性能:预训练过程,大规模的无监督训练可以学习通用语义,但是在特定任务上可能无法表现出相似的性能。因此通过利用有标签数据,进行有监督微调,可以针对对应数据和针对特定任务的性能。
2)针对特定领域提高适应性:在特定领域进行有监督训练,可以适应其中的专业数据、表达结果和语义。
3)针对数据稀缺的任务:某些很难获取大量有标签数据情况下,通过使用有限的标签数据来训练模型,便可获得较好的模型效果。
强化学习是一种利用反馈来学习策略的范式。具体而言,强化学习的模型(Agent)与环境交互,对于每个给定状态st采取动作at并从环境获得奖励rt,同时进入下一状态st+1,这一过程循环往复。在积累了这一系列交互经验后,模型通过调整自己的策略以让交互过程得到的奖励最大化。这样一来Agent就学习到了在给定状态下采取有益的动作的策略,实现了强化学习的目标。
LLM固然有很强的自然语言理解能力,但我们更希望它能够理解人类指令并做出对人有帮助的回答,因此,需要让LLM的行为与人类“对齐”。为此,以InstructGPT为代表的一系列工作便尝试通过强化学习让LLM与人类的行为对齐。InstructGPT的核心由两个模型构成:一个反馈模型(RM),它给定一对模型输入和输出,反馈该输出的合理程度(有多好)打分;一个生成式语言模型,给定输出生成一段输出,并利用RM给出的打分作为奖励进行强化学习。只要让RM能很好的反应人类的偏好,就可以让生成模型与人类行为进行对齐。[12]
奖励函数训练(Reward Modeling, RM):RM(Reward Modeling, RM)奖励函数训练是指为强化学习任务设计奖励函数。奖励函数是一个用于评估AI智能体在特定任务中表现的度量,引导智能体在学习过程中采取正确的行动。RBRM是一种基于排序的奖励建模方法,通过对多个候选输出进行人工排序,为输出赋予相对优劣,从而指导模型生成更好的回答。这种方法可以帮助解决常规奖励建模方法在一些情况下难以为模型提供足够明确指导的问题。
RLHF:基于RM/RBRM的PPO强化学习:PPO(Proximal Policy Optimization,最近邻优化策略)是一种强化学习算法,通过优化模型的策略(即在给定输入时选择动作的方式)来提高模型性能。在基于RM或RBRM的PPO训练中,模型利用设计好的奖励函数(或基于排序的奖励模型)来学习如何为特定任务生成更好的输出。通过与环境交互并获取奖励信号,模型不断调整自身策略,以便在未来的相似任务中获得更高的奖励。PPO算法的优势在于其能够在保持稳定性的同时实现较高的性能。
大语言模型的与人类对齐是指让人工智能模型理解、遵循并适应人类的价值观、需求和期望。这意味着让模型在处理各种任务时,不仅要提供准确和有帮助的信息,还要确保所生成的内容遵循道德、法律和社会规范,避免产生有害或误导性的结果。
为了实现与人类的对齐,需要在模型的训练和优化过程中充分考虑人类价值观。这包括:
1. 在监督式微调阶段使用具有明确指导意义的标注数据,
2. 在奖励建模阶段设计合适的奖励函数,
3. 在强化学习阶段根据实际反馈调整模型策略。
总之,与人类对齐是确保大型语言模型能够更好地服务于人类社会的关键因素。通过关注人类价值观并将其纳入模型训练过程,可以使模型更加可靠、安全和有用。
模型后训练
模型后训练阶段涉及创建专门的数据集,这些数据集包含旨在指导模型在不同情境下的回复效果,以下是两类常见的后训练任务目标:
1. 指令/对话微调:主要旨在使模型能够遵循指令、执行任务、参与多轮对话、遵守安全规范并拒绝不当请求。如今,许多数据集由AI自动生成,随后经过人工审核和编辑以确保质量。
2. 领域特定微调:该方法的目标是使模型适应特定领域的需求,如医学、法律或编程。通过在这些领域的高质量数据上进行微调,模型能够生成更专业、更准确的响应。
在后训练阶段,还会引入一些在预训练阶段未使用的特殊token。这些token有助于模型理解交互的结构。例如,通过标记用户输入的起始与结束、标记AI响应的起始位置等特殊token,可以确保模型能够正确区分提示和回答,从而生成更符合上下文的响应。
通过后训练的特殊token,模型不仅能够更好地理解任务和指令,还能在特定领域和复杂交互中表现更好。
常用训练框架
随着AI模型的规模越来越大,分布式训练技术越来越被广泛使用。现行的分布式训练方法主要包含两个部分:数据并行(Data Parallel)和模型并行(Model Parallel)。其中,数据并行是将模型完整拷贝到多张显卡中,对批次数据进行并行计算,适合规模小而数据多的训练场景;而模型并行适合超大规模参数的模型训练,将模型不同的部分分别加载到不同的显卡中,依次计算得出结果。
Deepspeed方法是微软开源的模型训练框架,主要有流水线并行, 以及ZeRO内存优化技术等。[18]
DeepSpeed 实现了ZeRO论文中描述的所有内容,目前已支持:优化器状态分区(ZeRO 阶段 1)、梯度划分(ZeRO stage 2)、参数划分(ZeRO stage 3、自定义混合精度训练处理一系列快速的基于 CUDA扩展的优化器、ZeRO-Offload 到 CPU 和磁盘。DeepSpeed ZeRO-2 主要仅用于训练,因为它的特征对推理没有用。 DeepSpeed ZeRO-3 也可用于推理,因为它允许将大型模型加载到多个 GPU 上,这在单个 GPU 上是不可能的。
集成DeepSpeed加速可以通过2种方式:1)在加速配置中通过DeepSpeed配置文件规范集成了DeepSpeed功能。只需提供您的自定义配置文件或使用我们的模板。本文的大部分内容都集中在这个特性上。这支持DeepSpeed的所有核心功能,并为用户提供了很大的灵活性。用户可能需要根据配置更改几行代码。2)通过deepspeed_plugin进行集成。这支持DeepSpeed功能的子集,并为其余配置使用默认选项。用户不需要更改任何代码,对于那些对DeepSpeed的大多数默认设置都很满意的人来说是好的。
在对话场景中,以基于deepspeed方法利用问答数据集对LLM模型进一步sft微调为例。进行模型的微调训练脚本如下:
deepspeed \
--include="localhost:0,1,2,3" \
./train_sft.py \
--deepspeed ./ds_config/ds_config_zero3.json \
--model_name_or_path TigerResearch/tigerbot-7b-sft \
--dataset_name TigerResearch/dev_sft \
--do_train \
--output_dir ./ckpt-sft \
--overwrite_output_dir \
--preprocess_num_workers 8 \
--num_train_epochs 5 \
--learning_rate 1e-5 \
--evaluation_strategy steps \
--eval_steps 10 \
--bf16 True \
--save_strategy steps \
--save_steps 10 \
--save_total_limit 2 \
--logging_steps 10 \
--tf32 True \
--per_device_train_batch_size 2 \
--per_device_eval_batch_size 2
在该启动命令配置中,模型的参数主要有以下几个部分:
模型指定部分,指定模型的本地路径或模型名称model_name_or_path(如果本地不存在默认会直接根据指定名称从huggingface进行下载),以及微调后的模型输出路径output_dir。
训练数据部分,需要将数据集划分为训练集和验证集,并分别进行指定训练数据dataset_name等。
模型参数部分,包括模型训练次数num_train_epochs、输入输出的文本长度max_source_length和max_target_length、批处理相关参数per_device_train_batch_size和per_device_eval_batch_size,模型学习率learning_rate等。
计算设备部分,如果需要指定其他运算设备或仅使用单卡,只需要调整CUDA_VISIBLE_DEVICES为指定设备ID即可。
其中,在ds_config_zero3.json配置文件中的配置参数如下,可以看到,在参数配置文件中,指定了学习率、优化器等多种模型参数。
{
"fp16": {
"enabled": "auto",
"loss_scale": 0,
"loss_scale_window": 1000,
"initial_scale_power": 16,
"hysteresis": 2,
"min_loss_scale": 1
},
"bf16": {
"enabled": "auto"
},
"optimizer": {
"type": "AdamW",
"params": {
"lr": "auto",
"betas": "auto",
"eps": "auto",
"weight_decay": "auto"
}
},
"scheduler": {
"type": "WarmupLR",
"params": {
"warmup_min_lr": "auto",
"warmup_max_lr": "auto",
"warmup_num_steps": "auto"
}
},
"zero_optimization": {
"stage": 3,
"offload_optimizer": {
"device": "cpu",
"pin_memory": true
},
"offload_param": {
"device": "cpu",
"pin_memory": true
},
"overlap_comm": true,
"contiguous_gradients": true,
"sub_group_size": 1000000000.0,
"reduce_bucket_size": "auto",
"stage3_prefetch_bucket_size": "auto",
"stage3_param_persistence_threshold": "auto",
"stage3_max_live_parameters": 1000000000.0,
"stage3_max_reuse_distance": 1000000000.0,
"stage3_gather_16bit_weights_on_model_save": true
},
"gradient_accumulation_steps": "auto",
"gradient_clipping": "auto",
"steps_per_print": 2000,
"train_batch_size": "auto",
"train_micro_batch_size_per_gpu": "auto",
"wall_clock_breakdown": false
}
Megatron是NVIDIA提出的一种由于分布式训练大规模语言模型的架构,是一个基于 PyTorch 的框架,针对Transformer进行了专门的优化。它可以高效利用算力、显存和通信带宽,大幅提升了大语言模型大规模预训练的效率。Megatron-LM已经成为许多大语言模型预训练任务的首选框架。[19]
大规模并行训练的一个关键因素是并行训练策略的选择。在[20、21] 中介绍了Megatron-LM常见的大规模训练策略。
如下为一段利用 Megatron-LM方法进行加速模型训练的示例,可以参考如下所示的脚本配置模型架构和训练参数:
GPUS_PER_NODE=8
MASTER_ADDR=localhost
MASTER_PORT=6001
NNODES=1
NODE_RANK=0
WORLD_SIZE=$(($GPUS_PER_NODE*$NNODES))
DISTRIBUTED_ARGS="--nproc_per_node $GPUS_PER_NODE --nnodes $NNODES --node_rank $NODE_RANK --master_addr $MASTER_ADDR --master_port $MASTER_PORT"
CHECKPOINT_PATH=/workspace/Megatron-LM/experiments/codeparrot-small
VOCAB_FILE=vocab.json
MERGE_FILE=merges.txt
DATA_PATH=codeparrot_content_document
GPT_ARGS="--num-layers 12
--hidden-size 768
--num-attention-heads 12
--seq-length 1024
--max-position-embeddings 1024
--micro-batch-size 12
--global-batch-size 192
--lr 0.0005
--train-iters 150000
--lr-decay-iters 150000
--lr-decay-style cosine
--lr-warmup-iters 2000
--weight-decay .1
--adam-beta2 .999
--fp16
--log-interval 10
--save-interval 2000
--eval-interval 200
--eval-iters 10
"
TENSORBOARD_ARGS="--tensorboard-dir experiments/tensorboard"
python3 -m torch.distributed.launch $DISTRIBUTED_ARGS \
pretrain_gpt.py \
--tensor-model-parallel-size 1 \
--pipeline-model-parallel-size 1 \
$GPT_ARGS \
--vocab-file $VOCAB_FILE \
--merge-file $MERGE_FILE \
--save $CHECKPOINT_PATH \
--load $CHECKPOINT_PATH \
--data-path $DATA_PATH \
$TENSORBOARD_ARGS
训练性能优化
本节主要分析如何在大模型训练过程中,优化训练性能,加快训练速度(即提高数据吞吐量)以及优化显存利用率。在huggingface、nvidia等社区里有提到很多在GPU设备上进行的训练优化方法,如下为一些较为通用的方法:
通过上述表格可以看出,不同的技术手段针对训练速度和显存管理有着各自的优势。合理选择并结合使用这些技术,可以显著提高深度学习模型在GPU上的训练效率。 在本节将对训练性能优化相关方法进行具体介绍。
并行化和批处理是训练过程的重要并行化手段。以Nvidia进行的性能测试实验为例,下图为具有 4096 个输入和 4096 个输出的全连接层的算术强度和硬件性能随BS变化的曲线图。[22]
可以看到,对于非常小的批次大小计算将始终受到带宽限制,批次大小 128 及以下的计算量受到带宽限制,通过更多的输入和输出在一定程度上提高了性能。
下图为具有4096个输入、1024个输出时,不同批次大小时的前向传播、激活梯度、权重梯度计算的性能实验数据: [23]
(a)(b)(c) 实验证明在不OOM情况下尽可能增加batch size,可以提高训练速度同时优化显存利用率,同时Batch size配置最好设置为可以提升计算效率。
梯度累积(Gradient Accumulation)通过累积多个小批量样本的梯度来减少显存需求,同时保持计算效率。针对显存不足以使用较大 batch size时,可以通过累积实现较大batch size对应效果的效果。整体流程为重复下述循环:
- 针对每个batch的训练数据,完成前向传播和反向传播后,判断是否达到设置的累积次数。
- 如果未达到,则不更新模型参数,但保存梯度不清零。
- 如果达到,梯度清零同时更新模型参数,进行后续循环。
不进行梯度累积的伪代码:
for batch in dataset:
optimizer.zero_grad() # 梯度清零
# === 计算代码 begin ===
outputs = model(inputs) # 前向传播,计算输出
loss = compute_loss(outputs, labels) # 计算损失
loss.backward() # 反向传播,计算梯度
# === 计算代码 end ===
optimizer.step() # 更新模型参数
进行梯度累积的伪代码:
for batch in dataset:
if batch_index % num_accumulation_steps == 0:
optimizer.zero_grad() # 每 num_accumulation_steps 步重置一次梯度
# === 计算代码 begin ===
outputs = model(inputs) # 计算输出
loss = compute_loss(outputs, labels) # 计算损失
loss.backward() # 反向传播,计算并累积梯度
# === 计算代码 end ===
if (batch_index + 1) % num_accumulation_steps == 0:
optimizer.step() # 每 num_accumulation_steps 步更新模型参数
如果不进行数据并行训练还是会出现OOM时,则可以考虑通过进一步显存优化的方法,例如梯度保存策略(Gradient checkpointing)优化。
梯度保存策略优化可以通过保存激活值来减少显存占用(一定程度增加计算量),为一种时间换空间方法。例如,通过gradient-checkpointing优化可以让将 10 倍大的神经网络放入显存中(需额外花费 20% 的计算时间)。可以发现该方法可以有效缓解显存资源问题,合理使用检查点可以平衡显存和计算资源。[24]
其主要思想是在训练过程中,选择性的保存前向传播中得到的激活值,反向传播过程中重新计算未保存的激活值。
对于具有n层的简单馈送神经网络,梯度计算图如下图所示:
默认策略为在峰值时,该算法会存储所有激活值,这意味着深度为 n 的网络需要 O(n) 的内存。在本例中,意味着需要 7 个内存单元。计算反向传播的默认策略如下:
内存节省策略为:可以通过在节点被消耗时忽略它们并在稍后重新计算来节省内存。这种“内存匮乏”的策略需要 O(1) 内存,但需要 O(n²) 计算步骤。如下图所示,此策略需要 4 个内存单位来计算目标。
另外的方法是保留一部分中间结果,这些保存的节点在gradient-checkpoints中的Checkpoint可以自动选择,也可以手动提供。对于上面的例子,中间策略可以是使用下面圈出的节点作为检查点。使用此检查点会产生一种需要 5 个内存单位的策略,并且其运行时间介于内存不足策略和默认策略之间。
因此,通常使用的优化后方法为sqrt(n)步放置checkpoint策略:对于长度为n的链,每sqrt(n)步放置检查点。如果要求对任何节点最多计算两次,这是内存效率最高的策略。内存需求是 ,计算需求是一个额外的向前传递。这是目前在openai的gradient-checkpoints中采取的默认策略。
以下是这三种策略的内存和计算的复杂度:
混合精度训练 (Mixed precision training)通过部分对精度要求低的参数使用FP16等低精度浮点数进行计算,减少了显存占用并加速了训练过程。例如,DeepSeek系列模型采用了FP8混合精度训练,显著提升了计算效率。
在一些自动混合精度比如torch中实现的方法,会自动将归一化等需要高精度的计算在梯度累计时仍使用FP32,这样可以保持精度避免溢出。 混合精度训练迭代示意图:[@micikeviciusMixedPrecisionTraining2018]
首先会拷贝一份FP32权重副本,然后权重转为FP16进行前向传播并计算loss后,通过FP16进行反向传播计算梯度,最后转换为FP32更新到FP32的权重上。
但需要注意,当小模型和batch且设备性能比较低时,训练速度也会消耗于GPU和GPU之间的IO上以及FP16与FP32的转换,会导致混合精度训练可能会没有效果甚至会更慢。
为了通过数据预加载 (Data preloading) 提高训练速度,需要以GPU能够处理的最大速率提供数据,从而确保GPU的利用率接近100%,可以通过两种DataLoader方法来加速数据供给:[26]
1. 使用锁页内存(pin_memory):此设置允许将数据预先加载到CPU的锁定内存中,锁页内存可以防止数据被交换到磁盘上,减少内存交换的时间(拷贝速度可提升一倍)。
2. 使用更多多线程(num_workers):通过增加线程数来预加,从而提高数据加载效率。
1. Optimizer :合适的优化器是既可以加快训练速度又可以提高显存利用率的。目前训练Transformer类模型最常用的优化器是Adam或AdamW(带权重衰减的Adam),Adam通过存储先前梯度的滚动平均值可以实现比较好的收敛,但这增加了与模型参数数量成正比的额外显存占用。
2. DeepSpeed Zero:针对训练大型模型时显存不足的问题,Deepspeed主要进行了较多的显存优化。
3. 模型简化:通过减少模型层数、参数数量或使用更简单的网络结构,可以在保持性能的同时降低计算复杂度。
4. 模型剪枝与压缩:模型剪枝和量化技术可以进一步减少模型大小和计算成本,提高推理速度。
5. 算子优化:算子融合、动态重显存计算等优化技术可以提升算力利用率。
6. 分布式训练与并行计算:分布式训练是解决大规模数据集和模型参数规模问题的关键技术。通过多机多卡并行计算,可以显著减少训练时间;深度学习框架如DeepSpeed和Megatron-LM支持算子融合、梯度累积等技术,进一步优化了分布式训练的效率
训练参数调优
在训练阶段,合理调整参数对提升模型性能和效果至关重要。以下是关键超参数及其调整策略:
1. Epochs:根据数据规模调整epoch数量,小数据集可适当增加epoch以促进模型收敛。但需注意,过高的epoch可能导致通用能力下降,若仅需提升下游任务性能,可接受一定程度的通用能力损失。
2. Batch Size:较大的Batch Size加速训练但可能收敛于次优解;较小的Batch Size有助于模型泛化但延长训练时间。需在训练速度与模型性能间找到平衡。
3. Global Batch Size:随着计算资源或分布式节点增加,可适当增大Batch Size并调整累积步数。
4. 权重衰减(Weight Decay):通过在损失函数中添加与权重大小成正比的惩罚项来防止过拟合,有助于保持较小权重值,增强模型泛化能力。
5. 梯度裁剪(Gradient Clipping):设置梯度阈值以防止梯度爆炸,确保参数更新稳定。
6. 学习率(Learning Rate):控制参数更新步长,过高导致震荡,过低减慢训练;动态调整学习率是优化训练过程的重要策略,常见的方法包括指数衰减、余弦退火以及自适应学习率调整。
7. Dropout率:正则化方法如dropout(训练时随机丢弃部分神经元)、L1/L2正则化等可以有效防止模型过拟合,提高泛化能力。
基于训练参数调整可实现的优化策略如:
1. 学习率衰减:随着训练进行,逐渐降低学习率,常用方法包括Step Decay、Exponential Decay、Cosine Annealing。
2. 早停法:在验证集上监控模型性能,连续几个epoch性能无提升时停止训练。
3. 数据增强:通过变换训练数据增加多样性,提高模型泛化能力。
4. 梯度裁剪:限制梯度大小,防止梯度爆炸。
5. 混合精度训练:使用FP16进行训练,减少内存消耗并提高训练速度。
6. 超参数优化:超参数的优化通常通过网格搜索、随机搜索或贝叶斯优化等方法实现。
通过以上方法,可以调整和优化大模型的训练过程,提高模型在特定任务上的性能,并确保模型在实际应用中的效果。
总结与展望
本文主要整理总结了LLM效果和性能优化一些技术和分析,在AI汽车行业性能优化相关实践中,一些典型场景需求和优化场景有:
自动驾驶模型训练场景:生产模型的训练周期可通过训练加速技术进行显著提升;
智能座舱场景:推理延迟从秒级压缩至百毫秒级,可支持多模态实时交互;
联网诊断场景:基于APC和并发优化等,提升并发同时降低硬件成本;
后续将基于 AI 汽车场验证的实战方案,进行更多实际的优化场景案例分享。
[1]J. Yang et al., “Harnessing the Power of LLMs in Practice: A Survey on ChatGPT and Beyond,” Apr. 27, 2023, arXiv: arXiv:2304.13712. doi: 10.48550/arXiv.2304.13712.
[2]A. Vaswani et al., “Attention Is All You Need,” Aug. 02, 2023, arXiv: arXiv:1706.03762. doi: 10.48550/arXiv.1706.03762.
[3]J. Alammar, “The Illustrated GPT-2 (Visualizing Transformer Language Models).” Accessed: Jun. 03, 2025. [Online]. Available: https://jalammar.github.io/illustrated-gpt2/
[4]A. Radford, K. Narasimhan, T. Salimans, and I. Sutskever, “Improving Language Understanding by Generative Pre-Training”.
[5]Y. Zhang et al., “Siren’s Song in the AI Ocean: A Survey on Hallucination in Large Language Models,” Sep. 24, 2023, arXiv: arXiv:2309.01219. doi: 10.48550/arXiv.2309.01219.
[6]G.-I. Yu, J. S. Jeong, G.-W. Kim, and B.-G. Chun, “Orca: A Distributed Serving System for Transformer-Based Generative Models”.
[7]S. Kim et al., “Full Stack Optimization of Transformer Inference: a Survey,” Feb. 27, 2023, arXiv: arXiv:2302.14017. doi: 10.48550/arXiv.2302.14017.
[8]M. Shoeybi, M. Patwary, R. Puri, P. LeGresley, J. Casper, and B. Catanzaro, “Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism,” Mar. 13, 2020, arXiv: arXiv:1909.08053. doi: 10.48550/arXiv.1909.08053.
[9]C. Chen, “Welford algorithm for updating variance,” Pain is inevitable. Suffering is optional. Accessed: Jun. 03, 2025. [Online]. Available: https://changyaochen.github.io/welford/
[10]Y. Zhai et al., “ByteTransformer: A High-Performance Transformer Boosted for Variable-Length Inputs,” Feb. 20, 2023, arXiv: arXiv:2210.03052. doi: 10.48550/arXiv.2210.03052.
[11]G. Xiao, J. Lin, M. Seznec, H. Wu, J. Demouth, and S. Han, “SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models,” Mar. 29, 2024, arXiv: arXiv:2211.10438. doi: 10.48550/arXiv.2211.10438.
[12]L. Ouyang et al., “Training language models to follow instructions with human feedback,” Mar. 04, 2022, arXiv: arXiv:2203.02155. doi: 10.48550/arXiv.2203.02155.
[13]E. J. Hu et al., “LoRA: Low-Rank Adaptation of Large Language Models,” Oct. 16, 2021, arXiv: arXiv:2106.09685. doi: 10.48550/arXiv.2106.09685.
[14]X. Liu et al., “P-Tuning v2: Prompt Tuning Can Be Comparable to Fine-tuning Universally Across Scales and Tasks,” Mar. 20, 2022, arXiv: arXiv:2110.07602. doi: 10.48550/arXiv.2110.07602.
[15]X. Liu et al., “P-Tuning: Prompt Tuning Can Be Comparable to Fine-tuning Across Scales and Tasks,” in Proceedings of the 60th Annual Meeting of the Association for Computational Linguistics (Volume 2: Short Papers), S. Muresan, P. Nakov, and A. Villavicencio, Eds., Dublin, Ireland: Association for Computational Linguistics, May 2022, pp. 61–68. doi: 10.18653/v1/2022.acl-short.8.
[16]X. L. Li and P. Liang, “Prefix-Tuning: Optimizing Continuous Prompts for Generation,” Jan. 01, 2021, arXiv: arXiv:2101.00190. doi: 10.48550/arXiv.2101.00190.
[17]B. Lester, R. Al-Rfou, and N. Constant, “The Power of Scale for Parameter-Efficient Prompt Tuning,” Sep. 02, 2021, arXiv: arXiv:2104.08691. doi: 10.48550/arXiv.2104.08691.
[18]S. Rajbhandari, J. Rasley, O. Ruwase, and Y. He, “ZeRO: Memory Optimizations Toward Training Trillion Parameter Models,” May 13, 2020, arXiv: arXiv:1910.02054. doi: 10.48550/arXiv.1910.02054.
[19]“How to train a Language Model with Megatron-LM.” Accessed: Jun. 03, 2025. [Online]. Available: https://huggingface.co/blog/megatron-training
[20]D. Narayanan et al., “Efficient Large-Scale Language Model Training on GPU Clusters Using Megatron-LM,” Aug. 23, 2021, arXiv: arXiv:2104.04473. doi: 10.48550/arXiv.2104.04473.
[21]V. Korthikanti et al., “Reducing Activation Recomputation in Large Transformer Models,” May 10, 2022, arXiv: arXiv:2205.05198. doi: 10.48550/arXiv.2205.05198.
[22]“Matrix Multiplication Background User’s Guide,” NVIDIA Docs. Accessed: Jun. 03, 2025. [Online]. Available: https://docs.nvidia.com/deeplearning/performance/dl-performance-matrix-multiplication/index.html
[23]“Linear/Fully-Connected Layers User’s Guide,” NVIDIA Docs. Accessed: Jun. 03, 2025. [Online]. Available: https://docs.nvidia.com/deeplearning/performance/dl-performance-fully-connected/index.html
[24]Y. Bulatov, “Fitting larger networks into memory.,” TensorFlow. Accessed: Apr. 23, 2025. [Online]. Available: https://medium.com/tensorflow/fitting-larger-networks-into-memory-583e3c758ff9
[25]P. Micikevicius et al., “Mixed Precision Training,” Feb. 15, 2018, arXiv: arXiv:1710.03740. doi: 10.48550/arXiv.1710.03740.
[26]“How to Optimize Data Transfers in CUDA C/C++,” NVIDIA Technical Blog. Accessed: Jun. 03, 2025. [Online]. Available: https://developer.nvidia.com/blog/how-optimize-data-transfers-cuda-cc/
dify,高效搭建 AI 应用" style="-webkit-tap-highlight-color: transparent;margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">
快速部署 Dify,高效搭建 AI 应用
Dify 让您无需编程即可快速构建 AI 应用。然而,本地私有化部署往往维护成本高且可靠性不足。本方案介绍如何在阿里云构建云原生高可用架构,实现 Dify 的快速部署,助力企业高效搭建 AI 应用。
点击阅读原文查看详情。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-09-30
Anthropic长文拆解上下文工程落地策略与实践
2025-09-30
OpenAI版抖音要来了!Sora 2加持,只能发AI生成视频
2025-09-30
Anthropic 深夜祭出 Claude Sonnet 4.5,能自主连续工作 30 小时!CEO:它更像你的同事
2025-09-30
AI编程学习:Chrome DevTools MCP 到底有多强?
2025-09-30
突发!Claude 4.5 发布,这次更新不止是模型!
2025-09-30
Claude Sonnet4.5发布,号称世界最强模型,超越gpt-5-codex
2025-09-30
Claude Sonnet 4.5 发布:全解析
2025-09-29
Nano Banana三大痛点的十种邪修解法,我和Lovart搭伙开了家上海Bistro餐厅
2025-08-21
2025-08-21
2025-08-19
2025-09-16
2025-07-29
2025-09-08
2025-08-19
2025-09-17
2025-08-20
2025-09-14
2025-09-30
2025-09-29
2025-09-28
2025-09-27
2025-09-27
2025-09-25
2025-09-23
2025-09-22