返回

大模型基础

重生之我要成为大模型高手

大模型基础

大模型基础知识

只是一些杂碎的知识罢了

基础

  1. 现在的语言模型大多数都是自回归的语言模型,通过链式法则表示:$$p(x_{1:L}) = p(x_1) p(x_2 \mid x_1) p(x_3 \mid x_1, x_2) \cdots p(x_L \mid x_{1:L-1}) = \prod_{i=1}^L p(x_i \mid x_{1:i-1}).$$
  2. 温度参数用于控制生成的随机性,越高随机性越高: $$\begin{aligned}\text { for } i & =1, \ldots, L: \x_i & \sim p\left(x_i \mid x_{1: i-1}\right)^{1 / T},\end{aligned}$$。从公式中可以看出来,温度越高,不同token的概率会更加接近
  3. 信息熵和交叉熵:$$H(p, q)=-\sum_x p(x) \log q(x)$$

分词和词向量

  1. 难点主要集中在分词标准,切分歧义和未登录词三部分
  2. 尤其是对于中文,上述问题更加严重。可以使用jieba工具实现中文分词。当然,现在一般不需要在这个地方进行改进
  3. 分词的方法现在主要有字典匹配、机器学习方法。字典匹配主要是一个贪心或者是图的过程。而机器学习方法现在用的比较多,根据在句子中的位置进行标记,包括 HMM,隐马尔科夫模型/CRF,条件随机场/深度学习。其中jieba对于未登记词的统计就是通过HMM实现的。
  4. 关于分词的详细之后再说吧
  5. 现在用的是embedding。以前用的是one-hot或者是word2vec,根据统计学原理,有CBOW和skip-gram两种算法,前者是上下文预测中心词,后者反过来。基于词之间相近性来构建相似度关系。不如embedding训练。

另一些基础

  1. 三大特征提取器:CNN、RNN、TF
  2. RNN循环从前往后获取信息,但是优化太难了而且不能并行。后面出现了LSTM和GRU,但是性能还是太弱了。不过现在使用TF依然有借鉴RNN的地方
  3. CNN和图片中的原理一致,利用相关关系来卷积提取特征,可以并行并保留相对位置但是问题是定长
  4. Transformer也定长,而且并行计算有一定压力,但是无奈性能强。

一些关于架构的面试题(或者说NLP面试题)

  1. BERT架构。google的好东西,用的双向Transformer架构实现。有很多科技:(1)双向Transformer(2)Masked LM 有一些单词被mask,之后可能被mask或者random或者不变。从而实现对应任务/放置过拟合/使得训练方向恢复一点。(3)NSP任务,做了下一个句子预测任务,取两个句子来判断是不是下一句。实际上BERT做分类任务都可以。
  2. 为什么LLM是Decoder架构。对于语言模型其实可以分为Decoder/Encoder only和两者混合的模型。不同的任务不同。首先,Encoder是Masked LM对生成任务没啥用,泛化性不太行。此外,双向Encoder会有低秩问题会削弱模型表达能力。而Encoder-Decoder由于参数量多一点,性能好一点。此外就是可以复用KV-Cache,而encoder-decoder的PrefixLM做不到。
  3. google对比了5B情况下各种架构的性能。decoder-only zero-shot性能最好,encoder-decoder需要做一定的finetune。

激活函数

  1. 可太多了,主要有三类变体:ReLU、sigmoid、新东西。激活函数的设计核心在于:
    1. 在输入 x 满足某些条件时,为恒等映射;
    2. 在输入 x 满足另外一些条件时,为置零映射;
    3. 在输入 x 是一个较大的正值时,更希望为恒等映射;在输入 x 为一个较小的负值时,更希望是一个置零映射;
  2. ReLU好理解的,变体有Leaky ReLU和PReLU(加了参数),ELU,GELU(虽然满足了所有条件但是计算比较复杂因此需要近似计算),Swish
  3. sigmoid、tanh两者可以转换
  4. SwiGLU利用门控线性单元函数做激活函数,向量做了门控之后和sigmoid做向量点乘。

llm概念

  1. 现在的模型主要分为GPT、Bert、XLNet、RoBERTa(更多的训练,更强的稳定性)、T5

  2. PrefixLM和Casual LM的区别在于,后者的Encoder-Decoder架构中是共享Transfomerm架构但是通过mask实现的,而前者则是对立的参数

image-20241212150246613
image-20241212150246613

大语言模型架构

现代的还是Attention居多

Attention

$$Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt{d_k}})V$$

  1. self-attention是自身序列位置之间的关系建模。target-attention则是和一组相关序列的关系建模。
  2. 常规attention中的k=v,self-attention的实现同样可以选择是否相同
  3. 主流的attention主要有
    1. Scaled Dot-Product Attention 最常用的Attention机制
    2. Multi-Head Attention: 这是Transformer中的一个改进,通过同时使用多组独立的注意力头(多个QKV三元组),并在输出时将它们拼接在一起,但是在设计上没啥区别
  4. self-attention计算中的padding mask,对于mask需要设置值为**-inf**使得softmax之后输出为0

Transformer

  1. Transformer中使用的是多头注意力机制,并行的学习多个不同的特征(实际上没有多并行),但是还是得降维放置计算量过大。
  2. Transformer通过使用点积并缩放放置梯度爆炸。由于完成qk计算之后是进行一个softmax的$e^{qk}$操作,如果不做缩放的话会到一个很大的范围,且由于在正区间的指数提升,很容易成为one-hot,导致梯度消失。解决方法就是缩放使得方差变为1。
  3. BERT使用的是gelu和self-attention
  4. BERT如何处理长文本:1、截断或者填充 2、sliding windows,通过将长文本分为多个短文本最后汇合 3、利用分层模型处理 4、使用针对长文本的模型。

MHA & MQA & MGA

  1. MHA $$MultiHead(Q,K,V)=Concat(head_1,…,head_h)W^O \ where ~ head_i = Attention(QW_i^Q, KW_i^K, VW_i^V) $$

  2. MQA 所有头共享一份key和value而单独使用不同的Query

  3. GQA 折中方案,分组采用query分为N组,每个组共享一个Key和Value

image-20241212161717111
image-20241212161717111

Flash Attention

原理很简单,尽可能使用SRAM来存储,减少通讯从而大大减少Attention计算时间,其中涉及到了很多的kernel fuse操作。

Flash Attention的计算有点基于稳定的Softmax,通过拆分计算方程去除依赖来实现并行化(对应的会提升计算代价)

image-20241212162046712
image-20241212162046712

从简单来看的话原理也简单,通过对矩阵分块计算,每个块都可以完全加载到SRAM上,并通过for循环实现整个注意力的计算。

其他细节

Transformer计算attention的时候为何选择点乘而不是加法?两者计算复杂度和效果上有什么区别?

  • K和Q的点乘是为了得到一个attention score 矩阵,用来对V进行提纯。K和Q使用了不同的W_k, W_Q来计算,可以理解为是在不同空间上的投影。正因为有了这种不同空间的投影,增加了表达能力,这样计算得到的attention score矩阵的泛化能力更高。
  • 为了计算更快。矩阵加法在加法这一块的计算量确实简单,但是作为一个整体计算attention的时候相当于一个隐层,整体计算量和点积相似。在效果上来说,从实验分析,两者的效果和dk相关,dk越大,加法的效果越显著。

为什么在进行softmax之前需要对attention进行scaled(为什么除以dk的平方根),并使用公式推导进行讲解

  • 这取决于softmax函数的特性,如果softmax内计算的数数量级太大,会输出近似one-hot编码的形式,导致梯度消失的问题,所以需要scale
  • 那么至于为什么需要用维度开根号,假设向量q,k满足各分量独立同分布,均值为0,方差为1,那么qk点积均值为0,方差为dk,从统计学计算,若果让qk点积的方差控制在1,需要将其除以dk的平方根,是的softmax更加平滑

为什么在进行多头注意力的时候需要对每个head进行降维?(可以参考上面一个问题)

  • 将原有的高维空间转化为多个低维空间并再最后进行拼接,形成同样维度的输出,借此丰富特性信息
    • 基本结构:Embedding + Position Embedding,Self-Attention,Add + LN,FN,Add + LN

为何在获取输入词向量之后需要对矩阵乘以embedding size的开方?意义是什么?

  • embedding matrix的初始化方式是xavier init,这种方式的方差是1/embedding size,因此乘以embedding size的开方使得embedding matrix的方差是1,在这个scale下可能更有利于embedding matrix的收敛。

简单介绍一下Transformer的位置编码?有什么意义和优缺点?

  • 因为self-attention是位置无关的,无论句子的顺序是什么样的,通过self-attention计算的token的hidden embedding都是一样的,这显然不符合人类的思维。因此要有一个办法能够在模型中表达出一个token的位置信息,transformer使用了固定的positional encoding来表示token在句子中的绝对位置信息。

你还了解哪些关于位置编码的技术,各自的优缺点是什么?(参考上一题)

  • 相对位置编码(RPE)1.在计算attention score和weighted value时各加入一个可训练的表示相对位置的参数。2.在生成多头注意力时,把对key来说将绝对位置转换为相对query的位置3.复数域函数,已知一个词在某个位置的词向量表示,可以计算出它在任何位置的词向量表示。前两个方法是词向量+位置编码,属于亡羊补牢,复数域是生成词向量的时候即生成对应的位置信息。

为什么transformer块使用LayerNorm而不是BatchNorm?LayerNorm 在Transformer的位置是哪里?

  • LN:针对每个样本序列进行Norm,没有样本间的依赖。对一个序列的不同特征维度进行Norm
  • CV使用BN是认为channel维度的信息对cv方面有重要意义,如果对channel维度也归一化会造成不同通道信息一定的损失。而同理nlp领域认为句子长度不一致,并且各个batch的信息没什么关系,因此只考虑句子内信息的归一化,也就是LN。

简答讲一下BatchNorm技术,以及它的优缺点。

  • 优点:
    • 第一个就是可以解决内部协变量偏移,简单来说训练过程中,各层分布不同,增大了学习难度,BN缓解了这个问题。当然后来也有论文证明BN有作用和这个没关系,而是可以使损失平面更加的平滑,从而加快的收敛速度。
    • 第二个优点就是缓解了梯度饱和问题(如果使用sigmoid激活函数的话),加快收敛。
  • 缺点:
    • 第一个,batch_size较小的时候,效果差。这一点很容易理解。BN的过程,使用 整个batch中样本的均值和方差来模拟全部数据的均值和方差,在batch_size 较小的时候,效果肯定不好。
    • 第二个缺点就是 BN 在RNN中效果比较差。

简单描述一下Transformer中的前馈神经网络?使用了什么激活函数?相关优缺点?

  • ReLU

$$ FFN(x)=max(0,~ xW_1+b_1)W_2+b_2 $$

Encoder端和Decoder端是如何进行交互的?(在这里可以问一下关于seq2seq的attention知识)

  • Cross Self-Attention,Decoder提供Q,Encoder提供K,V

Decoder阶段的多头自注意力和encoder的多头自注意力有什么区别?(为什么需要decoder自注意力需要进行 sequence mask)

  • 让输入序列只看到过去的信息,不能让他看到未来的信息

Transformer的并行化提现在哪个地方?Decoder端可以做并行化吗?

  • Encoder侧:模块之间是串行的,一个模块计算的结果做为下一个模块的输入,互相之前有依赖关系。从每个模块的角度来说,注意力层和前馈神经层这两个子模块单独来看都是可以并行的,不同单词之间是没有依赖关系的。
  • Decode引入sequence mask就是为了并行化训练,Decoder推理过程没有并行,只能一个一个的解码,很类似于RNN,这个时刻的输入依赖于上一个时刻的输出。

简单描述一下wordpiece model 和 byte pair encoding,有实际应用过吗?

  • 传统词表示方法无法很好的处理未知或罕见的词汇(OOV问题),传统词tokenization方法不利于模型学习词缀之间的关系”
  • BPE(字节对编码)或二元编码是一种简单的数据压缩形式,其中最常见的一对连续字节数据被替换为该数据中不存在的字节。后期使用时需要一个替换表来重建原始数据。
  • 优点:可以有效地平衡词汇表大小和步数(编码句子所需的token次数)。
  • 缺点:基于贪婪和确定的符号替换,不能提供带概率的多个分片结果。

Transformer训练的时候学习率是如何设定的?Dropout是如何设定的,位置在哪里?Dropout 在测试的需要有什么需要注意的吗?

  • 一般用的Adamw,并设置lr_schedularDropout测试的时候记得对输入整体呈上dropout的比率

MoE

已经炉火纯青了,主要用于在提高性能利用。一个问题在于如何防止过拟合(采用Top-k或者加一些噪声)

MoE 通常是加在FFN层的,通过实现负载均衡来在较低显存占用的同时取得更好的效果

$$Importance (X)=\sum_{x \in X} G(x)$$

$$L_{\text {importance }}(X)=w_{\text {importance }} \cdot C V(\text { Importance }(X))^{2}$$

对门控电路的结果平方作为重要性,

但是在这种情况下的MoE的门控电路是不可微的,一种合理的方式是添加一个softmax之后近似,此时计算如下:

$$l_{aux} = \frac{1}{E}\sum_{e=1}^E(\frac{c_e}{S})*m_e$$

由于deepseek比较火,因此也需要着重强调一下deepseek采用的MoE架构。与其他的MoE不同的是,DeepSeek有一个共享的专家,且采用了更加细粒度的路由机制:

image-20250216151502274
image-20250216151502274

看图倒是很好理解,由$K_s$个共享专家、$mN-K_s$个专家和一个残差组成。对应的loss也有变动:

image-20250216151716772
image-20250216151716772

关于这个的计算核心,其实在前面要除的系数$K^`$,这个表示去除共享专家之后的专家数量,通过这个计算可以使得最终的负载和专家数量无关,从而算是减少了超参数的数量,只需要调整$\alpha_1$就好了。

此外,在实际推理或者训练的过程中,相较于模型的负载均衡,另一个需要考虑的问题是设备的负载,这个倒是比较好解决,论文采用了一个损失函数来解决:

image-20250216152001664
image-20250216152001664

上述的改进只是V1的创新,实际上在此之后的V2,DeepSeek也做了创新:

  • 首先筛选M个设备,之后从M个设备选择TopK个专家。
  • 对通讯负载设计了损失函数
  • 对于超出设备容量的Token,只采用残差的结果,当然在训练的时候也使用了10%的样本不做token丢弃

V3也做了改进,将softmax换成了sigmoid,可能是由于V3的模型尺度增大(专家数量增多),使用softmax的归一会导致TopK的数量不足,更倾向于一个平均值,而sigmoid则是在[0,1]范围内,避免了问题。

最后展示一下大图

img
img

Normalization

常见的归一化方案有LN, BN。

BN用来处理一批数据间的均值和方差

BN的作用:

  1. 允许较大的学习率;
  2. 减弱对初始化的强依赖性
  3. 保持隐藏层中数值的均值、方差不变,让数值更稳定,为后面网络提供坚实的基础;
  4. 有轻微的正则化作用(相当于给隐藏层加入噪声,类似Dropout)

BN存在的问题:

  1. 每次是在一个batch上计算均值、方差,如果batch size太小,则计算的均值、方差不足以代表整个数据分布。
  2. batch size太大: 会超过内存容量;需要跑更多的epoch,导致总训练时间变长;会直接固定梯度下降的方向,导致很难更新。

LN对于单个样本的特征进行归一化操作,限制很少,但是性能在CNN上不如BN。

还有一个Norm叫做RMS,仅仅就是减去了均值的部分

位置编码

Attention直接一个QKV就把位置信息搞没了,所以一般的做法两种:

  • 输入的时候搞一下位置信息(绝对位置编码)
  • 在Attention架构中加一下使得分辨不同位置的token

绝对位置编码:

  • 训练式:直接将位置编码作为训练参数,但是问题在于外推性和固定的长度
  • 三角式(Attention is all you need 中的应用):$$\left{\begin{array}{l}\boldsymbol{p}{k, 2 i}=\sin \left(k / 10000^{2 i / d}\right) \ \boldsymbol{p}{k, 2 i+1}=\cos \left(k / 10000^{2 i / d}\right)\end{array}\right.$$

相对位置编码(在Attention的时候计算相对的位置距离,考虑到的是NLP对相对位置更关注,具有外推性,对长文本处理能力更强):

  • 经典式:相较于根据坐标实现注意力,其实现如下

image-20241212165811851
image-20241212165811851

把第一项中的位置编码去除,把第二项的位置编码换做二元位置向量。同理对于output也做相同的各操作。因而从坐标的位置关系变为一维的相对位置关系(之后的XLNET、T5、DeBERTa做了类似但改进)

特殊位置编码

  • CNN式(乐)
  • 复数式(少见)
  • RoPE旋转位置编码(Llama在用)

RoPE

RoPE是苏神提出的相对位置编码。

二维下的计算:

image-20241212170803164
image-20241212170803164

对于m,n的元素进行旋转。对于多维的,计算如下:

image-20241212170855458
image-20241212170855458

但是上述太稀疏了,所以可以直接展开:

image-20241212170929990
image-20241212170929990

而且有一定的远程衰减性

token数量和模型参数量的关系

https://github.com/wdndev/llm_interview_note/tree/main/02.%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B%E6%9E%B6%E6%9E%84/5.token%E5%8F%8A%E6%A8%A1%E5%9E%8B%E5%8F%82%E6%95%B0

Llama

Llama2用了如下技术:

  • 绝对位置编码提供输入的位置信息
  • 使用RMSNorm而不是LayerNorm
  • QK使用RoPE相对位置编码
  • KV cache使用能够GQA
  • FFN采用的SiLU激活函数

Llama3相较于Llama2使用了SwiGLU,提高了数据的质量

并行化

image-20241212172833160
image-20241212172833160

之前看过一篇系统并行化Transformer的论文,与这个问题类似,关键在于下一个操作对于上一个操作的维度依赖。比如token embedding是逐token操作,因此可以并行。才外上图的attention架构不能继续并行,对于MHA,还可以进一步实现并行化。最后的FFN是对每个token进行的,因此同样是可以实现同步进行的

DeepSeekv3

毕竟算是爆火的开源模型,应该是后续的一个重点。

创新点:

  • DeepSeekMoE:通过引入偏置项动态调整专家负载,避免了传统辅助损失带来的性能损失。
  • 多Token预测(MTP):在每个位置预测多个未来的 token,增加训练信号,提高模型的数据效率。
  • FP8训练
  • 基于DualPipe算法训练
  • 还有一些结构上的创新:包括MLA,MoE等等

由于后续大概率不太可能参与模型训练的工作,因此还是得好好理解一下DeepSeek模型结构所带来的优势。

img
img

现在一般来说更倾向于搞稀疏MoE来实现更好的效果,但是问题在于稀疏MoE还需要用一些辅助损失来保证负载均衡/训练稳定。DeepSeek的MoE并没有采用辅助损失函数,而是利用偏置项实现。 在训练过程中,如果一个专家被过度使用,则减小其偏置项;如果一个专家被闲置,则增大其偏置项。这样,可以动态调整专家负载,使其达到均衡。

接下来是MLA(Multi-Head Latent Attention ),通常来说现在的Attention都是会用到KV-cache的,但是长文本+大batch直接给你内存整炸也不太好,因此一个可行的方案就是论文的MLA:

image-20250108155218031
image-20250108155218031

对于KV而言,采用一个降维矩阵得到降维后的特征C,之后再进行K V计算从而减少了显存占用。

还有一些可以提到的技术: Multi-Token Prediction (MTP)。一般来说训练可能倾向于进行下一个token的预测,论文这里引入多个预测模块来实现未来的输出,并计算损失,带来的好处就是:

  • 增加训练信号: 通过预测多个 token,模型可以获得更密集的训练信号,提高数据效率。
  • 增强预规划能力: MTP 可以帮助模型更好地预先规划未来 token 的表示,使其更好地捕捉长距离依赖关系,从而提高模型的性能

资源问题

  • 数据并行:每个设备放一个模型
  • 模型并行:模型拆分到各个设备上之后数据流动
    • 张量并行:一个操作内并行计算
    • 流水向并行:不同层之间并行计算

优化器并行,优化器(尤其是Adam),要占用2倍的模型参数量。

MoE并行

显存占用

一般模型参数半精度就是16bit,乘以2就是模型参数占用,但是训练时候还需要优化器,所以实际上需要6倍于模型参数的显存占用。

微调

image-20241212191849308
image-20241212191849308

整体来说内容很多很多,比如基于模型参数的微调(A),选取部分模型参数更新(S)、重参数化(R)

PaLM: Scaling Language Modeling with Pathways

虽然论文只是搞出来了一个PaLM大模型,但是由于其提供了非常详细的profile信息,因此对预训练LLM有着极大的参考价值。

最大的PaLM 540B是在6000多个TPU上训练完成的,token数量高达780B。论文核心主要有以下几个部分:

  • Efficient scaling,Continued improvements from scaling, Breakthrough capabilities, Discontinuous improvements, Multilingual understanding,Bias and toxicity

模型结构

模型结构倒不是很复杂,主要有如下技术:

  • SwiGLU激活函数,并行化LayerNorm, MQA,ROPE,输入输出共享embedding,没有Bias参数,使用Sentence Piece词汇表

image-20250107202402631
image-20250107202402631

image-20250107202451701
image-20250107202451701

论文有如下论断:

硬件 FLOPs 利用率有几个问题。 首先,执行的硬件 FLOPs 数量取决于系统和实现,编译器中的设计选择会导致不同的操作数。 非物质化(Rematerialization)是一种广泛使用的内存使用与计算权衡技术。 为了使用梯度下降法高效计算大多数神经网络架构的后向传递,必须在内存中存储批次的许多中间激活。 如果无法全部存储,则可以重新计算某些前向传递操作(使某些激活状态重新实体化,而不是存储)。 这就需要权衡利弊,使用额外的硬件 FLOPs 可以节省内存,但训练系统的最终目标是实现每秒令牌的高吞吐量(从而缩短训练时间),而不是使用尽可能多的硬件 FLOPs。 其次,测量观测到的硬件 FLOPs 取决于计算或跟踪它们的方法。 观测到的硬件 FLOPs 是根据分析核算(Narayanan 等人,2021b)以及使用硬件性能计数器(Xu 等人,2021)报告的。

初始化

  • 权重初始化基于 $W \backsim N(0, 1/\sqrt{n_d})$, embedding初始化基于 $W\backsim N(0, 1)$,此外还有layerNorm初始化为$W\backsim N(0, \sqrt{n}$。代码实现参考:
def _init_weights(self, module):
    if isinstance(module, nn.Linear):
        torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
        if module.bias is not None:
            torch.nn.init.zeros_(module.bias)
    elif isinstance(module, nn.Embedding):
        torch.nn.init.normal_(module.weight, mean=0.0, std=0.02)
            
# in model
self.apply(self._init_weights)
  • 优化器采用Adam(这里涉及到了权重缩放和优化器学习率缩放的问题,Adam会根据权重矩阵的均方根实现学习率的缩放,由于初始化的操作,使得不同module(embdding和linear)实现不同的缩放)。学习率的超参数采用的是:
参数 学习率(前10000step) lr decay $\beta1$ $\beta2$ global grad norm clip weight decay
数值 $10^{-2}$ $1/\sqrt{k}$(k为步数) $0.9$ $1-k^{-0.8}$ 均值$1$ $lr^{2.0}$
  • 损失函数。损失函数采用的语言模型损失(交叉熵而且没有采用label smooth),并加入了一些辅助损失保证训练稳定性:
        if targets is not None: # target.shape = (B, S)
            logits = self.output(h) # targe.shape = (B, S, vocab_size)
            a = logits.view(-1, self.size(-1)) # a.shape = (B * S, vocab_size)
            b = targets.view(-1) # shape = (B * S)
            loss = F.cross_entropy(a, b)
            if loss_mask is not None:
                lm = loss_mask.view(-1)
                loss = torch.sum(loss * lm) / lm.sum()
            self.last_loss = loss
        else:
            # inference-time mini-optimization: only forward the lm_head on the very last position
            if self.config.is_causal:
                logits = self.output(h[:, [-1], :]) 
            else:
                logits = self.output(h)
            self.last_loss = None
            
        return logits, self.last_loss

注意,这里没有提到辅助损失函数,PaLM中使用的如下:$L = CE + z_{loss}$其中$Z_{loss}=10^{-4}\cdot log^2Z$简而言之就是让softmax的值更加接近于0。

  • Seq Length: 2048 (可恶,好大)
  • Batch Size: 前5000步512,11500步1025,25500步2048
  • 位重现:保证随机种子+保证位重现从而能够完美复现
  • dropout为0
  • 奇怪的峰值状态,训练初期会出现忽高忽低的尖峰情况,但是进行了消融实验确定了并不是由于坏数据导致的,可能只是由于参数和数据恰好结合的结果

评估结果(不看了)

微调

其实也算是评估里面的内容,论文在SuperGLUE对PaLM进行微调,其中使用Adam $5\times 10^{-5}$学习率32Batch,在15000step就收敛了。

接下来是另一些重点,关于如何对token、参数等找到合理的参数和对应关系

模型记忆

模型的记忆能力

image-20250107210329781
image-20250107210329781

参数量和记忆能力是息息相关的,而且其中Code数据集的重复度相对来说较多,至于重复出现的频率同样也是参数量越大频率越高。简而言之:模型会对常见的模板产生完全匹配的连续性从而产生“记忆”。根据LM的原理来看,还是和数据相关的

上述问题顺带着会引出一些安全问题,当然整体来说不是本文的探讨内容,还是越多越重复的数据就会导致记忆性。

数据集污染(不看)

表现误差分析(不看)

关于Scaling的讨论

如何提高模型的性能,实际上只有四部分:

  • 深度和宽度、token数量、token质量、不增加计算量的前提下增加模型容量(稀疏模型)

但是显然的,由于LLM训练的令人发指的计算量,对上面的部分进行详细的profile和性能比较对于一个企业而言是不可能的(还是得看各式各样的论文。)

上述是正文的内容,之后还有一些比较重要的部分。

计算占用

对于参数量为$N$的纯decoder架构,其计算量为$6N$,其中$2N$用于前向传播,$4N$用于反向传播。每个矩阵乘法需要依次乘法和一次加法。而密集注意力中的矩阵乘法为$6LH(2QT)$,其中 L、H、Q 和 T 分别是层数、头数、头维度和序列长度。加入知道吞吐P,那么峰值比例

$$R=\frac{P}{(6N+12LHQT)}$$

另外要讨论的是PerToken的计算量,对于不同的尺寸的参数有不同的结果:

image-20250107212006725
image-20250107212006725

这里详细计算还得确定一下。

重复计算的token

  • 为什么要考虑在重复的数据集上做多次训练?Token危机。多个epoch训练实际上是会降低模型性能的!然而由于目前互联网上的token质量和token数量有限,如何实现较好的效果也是一个问题。
  • 预训练数据集重复的影响是什么?过拟合
  • 1、模型参数规模与tokens数量需要匹配 前面讨论了
  • 2、多轮epoch的训练会降低模型性能 相较于大token少epoch训练,少token大epoch会导致过拟合
  • 3、更大规模的数据集会缓解重复epochs对模型性能下降的影响
  • 4、提高数据集的质量也无法挽救重复训练带来的过拟合
  • 5、参数数量和FLOPs在重复训练上的影响
  • 6、小计算量模型的过拟合趋势与大计算量的差不多
  • 7、多样的训练目标可以减轻多Epoch下降吗 MLM相对来说受影响会小一点
  • 8、Dropout是一个被大语言模型忽视的正则技术,虽然慢,但是可以降低多epochs的影响
  • 9、在训练过程中逐渐使用dropout是有效的策略
  • 10、dropout对不同规模模型的影响不同 对大模型效果会更差一点
  • 11、通过MoE扫描确定稠密模型的最佳超参数 由于MoE模型和稠密模型的训练趋势接近,因此可以先利用MoE的训练性能来进行提前的预估
  • 多epochs训练对大语言模型性能影响的总结

SFT token

理论上只要较少的高质量token就能实现很好的效果。

当扩大数据量而不同时扩大提示多样性时,收益会大大减少,而在优化数据质量时,收益会大大增加。

特定任务的模型可能从固定的任务类型中获益,以获得更高的性能;指令格式的多样性可能对特定任务模型的性能影响很小;即使是少量的数据(1.9M tokens)也能为特定任务模型的指令调整带来可喜的结果。

不过具体可能倾向于任务,对于第一个结论,论文实际上只是对齐任务,因此。

对于第二个结论,可能需要一些手段提高token的质量,例如论文就是通过一些语言模型对文本进行embedding之后聚类采样才能在较小的数据中取得更好的效果。

Calculation of LLM

首先一个概念是:在给定计算量预算下,模型参数量以及训练Token数应该同比提升

Parameters FLOPs FLOPs (in Gopher unit) Tokens
400 Million 1.92e+19 1//29,968 8.0 Billion
1 Billion 1.21e+20 1//4,761 20.2 Billion
10 Billion 1.23e+22 1//46 205.1 Billion
67 Billion 5.76e+23 1 1.5 Trillion
175 Billion 3.85e+24 6.7 3.7 Trillion
280 Billion 9.90e+24 17.2 5.9 Trillion
520 Billion 3.43e+25 59.5 11.0 Trillion
1 Trillion 1.27e+26 221.3 21.2 Trillion
10 Trillion 1.30e+28 22515.9 216.2 Trillion

不过考虑到自己的计算能力,一般不会选择太大的参数量。对于58M的NanoGPT,我们采用的token数量

预训练数据Token重复的影响:

  • 多轮epoch的训练会降低模型性能;
  • 更大规模的数据集会缓解重复epochs对模型性能下降的影响;
  • 提高数据集的质量也无法挽救重复训练带来的过拟合;
  • 小计算量模型的过拟合趋势与大计算量的差不多;
  • 多样的训练目标不一定减轻多Epoch的性能下降;
  • Dropout是一个被大语言模型忽视的正则技术,虽然慢,但是可以降低多epochs的影响;
  • 在训练过程中逐渐使用dropout是有效的策略;

关于缩放定律,根据openai的论文,训练的损失和token数量,参数量具有如下函数关系:

image-20250108150801726
image-20250108150801726

DeepMind也做出了类似的研究,得出来的结论是模型参数量大小应该和token数量同比例增加。参考前面的表格。

接下来是详细的计算过程:

符号说明:

  • n_layer:模型层数;
  • d_model:模型残差输出维度大小;
  • d_ff:前馈神经网络输出维度大小;
  • d_attn:注意力网络输出维度大小;
  • n_heads:每一层的多头注意力的数量;
  • n_ctx:输入的上下文长度大小;

image-20250108164557718
image-20250108164557718

首先是参数量统计,embedding还是比较好计算的(注意,图标中的参数量指的是运算的时候的参数量,因此涉及到n_ctx的存储),QKV是三个矩阵,因此就是3*n_layer*d_model*d_attn。project是在完成attention操作时候的一个从attn维度到d的映射。feedforward一般来说是两个全连接层。因此整体的参数量就是最后的一个结果。

image-20250108164856546
image-20250108164856546

接下来是一个per token的FLOPS的计算。QKV的计算,很简单,就是在上述的基础上进行矩阵乘法+矩阵加法,由于是batch计算的,因此不会引入额外的FLOPS计算。同理应用于接下来的所有部分。最后的De-embed是一个矩阵乘法的过程,因此是2dn。至于embedding的FLOPS计算很奇怪。

要注意的是,SwiGLU会引入额外的参数量。在FFN的参数量计算过程中会把参数量变为原来的3/2倍。

激活值的计算

首先对于QKV,激活值为:

$$batch*n_{ctx}d_{model}$$

接着Mask:

image-20250108170430343
image-20250108170430343

image-20250108170447924
image-20250108170447924

image-20250108170454468
image-20250108170454468

image-20250108170500925
image-20250108170500925

相当复杂的计算结构,但是接下来是可以化简的部分了。假设模型参数量为$\Phi$,当利用bfloat16的时候,参数存储为$2\Phi$。

在训练过程中的显存占用量主要包括:参数、梯度、优化器状态、中间激活值。假如利用bfloat16进行计算,则如下:

image-20250108170710400
image-20250108170710400

(注意,优化器采用double精度),但是中间激活值是与batch size相关的,因此增加batchsize会相当极端的提升显存占用。

训练时间估计:

对于每个token和模型参数,

image-20250108170842797
image-20250108170842797

(关于前向传播和反向传播的FLOP比例参考:https://epoch.ai/blog/backward-forward-FLOP-ratio:除了第一层以外,所有的层的backward:forward都是2:1)

image-20250108172309258
image-20250108172309258

因此加上激活重计算的话FLOPS就基本正确了。此外由于参数计算需要进行两次浮点运算:乘法和加法,因而最后还X2了一下。当然,由于重计算不一定使用,因此实际的时间为:

image-20250108172635217
image-20250108172635217

DeepSeek Math

其实是一个很早期的DeepSeek的文章,来自于2024-04,但是这一波热潮使得全球都关注到了DeepSeek的相关工作。首先,核心来看的话其实工作量有两个

  • 更大更多的数据
  • GRPO策略

首先是数据问题,通过对数据域名评分并不断加入相关网站来不断扩充数据集的量,使得最终的数据量达到了120B,并在简单的测试中击败了诸多模型,证明数据集扩充的有效性。

其次是GRPO,相较于之前的RPO策略,论文生成了多个数据并采用KL散度进行计算从而达到更好的效果。其实从各个角度来看,都是对算力限制的一个近似。由于PPO需要将近4个语言模型来实现优化,因而为了减少算力需求,从而采用了这种方法。

Licensed under CC BY-NC-SA 4.0