一、大模型相关
1、介绍rope和位置外推
旋转位置编码(Rotary Position Embedding,RoPE)是论文Roformer:Enhanced Transformer With Rotary Position Embedding提出的一种能够将相对位置信息依赖集成到self-attention中并提升transformer架构性能的位置编码方式。
和相对位置编码相比,RoPE具有更好的外推性。
【什么是大模型外推性?】
外推性指大模型在训练时和预测时的输入长度不一致,导致模型的泛化能力下降的问题。例如,如果一个模型在训练时只使用了512个token的文本,在预测时如果输入超过512个token,模型可能无法正确处理。这就限制了大模型在处理长文本或多轮对话等任务时的效果。
(1)基本概念
首先论文中定义一个长度为N的输入序列为:
其中wi表示输入序列中的第i个token,而输入序列SN对应的embedding表示为:
其中xi表示第i个token wi对应的d维词嵌入向量。
接着在做self-attention前,会用词嵌入向量计算q,k,v,同时加入位置信息,函数公式表达如下:
其中qm表示第m个token对应的词向量xm集成位置信息m之后的query向量。
而基于transformer的位置编码方法都是着重于构造一个合适的f{q,k,v}函数形式。
而计算第m个词嵌入向量xm对应的self-attention输出结果,就是qm和其他kn都计算一个attention score,然后再将attention score乘以对应的vn再求和求得输出向量om:
(2)绝对位置编码
由于transformer的self attention具有排列不变性,因此需要通过引入位置编码来让模型感知到输入序列中每个单词的位置信息,位置编码分为绝对位置编码和相对位置编码。
绝对位置编码根据单个单词的绝对位置来定义位置编码,每个位置都会分配一个位置编码,将位置编码的表征和单词本身的表征融合输入给self attention,相当于在输入层就把位置信息给弥补上去。
绝对位置编码从实现方式上又分为固定式和可学习式,固定式采用sin-cos位置编码,可学习式没有固定的位置编码式,通过初始化位置向量让模型根据上下文数据自适应地学习出来,Bert和GPT采用的可学习式。
在计算q,k,v向量之前,会计算一个位置编码向量pi,加到词嵌入xi上,位置编码向量pi同样也是d维向量,然后再乘以对应的变换矩阵W{q,k,v}:
而经典的位置编码向量pi的计算方式是:
p
i
,
2
t
p_{i,2t}
pi,2t表示d维位置向量pi中的第2t位置分量也就是偶数索引位置的计算公式。
- 优势:实现简单,可预先计算好,不用参与训练,速度快;
- 劣势:没有外推性,即如果预训练最大长度为512的话,那么最多只能处理长度为512的句子,再长就处理不了了。当然也可以将超过512的位置向量随机初始化,然后继续微调。
(3)相对位置编码
对两个单词之间的相对位置进行建模。并将相对位置信息加入到self attention模型结构中,形如DeBERTa采用的就是相对位置编码。self attention的本质是两个单词信息的内积操作,相对位置编码的思想是对内积的计算方式进行改进,在内积中注入两个单词的相对位置因素。
- 优势:直接体现了相对位置信号,效果更好。具有外推性,处理长文本能力更强。
(4)旋转位置编码
旋转位置编码是一种固定式的绝对位置编码策略,但是它的绝对位置编码配合transformer的注意力机制能达到相对位置编码的效果。
RoPE本质是对两个token形成的query和key向量做一个变换,使得变换后的query和key带有位置信息,进一步使得Attention的内积操作不需要做任何更改就能自动感知到相对位置信息。即RoPE的出发点和策略用的事相对位置思想,但实现方式用的是绝对位置编码。
固定式表明旋转位置编码没有额外需要模型自适应学习的参数,因此RoPE是一种高效的编码方式。绝对位置编码表明RoPE给文本的每个位置单词都分配了一个位置表征,和sin-cos位置编码一样,RoPE通过token在句子中的位置,token embedding 中每个元素的位置,这两个要素一起确定位置编码的表达。
RoPE的公式:
第一项代表某个位置为m的token的原始query向量,0-d-1代表向量每个位置的元素,d代表向量维度;第二项为一个同样长度为d的带有cos函数的向量,她和query向量逐位相乘;第三项由原始query百年患儿爱,第四项和第二项类似是将cos替换为sin。
该公式的目的是将原始query向量改造成一个带有位置信息的新向量,位置信息由m和θ进行表征,其中m为token在句子中的位置,θ的下标和向量中各元素的位置直接相关,公示如下:
因此只要给到某个token的输入query向量,知道token在上下文窗口处于第几位,就可以将它的query向量通过RoPE改造成一个新的向量形式。
以“我爱你”这句话中的第二个词“爱”为例,设词向量的维度d=4,词向量表征为[0.2,0.1,-0.3,0.7]。
公式中的第三项由原始向量变换而成,对于原始输入向量,将前后两个元素位置构成一对,交换二者的位置,并且对于偶数位取了相反数,因此每个元素位的注入位置信息的过程,可以看成是该元素和它相邻的元素,分别经过sin,cos三角函数加权求和的结果,比如q0的RoPE结果是q0和q1这一对元素经过三角函数变换的结果。
在Transformer原生的sin-cos位置编码中,采用相加的形式将位置编码融入到词向量中,而在RoPE中采用的是类似哈达玛积的乘积形式。
旋转位置编码如何表达相对位置信息:
- 实现相对位置能力的途径不同:sin-cos位置编码由于三角函数的性质,导致它本身就具备表达相对距离的能力,而RoPE本身不能表达相对距离,需要结合Attention的内积才能激发相对距离的表达能力。
- 和原输入的融合计算方式不同:sin-cos位置编码直接和原始输入相加,RoPE采用类似哈达玛积相乘的形式。
2、降低transformer复杂度 O ( n 2 ) O(n^2) O(n2)
transformer最重要的特性是global interaction,即对于任意两个位置的token(不论它们离得多远),它们之间都能直接进行信息交互。这个特性解决了传统序列建模中长依赖的问题。
但transformer也有一个典型问题:它的计算复杂度和空间复杂度均为
O
(
n
2
)
O(n^2)
O(n2),其中N为序列长度。
因此实际应用中很难将transformer应用到长序列任务上。
(1)transformer的计算复杂度
将transformer中标准的attention称为softmax attention。令x为长度为N的序列,其维度为F,$
x
∈
R
N
×
F
x \in \mathbb{R}^{N \times F}
x∈RN×F。x可看做softmax attention的输入。
softmax attention首先使用线性变换将x变成query,key和value:
其中
W
Q
W_Q
WQ、
W
K
W_K
WK、
W
V
W_V
WV都是待训练的参数矩阵;D是Q和K的维度,M是V的维度。有次可得Q、K、V的shape分别为:
在常见的transformer中,通常M=F=D。
有了Q,K,V,softmax attention的计算如下:
首先回忆一下矩阵乘法的计算复杂度,对于矩阵 A ∈ R N × M A \in \mathbb{R}^{N \times M} A∈RN×M和 B ∈ R M × L B \in \mathbb{R}^{M \times L} B∈RM×L,它们的矩阵乘法共需要 N × L × M N \times L \times M N×L×M次乘法运算。按照线性代数的方式理解:为了计算这两个矩阵的乘积,需要拿矩阵A的每一行与矩阵B的每一列做点积。因此总共需要 N × L N \times L N×L次点积。每次点积包含M次乘法和M - 1次加法。考虑加法复杂度远小于乘法,因此总的计算复杂度为 O ( N L M ) O(NLM) O(NLM)
回到transformer,softmax attention的计算主要包含两次矩阵乘法操作。
第一次矩阵乘法是
Q
×
K
T
Q \times K^T
Q×KT,结合上文关于矩阵乘法复杂度的结论和这两个矩阵的大小(公式(4)和公式(5)),可知
Q
×
K
T
Q \times K^T
Q×KT的复杂度为
O
(
N
2
D
)
O(N^2D)
O(N2D)。
第二次矩阵乘法是softmax的结果与V的乘积。softmax输出的矩阵大小为
N
×
N
N \times N
N×N,矩阵V的大小为
N
×
D
N \times D
N×D,所以这一次矩阵乘法的复杂度为
O
(
N
×
D
×
N
)
=
O
(
N
2
D
)
O(N \times D \times N) = O(N^2D)
O(N×D×N)=O(N2D)
因为这两次矩阵乘法是顺序执行的,因此总的复杂度为它们各自复杂度之和。因为这两个复杂度相等,相加只是引入一个常数项,可以忽略,因此softmax attention总的复杂度为
O
(
N
2
D
)
O(N^2D)
O(N2D)。
下图为softmax attention中参与每个token attention score计算的其他token的位置(只考虑decoder):
行和列都表示位置,蓝色表示当前token,绿色表示参与当前token计算的其他token的位置。
(2)Sparse Attention
对于图中的softmax attention,容易看到对于每一个token,它都会attend到它前面所有token,所以常说softmax attention是密集的(dense)。
与密集相对的就是稀疏(sparse)了。Sparse Attention的主要思路是减少每个token需要attend的token数量。
(2.1)Factorized Self-Attention(Sparse Transformer)
Paper:Generating Long Sequences with Sparse Transformers (2019)
Key Contribution:提出了两种稀疏Attention的方法:Strided Attention和Fixed Attention。这两者均可将transformer的
O
(
N
2
)
O(N^2)
O(N2)复杂度降低至
O
(
N
N
)
O(N \sqrt N)
O(NN)
Factorized Self-Attention的一个基础假设是:在softmax attention中,真正为目标token提供信息的attended token非常少。
换言之,该假设意味着:对于softmax attention,在经softmax得到的attention weights中,其中大部分值都趋于0,只有少数值明显大于0.因此attention weights比较稀疏。
①Strided Attention
由两种Attention机制构成,分别记为SA1和SA2:
- SA1:每个token只能attend它左边相邻的L个token;
- SA2:每个token只能attend它左边部分token,这些attended token选择方法是:从自己开始往左边数,每隔L就会有一个token可以attend。
Strided Attention的SA1。图中每个token只能attend到它左边相邻的L个token,图中L=3。
Strided Attention的SA2,图中L=3。
Strided Attention的SA1方法和SA2方法的本质实在选择哪些token可以attend。
这两种attention方法有三种方法用在transformer结构中:
- 交替使用。在第1个transformer block中使用SA1,在第2个transformer block中使用SA2,在第3个transformer block中又使用SA1,以此类推。这种方法能work的原因:虽然SA1只能看到左边的L个相邻位置,但可以认为在SA1中,每个token聚合了它左边L个token的信息。因此在SA2中,虽然它是跳着L个位置看的,但整体感受野等价于整个序列。
- 联合使用。将SA1选择的attended token和SA2选择的attended token合在一起使用。在计算attention时,首先用SA1去选择一些token,再用SA2去选择一些token,然后计算attention时只使用选择出的token参与计算即可。
- 多头使用。类似transformer采用的多头机制,这里每个头可以使用SA1、SA2或transformer中的softmax attention。
然后来看L的选择。只要将L的值设为 N \sqrt N N,那么整个Strided Attention的计算复杂度就为 O ( N N ) O(N \sqrt N) O(NN)。
②Fixed Attention
也有两种机制FA1和FA2。
Fixed Attention中的FA1(绿色)和FA2(橙色),L=3
先看FA2。橘色区域的位置是固定的,即从左往右数,每隔L个位置,选中一个token。
在FA1中,对于每个当前token,往它左边遍历,直到遇到第一个FA2选中的token。
Fixed Attention的使用方法和Strided Attention的三种方法一致。
作者结论:Strided Attention适用于图像、音频;Fixed Attention适用于文本。
理由:Strided Attention在attended token的位置上做了强假设:哪些位置的token应该被attended,与当前token位置强相关。而在文本上这类假设不成立,所以在Fixed Attention中,哪些位置的token应该被attended,与当前token位置无关。
即图像、音频的局部信息很重要,而文本全局信息很重要。
(2.2)Blockwise Self-Attention
Paper:Blockwise Self-Attention for Long Document Understanding (2019)
Key Contribution:通过分块来降低Softmax Attention的计算复杂度。
blockwise self-attention对空间复杂度做了更细致的分析。
一个模型的memory usage主要来自三部分:model memory、optimizer memory、activation memory。optimizer memory是model memory的三倍,这是因为optimizer memory需要为每个参数存储梯度、first momentum和second momentum。
model memory和optimizer memory可以直接计算出来。比如对于model memory,可以通过模型大小和参数类型(如FP16、FP32、INT8)来推算出准确值。同理,optimizer memory也可以精确计算出。而activation memory与具体实现有关。所以在paper中,作者用pytorch的内存分析工具来看训练时总的内存开销,然后减去model memory和optimizer memory,以此估算activation memory。
以Bert-base为例,其中activation memory独占87.6%,属于内存开销最大的部分。
blockwise self-attention思想:将一个长度为N的序列,平均分成n个短序列。当原始序列长度N无法被n除尽时,对原始序列进行padding,使它能被除尽。
假设序列长度N=12,每个token的维度为D。在transformer中,Q、K、V三个矩阵的大小都为
N
×
D
N \times D
N×D(
12
×
D
12 \times D
12×D)。在blockwise self-attention中,假设分块数n=3,那么每个分块中的序列长度为N/n = 12/3 = 4。所以输入序列x可被划分为n=3个子序列:x1,x2,x3,它们的长度都为
4
×
D
4 \times D
4×D。在计算self-attention时,每个Qi会去选择一个Kj和Vj来计算:
在只有一个attention头的情况下,Qi选择Kj、Vj的方法是:shfiting one position。Q1选择K2、V2,Q2选择K3、V3,Q3选择K1、V1,即Qi始终选下一个Ki+1和Vi+1。
在多头attention的情况下,记序列Index(Q)为单头attention下每个Qi对应的Kj和Vj的编号j:
仍以上面的示例为例,在单头情况下,Index(Q)为:
在多头情况下,第k个头的
I
n
d
e
x
(
Q
)
k
Index(Q)^k
Index(Q)k定义如下:
例如,第一个头的
I
n
d
e
x
(
Q
)
1
Index(Q)^1
Index(Q)1为:
第二个头的
I
n
d
e
x
(
Q
)
2
Index(Q)^2
Index(Q)2为:
因为分块数为3,所以需要取余数,最终结果为:
最后分析复杂度。公式(2.2.1)中的复杂度为
O
(
N
2
n
2
)
O(\frac {N^2} {n^2})
O(n2N2),因为对于每一个分块,都需要用公式(2.2.1),所以总复杂度为:
在原文中,通常选为2。注意,在大O 计法中一般会忽略掉常数项。所以在这种意义下,Blockwise Self-Attention的复杂度仍为O(N²) 。
总结:相比于Sparse Transformer中的Factorized Self-Attention,Blockwise Self-Attention更简单,且从效果上来看,优于Factorized Self-Attention。
(2.3)Longformer
paper:Longformer: The Long-Document Transformer (2020)
Key Contribution:设计了多种不同的Local Attention和Global Attention方法。
Longformer一共提出三种attention,分别是Sliding Window based Attention(SW-Attention)、Dilated Sliding Window based Attention(DSW-Attention)和Global Attention(G-Attention)。
①Sliding Window based Attention(SW-Attention)
和Strided Attention中的SA1完全一样。
SW-Attention只Attend它左边的L个token。在SW-Attention中,L被称为“窗口大小”,而在Strided Attentio中,L被称为“步长”。
我们可以在transformer中只使用SW-Attention来构建具有Global Interaction的网络
3、大模型推理加速
(1)KV Cache
(1.1)KV Cache诞生背景
对于仅解码器transformer架构模型的推理,我们给一个输入文本,模型会输出一个回答(长度为N),该过程执行了N次推理过程。即类GPT的仅解码器模型一次推理只输出一个token,输出的token会与输入tokens拼接在一起,然后作为下一次推理的输入,这样不断反复直到遇到终止符。
针对一个仅解码器transformer架构的模型,假设用户输入为“recite the first law",模型续写得到的输出为“A robot may not”,模型生成过程如下:
① 将“recite the first law”输入模型,得到每个token的注意力表示。使用“law”的注意力表示,预测得到下一个token为“A”。
②将“A”拼接到原来的输入,得到“recite the first law A”,将其输入模型,得到注意力表示,使用“A”的注意力表示,预测得到下一个token为“robot”。
③将“robot”拼接到原来的输入,依此类推,预测得到“may”,最终得到“recite the first law A robot may not”。
仅解码器transformer架构的自回归模型为带mask的self attention。因此,在没有KV Cache的情况下,其计算过程如下所示。
正常情况下,attention的计算公式如下:
为了方便,暂时忽略scale项,因此,attention的计算公式如下(softmaxed表示已经按行进行了softmax):
当
Q
K
T
QK^T
QKT变成矩阵时,softmax会针对行进行计算:
其中,Att1(Q,K,V)表示attention的第一行,Att2(Q,K,V)表示attention的第二行。
对于Att1(Q,K,V),由于
Q
1
K
2
T
Q_1K_2^T
Q1K2T这个值会mask掉,
Q
1
Q_1
Q1在第二步参与的计算与第一步完全一样,并且
V
1
V_1
V1参与计算Attention时也仅仅依赖于
Q
1
Q_1
Q1,与
Q
2
Q_2
Q2毫无关系。
对于Att2(Q,K,V),
V
2
V_2
V2参与计算attention时也仅仅依赖于
Q
2
Q_2
Q2,与
Q
1
Q_1
Q1毫无关系。
可以得出以下结论:
- 当前计算方式存在大量冗余计算,每次生成新的token都需要计算之前的KV;
- A t t k ( Q , K , V ) Att_k(Q,K,V) Attk(Q,K,V)的计算过程中,主要与 Q k Q_k Qk有关。 V k V_k Vk参与计算attention时也仅仅依赖于 Q k Q_k Qk。
- 每一步中,只需根据 Q k Q_k Qk计算 A t t k ( Q , K , V ) Att_k(Q,K,V) Attk(Q,K,V)即可,之前已经计算的attention完全不需要重新计算,但K和V是全程参与计算的,所以我们需要把每一步的k,v缓存起来。
(1.2)KV Cache步骤
因为self attention中带mask,因此在推理的时候,前面已经生成的token不需要与后面的token产生attention,从而使得前面已经计算的K和V可以缓存起来。
一个典型的带有KV cache优化的生成大模型的推理过程包含两个阶段:
- 预填充阶段:输入一个prompt序列,为每个transformer层生成key cache和value cache
- 解码阶段:使用并更新KV Cache,一个接一个地生成token,当前生成的token依赖于之前已经生成的token。
预填充阶段计算过程如下:
解码阶段计算过程如下:
(1.3)使不使用KV Cache的对比
(1.4)为什么transformer推理要做kv缓存?
因为在transformer中,文本是逐个token生成的,每次新的预测,会基于之前生成的所有token的上下文信息。
这种对顺序数据的依赖会减慢生成过程,因为每次预测下一个token,都需要重新处理序列中所有之前的token。
比如,如果我们要预测第100个token,那模型必须使用前面99个token的信息,这就需要对这些token进行矩阵运算,而这个矩阵运算是非常复杂和耗时的。
因此KV缓存是为了减少这种耗时的矩阵运算,在transformer推理过程中,会将这些键和值存储在缓存中,这样模型在生成后续token时,直接访问缓存,而不需要重新计算。
(1.5)带有KV缓存优化的大模型推理过程包含几个阶段
- 预填充阶段:输入prompt序列,为每个transformer层生成KV Cache,同时输出第一个token;
- 解码阶段:发生在计算第二个输出token至最后一个token过程中,这时cache是有值的,每轮推理只需读取cache,同时将当前轮计算出的新的key、value追加写入至cache。
(1.6)如何估算KV缓存消耗的显存大小
KV缓存通常使用float16和bfloat16数据类型,也就是以16位的精度存储张量。对于一个token,KV缓存会为每一层和每个注意力头存储一对KV张量。
KV张量的总显存消耗,可以通过下面这个公式计算,以字节为单位。
层数
×
\times
× KV注意力头的数量
×
\times
× 注意力头的维度
×
\times
× (位宽/8)
×
\times
× 2
2是因为有两组张量,也就是键和值。位宽通常为16位,由于8位1字节,因此将位宽除以8,这样在KV缓存中,每16位参数占用2个字节。
(1.7)KV缓存为何在长文本和复杂模型结构下成瓶颈
以llama3 8B为例,上面的公式变为:
32
×
\times
× 8
×
\times
× 128
×
\times
× 2
×
\times
× 2 = 131072字节
这里注意,虽然llama3 8B有32个注意力头。不过由于GQA的存在,只有8个注意力头用于键和值,对于一个token,KV缓存占用131072字节,差不多0.1MB.
这看起来好像不大,但对于许多不同类型的应用,大模型需要生成成千上万的tokens。
比如,如果想利用llama3 8B的全部上下文大小是8192,KV缓存将为8192个token存储键值张量,差不多会占用1.1G现存。
换句话说,对于一块24G显存的消费级GPU来说,KV缓存将占用其总显存的4.5%。
对于更大的模型,KV缓存增长得更快。
所以KV缓存会随着文本长度的增加和模型结构的复杂性增强大幅膨胀,对于需要处理超长上下文的应用可能会成为瓶颈。
(1.8)KV缓存缺陷的解决方案
量化KV缓存,量化可以降低参数精度,比如从16位降到4位,换言之,一个16位张量可以缩减到原来的1/4,而通过两位量化甚至可以缩减到1/8。
那量化有没有缺点?
量化可能会减慢解码过程,并且可能显著影响LLM的准确性。
实际中需要通过调整量化超参,减轻这些影响,另外可以通过重写量化算子,对量化操作和反量化操作与其他算子做一些融合,来提高降低解码速度的影响。
(1.9)有关KV缓存的最新技术
微软提出一种名为YOCO(You Only Cache Once)的编码器-解码器架构。
YOCO架构关键特点:
- 双重解码器结构:YOCO由自解码器和交叉解码器两部分组成,自解码器生成全局键值(KV)缓存,交叉解码器通过交叉注意力机制重用这些缓存。
- 单次缓存:与标准transformer相比,YOCO只缓存一次KV对,显著减少了GPU内存需求。
- 推理效率:YOCO的计算流程允许预填充阶段提前退出,而不影响最终输出,从而显著加快预填充阶段的速度。
①自解码器
- 定义:自解码器是YOCO架构中的一个组件,负责生成全局键值缓存。它使用高效的自注意力机制来编码输入序列,并生成中间向量表示,这些表示随后被用于交叉解码器。
- 工作机制:自解码器利用诸如滑动窗口注意力或门控保留等技术,以减少计算和内存需求。这些技术允许自解码器在保持性能的同时,只使用常数大小的缓存。
- 设计选择:
- 门控保留:通过数据依赖的门控机制增强保留网络,实现训练并行性、良好性能和低推理成本。
- 滑动窗口注意力:限制注意力范围在一个固定窗口大小内,减少了推理时KV缓存的内存复杂度。
②交叉解码器
+功能:
+ 重用KV缓存:交叉解码器通过交叉注意力机制重用由自解码器生成的全局KV缓存。这种设计允许模型在不同的解码层之间共享和传递信息。
+ 生成输出向量:交叉解码器堆叠在自解码器之上,利用共享的KV缓存来计算最终的输出向量,这些输出向量随后用于预测下一个token。
- 工作流程
- 生成KV缓存:自解码器首先处理输入序列,生成中间向量表示,并由此产生全局KV缓存;
- 交叉注意力:交叉解码器的每层都会使用这些KV缓存,通过计算Q、K、V之间的注意力分数来更新表示;
- 输出向量计算:通过标准的多头注意力机制,交叉解码器将自解码器的输出与KV缓存相结合,生成每个位置的输出向量。
4、交叉注意力机制
(1)定义与基本原理
(1.1)普通注意力机制
- 普通注意力机制(自注意力机制)用于同一输入序列内部的元素之间的关系建模。每个元素根据其他元素的特征动态调整自己的表示。
- 计算方式为:对于输入序列中的每个位置,生成查询、键和值,通过计算查询与所有键的相似度,得到注意力权重,然后加权求和对应的值。
(1.2)交叉注意力机制
- 交叉注意力机制用于两个不同输入序列之间的信息交互。一个序列作为查询,另一个序列提供键和值。
- 其计算过程类似自注意力,但关注的是两个不同输入之间的关系。例如,在机器翻译中,源语言句子作为键和值,目标语言句子作为查询。
(2)计算步骤
(2.1)普通注意力计算步骤
- 对输入序列生成查询、键和值;
- 计算查询与所有键的点积,得到注意力得分;
- 应用softmax函数,将得分归一化为权重;
- 使用权重对值进行加权求和,输出加权结果。
(2.2)交叉注意力计算步骤
- 对两个输入序列分别生成查询(来自第一个序列)和键值对(来自第二个序列)
- 计算查询与第二个序列中所有键的点积,得到注意力分数;
- 使用softmax函数,将得分归一化为权重;
- 使用权重对第二个序列中的值进行加权求和,输出结果。
(3)应用场景
- 普通注意力:广泛应用于自然语言处理任务中,如文本分类、情感分析等,因为它能有效捕捉长距离依赖关系。
- 交叉注意力:常用于需要对齐不同模态信息的任务,如机器翻译、图像描述生成和多模态学习等。在这些场景中,模型需要根据一个输入动态关注另一个输入的相关部分。
(4)优劣对比
5、FlashAttention
(1)按照softmax公式的计算有什么问题,一般在工程实现的时候怎么做
标准的softmax公式:
因为包含了幂指数计算,所以有一个明显的问题:数值溢出。对于大模型常用的半精度fp 16,最大值也才65536,所以当xi大于11时,e的12次方等于162754.7914,大于65536。
所以实际工程实现相对于原生的softmax,它先要减去一个max值,确保计算过程中不会发生数值溢出:
对于标准的softmax计算,需要3步:
- 计算最大值m
- 计算分母
- 依次计算分子
若不做任何优化,至少要和GPU做6次通信(3次写入,3次写出)。
6、transformer的QKV
Q,K,V是由输入的词向量x经过线性变换得到的,其中各个矩阵w可以经过学习得到,这种变换可以提升模型的拟合能力。得到的Q,K,V可以理解为:
- Q:要查询的信息
- K:被查询的向量
- V:查询得到的值
V可以看作表示单个输入特征的向量。当我们直接把一组V输入到网络中进行训练,那这个网络就是没有引入Attention机制的网络。
但如果引入Attention,就需要将这组V乘以一组权重α,那么就可以有重点性地关注输入特征,如同人的注意力一般。
举例:相亲
有一个女生,有N个相亲对象,她想从N个相亲对象中寻找出最符合自己期望的那个,便于她分配注意力来管理时间。
- Q表示女生对相亲对象的需求;
- K表示每个人的自身条件;
- V表示匹配结果
无论是女生还是相亲对象,都有着自己的一套QKV来记录他们的选择情况。
对于女生来说,当然相亲对象的条件约符合她的理想越好,放在QKV上来讲就是女生的Q和相亲对象的K之间的相似度越高越好。
二、Embedding初步
1、什么是表示?
为了提高机器学习系统的准确率,我们需要将输入信息转换为有效的特征,这就是表示。一个好的表示具有以下几个优点:
- 一个好的表示应该具有很强的表示能力,即同样大小的向量可以表示更多信息。
- 一个好的表示应该使后续的学习任务变得简单,即需要包含更高层的语义信息。
- 一个好的表示应该具有一般性,是任务或领域独立的。虽然目前大部分表示学习方法还是基于某个任务来学习,但我们期望其学到的表示可以比较容易地迁移到其他任务上。
2、局部表示和分布式表示
以颜色表示为例,若在计算机中表示颜色,局部表示的方法是以不同名字来命名不同的颜色,也称为离散表示或符号表示。局部表示通常可以表示为one-hot向量形式。one-hot向量为有且只有一个元素为1,其余元素都为0的向量。另一种表示颜色的方法是用RGB值来表示颜色,不同颜色对应到R、G、B三维空间的一个点,这种表示方式称为分布式表示。分布式表示通常可以表示为低维的稠密向量。
局部表示优点:
- 这种离散的表示方式具有很好的解释性,有利于人工归纳和总结特征,并通过特征组合进行高效的特征工程。
- 通过多种特征组合得到的表示向量通常是稀疏的二值向量,当用于线性模型时计算效率非常高。
局部表示不足:
- one-hot向量的维数很高且不能扩展。如果有一种新的颜色,我们需要增加一维来表示。
- 不同颜色之间的相似度都为0(所以基向量都是正交的),即我们无法知道“红色”和“中国红”的相似度要高于“红色”和“黑色”的相似度。
分布式表示改进:
- 分布式表示的向量维度一般都比较低。我们只需要一个三维的稠密向量就可以表示所有颜色。
- 分布式表示也很容易表示新的颜色。
- 不同颜色之间的相似度也很容易计算。
3、嵌入是什么
我们可以使用神经网络将高维的局部表示空间
R
∣
V
∣
R_|V|
R∣V∣映射到一个非常低维的分布式表示空间
R
∣
D
∣
R_|D|
R∣D∣,并且
∣
D
∣
<
<
∣
V
∣
|D| << |V|
∣D∣<<∣V∣。在这个低维空间中,每个特征不再是坐标轴上的点,而是分散在整个低维空间中。在机器学习中,这个过程称为嵌入。嵌入通常指将一个度量空间中的一些对象映射到另一个低维的度量空间中,并尽可能保持不同对象之间的拓扑关系。比如自然语言中词的分布式表示,经常叫做词嵌入。推广开来,还有sentence embedding,image embedding,video embedding,item embedding都是一种将源数据映射到另一个空间。
4、常见的词嵌入模型
词嵌入模型基于的基本假设(分布式假设)是出现在相似的上下文中的词含义相似,以此为依据将词从高维稀疏的one-hot向量映射为低维稠密的连续向量,从而实现对词的语义建模。词嵌入模型大致可以分为两种类型,一种是基于上下文中词出现的频次信息,包括:潜在语义分析(Latent Semantic Analysis,LSA),GLoVe等。另一种则是基于上下文中出现的词的预测,包括:word2vec,FastText等。
5、TF-IDF
(1)什么是TF-IDF
TF-IDF是一种用于文本分析的统计方法,能够评估一个单词在文档集合中的重要性。
(2)从头开始编写TF-IDF的步骤
- 计算词频:
- 词频是某个词在一个文档中出现的次数;
- 可以用词频除以文档中的总词数来标准化。
- 计算逆文档频率:
- 你文档频率衡量某个词在文档集合中的重要性
- 公式:IDF=log(总词数/包含该词的文档数)
- 计算TF-IDF
- TF-IDF = TF * IDF
(3)TF-IDF中的归一化
在TF-IDF中,归一化是一种调整方法,用于平衡不同长度文档的影响。归一化确保计算出的TF-IDF值在不同文档之间具有可比性。归一化的目的和方法:
- 平衡文档长度:长文档通常包含更多的词语。未经归一化处理的IF-IDF可能会高估文档中词语的重要性。
- 提升可比性:通过归一化,不同长度的文档中的词频和TF-IDF值可以在同一尺度下进行比较。
常见的归一化: - L2归一化:又称欧几里得归一化。通过将每个向量除以其L2范数(向量元素的平方和的平方根)实现。
- L1归一化:又称曼哈顿归一化。通过将每个向量除以其L1范数(向量元素的绝对值之和)实现。
(4)为什么我们这个时代需要了解TF-IDF,以及如何在复杂模型中使用它?
- 信息检索:TF-IDF在搜索引擎中广泛应用,用于衡量文档和查询之间的相关性。了解TF-IDF有助于改进搜索结果的准确性和用户满意度。
- 文本分析:TF-IDF能够帮助识别文档中重要的关键词,这在文本分类、主题建模和情感分析等任务中非常有用。
- 去噪:TF-IDF可以有效过滤高频的无意义词语(如“的”,“是”),突出真正有价值的信息。
(5)复杂模型中的TF-IDF
- 特征工程
- 在使用复杂模型(如神经网络、SVM等)进行文本分类或聚类之前,TF-IDF可以作为特征提取的一个步骤,将输入的文本数据转换为向量形式,使其适合模型输入。
- 示例:在电影评论分类任务中,先计算每个评论的IF-IDF值,然后将这些值作为输入特征,供神经网络训练。
- 结合词向量
- 可以将TF-IDF结合词向量使用,以增强模型的表现。具体方法是计算文本的TF-IDF加权词向量平均值,使得重要词语对文本表示的影响更大。
- 示例:在文档相似度计算中,使用TF-IDF加权的词向量平均值来表示文档,再计算余弦相似度。
- 图神经网络
- TF-IDF可以用于构建图结构的节点特征。例如,在知识图谱中,每个节点可以用其关联文本的IF-IDF表示,帮助图神经网络更好地理解节点间的关系。
- 示例:在社交网络分析中,将用户的帖子内容转换为TF-IDF表示,并将其作为图神经网络的输入,进行社区检测或影响力分析。
6、潜在语义分析(LSA)
LSA使用单词向量空间矩阵对文本的语义内容进行表示。它的基本想法是,文本中所有单词的出现情况表示了文本的语义,所以我们可以用一个向量表示文本的语义,向量的每一维对应一个单词,其数值为该单词在该文本中出现的频数或权值。
其中,权值常用词频-逆文本频率(TF-IDF)表示。
7、word2vec模型
word2vec是2013年由google提出的。它提出了一对常用模型:Skip-gram(用一个词语作为输入来预测它周围的上下文)和CBOW(用一个词的上下文作为输入来预测这个词语本身),它们都是浅层的两层神经网络,但是经历了大量数据的训练。
word2vec模型实际上经历了两个阶段,第一阶段是建立语言模型,第二阶段是通过模型获取词向量。word2vec的整个建模过程实际上与自编码器(auto-encoder)的思想很类似,即先基于训练数据构建一个神经网络,当这个模型训练好后,我们并不会用这个训练好的模型处理新的任务,我们真正需要的是这个模型通过训练学习到的参数,即隐层的权重矩阵(word vectors)。
word2vec这两个模型在隐藏层都不使用非线性激活函数,因为word2vec不是为了做语言模型,它不需要预测得更准。不使用非线性激活函数可以使网络求梯度更简单,加快网络训练。
(1)CBOW
输入上下文单词的one-hot编码(假设单词向量空间维度为V),所有one-hot分别乘以共享的输入权重矩阵W1,所得的向量相加求平均得到1N的隐层向量。再乘以输出权重矩阵W2得到1V输出向量。然后将模型视为一个V分类问题,输出向量就是V个logits,利用softmax函数处理得到V-dim概率分布,概率最大的index所指示的单词就是预测出的中间词。最后使用交叉熵损失函数对预测结果进行评估,使用梯度下降优化参数矩阵W1,W2。
举例来说,假设我们现在的训练语料是只有四个单词的I drink coffee everyday。我们选coffee作为中间词,window size设为2。也就是说,模型的输入是:[“I”,“drink"和"everyday”]三个单词的one-hot编码,期望输出是coffee。
由于输入的单词进行了one-hot编码,而庞大的语料库中单词的数量是极大的,one-hot编码会浪费很多内存。我们可以用Lookup Table来优化从输入到隐藏层这个计算,其实这个Lookup Table就是W1参数矩阵。由于输入是one-hot向量,所以输入向量乘上W1矩阵,就是选择了W1矩阵中对应one-hot为1的Index的那一行。
对于K个输入one-hot,即分别选择了W1中对应的K行,然后求和即可。后来word2vec的工具包中奖求和改进为求平均值,就是对求和结果除以K。
(2)Skip-Gram模型
Skip-Gram模型与CBOW模型相反,它以中心词作为单个输入向量,输出预测得目标上下文词。
训练目标是最小化输出层中所有上下文词的预测误差总和。在Skip-Gram模型中输入是coffee的one-hot编码,期望输出是[“I”,“drink"和"everyday”]。真实的网络结构输出层只有一个1*V的输出限量。然后我们分别计算"I",“drink"和"everyday"三个单词对应的损失,将损失加和,然后使用反向传播进行优化。同时"I”,"drink"和"everyday"三个单词,虽然距离中心词的距离不同,但我们同等地对待它们。
(3)CBOW和Skip-Gram比较
- CBOW训练速度比Skip-Gram更快,对同样一个样本窗口,CBOW模型计算一次梯度,而Skip-Gram模型计算窗口大小次数的梯度。原文中同一个任务,CBOW训练花费几小时,而Skip-Gram花费了3天。
- Skip-Gram模型由于对低频词迭代更充分,因此对低频词的表示通常优于CBOW模型。比如,给定一个上下文,yesterday was a really […] day。CBOW模型会预测更高频的词汇如beautiful或nice,像低频词delightful就会很少被模型关注到,因为CBOW模型设计来预测中心词,在这个场景下,delightful和beautiful是竞争关系,而大部分语料都会将模型往beautiful方向进行训练。但Skip-Gram模型是设计来预测上下文的,delightful和beautiful不存在竞争关系,beautiful + context被模型视作一个新的样本而已。
- CBOW更好地学习单词之间的句法关系,而Skip-Gram可以更好的捕捉更好的语义关系。比如,对于单词“cat”,CBOW将检索词形相似的词,如复数形式“cats”,而Skip-Gram会考虑语义相关但词形上不同的词,如dog。
(4)word2vec为什么要用2个权重矩阵
- 参数分离:中心词和上下文词有不同的作用和意义,因此我们需要分别为它们设置不同的权重矩阵,保证模型能够捕捉到两者的不同语义关系。通过两个矩阵,模型能够独立学习目标词的表示和上下文词的表示。
- 词向量的独立表示:两个矩阵的使用使得目标词和上下文词可以有独立的词向量表示,这样可以让每个单词在不同的上下文中获得更丰富的语义信息。
Skip-gram 两个矩阵的目的: - W1:通过目标词来学习目标词的稠密向量表示。每个目标词都有一个独立的词向量,该词向量通过W1从词汇表中惊醒学习。这个词向量表示目标词的语义信息。
- W2:用目标词的词向量去预测它的上下文词,通过W2,将目标词的稠密向量映射到输出空间,然后使用softmax函数计算上下文词的概率分布,从中选择最有可能的上下文词。
CBOW两个矩阵的目的:
- W1:多个上下文单词通过W1映射为各自的词向量。这些词向量可以通过加权或平均的方式汇总,形成一个上下文词向量的汇总表示。这个表示包含了上下文词的语义信息,是用来预测目标词的基础。
- W2:通过将汇总后的上下文词向量与输出矩阵W2相乘,得到一个V维的向量。然后通过softmax函数计算出每个单词作为目标词的概率分布,最终预测出最可能得目标词。
(5)word2vec训练有哪些tricks
- word pairs and phase
一些单词组合(或词组)的含义和拆开以后具有完全不同的意义。比如Boston Globe是一种报刊的名字,而单独的boston和globe这样单个的单词却表达不出这样的含义。因此,在文章中只要出现Boston Globe,我们就应该把它作为一个单独的词来生成词向量,而不是将其拆开。同理还有New York,United State等。 - Sub-sampling
对于the这种常用高频单词,我们将会有大量的(“the”,…)这样的训练样本,而这些样本数量远远超过了我们学习“the”这个词向量所需的训练样本数。比如(“fox”,“the”)这样的训练样本并不会给我们提供关于fox更多的语义信息,因为the在每个单词的上下文中几乎都会出现。sub-sampling就是对于我们在训练原始样本中遇到的每一个单词,它们都有一定概率被我们从文本中删掉,而这个被删除的概率与单词的频率有关。删除单词wi的频率为:
其中f(wi)为单词wi出现的频率,t是阈值,通常取 1 0 − 5 10^{-5} 10−5。
- Hierarchical softmax
层次softmax的提出,是为了解决softmax函数在语料库单词量过大时,计算过于复杂的问题。当语料库中单词数量为V时,计算softmax的时间复杂度为O(V)。
首先构造一颗二叉树,将原始字典D划分为两个子集D1和D2,然后对子集D1和D2进一步划分。重复这一过程直到集合中只剩下一个word。这样就将原始大小为V的字典D转换成一棵深度logV的二叉树。树的叶节点与原始字典里的word一一对应,数量为V;非叶子结点对应着某一类word的集合,数量为V-1。
对于训练样本里的一个target word w t w_t wt,其对应二叉树编码为{1,0,1,… ,1},则构造的似然函数为:p(wt|context)=p(D1=1|context)p(D2=0|D1=1)…p(wt|Dk=1)。其中每一项乘积因子都是一个逻辑回归函数。
可以通过最大化这个似然函数来求解二叉树的参数,即非叶子结点上的向量,用来计算游走到某一子节点的概率。层次softmax通过构造一棵二叉树将目标概率的计算复杂度从最初的V降低到了logV的量级。但是却增加了词与词之间的耦合性。比如一个word出现的条件概率的变化会影响到其路径上所有非叶子节点的概率变化。间接地对其他word出现的条件概率带来影响。并且如果使用哈夫曼树的话,对于一些生僻词,搜索的深度会很深。
(6)Negative Sampling
- 负采样的背景
在word2vec的skip-gram中,我们的目标是通过中心词预测中心词的上下文词。然而,当词汇表非常大时,计算所有词的预测概率会变得非常耗时。为了解决这个问题,负采样技术被引入。 - 负采样的工作原理
负采样通过从词汇表中随机选择一些词作为负样本来简化训练过程。比如我们有一个训练样本,中心词为wc,它周围上下文共有2c个词,记作context(w)。由于这个中心词wc和context(w)相互存在,因此它是真实的正样本。通过负采样,我们可以得到n个和wc完全不同的wi,这样context(w)和wi就组成了n个并不真实存在的负样本。利用这一个正样本和n个负样本,我们进行二次逻辑回归,得到每个wi对应的模型参数和每个词的词向量。模型的目标是最大化正样本的预测概率,同时最小化负样本的预测概率,从而减少计算量。
对于小规模数据集,选择5-20个negative words比较好,对于大规模数据集,选择2-5个negative words。
一个单词被选作negative sample的概率跟它出现的频次有关,出现频次越高的单词越容易被选作negative words。
每个单词被赋予一个权重,即f(wi), 它代表着单词出现的频次。公式中开3/4的根号是基于经验的,原文中提到这个公式的效果要比其它公式更加出色。
8、Glove模型
word2vec模型对一个滑动窗口内上下文词或中心词进行预测,其输出本质是局部空间中(滑动窗口内)单词同时出现的概率分布。glove模型认为全局单词同时出现的概率的比率能够更好地区分单词。比如假设我们要表示“冰”和“蒸汽”这两个单词。对于和“冰”相关,和“蒸汽”无关的单词,比如“固体”,我们可以期望 P ( 冰 ∣ 固体 ) P ( 蒸汽 ∣ 固体 ) \frac{P(冰|固体)}{P(蒸汽|固体)} P(蒸汽∣固体)P(冰∣固体)值较大。类似地,对于和“冰”无关,和“蒸汽”相关的单词,比如“气体”,我们期望 P ( 冰 ∣ 气体 ) P ( 蒸汽 ∣ 气体 ) \frac{P(冰|气体)}{P(蒸汽|气体)} P(蒸汽∣气体)P(冰∣气体)值较小。相反,对于像“水”之类同时和“冰”、“蒸汽”相关的单词,以及“时尚”之类同时和“冰”、“蒸汽”无关的单词,我们可以期望 P ( 冰 ∣ 水 ) P ( 蒸汽 ∣ 水 ) \frac{P(冰|水)}{P(蒸汽|水)} P(蒸汽∣水)P(冰∣水)和$\frac{P(冰|时尚)}{P(蒸汽|时尚)}应接近1,这就是glove的朴素想法。
(1)词共现
①glove的词共现处理
glove通过直接构建全局词共现矩阵来学习词向量,这意味着glove模型在整个语料库中统计每个词对的共现次数,然后根据这些全局的共现统计来训练模型。
- 全局信息:glove使用整个语料库中的词共现信息,构建一个包含所有词对的共现矩阵。
- 统计共现频率:词共现次数直接用于构建目标函数,最小化词向量之间的点积和共现信息的差异。
- 对数处理:glove对共现次数取对数,减少了高频词的影响,帮助模型捕捉更丰富的词语义关系。
②word2vec的词共现处理
word2vec通过局部上下文窗口来处理词共现,但它并不直接构建词共现矩阵,而是通过skip-gram或CBOW模型在训练过程中逐步学习。它利用每个目标词的局部上下文来进行模型优化。
- 局部上下文:word2vec仅在每次训练时使用局部上下文的词共享信息,而不会显式地构建一个全局的词共现矩阵。
- 正样本和负样本:在skip-gram中,word2vec对目标词周围的上下文词作为正样本进行训练,并通过负采样选择随机的词作为负样本来优化模型。
- 隐式处理词共现:word2vec并不存储词共现次数,而是通过训练优化词向量,使其在局部上下文中相似词的向量接近,不相似词的向量远离。
(2)glove vs word2vec
①相似点
- 目的:两者都旨在通过学习词向量来捕捉词语的语义关系,使得相似的词在向量空间中更接近。
- 静态词向量:glove和word2vec都属于静态词向量模型,这意味着一个词的向量是固定的,无论它在不同上下文中出现时语义是否有变化。
②差异 - 核心思想:glove基于全局词共现矩阵构建词向量,word2vec基于上下文窗口,使用CBOW或skip-gram预测任务;
- 训练方式:glove通过统计词的共现频率来训练,word2vec中CBOW预测中心词,skip-gram预测上下文词;
- 全局和局部:glove使用整个语料库的词共现信息,捕捉全局关系,word2vec仅使用局部上下文窗口(固定窗口大小,通常为5或10);
- 性能:glove在大语料上表现较好,能更好地捕捉全局关系,word2vec在较小的语料上表现良好,特别是在训练速度和资源方面有优势;
- 训练目标:glove通过最小化点积和词共现概率之间的差异,word2vec通过最大化中心词和上下文词的概率。
9、FastText
(1)字符级别的n-gram
英语单词通常有其内部结构和形成方式。例如,我们可以从dog、dogs和dogcatcher的字面上推测他们的关系,这些词都有同一个词根dog。
在word2vec中,并没有利用构词学中的信息,这些单词内部形态信息因为它们被转换成不同的id丢失了。例如,dog和dogs分别用两个不同的向量表示,而模型中并未直接表达这两个向量之间的关系。鉴于此,fasttext提出了子词嵌入(subword embedding)的方法,从而试图将构词信息引入word2vec中的CBOW。
fasttext使用了字符级别的n-grams来表示一个单词。比如对于单词book,假设n的取值为3,则它的trigram有:
“<bo”,“boo”,“ook”,“ok>”
(2)fasttext的核心思想
仔细观察模型的后半部分,即从隐藏层输出到输出层输出,会发现它就是一个softmax线性多类别分类器,分类器的输入是一个用来表征当前文档的向量。
模型的前半部分,即从输入层输入到隐藏层输出部分,主要在做一件事情:生成用来表征文档的向量。它是通过叠加这篇文档的所有词及n-gram的词向量,然后取平均。叠加词向量背后的思想就是传统的词袋法,即将文档看成一个由词构成的集合。
于是fasttext的核心思想是:将整篇文档的词及n-gram向量叠加平均得到文档向量,然后使用文档向量做softmax多分类。这中间涉及两个技巧:字符级n-gram特征的引入以及分层softmax分类。
(3)模型架构
其中x1,x2,…,xN-1,xN表示一个文本中的n-gram向量,每个特征是词向量的平均值。这个CBOW类似,CBOW用上下文去预测中心词,而fasttext用全部的n-gram取预测指定类别。
fasttext模型输入一个词的序列(一段文本或者是一句话),输出这个词序列属于不同类别的概率。序列中的词和词组组成特征向量,特征向量通过线性变换映射到中间层,中间层再映射到标签。fasttext在预测标签时使用了非线性激活函数,但在中间层不使用非线性激活函数。
(4)面试要点
- fastText的核心思想是将整篇文档的词以及n-gram向量进行叠加求平均值,从而得到文档向量,然后再使用文档向量进行softmax多分类。
- 其模型架构有3层:输入层、隐含层和输出层。输入的是多个单词及n-gram特征,特征都是embedding过的,隐含层对特征向量进行求和取平均操作,输出层输出文档对应的类别。
- fastText有两个功能:文本分类、快速词向量训练。
(5)fastText相比于word2vec有什么优势?
- fastText使用字符级别的n-gram表示一个单词,即当两个词有相同的词缀时,可以学习到其语义之间的相似性,从而克服word2vec忽视单词内部形态特征的问题。
对于低频词生成的词向量效果会更好。因为它们的n-gram可以和其他词共享; - 对于训练词库之外的单词,仍然可以构建它们的词向量。我们可以叠加它们的字符级n-gram向量。
(6)FastText模型与CBOW模型区别
FastText在模型结构上和CBOW几乎是一样的,只是把预测中心词换成了预测类别,所以FastText是监督学习,需要标注语料类别,而CBOW是无监督学习。CBOW是将window_size的上下文词向量做平均,提取的是上下文词特征,而FastText是将一个句子所有词做平均,提取的是句子的特征。FastText可以看做是CBOW的一种拓展。
两点改进:
- subword n-gram。对于一个单词“google”,为了表达单词前后边界,我们加入<>两个字符,即变形为“<google>”。假设我们希望抽取所有的trigram信息,可以得到如下集合:G = {<go,goo,oog,ogl,gle,le>}。在实践中,我们往往会同时提取单词的多种n-gram信息,如2/3/4/5-gram。这样,原始的一个单词google,就被一个字符级别的n-gram集合所表达。这种操作对低频词/罕见词和一些在词典中都没有出现过的词或带有某些拼写错误的词会有比较好的效果。
- 层次softmax。这点和我们在word2vec中提到的层次softmax基本一样,只是在word2vec中,树是基于单词建立的,这里的树是基于文本分类建立的,因为两者预测目标不同。
(7)glove vs FastText
相似点:
- 静态词向量:与glove一样,FastText也是一种静态词向量生成模型,一个词在不同的上下文中拥有相同的向量表示。
- 目标:都致力于捕捉词语的语义关系,使得语义相近的词在向量空间中的距离较近。
差异点: - 核心思想:glove基于全局词共现信息,优化全局词汇表中的词向量;FastText基于word2vec,增加了子词信息(n-gram),更好地处理稀有词和形态学。
- 词形处理:glove对每个词生成一个唯一的词向量,忽略子词信息;fasttext对每个词生成多个子词(n-gram)的向量,词的向量是这些子词向量的组合。
- 稀有词表现:glove对词表之外的稀有词没有处理方式;fasttext通过子词分解,能够更好地处理稀有词和未登录词。
- 上下文依赖:glove不考虑词的形态特征;fasttext考虑词的内部结构,通过子词分解,捕捉更多语义信息。
三、大模型量化
1、什么是量化
通过降低模型参数的精度,将浮点数转换为整数或定点数,从而实现模型的压缩和优化。这样做的主要目的是减少模型的存储需求、加快推理速度,降低模型的计算复杂度,使得大模型能够高效地在资源受限的设备上运行,例如移动设备、嵌入式系统等场景。
2、精度
- bit是计算机中最小的数据单位,只能存储0或1两种状态。一个bit可以表示两种状态,即0或1;
byte 字节是计算机中常用的数据单位,由 8 个 bit 组成; - float 浮点数是一种用于表示实数的数据类型,通常由 32 位或 64 位的二进制数表示,其中 32 位的浮点数称为单精度浮点数,64 位的浮点数称为双精度浮点数;
- integer 整数是一种用于表示整数的数据类型,通常由 8 位、16 位、32 位或 64 位的二进制数表示,其中 8 位的整数称为字节,16 位的整数称为短整数,32 位的整数称为整数,64 位的整数称为长整数。
FP32
32位浮点数,通常用于表示单精度浮点数,符号位占1位,指数位占8位,尾数位占23位,精度约为7位有效数字。
FP16
16位浮点数,通常用于表示半精度浮点数,符号位占1位,指数位占5位,尾数位占10位,精度约为3位有效数字。
BF16
16位浮点数,通常用于混合精度浮点数,符号位占1位,指数位占8位,尾数位占7位,精度约为2位有效数字。
INT8
8位整数,通常用于表示8位整数,范围为-128到127。
3、量化对象
- 权重:量化权重可达到减少模型大小和内存占用空间。
- 激活:量化激活不仅可以大大减少内存占用,结合权重的量化还可以充分利用整数计算获得性能提升。
- KV cache:量化KV缓存对于提高长序列生成的吞吐量至关重要。
- 梯度:在训练深度学习模型时,梯度通常是浮点数,主要作用是在分布式计算中减少通信开销,同时也减少反向传播时的开销。
4、量化的类型
按照量化方法可以划分为线性量化、非线性量化(如对数量化)等多种方式,目前较为常用的是线性量化。其中线性量化又可以按照对称性划分为对称量化和非对称量化。
(1)对称线性量化
对称量化的特点是量化后的值中零点必须对应于原始值中的零。即量化操作的零点固定不变。这种方式通常使用两个参数(量化的最小值和最大值)来定义量化范围,而这个范围是以零为中心对称的。
(2)非对称性量化
非对称量化不强制要求量化后的零点对应于原始数据中的零点。这种量化方法使用三个参数来定义从原始数值到量化数值的映射关系:量化最小值、量化最大值和零点。这意味着量化操作可以有一个任意的零点,这个零点被映射到量化范围内的某个整数值上。
非对称量化特别适合于原始数据分布不关于零对称的情况,例如当数据集中包含大量正值或负值时。它允许更灵活的量化范围定义,以适应数据的实际分布,从而减少量化误差,提高量化后的模型精度。
4、量化的粒度
量化粒度决定了量化操作的精细程度。量化粒度影响着量化参数的共享方式,即量化的规模和范围。不同的量化粒度可以带来不同的精度和效率的权衡。
量化粒度有以下几种形式:
- per-tensor:整个张量或整个层级共享相同的量化参数(scale和zero-point)。这个方式的优点是存储和计算效率较高,但可能会导致精度损失,因为一个固定的量化参数难以覆盖所有数据的动态范围。
- per-channel:每个通道或每个轴都有自己的量化参数。这种方式可以更准确地量化数据,因为每个通道可以有自己的动态范围,但会增加存储需求和计算复杂度。
- per-group:在量化过程中,将数据分成块或组,每块或每组有自己的量化参数。
量化粒度的选择取决于模型的特定需求和目标硬件平台的特性。较小的量化粒度可以提供更高的精度,但可能会增加模型大小和推理延迟。例如,在TensorRT中,默认对激活值使用per-tensor量化,而对权重使用per-channel量化,这是基于多次实验得出的结论。
5、weight packing
权重打包涉及将多个权重参数合并或“打包”成一个量化单元,以减少模型的内存占用和提高计算效率。权重打包技术可以和量化结合使用,通过减少模型参数的数量来进一步压缩模型大小,同时保持模型性能。这通常通过将权重矩阵分解成较小的块,然后对这些块应用量化和编码技术来实现。
6、PTQ和QAT
训练后量化(PTQ)是一种在模型训练完成后应用的技术,目的是减少模型的大小和提高推理速度,同时尽量保持模型的性能。PTQ特别适合部署到资源受限的设备上,如移动设备和嵌入式设备。在PTQ中,量化操作可以应用于模型的输入、权重和激活值上。
量化感知训练(QAT)是一种在训练深度学习模型时引入量化操作的技术,目的是在减少模型大小和提高运行效率的同时,尽量减少量化带来的精度损失。
与传统的PTQ不同,QAT在训练过程中模拟量化的效果,使模型能够适应量化带来的信息损失,从而在实际应用量化时保持更高的性能。
四、大模型蒸馏
蒸馏就是将大模型学习到的概率分布直接复制到一个小模型中,被复制的模型称为教师模型,一般是参数量较大、性能很强的优秀模型,新模型称为学生模型,一般是参数量较少的小模型。
蒸馏时,教师模型会根据输入生成多个可能输出的概率分布,然后学生模型学习这个输入和输出的概率分布情况。经过大量训练,学生模型就可以模仿教师模型的行为,或者说学习到了教师模型的知识。
比如在图像分类任务中,给出一张图,教师模型可能会输出类似如下的概率分布:
- 猫 0.7
- 狗 0.4
- 车 0.1
然后把这张图和输出的概率分布信息一起提交给学生模型进行模仿学习。
因为蒸馏是把教师模型的知识压缩到一个更小更简单的学生模型中,新的模型可能会丢失一些信息;另外学生模型可能过度依赖教师模型,导致模型的泛化能力不佳。
为了让学生模型的学习效果更好,我们可以采用一些方法和策略。 - 引入温度参数:假设有一位老师讲课速度非常快,信息密度很高,学生可能难以跟上。这时如果老师放慢速度,简化信息,就会让学生更容易理解。在模型蒸馏中,温度参数起到的就是类似“调节讲课速度”的作用,帮助学生模型更好地理解和学习教师模型的知识。专业点说就是让模型输出更加平滑的概率分布,方便学生模型捕捉和学习教师模型的输出细节。
- 调整教师模型和学生模型的结构:一个学生想要从一个专家那里学点东西可能是很难的,因为他们之间的知识差距太大,直接学习可能听不懂,这时可以在中间加入一个老师,它既能理解专家的话,又能转化为学生可以听懂的语言。中间加入的这个老师可能是一些中间层或者辅助神经网络,或者这个老师可以对学生模型进行一些调整,让它能更匹配教师模型的输出。
五、TCP三次握手、四次挥手
1、TCP三次握手
- 目的:建立客户端和服务器之间的TCP连接,确保双方的接收和发送能力正常。
- 过程:
- SYN:客户端发送一个SYN同步包,告诉服务器它希望建立连接,并且客户端初始化一个序列号,此时客户端处于SYN_SEND状态;
- SYN-ACK:服务器收到SYN包后,回应一个SYN-ACK包,表示接受连接请求,同时给出自己的初始序列号,此时服务器处于SYN_RECEIVED状态;
- ACK:客户端收到服务器的SYN-ACK后,发送一个ACK包,确认收到服务器的序列号,此时,双方进入ESTABLISHED状态,连接正式建立。
2、TCP四次挥手
- 目的:正常关闭TCP连接,确保双方的数据传输完毕并且连接能被安全关闭。
- FIN:主动关闭连接的一方(比如客户端)发送一个FIN(结束)包,表示自己没有数据发送了。此时,客户端进入FIN_WAIT_1状态;
- ACK:接收方(服务器)收到FIN包后,回应一个ACK包,表示收到关闭请求。此时,服务器进入CLOSE_WAIT状态,客户端处于FIN_WAIT_2状态;
- FIN:服务器准备关闭连接时,发送一个FIN包给客户端,表示它的数据也发送完毕,准备关闭连接。此时,服务器进入LAST_ACK状态;
- ACK:客户端收到FIN包后,发送一个ACK包给服务器,确认关闭连接。此时,客户端进入TIME_WAIT状态,服务器进入CLOSED状态。客户端在TIME_WAIT状态等待2MSL(最大报文生存时间)后再完全关闭连接。
3、为什么需要三次握手
确保客户端和服务器双方都能确认连接的建立,并且彼此都知道对方的序列号,避免数据丢失或错误。
4、为什么四次挥手需要这么多步骤
需要四次挥手是因为TCP连接是全双工的(即可以同时发送和接收数据),每个方向的关闭必须单独处理。为了保证每一方的数据都能正常传输和接收,关闭必须是双方协商的过程。
5、什么是TIME_WAIT状态?
是为了确保服务器最后的ACK包能够到达客户端,避免网络延迟等问题导致的数据丢失或重复。在这个状态下,客户端需要等待一个较长时间确保链接完全关闭。
六、数据结构
- 浏览器的网络栈结构管理浏览历史
- 消息中间件用队列
- 树结构文件管理
七、数据集索引
- B-树索引:这种索引方式使得表中的每一行在索引上都有一个对应的值。B-树索引时数据库中最常用的索引类型,对于范围查询和排序操作非常有效。
- 哈希索引:哈希索引是基于哈希表的实现,它可以根据索引列对应的哈希值来快速获取表的记录行。哈希索引适用于等值查询,但对于范围查询和排序操作则效率较低。
- 普通索引:允许在定义索引的列中插入重复值和空值,主要用于提高查询效率。
- 唯一索引:确保索引列是唯一的,避免数据出现重复。
- 主键索引:是为主键字段创建的索引,一个表只能有一个主键,不允许有空值。
- 空间索引:是对空间数据类型的字段创建的索引,用于地理空间数据类型。
- 全文索引:主要用于在文本字段中进行全文搜索,查找文本中的关键字。
- 单列索引:只包含原表的一个列,可以是普通索引、唯一索引或全文索引。
- 多列索引(组合索引):在表的多个字段组合上创建一个索引,用于提高基于多个字段的查询效率。
八、负载均衡
1、负载均衡部署方案和策略
- 硬件负载均衡器:通常选择市场上已有的硬件负载均衡设备,如F5等。这些设备具有高性能、高稳定性、高可靠性等特点,但成本较高。
- 软件负载均衡器:软件负载均衡器是运行在服务器上的软件程序,如nginx等。它们通过配置来实现负载均衡,成本较低,灵活性更高。
2、负载均衡策略
- 轮询:按照顺序将请求依次分配给每个服务器,确保每个服务器都能平均分担负载;
- IP哈希:根据客户端的IP地址将请求分配给服务器,相同IP的客户端始终被分配到同一台服务器上,这对于某些需要保持会话的应用程序很有用;
- 加权轮询:为每个服务器分配一个权重值,根据权重值决定分配请求的比例,权重越高的服务器分配到的请求数量就越多。
- 最少连接:将请求分配给当前连接数最少的服务器,以确保每个服务器的负载尽可能平衡。
- 随机策略:通过系统随机函数,根据后台服务器列表的大小值来随机选择其中一台进行访问。
九、分布式系统部署方案
1、容器化方案
- 将应用程序打包为容器,每个容器包含应用程序的所有依赖项和运行环境。
- 使用容器编排工具(Docker和Kubernetes)在多个服务器上部署和管理容器。
- 提供了轻量级、可移植和可复制的部署方式,使得应用程序的迁移和扩展更加容易。
2、微服务架构
- 将大型应用程序拆分为多个小型、独立的服务。
- 每个服务都有自己的数据库和业务逻辑,可以独立部署和扩展。
- 通过服务间的通信和写作,实现复杂的应用程序功能。
3、分布式缓存
- 将数据缓存在多个服务器上,以提高读取和写入性能。
- 常见的分布式缓存系统有redis等。
十、从浏览器输入URL到页面展示发生了什么
- DNS解析:当用户在浏览器地址栏输入URL后,浏览器首先会向DNS服务器发出请求,解析该URL中的域名对应的IP地址。这个过程中,主域名服务器负责维护域名信息,辅助域名服务器作为备份提供解析服务,缓存域名服务器则从远程服务器取得答案并存放在高速缓存中以加快解析速度,转发域名服务器则负责非本地域名的查询。
- TCP连接建立:解析出IP地址后,浏览器会根据IP地址和默认端口80,通过TCP协议与服务器建立连接。
- HTTP请求与响应:连接建立后,浏览器会向服务器发送HTTP请求,请求内容是URL中域名后面部分对应的文件。服务器接收到请求后,会做出相应的响应,把请求的文件(通常是HTML文本)发送给浏览器。
- 页面渲染:浏览器收到服务器的响应后,会开始解析HTML文件并构建DOM树和CSS树,然后将这两个树合并成渲染树,根据渲染树进行布局和绘制,最终将页面内容展示出来。
十一、HTTP和HTTPS区别
- 安全性:HTTP协议传输的数据都是未加密的,因此它是明文传递,存在安全风险。而HTTPS协议是由SSL+HTTP协议构建的,可以进行加密传输、身份认证的网络协议,比HTTP协议安全;
- 端口:HTTP的URL由“http://”起始,默认端口号为80。而HTTPS的URL由“https://”起始,默认端口号为443;
- 资源消耗:由于HTTPS需要对传输的数据进行加密和解密,因此相较于HTTP,HTTPS需要消耗更多CPU和内存资源。
- 速度:由于HTTPS需要对数据进行加密和解密,因此其传输速度相较于HTTP会有所降低。
十二、KV缓存面试要点
1、KV缓存的作用
- 在transformer的解码阶段,每一步都需要计算注意力,涉及到所有历史token
- 问题:计算注意力时,每次都需要重新计算所有的key和value,导致计算重复和内存占用大
- 核心思想:缓存已计算的key和value,在后续解码时只需计算新token的key和value,从而减少计算量,提高推理效率。
2、工作流程
(1)初始化缓存
创建key和value缓存数组,通常是(num_layers,batch_size,num_heads,seq_length,head_dim)形状的张量。
(2)解码时缓存key-value
- 计算当前token的key和value并存入缓存
- 计算query,并直接与缓存中的key和value计算注意力,避免重复计算历史信息。
(3)更新缓存 - 在每一步的解码过程中,新的key-value追加到缓存中
- 只需计算新token的注意力分数,答复减少计算量。
十三、FlashAttention面试要点
flashattention是一种高效计算transformer自注意力的方法,专门优化显存占用和计算速度,适用于大规模transformer的推理和训练。
1、核心目标
(1)优化显存访问
- 传统注意力计算中,需要存储完整的QK^T以及softmax结果,显存占用较大。
- flashattention通过分块计算减少显存需求。
(2)提高计算效率
采用I/O亲和性优化,减少张量在GPU显存和缓存之间的数据传输,提高计算吞吐量。
2、核心技术
(1)分块计算
- 传统注意力机制计算 O ( N 2 ) O(N^2) O(N2) 复杂度的 Q K T QK^T QKT,显存占用哒。
- flashattention将查询和key-value分块,每次只在较小的block内计算
Q
K
T
QK^T
QKT和softmax。
优点:减少中间存储数据,降低显存占用。
(2)逐步softmax - 在计算softmax时,通常需要存储完整的
Q
K
T
QK^T
QKT结果,而flashattention采用逐块计算最大值,减少存储需求:
- 计算 Q K T QK^T QKT的最大值,并进行数据稳定性归一化。
- 逐块计算softmax,避免显存溢出。
(3)计算与存储分离
flashattention通过显存优化(I/O亲和性优化),减少数据加载和存储时间:
- 传统注意力计算中,每次计算需要频繁地显存加载key和value
- flashattention通过一次加载key-value并复用,减少显存贷款瓶颈。