网站中链接怎么做,都江堰网站建设,wordpress分类目录没有内容,百度问答怎么赚钱建议先阅读我之前的博客#xff0c;掌握一定的自然语言处理前置知识后再阅读本文#xff0c;链接如下#xff1a;
带你从入门到精通——自然语言处理#xff08;一. 文本的基本预处理方法和张量表示#xff09;-CSDN博客
带你从入门到精通——自然语言处理#xff08;二…建议先阅读我之前的博客掌握一定的自然语言处理前置知识后再阅读本文链接如下
带你从入门到精通——自然语言处理一. 文本的基本预处理方法和张量表示-CSDN博客
带你从入门到精通——自然语言处理二. 文本数据分析、特征处理和数据增强-CSDN博客
带你从入门到精通——自然语言处理三. RNN扩展和LSTM-CSDN博客
带你从入门到精通——自然语言处理四. GRU和seq2seq模型-CSDN博客 目录
五. Transformer中的自注意力机制和输入部分
5.1 自注意力机制
5.2 Transformer整体架构
5.3 输入部分
5.3.1 输入部分整体架构
5.3.2 嵌入层
5.3.2 位置编码器 五. Transformer中的自注意力机制和输入部分 Transformer模型于2017年在Google的论文《Attention is All You Need》中首次被提出transformer是一种基于自注意力机制Self-Attention和seq2seq架构的深度学习模型。
5.1 自注意力机制 传统的注意力机制中的Q、K、V向量三者是不同源的通常Q向量来自解码器而K、V向量来自编码器这种注意力机制被称为一般注意力机制或者交叉注意力机制而自注意力机制要求Q、K、V向量三者同源即三者都来自编码器或者解码器。 最早的自注意力机制的引入是应用到LSTM模型中的LSTM模型没有编码器和解码器的概念因此Q、K、V向量三者默认是同源的为了方便这里使用RNN模型代替LSTM模型进行描述其基本思想是一致的。 首先初始化RNN模型的隐藏状态h0以及上下文向量c0通常使用全0张量来进行初始化传统的RNN模型使用隐藏状态h0和当前时间步的输入x来更新隐藏状态但带有自注意力的RNN模型则使用上下文向量c0和当前时间步的输入x来更新隐藏状态此后使用上一个时间步的隐藏状态作为Q向量此前所有时间步的上下文向量作为K向量依次计算注意力分数通常忽略初始的全0上下文向量c0注意力分数的计算可以使用加性注意力、点积注意力等等随后对所有注意力分数使用softmax函数进行归一化并使用归一化后的注意力分数对所有V向量V向量也为所有时间步的上下文向量即K向量V向量做加权平均得到新的上下文向量RNN模型使用这一新的上下文向量以及当前时间步的输入继续更新隐藏状态依次往复。 带有自注意力的RNN模型的架构如下 5.2 Transformer整体架构 Transformer整体架构图如下 Transformer模型可以分为四个部分输入部分、编码器部分、解码器部分、输出部分后文会详细介绍各个部分。 Transformer模型主要有如下两个优势 并行计算与传统的RNN及其变体不同transformer模型使用自注意力机制并摒弃了序列化的计算过程允许模型并行处理整个输入序列有着更高的计算效率和更强的性能。 捕捉长距离依赖自注意力机制能够直接计算输入序列中任意两个元素之间的关系从而更好地捕捉长距离依赖缓解长程依赖问题。 5.3 输入部分
5.3.1 输入部分整体架构 Transfomer输入部分包含编码器源文本的嵌入层以及位置编码器、解码器目标文本的嵌入层以及位置编码器即下图部分 Transformer模型的最终输入为 上述公式中的input_embedding是指输入文本每个token经过Embedding层后得到的低维稠密词向量而positional_encoding则是输入文本中每个token的位置编码向量两个向量有着相同的长度在原论文中向量长度为512。 5.3.2 嵌入层 嵌入层Embedding Layer的作用是将输入文本中的每个token转换为一个固定长度的低维稠密词向量便于模型更好地捕捉到词汇的语义信息和语法信息。 嵌入层的代码实现如下
class MyEmbedding(nn.Module):def __init__(self, vocab_size, embedding_size):super().__init__()self.vocab_size vocab_sizeself.embedding_size embedding_sizeself.ebd nn.Embedding(vocab_size, embedding_size)def forward(self, x):# 扩大embedding后的词向量值return self.ebd(x) * math.sqrt(self.embedding_size)if __name__ __main__:ebd MyEmbedding(5, 3)t torch.randint(0, 5, (4,))print(ebd(t))
tensor([[-0.4648, -0.7602, 1.1441],[ 2.1027, 0.5997, 0.6691],[-0.6455, 0.0878, 2.3561],[-1.0119, 0.5721, -0.9876]], grad_fnMulBackward0) 5.3.2 位置编码器 RNN模型是依次输入各个token并进行编码因此RNN模型能够直接感知输入序列中各个token之间的位置关系而在transformer模型中对于输入序列是并行进行编码的因此它无法直接感知输入序列中各个token的位置关系所以transformer中引入了位置编码器Positional Encoding位置编码器能够为embedding后的词向量引入该词在输入序列中位置信息。 位置编码器能够将各个token在输入序列中的位置信息转换为一组向量这些向量会与embedding后的词向量相加在transformer中位置编码的公式如下 上式中pos是token在输入序列中的实际位置例如第1个token为0第2个token为1以此类推i是词向量长度的下标索引是词向量的长度transformer中的位置编码方式属于绝对位置编码。 因此post时该token的位置编码向量可以表示为 上述表达式中角频率w的取值为位置编码向量中的不同下标索引都对应了了一个不同的正余弦波。 Transformer中的位置编码方法有以下三个特点 1. 每个token的位置编码向量的下标索引越大其编码值所对应的sin和cos函数的角频率越小这一特点保证了每个token的位置编码向量唯一。 2. 位置编码向量的值是有界且连续的这也是正余弦函数的特性这一特点提高了模型的泛化能力使模型能够更好处理长度和训练数据不一致的序列。 3. 不同的位置编码向量可以通过线性变换得到即有这里的T表示一个线性变换矩阵具体的表达式如下 基于矩阵乘法和如下的三角函数的两角和公式可以即可推导出上述表达式。 上述表达式中的也被称为旋转矩阵这一特点使得位置编码向量不仅能表示一个token的绝对位置还可以表示该token与其他token的相对位置。 位置编码器的代码实现如下
class PositionalEncoding(nn.Module):def __init__(self, embedding_size, dropout_p0.1, max_len5000):super().__init__()self.dropout nn.Dropout(dropout_p)# pe.shape (max_len, embedding_size)pe torch.zeros(max_len, embedding_size)# pos,shape (max_len, 1)pos torch.arange(0, max_len).unsqueeze(1)# idx.shape (embedding_size // 2,)idx torch.arange(0, embedding_size, 2, dtypetorch.float32)# 利用广播机制进行计算pe[:, ::2] torch.sin(pos / (10000 ** (idx / embedding_size)))pe[:, 1::2] torch.cos(pos / (10000 ** (idx / embedding_size)))# self.register_buffer用于将一个张量注册为模型的缓冲区(buffer)# 缓冲区中的数据和模型的参数类似都会被保存到模型的状态字典中# 缓冲区中的数据不被视为可训练的参数即不会在优化器更新模型参数时被更新。self.register_buffer(pe, pe)def forward(self, x):x x self.pe[:x.size(1)]return self.dropout(x)if __name__ __main__:# embedding_size必须为偶数ebd MyEmbedding(5, 8)pe PositionalEncoding(8)t torch.randint(0, 5, (2, 4))print(pe(ebd(t)).shape)# torch.Size([2, 4, 8])