目录

大模型底层八股

Transformer八股

为什么在Transformer模型中使用Layer Normalization(Layer Norm)而不是Batch Normalization(Batch Norm)

Layer Norm和Batch Norm是两种不同的归一化方法,各自适用于不同的场景。Batch Norm在卷积神经网络中得到广泛应用,它通过对同一批次(batch)中不同样本的相同特征进行归一化,来加速训练过程并减少过拟合。然而,在自然语言处理任务中,由于输入序列的长度不一致,并且Batch Norm对批次大小较为敏感,因此并不适合用于Transformer模型。

相比之下,Layer Norm对每个样本独立进行归一化,使得模型对序列长度和批次大小不敏感,更适合处理大型语言模型任务。此外,Layer Norm还能够减缓梯度消失问题,使得模型在训练过程中更加稳定。因此,在Transformer模型中选择了Layer Norm作为归一化方法

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/11040cade517023b4c15b886881ac0a1.webp?source=1def8aca

Attention手搓

Self-attention(自注意力)机制是Transformer模型的核心组成部分,它允许模型在处理序列数据时,为序列中的每个元素(如词或标记)分配不同的注意力权重,从而捕捉序列内的依赖关系。 Self-attention的基本公式如下:

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/931a914b10b561c98a27dd83abd03a91.png

  1. 计算Query(Q)、Key(K)和Value(V): 这些矩阵是通过将输入序列的嵌入(或隐藏状态)与三个不同的权重矩阵(Wq、Wk、Wv)相乘得到的。这三个权重矩阵是模型需要学习的参数。
  • Q = X * Wq
  • K = X * Wk
  • V = X * Wv 其中,X是输入序列的嵌入矩阵,维度为,N是序列长度,D是嵌入维度。
  1. 计算注意力得分: 使用Query和Key计算注意力得分,这反映了序列中每个元素对其他元素的重要性。
  • 得分 = Q * K^T
  1. 应用softmax函数: 将得分通过softmax函数转换为概率分布**,确保所有注意力权重的总和为1。**
  • 概率分布 = softmax(得分 / √D)
    • 为什么要除以根号D除以根号 d的主要原因是为了防止点积结果的数值过大,从而导致Softmax函数的梯度消失问题。
  1. 计算加权的Value: 将Value与softmax得到的概率分布相乘,得到加权后的Value,这是考虑了序列中其他元素的上下文信息的新表示。
  • 加权Value = 概率分布 * V
  1. 输出: 将加权Value相加,得到最终的输出,这是序列中每个元素的上下文表示。
  • 输出 = 加权Value之和 参数量的计算:
  • 每个权重矩阵(Wq、Wk、Wv)的参数量为,因此总共有3个权重矩阵,参数量为。 为什么用多头(Multi-Head)注意力:
  • 多头注意力允许模型在不同的表示子空间中学习信息,这样可以让模型同时关注不同的信息维度。每个头学习到的信息可以独立地编码输入序列的不同方面,然后将这些信息综合起来,得到更丰富的表示。 为什么要除以根号D:
  • 将得分除以根号D(得分归一化)可以防止内积过大导致softmax函数梯度变得非常小,这有助于数值稳定性,使得学习过程更加稳定。此外,它还可以看作是一种缩放因子,帮助模型在不同维度上保持一致的性能。

自注意力机制code dmo(qv同源)

假设我们有一个简单的句子:“猫喜欢追逐老鼠”。如果我们要对“喜欢”这个词进行编码,一个简单的方法是只看这个词本身,但这样会忽略它的上下文。“喜欢”的对象是“猫”,而被“喜欢”的是“追逐老鼠”。在这里,“猫”和“追逐老鼠”就是“喜欢”的上下文,而注意力机制能够帮助模型更好地捕获这种上下文关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 使用PyTorch实现简单的点积注意力
import torch
import torch.nn.functional as F

# 初始化Query, Key, Value
Q = torch.tensor([[1.0, 0.8]])  # Query 对应于 "喜欢" 的编码
K = torch.tensor([[0.9, 0.1], [0.8, 0.2], [0.7, 0.9]])  # Key 对应于 "猫", "追逐", "老鼠" 的编码
V = torch.tensor([[1.0, 0.1], [0.9, 0.2], [0.8, 0.3]])  # Value 也对应于 "猫", "追逐", "老鼠" 的编码

# 计算注意力权重
d_k = K.size(1)
scores = torch.matmul(Q, K.transpose(0, 1)) / (d_k ** 0.5)
weights = F.softmax(scores, dim=-1)

# 计算注意力输出
output = torch.matmul(weights, V)

print("注意力权重:", weights)
print("注意力输出:", output)

输出:

1
2
注意力权重: tensor([[0.4761, 0.2678, 0.2561]])
注意力输出: tensor([[0.9529, 0.1797]])

这里,“喜欢”通过注意力权重与“猫”和“追逐老鼠”进行了信息的融合,并得到了一个新的编码,从而更准确地捕获了其在句子中的语义信息。

通过这个例子,我们可以看到注意力机制是如何运作的,以及它在理解序列数据,特别是文本数据中的重要性

multi head Attention的对比

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/f8c7d723c1ed2fe8ea842d3418ae6c60.png

Multi-Head Attention(多头注意力机制)本质上就是多个自注意力(Self-Attention)机制的并行应用,然后将它们的输出进行合并,并通过一个线性变换来得到最终的输出。

使得模型能够更好地捕捉输入序列中的不同特征和关系。

Multi-query Attention 与 Grouped-query Attention 是否了解?区别是什么?

几种位置编码

注意力机制则是位置不敏感的·,即使调换序列中两个元素的位置对编码后的结果也不会产生影响。

因此,有必要将元素对应的位置信息添加到表示中,或者在计算注意力得分时考虑两个元素之间的相对位置。这些方法统称为位置编码,可以分为绝对位置编码和相对位置编码。

绝对位置编码 Absolute Position Encoding

绝对位置编码是指在输入序列经过词嵌入后的第kk个token向量xk∈Rdxk∈Rd中加入(add)位置向量pk∈Rdpk∈Rd;其过程等价于首先向输入引入(concatenate)位置索引kk的one hot向量pk:xk+pkpk:xk+pk,再进行词嵌入;因此绝对位置编码也被称为位置嵌入(position embedding)

原理

  1. 生成位置编码

    • 位置编码向量通常使用正弦和余弦函数生成,以确保不同位置的编码具有不同的特征。
  2. 加到输入嵌入上

    • 生成的位置编码向量被直接加到输入嵌入上: [ X_{pos} = E_{pos} + PE_{pos} ] 其中,EposEpos 是输入嵌入,PEposPEp**os 是位置编码。

      https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/35fa76f05396c65fd2904945da53d9f5.png

缺点

  • 位置依赖:绝对位置编码是基于具体位置的,因此在处理不同长度的序列时可能不够灵活
  • 缺乏相对位置信息:绝对位置编码无法直接捕捉序列中元素之间的相对位置信息。

相对位置编码 Relative Position Encoding

相对位置编码并不是直接建模每个输入token的位置信息,而是在计算注意力矩阵时考虑当前向量与待交互向量的位置的相对距离。

从绝对位置编码出发,其形式相当于在输入中添加入绝对位置的表示。

原理

  1. 相对位置表示
    • 相对位置编码直接表示序列中元素之间的相对位置。例如,元素 ii 和元素 jj 之间的相对位置可以表示为 j−iji
    • 这种相对位置表示可以捕捉到序列中元素之间的相对关系,而不是具体的位置。
  2. 相对位置编码向量
    • 为每个相对位置生成一个编码向量,这些向量可以通过学习得到,或者使用类似绝对位置编码的方法生成。
    • 在自注意力机制中,注意力权重的计算公式可以修改为: [ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T + QP^T}{\sqrt{d_k}}\right) V ] 其中,PP 是相对位置编码向量。

优点

  • 捕捉相对位置信息:相对位置编码能够直接捕捉序列中元素之间的相对位置信息,更加灵活。
  • 适应不同长度的序列:相对位置编码不依赖于具体位置,因此在处理不同长度的序列时更加灵活。

缺点

  • 计算开销:在某些情况下,相对位置编码可能会增加计算开销。

几种ffn,前馈神经网络

为什么需要ffn

Transformer中:其中两层感知机中,第一层会将输入的向量升维,第二层将向量重新降维。最后加个ReLU。这样子就可以学习到更加抽象的特征。(实现非线性变换,增强拟合能力)

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/8c03b72687b7108fd8bd2d6778853385.png

混合精度训练

混合精度训练的基本原理

混合精度训练的核心思想是将模型的某些部分(如前向传播和反向传播)使用低精度(FP16)计算,而其他关键部分(如权重更新)使用高精度(FP32)计算。具体来说,混合精度训练通常包括以下几个步骤:

  1. 模型参数和梯度的存储:模型的权重参数通常以FP32格式存储,以确保数值稳定性和精度。
  2. 前向传播和反向传播:在前向传播和反向传播过程中,计算可以使用FP16格式,以加速计算和减少内存使用。
  3. 损失缩放(Loss Scaling):为了避免在低精度计算中出现数值下溢(underflow)问题,通常会对损失进行缩放。损失缩放的基本思想是将损失乘以一个缩放因子,使得梯度在反向传播过程中不会变得过小。
  4. 权重更新:在权重更新阶段,梯度通常会被转换回FP32格式,并使用FP32格式的权重进行更新

分布式训练dp,mp,ddp,pp;zero的三个stage

多模态clip

多模态的实现方式(双流、单流)

VLLM底层

batch上如何优化

原始的朴素批处理方法

处理完这一批后再开始下一批

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/5edaab8256d095636be685cf8aa82337.png

连续批处理

它采用了迭代级调度,其中批大小根据每次迭代确定。结果是,一旦批中的一个序列完成生成,就可以在其位置插入一个新的序列,从而实现比静态批处理更高的GPU利用率。

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/538568d012319c3ca4d7b3519bc15782.png

综上:处理请求分布差异越大,优化收益越高

预测推理(Speculative inference

预测推理也称为推测采样、辅助生成或分块并行解码,是并行执行 LLM 的另一种方式。通常,GPT 风格的大语言模型是自回归模型,逐个生成文本标记。

生成的每个标记都依赖于它之前的所有标记来提供上下文。这意味着在常规执行中,不可能从同一个序列并行生成多个token,必须等待第 n 个token生成后才能生成 n+1 个token

图 12 显示了预测推理的示例,其中临时模型临时预测并行验证或拒绝的多个未来步骤。在这种情况下,临时模型中的前两个预测token被接受,而最后一个在继续生成之前被拒绝并删除。

https://dongnian.icu/note/llm/llm_concept/06.%E6%8E%A8%E7%90%86/llm%E6%8E%A8%E7%90%86%E4%BC%98%E5%8C%96%E6%8A%80%E6%9C%AF/media/refs/heads/master/image_12.png

prefix Caching 优化

输入的头部由于system prompt等因素,相似新高,可以考虑缓存相关计算结果,避免重复计算和显存浪费

  • batch内的复用
  • batch之间的复用

KV-Cache

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/28e3bf634a58e3512135107171df837d.png

训练加速—Pipeline并行

(b)Pipeline并行化将模型(垂直)分片为块,其中每个块包含在单独设备上执行的层的子集。图 2a 说明了四路Pipeline,其中模型按顺序分区,并且所有层的四分之一子集在每个设备上执行。一个设备上的一组操作的输出被传递到下一个设备,后者继续执行后续块。FnF**n和 BnB**n分别表示设备 nn 上的前向传播和后向传播。每个设备上存储模型权重的内存需求被分成四份。

只要分的够细,就会看图C,通过微批处理

(c)微批处理可以在一定程度上缓解这种情况,如图 2c 所示。输入的全局批次大小被分成子批次,这些子批次被一一处理,最后累积梯度。请注意,Fn,mF**n,m 和 Bn,mB**n,m 分别表示设备nm批次的前向和后向传递。这种方法缩小了管道气泡的尺寸,但并没有完全消除它们

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/13e05a79d624035eddbdba8840412298.png

训练加速—Tensor并行(相当于每一层,直接切分为多个并行)

Tensor并行化将模型的各个层(水平)分片为更小的、独立的计算块,这些计算块可以在不同的设备上执行。Transformer的主要组成部分,注意力块和多层感知器(MLP)层是可以利用Tensor并行化的。在多头注意力块中,每个头或一组头可以分配给不同的设备,以便它们可以独立且并行地计算。

https://raw.githubusercontent.com/kengerlwl/kengerlwl.github.io/refs/heads/master/image/1f448b2cd4b86e188b8b0eb83c9847b2/8fb5185ef70f6cc077349f1b10116b2a.png

Tensor并行化是有局限性,它需要将层划分为独立的、可管理的块,不适用于 LayerNorm Dropout 等操作,而是在tensor并行中复制。

37.2° Blog | 37.2° Blog