【超详细含面试题】Transformer模型解析

一些细节的理解And面试题:

纯多头注意力输出的维度和原始输入是不一样的,需要一个线性变换进行维度转换

这样,多头注意力模块输出就和原输入一样了,然后经过一个MLP,这个MPL一般包含两个线形层,第一个将d_model扩展为4*d_model,第二个将维度降回去,用于进一步提取特征。

为何使用多头注意力,不是仅仅单头

多个头时,每个头有自己独立的权重矩阵,可以用来捕获不同的特征,分别求出结果之后经过一个Linear融合,达到更好的效果

三个Attention都起到了什么作用?

Encoder的Attention得分矩阵是每一个token对其他token的重视程度,乘以V之后得到一个对原始输入的加强表示。

解释一下Masked-Self-Attention如何起作用的

mask是一个三角矩阵,进行掩码操作之后,QK 相乘之后我们不想让decoder看到的部分就都是0,进行softMax计算注意力得分的时候这一部分就不会参与计算

Transformer为什么Q和K使用不同的权重矩阵生成,为何不能使用同一个值进行自身的点乘?

Q(查询)和K(键)使用不同的权重矩阵生成,是为了在计算注意力得分时能够捕捉到输入序列中不同的特征。如果使用同一个值进行自身的点乘,模型无法有效区分查询向量和键向量的不同特征,导致注意力机制失去灵活性和区分能力。因此,通过不同的权重矩阵生成Q和K,可以增强模型的表达能力,确保注意力机制能够更好地识别和利用输入序列中的信息。

QKV的深入理解:Q是查询,KV是待查询信息所以交叉注意力的时候Q来自解码器,旨在从Encoder过来的全局信息中提取一些有用信息。

Transformer局限性

decoder解码的时候依赖的是之前自回归生成的信息,所以用于补全文本的能力就会稍微差一点,以及Attention组件计算消耗非常大,所以后来也出现了一批用于简化attention的方法

整体架构

Transformer是一个黑盒模型,经典的应用是输入一串文字序列,转换出另外的文字序列,比如说翻译任务

本质上这个黑盒就是一个Encoder-Decoder架构,先来看一个概览图:

架构图如下图所示

transformer的结构可以分为 输入层、编码层、解码层、输出层,我们接下来逐一进行分析

输入层

输入层为将自然语言输入进行词嵌入后和位置嵌入相加得到的结果

词嵌入

以翻译任务为例子,我们的直接输入是“我是学生”,然而,机器无法直接解读汉字字符,需要进行向量表达,我们常见的表达方式包括:

  • 稀疏表达:one-hot编码,对每一个word都用进行独热编码标识,缺点很多,首先占用空间很大,其次含义相近的字很难在向量空间相似
  • 稠密表达:pytorch中调用的nn.embedding()函数就是一种稠密表达,可以在训练过程中找到合适参数,使得输入(有格式要求)变为稠密表达,同时含义相近的词在向量空间上距离也会很近。

Extension:

  • nn.embedding() 在forward中,输入的x应该是 输入的编号序列,假如我们有字典映射:[0:我,1:是,2:学,3:生],那么输入序列则会被映射为[0,1,2,3];nn.embedding(),的输入正是这种序列,所以进行数据处理的时候需要自行构建字典映射。
  • 在《机器学习方法》(李航著)  中,作者提到Transformer的输入层模块编码方式为one-hot向量乘以一个线性变换矩阵,这种实现方法和 nn.embedding()有区别,但是殊途同归得到了稠密向量,相比之下nn.embedding()更为高效。

代码实现

class Embeddings(nn.Module):
    """
    类的初始化
    :param d_model: 词向量维度,512
    :param vocab: 当前语言的词表大小
    """
    def __init__(self, d_model, vocab):

        super(Embeddings, self).__init__()
        # 调用nn.Embedding预定义层,获得实例化词嵌入对象self.lut
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model  #表示词向量维度

    def forward(self, x):
        """
        Embedding层的前向传播
        参数x:输入给模型的单词文本通过此表映射后的one-hot向量
        x传给self.lut,得到形状为(batch_size, sequence_length, d_model)的张量,与self.d_model相乘,
        以保持不同维度间的方差一致性,及在训练过程中稳定梯度
        """
        return self.lut(x) * math.sqrt(self.d_model)

位置嵌入(position embedding)

目前而言,位置嵌入和Transformer架构强绑定,其他架构虽然零星使用,但是未能成为主流方案。【位置编码领域存在较大改进空间,需要在之后的深入学习之后了解其原理】

Transformer完全依赖自注意力机制(Self-Attention)处理输入序列,而自注意力对输入元素的排列是位置无关的。例如:

  • 输入序列 [A, B, C] 和 [B, A, C] 经过自注意力计算后,输出结果可能无法体现顺序差异;
  • 句子 I like the movie, but hate the ending 和 I hate the movie, but like the ending 的单词相同但顺序不同,语义截然相反,模型必须依赖位置信息区分两者

代码实现

class PositionalEncoding(nn.Module):
    """实现Positional Encoding功能"""
    def __init__(self, d_model, dropout=0.1, max_len=5000):
        """
        位置编码器的初始化函数
        :param d_model: 词向量的维度,与输入序列的特征维度相同,512
        :param dropout: 置零比率
        :param max_len: 句子最大长度,5000
        """
        super(PositionalEncoding, self).__init__()
        # 初始化一个nn.Dropout层,设置给定的dropout比例
        self.dropout = nn.Dropout(p=dropout)

        # 初始化一个位置编码矩阵
        # (5000,512)矩阵,保持每个位置的位置编码,一共5000个位置,每个位置用一个512维度向量来表示其位置编码
        pe = torch.zeros(max_len, d_model)
        # 偶数和奇数在公式上有一个共同部分,使用log函数把次方拿下来,方便计算
        # position表示的是字词在句子中的索引,如max_len是128,那么索引就是从0,1,2,...,127
        # 论文中d_model是512,2i符号中i从0取到255,那么2i对应取值就是0,2,4...510
        # (5000) -> (5000,1)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值