Transformer以及变体的解读
一.Transformer

1.前置处理
首先需要将输入进来的文字进行token的划分,然后对每个token做一个embedding处理,让其向量化(假设向量化为一个512维的向量),然后再对其进行一个位置编码(不同的位置获得不同的向量,再加到自己本身的词向量上),假设有10个token,我们这就获得了一个10x512的矩阵。
2.Encoder处理
拿着这些词向量,我们就到多头注意力进行计算,会先会有WQ,WK,WV三个512x512用于训练的矩阵,跟原本的词向量相乘,每个词向量就都反别获得了1x512的Q,K,V。
多头注意力中的多头其实值的是我们要对Q,K,V进行拆分来计算,这样有两大好处:
1.词向量中不同向量所关注的内容不同,有的可能更关注语义,有的则是语法
2.便于并行计算
我们计算的公式如下:

将自己的Q和其他所有词向量的K做点积,再除以一个常数防止梯度爆炸,最后套一层softmax表示一组权值(表示其余词向量对自己的重要程度的权值)

最后再用权值乘以每个词向量自己的V相加,就得到attention值啦,也就是下面这个著名公式:

我们把这64个attention再进行拼接,再乘一个权重矩阵W0,这样就还是得到10x512的新的词向量:

接下来我们就是对这个网络进行残差网络和归一化(LayerNorm)了
LN(x) = (x - mean) / std * gamma + beta,使其变为均值为0,方差为1的分布,保证不发生梯度爆炸或者梯度消失

最后再传入一个简单的前馈神经网络,里面有激活函数,让整个变化不再是线性变化,出来后还是要经过一个Add&Norm,最后输出
整个过程一般要经过6次,论文里是这么写的
3.Decoder处理
这里我们处理的数据是翻译之后的句子,我直接说怎么生成了:
1.生成
比如说处理一个翻译任务,最开始的词向量只会有一个<sos>表示序列开始,然后把这个小序列丢到掩码自注意力里面,还是经历子注意力那一套路子,算出最后的attention作为新的词向量,传进多头交叉注意力机制模块:
在这里面Decoder输出的attention只需要将词向量乘以wq2,得到q向量,Encoder就要乘以wk2和wv2,进行一个交叉注意力机制。
因为这里做的是翻译任务,所以才需要交叉注意力机制,使输入和输出进行一个对齐:

最后还是重复6轮,得到了nx512的向量组合。我们选取最后一个token的向量(因为我们是要预估这个向量的下一个向量,前面向量的存在只是为了让最后一个向量提取特征)经过最后的线性层,让512维的词向量经过一轮线性变换映射到一个更大的词语空间中(例如这里是个512x4096的矩阵,就能映射到4096个词语),最后softmax就行概率化,选取概率最大的就行了(也不一定,要看top_k,top_p,tempture)
大致流程:
第1步:
输入: [<bos>] (1×512)
┌─ 掩码自注意力: Q,K,V 全来自 [<bos>] → 输出 (1×512)
├─ 残差+LN
├─ 交叉注意力: Q 来自上面, K,V 来自 E → 输出 (1×512)
├─ 残差+LN
├─ FFN → 输出 (1×512)
└─ 残差+LN
→ Linear → softmax → 选出 "我"
第2步:
输入: [<bos>, 我] (2×512)
┌─ 掩码自注意力: Q,K,V 全来自 [<bos>, 我] → 输出 (2×512)
├─ 残差+LN
├─ 交叉注意力: Q(2×512), K,V 来自同一个 E → 输出 (2×512)
├─ 残差+LN
├─ FFN → 输出 (2×512)
└─ 残差+LN
→ 取最后一行 → Linear → softmax → 选出 "爱"
第3步:
输入: [<bos>, 我, 爱] (3×512)
┌─ 掩码自注意力 → 输出 (3×512)
├─ 交叉注意力: Q(3×512), K,V 还是同一个 E → 输出 (3×512)
├─ FFN → 输出 (3×512)
└─ ...
→ 取最后一行 → Linear → softmax → 选出 "你"
第4步:
输入: [<bos>, 我, 爱, 你] (4×512)
→ ... → 选出 <eos>
遇到 <eos>,停止。
2.训练
训练的话就是decoder我们直接输入最后的答案,但是会进行一个掩码操作:
| 位置 | 能看到 | 预测目标 |
|---|---|---|
| 位置1 | t1 | t2 |
| 位置2 | t1, t2 | t3 |
| 位置3 | t1, t2, t3 | t4 |
| 位置4 | t1, t2, t3, t4 | t5 |
| 位置5 | t1, t2, t3, t4, t5 | t6 |
上面的这些操作都是同时进行的,而不是按顺序进行的,所以要设置掩码,大概就是一个上三角矩阵,这样位置1在进行预测的时候就只是预测他之后的东西了:
位置1 位置2 位置3 位置4
位置1 [ ✓ ✗ ✗ ✗ ]
位置2 [ ✓ ✓ ✗ ✗ ]
位置3 [ ✓ ✓ ✓ ✗ ]
位置4 [ ✓ ✓ ✓ ✓ ]
二.GPT
1.Decoder-only

现代生成式的大模型基本都是这个结构,用户的输入直接从decoder进入,我们需要生成的输出就是在用户输入后生成的文本——user_prompt<分隔符>ai_message
encoder decoder的目的是处理seq2seq任务,也就是说,我需要的是从一个完整序列表示到另外一个完整序列表示的映射关系,翻译任务和总结任务是符合这个映射关系的,而gpt这种对话或问答任务实际上很难使模型总结出一个seq2seq映射,毕竟不同问题不同答案是多种多样的,不像翻译和总结一样有规律可循。而模型在已经学习到每个token的内在信息和关联后直接让decoder在用户输入上续写输出就可以了。
2. KV cache
按照上面生成的结构
比如说我先有第一个词-->嵌入词向量-->用自己的q去查询别人(自己)的k-->算加权v为attention-->保留这一轮的k1和v1->往后走输出第二个字符->嵌入词向量->第二个词用自己的q与查询别人(一共两个)的k(包含k1)-->依旧加权生成第二个token的attention-->保留k2,v2->....
第1步: 输入 "今"
算出 K₁, V₁ → 缓存 [K₁], [V₁]
用 Q₁ 和 [K₁] 算注意力 → 输出 "天"
第2步: 输入 "天"(只有新 token)
算出 K₂, V₂ → 缓存 [K₁,K₂], [V₁,V₂]
用 Q₂ 和 [K₁,K₂] 算注意力 → 输出 "很"
第3步: 输入 "很"
算出 K₃, V₃ → 缓存 [K₁,K₂,K₃], [V₁,V₂,V₃]
用 Q₃ 和 [K₁,K₂,K₃] 算注意力 → 输出 "好"
这样的好处就是在算第100个token的时候,前面99个token的kv值不需要再用权重矩阵算一遍,直接提取就可以了!!
ps:Encoder中算完一遍就不需要再算了,一直提供给Encoder就行:
Encoder
I love you
Decoder
<bos> [ 0.1 0.3 0.6 ] ← <bos> 应该关注源序列哪个词
我 [ 0.2 0.5 0.3 ] ← "我" 应该关注源序列哪个词
更多推荐



所有评论(0)