动手点关注
干货不迷路
本文旨在让无大模型开发背景的工程师或者技术爱好者无痛理解大语言模型应用开发的理论和主流工具,因此会先从与LLM应用开发相关的基础概念谈起,并不刻意追求极致的严谨和完备,而是从直觉和本质入手,结合笔者调研整理及消化理解,帮助大家能够更容易的理解LLM技术全貌,大家可以基于本文衍生展开,结合自己感兴趣的领域深入研究。若有不准确或者错误的地方也希望大家能够留言指正。
本文体系完整,内容丰富,由于内容比较多,分多次连载 。
第一部分 基础概念
1.机器学习场景类别
2.机器学习类型(LLM相关)
3.深度学习的兴起
4.基础模型
第二部分 应用挑战
1.问题定义与基本思路
2.基本流程与相关技术
1)Tokenization与Embbeding
2)向量数据库
3)finetune(微调)
4)模型部署与推理
5)prompt
6)编排与集成
7)预训练
第三部分 场景案例
常用参考
2.基本流程与相关技术
4)模型部署与推理
模型适配优化
在提升模型推理性能之前,有必要理解衡量模型大小与模型性能的关系,这对于我们进一步理解模型推理优化理论和方法大有帮助。在本节会先介绍通用的模型大小相关指标及对推理性能的影响,然后进一步的分析transformer大模型的具体细节。
模型大小指标
模型大小可以从多个维度来评价,包含参数量,计算量,访存量几个维度。
参数量
参数量,即模型参数,是模型中的参数的总和,参数多少是由模型结构决定的,分为可学习参数和不学习参数的两类。主要有权重(Weights)和偏置(Biases)。它包含了模型结构中各层参数之和,其中,有参数的层包含卷积、全连接、BatchNorm、Embedding,而一些激活层,池化层等没有参数。具体来说,有参数的层参数量计算逻辑(不考虑偏置bias):
- Linear(M->N):M×N
- Conv2d(Cin,Cout,K): Cin×Cout×k×k
- BatchNorm(N): 2N
- Embedding(N,W):N×W
模型 参数与硬盘 占用,运行 内存 占用都有直接关系。 模型参数 占整个 模型工程 文件的绝大部分(还有配置文件,元数据信息等脚本和信息), 而其占用的空间是参数数 量 及参数精度共同决定 :
模型空间 大小 = 参数量 * 参数精度
以pyTorch训练的参数精度为float32(fp32 ,全精度) 的模型为例, 即一个参数占用4个字节 ,故 一个模型1 000万, 那么 其 大小 大概为: 1000万 *32 / 8 / 1024 / 1024 = 约40Mb。
而对于模型运行期内存/显存占用,除参数占用外(如果对于混合精度训练来讲,一般假设为6个字节,内存中同时维护f32和fp16的模型参数),还需要考虑其它因素,包含如模型训练时需要包含优化器占用,激活层信息,梯度信息,以及批次(batch-size)样本占用等,而推理时显存占用(未经优化)就是模型显存(参数)与每一批次的样本大小,以及额外开销(<=20%)之和。
最后,以chatGLM2-6B为例,它有62亿参数,权重参数文件采用半精度存储(fp16),理论计算11.5GB,实际占用12.5GB空间(包含网络结构及部分layer norm等数据会保留源格式fp32), 因此,想要加载运行该模型至少需要有15 GB左右的显存 。
计算量
计算量是指模型进行一次完整的前向传播所发生的浮点运算个数,也即模型的时间复杂度,反映了模型对硬件计算单元的需求。计算量一般用 OPs (Operations) ,即计算次数来表示。由于最常用的数据格式为 float32,因此也常常被写作 FLOPs (Floating Point Operations),即浮点计算次数。特别注意,FLOPS(Floating Point Operations Per Second),每秒浮点运算次数,是一个衡量硬件计算性能的指标,FLOPS与FlOPs不是一个概念,FLOPS反映的计算速度,而FLOPs是计算的总量。
MFLOPS(megaFLOPS)等于每秒一百万(=10^6)次的浮点运算
GFLOPS(gigaFLOPS)等于每秒十亿(=10^9)次的浮点运算
TFLOPS(teraFLOPS)等于每秒一万亿(=10^12)次的浮点运算
PFLOPS(petaFLOPS)等于每秒一千万亿(=10^15)次的浮点运算
EFLOPS(exaFLOPS)等于每秒一百亿亿(=10^18)次的浮点运算
如,NVIDIA A100 GPU 频率为 1.41GHZ,CUDA Cores(FP32)数量为 6912,那么 峰值FLOPS = 1.41G69122(乘加视作两次浮点运算) = 19.49TFLOPS
除此之外,MACs(Multiply–Accumulate Operations)乘加累积操作数,也 可以用来,1 MAC = 1次加法+1次乘法操作。
参数量和计算量并不是完全正相关,对于卷积之类的运算,它的参数量比较小,但是运算量非常大。而全连接层,它的参数量非常多,但运算量并没有显得那么大。还有很多结构没有参数但存在计算,比如max-pooling和 Dropout 等 。
访存量
访存量,也叫内存操作数,一般用MOPs(memory operations)表示,指的是输入单个样本,模型完成一次前向传播过程中所发生的内存交换总量,也即模型的空间复杂度,反映了模型对存储单元带宽的需求。单位是Byte(或者 KB/MB/GB)。
模型大小对推理性能的影响
参数量和内存占用是一般对硬盘和内存有直接的关系,影响模型加载速度,但对于模型推理性能来讲,影响比较有限。
计算量和访存量虽然会直接影响推理速度,但需要综合考虑模型本身结构及计算平台性质。
计算密度就是评价模型计算速度的一个指标,也叫计算强度(Arithmetic Intensity),计算访存比,是指程序执行的计算量 与支持这些 计算量计算 所需的访存量的比例,反映一个程序相对于访存来说计算的密集程度,计算密度越大,其内存使用效率越高,反之,越小,受限越受带宽影响,单位是 FLOPs/Byte。
这里介绍一个用于评价性能边界的模型RoofLine,它描述了一个模型/程序在一个计算平台的限制下,到底能达到多快的浮点计算速度 ,如下图:
计算速度(FLOPs/S) = min(计算速度 × 带宽,峰值计算速度)
从图上可以看出,当计算密度在临界点内,计算性能是会受到内存带宽的限制。对于 临界点以内的程序称之为访存密集型(Memory-Bound或Memory-bound)程序,大于临界值的称之为计算密集型(Compute-Bound)程序。
而对于模型来讲,也是一个程序,其模型结构中不同算子由于其计算密度不同,会分为 计算密集型算子 和 访存密集型算子。 常见的访存密集型算子有:Concat、Eltwise Add、ReLU、MaxPooling等, 计算密集型算子: Conv、DeConv、FC、MatMul、LSTM等。 但是算子分类并不绝对,它会受到硬件性质和参数的不同而不同。
比如,计算密度为1.5的程序,由于硬件设备不同,它可能会落到不同区间内。
再比如,上图,算子参数不同,同样也会有类似情况。
而最终的推理计算时间便是:
对于访存密集型算子,推理时间跟访存量呈线性关系,而对于计算密集型算子,推理时间跟计算量呈线性关系。按照 RoofLine 模型,在计算密集区,计算量越小,确实推理时间越小。但是在访存密集区,计算量与推理时间没关系,真正起作用的是访存量,访存量越小,推理的时间才越快。在全局上,计算量和推理时间并非具有线性关系。
对于以transformer为模型结构的大模型,其构成的访存密集型算子占比在在算子数量和执行时间上都超过计算密集型算子,性能瓶颈更多地表现在访存密集型算子上,因此,不能简单只是看计算平台的计算能力,比如GPU的tensor core数量,也更要关注显存带宽大小,往往这一指标更为重要,这与我们前一小节对GPU各个指标对深度学习效率影响的结论也是一致的。
一些计算工具
对于参数量和Flops计算,有很多的工具支持。常见的有pytorch_model_summary,thop,ptflops等。
英伟达为开发者提供了 nvprof和nsight Compute等工具,通过它可以分析cuda程序的性能,可以计算运行在英伟达GPU上的计算量,访存量,计算强度等数据,可对模型进行roofline model分析,并提供一些优化建议。
分析案例可参考:
https://developer.nvid ia.com/blog/accelerating-hpc-applications-with-nsight-compute-roofline-analysis/
https://arxiv.org/pdf/2009.02449.pdf
https://gitlab.com/NERSC/roofline-on-nvidia-gpus
未完待续,在下一节里,将具体到大模型transformer模型结构相关参数计算及性能瓶颈分析的内容,欢迎关注。
合集目录:
3)微调
4)模型部署与推理
一文探秘LLM应用开发(8)-模型部署与推理(DEMO验证)
一文探秘LLM应用开发(9)-模型部署与推理(模型适配优化之GPU面面观-1)
一文探秘LLM应用开发(10)-模型部署与推理(模型适配优化之GPU面面观-2)