首页 > 其他分享 >Transformer

Transformer

时间:2023-12-01 16:14:45浏览次数:53  
标签:Transformer torch self attention value query model

Attention

什么是注意力机制?

对于人类来说,注意力机制是在注意力有限的情况下,只关注接受信息的一部分,而忽略其他部分。
对于Transformer来说,以NLP为例,注意力机制就是对于当前token来说,为其所在序列中
对任务而言更重要的元素赋予更高权重(注意力)。

感知机可以认为是对不同选项赋予不同优先级(权重),以做出最终决策:

而注意力机制是对一个句子/一张图片内部元素赋予不同注意力(权重)的过程.

关于\(Q,K,V\)的Intuition

\(key-value\)可以理解给定一个键值对(map),对于一个询问\(query\),我们需要计算\(query\)作为键,
在给定键值对\(key-value\)对中其值是多少。例如我们有乒乓球选手各项能力以及其胜率的键值对:

如果查询是一个新选手的各项能力,我们想知道其对应胜率。

为回答这个问题,我们大概率不能在\(key-value\)中找到与\(query\)等值的\(key\),进而直接得到\(value\).

一个解决方案是求解\(query\)与所有\(key\)的相似度,在用相似度与\(value\)相乘,用最终的和作为\(qeury\)对应的值。
例如:

假设这位选手和键值对表中选手的相似度为\(0.7, 0.2, 0.1\), 那么我们可以预测这位选手的胜率为
\(0.7\times 0.7 + 0.2\times 0.5 + 0.1\times 0.8 = 0.67\).

对于Transformer来说,相似度是使用向量点积量化的。

什么是self-attention

以NLP问题为例,当计算注意力数值时,\(Q, K, V\)均来自某个句子序列自身(self).

Transformer

self-attention

An attention function can be described as mapping a query and a set of key-value pairs to an output, where the query, keys, values, and outputs are all vectors. The output is computed as a weighted sum of values, where the weight assigned to each value is computed by a compatibility function of the query with the corresponding key.

输入: 向量序列; 输出: 相同个数的向量,每个向量均考虑了所有输入向量.

具体的计算过程如下,以\(b_1\)为例:根据\(a_1\),考虑序列所有元素与\(a_1\)的相关性(权重越大相关性越大,将注意力集中
在相关性大的元素)

接下来的问题是,两个向量之间的相关程度\(a\)如何量化呢?一个方法是用两个向量的点积:

注意Transformer中在计算点积之前,通过线性投影矩阵将输入向量投影至\(query\)和\(value\)空间.
通过计算投影至\(query\)和\(value\)空间的向量点积,我们得到输入序列各元素与\(a_1\)的相关程度.
接着将相关程度通过\(Softmax\),得到归一化后的注意力权重. (非必须,使用\(ReLU\)或其他激活函数也可以,
只是Transformer是这样设计的)

根据得到的attention scores,从序列中提取与\(a_1\)有关的信息:

self-attention矩阵计算

Transformer中的self attention相比RNN的一个优势是计算可以并行化.

从\(a_i\)到\(Q, K, V\)的计算过程: \(Q = W^q I, K = W^k I, V = W^v I\).

通过\(Q, K\)计算attention score: \(A = Q K^T, A' = softmax(A)\).

通过注意力分数\(A'\)和\(V\)计算self attention的输出: \(O = V A'\):

综上,self attention的计算过程的矩阵乘法表示:

代码过程如下:

Self-attention

import torch 
import torch.nn as nn
import math 

NEG_INF = float('-inf')

class ScaledDotProductAttention(nn.Module):
def init(self,
d_model: int,
d_query_key: int,
d_value: int):
"""
d_model: 输入的词嵌入向量的长度
d_query_key: 单个query, key的向量长度
d_value: 单个value向量的长度
"""
super(ScaledDotProductAttention, self).init()
# key, query, value对应的线性投影矩阵
self.wk = nn.Parameter(torch.zeros(d_model, d_query_key))
self.wq = nn.Parameter(torch.zeros(d_model, d_query_key))
self.wv = nn.Parameter(torch.zeros(d_model, d_value))
self.div = math.sqrt(d_query_key) # 长的Q, K的点积数值过大会让softmax饱和
self.softmax = nn.Softmax(dim=2)

def forward(self, 
            mask: torch.Tensor,
            x_key_value: torch.Tensor,
            x_query: torch.Tensor):
    """ 
    mask: bool Tensor, 为true的地方表示允许分配注意力
    """
    # 对于Encoder, Q, K, V的输入相同
    # 对于Decoder, K, V来自Encoder最后一层输出, Q来自翻译后的token
    if x_query is None:
        x_query = x_key_value  
    
    # linear transform
    k = x_key_value @ self.wk
    v = x_key_value @ self.wv 
    q = x_query @ self.wq
    
    # batch matrix multiply: Q K.transpose(1, 2), 得到注意力分数
    attention = torch.einsum('nik, njk -> nij', q, k) / self.div
    attention = torch.where(mask, attention, NEG_INF)  
    attention = self.softmax(attention)
    output = torch.einsum('nij, njk -> nik', attention, v)
    return output 

Multi-head self-attention

当我们获得\(Q, K, V\)时,我们可以用线性投影矩阵将其投影至多个空间,接着并行计算各个空间的
attention score, output. 最后将所有output链接作为最终输出.

这样做的目的是一方面模拟了卷积操作多个输出channel; 每个\(Q K^T\)可以认为是从一个视角的注意力
分布,计算不同形式的相关性可以获得不同维度的信息。

对\(Q, K, V\)做进一步投影,计算各自子空间中的attention score, output:

最后将输出concatenate, 并用一个线性变换\(W^O\)将信息投影至输出空间.

代码如下:

Multi-head Self-attention

import torch 
import torch.nn as nn 

class MultiHeadAttention(nn.Module):
def init(self,
num_heads: int,
d_model: int,
p_drop: float=0.1):
super(MultiHeadAttention, self).init()

    # num_heads个注意力模块并行计算, 考虑残差连接以及计算效率每个头的输出维度为d_model(输入维度) / num_heads
    self.multi_head_attention = nn.ModuleList(
        ScaledDotProducAttention(d_model,
                                 d_model // num_heads,
                                 d_model // num_heads)
        for _ in range(num_heads)
    )
    self.wo = nn.Parameter(torch.zeros(d_model, d_model))  # value空间线性投影至output空间
    self.dropout = nn.Dropout(p_drop)
    
def forward(self,
            mask: torch.Tensor,
            x_key_value: torch.Tensor,
            x_query: torch.Tensor=None):
    output = torch.concatenate([
        attention(mask, x_key_value, x_query)
        for attention in self.multi_head_attention
    ], dim=2)
    output = output @ self.wo
    output = self.dropout(output)
    # residual connection
    output += x_key_value if x_query is None else x_query
    # layer normalization
    return torch.layer_norm(output, normalized_shape=output.size()[1:])

Positional Encoding

上述计算过程均可以用矩阵乘积实现,其中并不设计每个输入元素的位置信息(交换两个元素的位置对输出没有影响).

而语句或图片等信息是具有位置的结构信息,Positional Encoding通过对每个输入向量加入一个
唯一的等长向量,嵌入作为其位置特征。

Encoder

输入一串向量,输出相等数目向量。

代码实现:

EncoderLayer & Encoder

class EncoderLayer(nn.Module):
    """ 
    x --> Multi-head Attention --> residual & layer norm
    --> Feed Forward --> residual & layer norm
    """
    def __init__(self,
                 num_heads: int,
                 d_model: int,
                 d_ffn: int,
                 p_drop: float=0.1):
        """ 
        d_model: 输入特征向量(embedding)长度
        d_ffn: FFN隐藏层维度
        """
        super(EncoderLayer, self).__init__()
        self.muti_head_attention = MutiHeadAttention(num_heads, d_model, p_drop)
        self.feed_forward = PositionWiseFeedForward(d_model, d_ffn, p_drop)
def forward(self, 
            padding_mask: torch.Tensor,
            x: torch.Tensor):
    """ 
    x: embedding / 上一层EncoderLayer
    """
    x = self.multi_head_attention(padding_mask, x)
    x = self.feed_forward(x)
    return x 

class Encoder(nn.Module):
"""
N x EncoderLayer
"""
def init(self,
num_layers: int,
num_heads: int,
d_model: int,
d_ffn: int,
p_drop: float=0.1):
super(Encoder, self).init()
self.layers = nn.ModuleList(
EncoderLayer(num_heads, d_model, d_ffn, p_drop)
for _ in range(num_layers)
)

def forward(self, 
            padding_mask: torch.Tensor,
            src: torch.Tensor):
    for layer in self.layers:
        src = layer(padding_mask, src)
    return src 

Decoder

Transformer中的Decoder是自回归的(Autoregressive), 接受来自Encoder的输入的同时,输出预测句子的单词token, 并且token作为
下一个单词预测的输入. 也就是说AT Decoder的输出逐个生成,且本次输出作为下一次预测的输入.

代码实现:

Masked Self-attention

对于输出\(b_i\)来说, 计算时只考虑\(a_{1\sim i}\).

why? :

masked代码实现: 在\(i\)之后的位置用一个很小的数覆盖,之后的Softmax处理会近似忽略这些位置.

Cross-attention

Decoder中的Self-attention的\(K, V\)来自Encoder输出,\(Q\)来自自身.

具体计算过程:

Decoder代码:

DecoderLayer & Decoder

class DecoderLayer(nn.Module):
    """ 
    x - Masked Multi-head Attention - q 
    (k, v), q - Multi-head Attention - redidual & layer norm - 
    FFN - residual & layer norm 
    """
    def __init__(self,
                 num_heads: int,
                 d_model: int,
                 d_ffn: int,
                 p_drop: float=0.1):
        """ 
        d_ffn: FFN隐藏层维度 
        """
        super(DecoderLayer, self).__init__()
        self.masked_multi_head_attention = MultiHeadAttention(num_heads, d_model, p_drop)
        self.muli_head_attention = MultiHeadAttention(num_heads, d_model, p_drop)
        self.feed_forward = PositionWiseFeedForward(d_model, d_ffn, p_drop)
def forward(self,
            padding_mask: torch.Tensor,
            mask: torch.Tensor,
            x_decoder: torch.Tensor,
            x_encoder: torch.Tensor):
    x_decoder = self.masked_multi_head_attention(mask, x_decoder)
    output = self.muli_head_attention(padding_mask, x_encoder, x_decoder)
    output = self.feed_forward(output)
    return output 

class Decoder(nn.Module):
"""
N x Decoder Layer
"""
def init(self,
num_layers: int,
num_heads: int,
d_model: int,
d_ffn: int,
p_drop: float=0.1):
super(Decoder, self).init()
self.layers = nn.ModuleList(
DecoderLayer(num_heads, d_model, p_drop)
for _ in range(num_layers)
)

def forward(self, 
            padding_mask: torch.Tensor,
            mask: torch.Tensor,
            tgt: torch.Tensor,
            encoder_out: torch.Tensor):
    for layer in self.layers:
        tgt = layer(padding_mask, mask, tgt, encoder_out)
    return tgt 

Transformer完整架构

代码实现: https://github.com/CodesChangeHair/LearningDL/tree/main/Transformer


参考资料

标签:Transformer,torch,self,attention,value,query,model
From: https://www.cnblogs.com/w-like-code/p/17861948.html

相关文章

  • 简化版Transformer来了,网友:年度论文
    前言 从大模型的根源开始优化。本文转载自机器之心仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV各大方向专栏与各个部署框架最全教程整理【CV技术指南】CV全栈指导班、基础入门班、论文......
  • ENTROFORMER: A TRANSFORMER-BASED ENTROPY MODEL基于transformer的熵模型
    目录简介模型核心代码性能实验简介\(\quad\)由于cnn在捕获全局依赖关系方面效率低,因此该文章提出了基于tansformer的熵模型——Entoformer;并针对图像压缩进行了top-kself-attention和adiamondrelativepositionencoding的优化;同时使用双向上下文模型加快解码。模型核心代......
  • 简化版Transformer :Simplifying Transformer Block论文详解
    在这篇文章中我将深入探讨来自苏黎世联邦理工学院计算机科学系的BobbyHe和ThomasHofmann在他们的论文“SimplifyingTransformerBlocks”中介绍的Transformer技术的进化步骤。这是自Transformer开始以来,我看到的最好的改进。大型语言模型(llm)可以通过各种扩展策略扩展其功......
  • transformer中decoder到底是串行还是并行
    在Transformer中,Decoder部分内部的不同层通常可以并行工作,这意味着每个Decoder层可以同时处理整个序列。比如,在处理Self-Attention时,模型可以同时计算所有位置的注意力权重。但在生成输出序列时,尽管Decoder内部的不同层可以并行工作,模型仍然需要按顺序逐步生成每个词。这是因为Tr......
  • Meta对Transformer架构下手了:新注意力机制更懂推理
    前言 作者表示,这种全新注意力机制(Sytem2Attention)或许你也需要呢。本文转载自机器之心仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV各大方向专栏与各个部署框架最全教程整理【CV技术......
  • nn.transformer
     torch上给的案例transformer_model=nn.Transformer(nhead=16,num_encoder_layers=12)#创建一个具有16个注意力头和12个编码器层的Transformer模型src=torch.rand((10,32,512))#创建一个形状为(10,32,512)的随机输入张量,代表序列的编码器输入tgt=torch.rand......
  • 无依赖安装sentence-transformers
    安装pipinstall--no-cache-dirtorch==1.8.0+cpu-fhttps://download.pytorch.org/whl/torch_stable.htmlpipinstalltransformerstqdmnumpyscikit-learnscipynltksentencepiecepipinstall--no-depssentence-transformers可以使用pipdeptree查看依赖......
  • 论文阅读:Point Cloud Transformer
    PointCloudTransformer摘要不规则的领域和缺乏排序使得设计用于点云处理的深度神经网络具有挑战性。本文提出了一个名为PointCloudTransformer(PCT)的新型框架,用于点云学习。PCT以Transformer为基础,Transformer在自然语言处理中取得了巨大的成功,并在图像处理中显示出巨大的潜力......
  • 自然语言处理预训练—— 来自Transformers的双向编码器表示(BERT)
    我们已经介绍了几种用于自然语言理解的词嵌入模型。在预训练之后,输出可以被认为是一个矩阵,其中每一行都是一个表示预定义词表中词的向量。事实上,这些词嵌入模型都是与上下文无关的。让我们先来说明这个性质。 从上下文无关到上下文敏感ELMo(EmbeddingsfromLanguageModels)是......
  • 机器学习——Transformer
    10.6.2节中比较了卷积神经网络(CNN)、循环神经网络(RNN)和自注意力(self-attention)。值得注意的是,自注意力同时具有并行计算和最短的最大路径长度这两个优势。因此,使用自注意力来设计深度架构是很有吸引力的。对比之前仍然依赖循环神经网络实现输入表示的自注意力模型 (Cheng etal.,......