GPT模型中的计算

计算步骤

模型框架
在这里插入图片描述

  1. 输入
  2. Embedding
  3. 多层transformer的block (12层)
  4. 拿到两个输出端结果
  5. 计算损失
  6. 反向传播
  7. 更新参数

下面主要介绍上述步骤中的2.Embedding和3.transformer的block层

Embedding

Embedding层就是以one hot为输入、中间层节点为字向量维数的全连接层。而这个全连接层的参数,就是一个“字向量表”。实现text输入维度的变换。
在这里插入图片描述
Embedding操作(此处指text embedding)实际上是一个查表操作,one hot型的矩阵相乘,就像是相当于查表,于是它直接用查表作为操作,而不写成矩阵再运算,这大大降低了运算量。再次强调,降低了运算量不是因为词向量的出现,而是因为把one hot型的矩阵运算简化为了查表操作。

代码部分

 def input_emb(self,seqs, segs):
        # device = next(self.parameters()).device
        # self.position_emb = self.position_emb.to(device)
        return self.word_emb(seqs) + self.segment_emb(segs) + self.position_emb

包括text embed和position embed

  1. text embed
self.word_emb = nn.Embedding(n_vocab,model_dim)
self.word_emb.weight.data.normal_(0,0.1)

self.segment_emb = nn.Embedding(num_embeddings= max_seg, embedding_dim=model_dim)
self.segment_emb.weight.data.normal_(0,0.1)

text embed部分调用nn.Embedding,构造词/段向量矩阵,查表实现降维。

  1. position embed
self.position_emb = torch.empty(1,max_len,model_dim)
nn.init.kaiming_normal_(self.position_emb,mode='fan_out', nonlinearity='relu')
self.position_emb = nn.Parameter(self.position_emb)

position embed则是初始化一个包含输入序列长度作为维度的矩阵,然后作为参数训练学习位置信息。

Transformer的block层

在这里插入图片描述
由右上图知,GPT包含的block和Transformer的Decoder比较像,每个block包含两个子层:

  1. Sublayer1:Masked Multi-Head Attention(mask多头注意力层)
  2. Sublayer2:Feed Forward Network(FFN层)

每一个子层后均有残差连接和归一化操作

sublayer1:mask多头注意力层

输入: q, k, v, mask
计算注意力(如下图左所示):

  1. Linear(矩阵乘法)
  2. Scaled Dot-Product Attention
  3. Concat(多个注意力的结果)
  4. Linear(矩阵乘法)

残差连接和归一化操作:Dropout操作 → 残差连接 → 层归一化操作
在这里插入图片描述
我们以上图中的左图介绍整个注意力层的计算过程

扫描二维码关注公众号,回复: 14575221 查看本文章
  1. 矩阵乘法

将输入的Q,K,V与矩阵作乘法,得到新的Q,K,V
在这里插入图片描述

  1. Scaled Dot-Product Attention

这个操作名字看起来很长,实际上是 点乘+缩放+mask(可选)+Softmax+点乘 来计算注意力值的过程
第一步:Q与K的转置进行点乘,计算Q和K的相似度
第二步:缩放,除以一个比例因子
第三步:Mask矩阵中一部分值(可选,GPT中有此操作)
第四步:Softmax,转化为每个token的概率
第五步:Softmax后的矩阵值点乘V值,每个value通过概率加权,得到注意力分数。
在这里插入图片描述
在这里插入图片描述
补充:
Mask操作:masked_fill_(mask, value)
掩码操作,用value填充tensor中与mask中值为1位置相对应的元素。mask的形状必须与要填充的tensor形状一致。(这里采用-inf填充,从而softmax之后变成0,相当于看不见后面的词)
transformer中的mask操作

mask后可视化矩阵:
直观理解是每个词只能看到它之前的词(因为目的就是要预测未来的词嘛,要是看到了就不用预测了)
在这里插入图片描述

  1. Concat操作

综合多个注意力头的结果,实际上是对矩阵做变换:permute,reshape操作,视具体情况降维。(如下图红框中所示)

context = torch.matmul(self.attention,v)    # [n, num_head, step, head_dim]
context = context.permute(0,2,1,3)          # [n, step, num_head, head_dim]
context = context.reshape((context.shape[0], context.shape[1],-1))  
return context  # [n, step, model_dim]
  1. 矩阵乘法

一个Linear层,对注意力结果线性变换

整个mask多头注意力层的代码

def forward(self,q,k,v,mask,training):
        # residual connect
        residual = q
        dim_per_head= self.head_dim
        num_heads = self.n_head
        batch_size = q.size(0)

        # 1.线性变换,linear projection
        key = self.wk(k)    # [n, step, num_heads * head_dim]
        value = self.wv(v)  # [n, step, num_heads * head_dim]
        query = self.wq(q)  # [n, step, num_heads * head_dim]

        # split by head
        query = self.split_heads(query)       # [n, n_head, q_step, h_dim]
        key = self.split_heads(key)
        value = self.split_heads(value)  # [n, h, step, h_dim]
        
        #2.3 Scaled Dot-Product Attention计算注意力分数 + Concat连接多头注意力
        context = self.scaled_dot_product_attention(query,key, value, mask)    # [n, q_step, h*dv]
        
        #4.Linear层,对注意力结果线性变换
        o = self.o_dense(context)   # [n, step, dim]
        
        #残差连接和归一化操作
        o = self.o_drop(o)
        o = self.layer_norm(residual+o)
        return o

注意到,最后进行了残差连接和归一化操作,包括:

  1. Dropout操作
  2. 残差连接
  3. 层归一化操作

sublayer2:FFN前馈网络

主要是一个多层感知机结构

  1. 线性层(矩阵乘法)
  2. relu函数激活
  3. 线性层(矩阵乘法)

之后同样进行了残差连接和归一化操作,包括:

  1. Dropout操作
  2. 残差连接
  3. 层归一化操作
class PositionWiseFFN(nn.Module):
    def __init__(self,model_dim, dropout = 0.0):
        super().__init__()
        dff = model_dim*4
        self.l = nn.Linear(model_dim,dff)
        self.o = nn.Linear(dff,model_dim)
        self.dropout = nn.Dropout(dropout)
        self.layer_norm = nn.LayerNorm(model_dim)

    def forward(self,x):
        #1.2.线性层 + relu
        o = relu(self.l(x))
        #3.线性层
        o = self.o(o)
        #4.dropout
        o = self.dropout(o)
		#5.6.残差连接 + 层归一化
        o = self.layer_norm(x + o)
        return o    # [n, step, dim]

猜你喜欢

转载自blog.csdn.net/weixin_45577864/article/details/119722158