大模型知识(二)transformer小知识
两种常见的预训练任务:
因果语言建模(来源于NNLM)
遮盖语言建模(来源于CBOW)
预训练没有先验知识,需要海量训练数据,时间和经济成本都很高。
因此,使用标注好的任务语料对模型进行二次训练(迁移学习),这个过程称为微调。
微调充分利用了预训练的知识,需要的数据量很少。
1. 总体结构:编解码结构:编码将输入序列变成等长的连续表示,自回归解码将连续表示变成一个输出序列;编解码都使用堆叠的自注意力机制和逐点全连接层

2. 编码层:6层相同的层,每一层有两个子层,第一个子层是多头自注意力层,第二个子层是逐点全连接前馈层。两个子层都包括一个残差连接和层归一化,即每个子层的输出是layernorm(x+sublayer(x))
3. 解码层:6层相同的层,每一层有三个子层,第一个子层是掩码多头注意力层,指的是因果掩码,预测不能依赖未来信息,第二个子层是多头自注意力层,其中query来自第一个子层,key value来自于编码器,第三个子层是逐点全连接前馈层。三个子层都包括一个残差连接和层归一化,即每个子层的输出是layernorm(x+sublayer(x))。
4. 注意力机制:将一个query和一组key-value对映射为一个output,其中query,keys,values,output都是向量。output等于value的加权和,每个value的权重由query和相应的key计算得来(query通过和key内积并softmax的方式,得到query和各个value的相似度,然后加权求和,得到一个dv维的向量)
K 是为了被检索而存在的特征,V 是为了被阅读而存在的内容。必须用 Q 去匹配 K,才能决定去读取哪个 V。
5. 缩放点积注意力:query和key的维度是dk,value的维度是dv。将一组queries组合成一个矩阵Q,同理得到K, V

为什么除以根号dk,因为q k 都是dk维度,
防止注意力分数值过大,落入softmax的饱和区,导致梯度消失

注意mask的位置在scale和softmax之间
6. 多头注意力:

b batch_size
n sequence_length
d hidden_size
h num_heads
k head_dim
输入张量x为:batch_size*sequence_length*hidden_size
1)线性投影
x*[hidden_size*hidden_size] 得到 Q K V (K和V的sequence_length要相等)
形状为batch_size*sequence_length*hidden_size
2)分头
hidden_size=num_heads*head_dim
对Q K V进行reshape和transpose操作,将注意力头的数量提前,成为批处理维度的一部分
形状为batch_size*num_heads*sequence_length*head_dim
3)计算注意力得分并加attention mask
关键:只有在 输入 softmax 之前 把无效位置设为 -inf,才能让它们的权重变成 0。如果在 softmax 之后再 mask,就无法保证概率和为 1,且无法真正“忽略”这些位置。
Q*K.T=batch_size*num_heads*sequence_length*sequence_length
4)加权求和
weight*V=batch_size*num_heads*sequence_length*head_dim
5)合并头并最终投影
output: batch_size*sequence_length*num_heads*head_dim-->batch_size*sequence_length*hidden_size
final_output: batch_size*sequence_length*hidden_size
多头注意力有三种应用:(1)kv来自编码器,q来自解码器 (2)自注意力中qkv来自同一个地方 (3)自回归解码中加入掩码机制(另外attention mask可以遮盖padding等防止模型关注)
import torch
import torch.nn as nn
import torch.nn.functional as F
import math
class mha(nn.Module):
def __init__(self,config):
super().__init__()
self.emb_dim=config.hidden_size
self.num_attention_heads=config.num_attention_heads
self.head_dim=self.emb_dim//self.num_attention_heads
self.q_proj=nn.Linear(self.emb_dim,self.emb_dim)
self.k_proj=nn.Linear(self.emb_dim,self.emb_dim)
self.v_proj=nn.Linear(self.emb_dim,self.emb_dim)
self.o_proj=nn.Linear(self.emb_dim,self.emb_dim)
def forward(self,x,attn_mask=None):
q=self.q_proj(x)
k=self.k_proj(x)
v=self.v_proj(x)
batch_size,seq_len,_=q.size()
q=q.view(batch_size,seq_len,self.num_attention_heads,self.head_dim).transpose(1,2)
k=k.view(batch_size,seq_len,self.num_attention_heads,self.head_dim).transpose(1,2)
v=v.view(batch_size,seq_len,self.num_attention_heads,self.head_dim).transpose(1,2)
# batch_size,self.num_attention_heads,seq_len,self.head_dim
attn_score=torch.matmul(q,k.transpose(-1,-2))
# batch_size,self.num_attention_heads,seq_len,seq_len
dk=k.size(-1)
attn_score=attn_score/math.sqrt(dk)
if attn_mask is not None:
attn_score=attn_score.masked_fill(attn_mask==0,-float('inf'))
weights=F.softmax(attn_score,dim=-1)
output=torch.matmul(weights,v)
# batch_size,self.num_attention_heads,seq_len,self.head_dim
output=output.transpose(1,2).contiguous().view(batch_size,seq_len,self.emb_dim)
output=self.o_proj(output)
return output
7. 逐点前馈网络
编码器和解码器都包括一个逐点前馈网络,包括一个线性层,一个RELU激活函数,一个线性层
8. 嵌入层和softmax
编码器和解码器的嵌入层都是将token映射为dmodel维向量
在解码器最后,对每个位置的输出向量(维度dmodel)施加一个线性变换(全连接层),将其映射到词汇表大小∣V∣的 logits 空间,再通过Softmax得到下一个 token 的概率分布:
P(wt∣w<t)=Softmax(Wout⋅ht)
transformer在三处共享权重矩阵:输入嵌入层,输出嵌入层,预softmax线性变换层,显著提升性能并减少参数量
在 Transformer 模型中,“在嵌入层将权重乘以根号dmodel”是一个关键的缩放技巧,其主要目的是平衡词嵌入(token embeddings)与位置编码(positional encodings)的尺度(scale),从而确保模型训练的稳定性。PyTorch 的 embedding 初始化使其向量长度与维度无关(恒定小值),而 PE 长度随 dmodel增长。通过 ×dmodel将 embedding 的尺度提升到与 PE 匹配,确保两者在相加时贡献均衡。
层后归一化:原使用方式

层后归一化:归一化层在跳跃连接外,在跳跃连接后
层前归一化:归一化层在跳跃连接内,在内部最前面
9. 位置编码

内部式子相同,只是sin和cos的区别

d代表位置编码向量的总维度数

对于同一个词的不同维度,2i代表偶数维,2i+1代表奇数维

由于有足够的数据,足够深的网络,所以模型可以训练整合位置和词向量的信息
由于词向量维度够高,所以'碰撞'发生的可能性很低
10 teacher forcing
在训练解码器时,使用真实的目标序列前一个词作为输入,而不是模型预测的词
- 加速训练,稳定收敛
- 支持并行计算(通过掩码防止信息泄露)
11 编码器模型(Auto-Encoding)
适合理解整个句子语义的任务,包括分类,NER,抽取式问答等
包括BERT DistilBERT(更小的模型) RoBERTa(更大数据量,更长时间) ModernBERT(2万亿个次元)
12 解码器模型(Auto-Regressive)
适合文本生成任务
13 编码器-解码器模型
适合给定输入生成新文本的任务,包括摘要,翻译,生成式问答等
T5(将所有的NLU NLG任务转换成seq2seq形式解决)
BART (同时结合了BERT和GPT的预训练过程)
M2M-100(语言对之间可能存在共享知识来处理小众语言之间的翻译)
BIGBIRD(通过线性扩展的稀疏注意力形式扩展可处理的文本长度)
14 两阶段推理过程
预填充:分词(词元);嵌入转换;初始处理
解码:注意力计算(回顾所有先前的词元以理解上下文);概率计算;词元选择;持续性检查
需要考虑的关键指标:1 首次响应时间TTFT 2 输出每词元的时间 TPOT 3 吞吐量 同时处理的请求数量 4 显存使用情况
15 一些新型模型结构
Mamba;RWKV;RetNet;Hyena
16 MoE结构
将特定FFN替换成MoE,使得模型可以在推理过程中仅激活部分参数,从而在不显著提升计算成本的同时实现对模型参数的扩展。
每个MoE包含K个专家,每个专家是一个FFN,对于输入,模型首先通过一个路由网络来计算各专家的权重,然后选择概率最高的k个激活,最后通过softmax计算出k个专家权重,没选中的权重置为0

17 手撕GQA

import math
import torch
import torch.nn as nn
class GQA(nn.Module):
def __init__(self,hidden_dim=128,head_size=8,key_head_size=4,dropout_rate=0.1):
super().__init__()
self.hidden_dim=hidden_dim
self.head_size=head_size
self.key_head_size=key_head_size
# key_head_size=1时就是MQA
self.head_dim=self.hidden_dim//self.head_size
self.q_proj=nn.Linear(hidden_dim,hidden_dim)
self.k_proj=nn.Linear(hidden_dim,self.key_head_size*self.head_dim)
self.v_proj=nn.Linear(hidden_dim,self.key_head_size*self.head_dim)
#这边注意
self.dropout=nn.Dropout(dropout_rate)
self.output_proj=nn.Linear(hidden_dim,hidden_dim)
def forward(self,X,attention_mask=None):
b,s,_=X.shape
Q=self.q_proj(X)
K=self.k_proj(X)
V=self.v_proj(X)
Q=Q.view(b,s,self.head_size,self.head_dim).transpose(1,2)
K=K.view(b,s,self.key_head_size,self.head_dim).transpose(1,2)
V=V.view(b,s,self.key_head_size,self.head_dim).transpose(1,2)
K=K.repeat_interleave(repeats=self.head_size//self.key_head_size,dim=1)
V=V.repeat_interleave(repeats=self.head_size//self.key_head_size,dim=1)
#torch.repeat_interleave(input, repeats, dim=None) 会对张量 input 中的每个元素进行指定次数的重复,并将结果拼接成一个新的张量。
#input:输入张量
#repeats:每个元素的重复次数(可以是整数或与 input 维度匹配的张量)
#dim:指定重复的维度(不指定时会先将张量展平)
# 把维度转换一下
attention_value=Q @ K.transpose(-1,-2)
attention_weight=attention_value/math.sqrt(self.head_dim)
if attention_mask is not None:
attention_weight=attention_weight.masked_fill(attention_mask==0,-float('inf'))
attention_weight=torch.softmax(attention_weight,-1)
attention_weight=self.dropout(attention_weight)
output=attention_weight @ V
output=output.transpose(1,2).contiguous()
#contiguous() 方法的作用是确保张量(tensor)在内存中是连续存储的
#某些操作(如 view())只支持连续张量,对不连续张量使用会报错
#b,s,head_num,head_dim
output=output.view(b,s,-1)
# 再把维度转换回来
output=self.output_proj(output)
return output更多推荐




所有评论(0)