1.Transformer之序列到序列任务中的编码-解码

1.简介

在这里插入图片描述
seq2seq 就是一个任务,实现的是从序列到序列的映射,比如将我爱你翻译成i love you,实现这个任务的方式有很多种,不同的架构,不同的算法等等
encoder-decoder就是一种网络架构,实现编码和解码的功能

1.1通过rnn的编码解码机制引出attention机制


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上图缺失target——batch
在这里插入图片描述
上图缺失initial-state=
在这里插入图片描述

2.序列到序列的注意力机制(seq2seq with attention)

2.1 seq2seq with attention网络架构


在这里插入图片描述

1.相当于循环神经网络虽然后一个节点保留了前一个结点的部分信息,但是最后只是输出了一个特征c,用于解码器进行解码
2.可以让每一个结点都进行输出一个特征c,然后将这n个c进行累加,但是存在的问题是解码器可能不需要这么多的特征
3.为了解决上面的问题,将每个特征添加一个权重,就是上图中的alpha t1,即提取对应数据的特征,并计算对应特征的权重,最后进行累加,即注意力机制

2.2 seq2seq with Attention编码器Encoder原理

在这里插入图片描述
上图遮挡部分为激活函数alpha

2.3 seq2seq with Attention编码器Decoder原理

在这里插入图片描述
在这里插入图片描述

所以现在的问题就转换成了如何计算权重的问题了,如上面的ci的计算。eij为打分函数

2.4 seq2seq with Attention原理实现

transformer
注意力机制有很多种,软/硬/自/多头/全局/局部注意力机制
注意力机制的计算分为两步
1、在输入信息上计算注意力分布
2、根据注意力分布计算输入信息的加权平均
这里需要注意的是一个概念叫做查询序列,在预测n的时候,会查询1~n-1,即前面的所有序列,即预测序列的前序序列,即查询序列query,前序序列可以是动态生成的,也可以是可学习的参数
在这里插入图片描述
点奇函数是矩阵想成,导致方差太大,提出缩放点积解决这个问题

软注意力机制


加权平均,即不断的求数学期望
在软注意力机制中,x参与权值计算,然后经过softmax得到注意力的权值分布后,再与x进行累加,相当于一个人(x)干两个活。如上图所示。

键值对的注意力机制

在这里插入图片描述
就是不再是一个人干两个活了,使用key键来计算注意力分布,使用value值计算聚合信息
在这里插入图片描述
通过上图可以看出这个计算过程通过key先与向量计算注意力分布(分布通过softmax计算,即权值分布),然后再2与值value相乘,进行特征的提取,最后进行累加
注意这里如果key=value时,就相当于是软注意力机制了

自注意力机制

在这里插入图片描述
上面可以看到前序序列即查询序列Q和键K和值V都对输入通过W权重进行相乘,这样的话每次的更新Wq Wk Wv都能够保留x的相关信息。这里x相当于一个人干了三个人的活,其中q,k,v和上面的键值对注意力机制的计算方式相同
在这里插入图片描述
在这里插入图片描述
输出序列计算方式和键值对注意力机制一样,就是权值分布再乘上value,即乘上WvX

3.Transformer通用的特征提取器

在这里插入图片描述
inputs 源序列
outputs (probabilites)目标序列
outputs (shift right)当前预测的目标单词的前序序列
比如在目标序列[t1, t2,…tq-1, tq, tq+1, tq+2 …] 中要预测tq,那么在前序序列那里就是[t1,t2,…tq-1]

上面的图处理流程:

1.第一步进行词嵌入

在这里插入图片描述

先对词进行编码,然后将编码后的映射成向量便于计算,映射方式(即嵌入方式)有onehot编码,tfidf

词序列编码
1
2
3
4

此时input就是[1,2,3,4],对输入进行enbedding,使用onehot可以得到编码所对应的向量

编码向量.
11000
20100
30010
40001

可以知道这个纬度是什么样的,一般输入中有重复的字,但是onehot编码没有重复的,所以假设句子长度为N,而词嵌入维度为d_model,就可以得到输入序列的位置编码R(Nxd_model)
一般取长句子的N作为N,然后长度不够的句子进行padding补零,如果还有比N大的,那么只能截断了

输入序列,目标序列与输出序列

在这里插入图片描述

词嵌入与位置编码

输入序列词嵌入与位置编码

位置信息很重要,进行词嵌入就丢失了前后的依赖信息,即使把句子打乱了还是有相同的词嵌入,所以要加入位置信息,通过下面的pos两个公式可以将位置信息计算出来,公式来源于经验
将位置嵌入信息和词嵌入信息进行累加,就得到了所有的信息内容,即包含语义信息又包含位置信息
在这里插入图片描述
在这里插入图片描述
上图可以看出是位置嵌入和词嵌入相加得到的输入信息

输出序列词嵌入与位置编码

同上
在这里插入图片描述

第二步encoder网络结构

在这里插入图片描述
上图可以看出流程为:
+多头注意力机制
+残差网络的累积加归一化
+前馈神经网络即全连接网络,加残差网络加归一化
Nx表示不断的堆叠,经验值是堆叠6层

在这里插入图片描述

如上图,e0作为词嵌入和位置嵌入输入,然后通过e0通过编码器得到e1然后不断进行堆叠(n=6),并且可以注意到词嵌入和位置嵌入的向量维度是一致的可以直接进行相加
对于编码器部分看下图,上面的公式和下图是一一对应的
在这里插入图片描述

分出三个叉、由于对头注意力机制的核心是自注意力机制,就是有三个权重Wq Wk Wv

多头注意力机制与缩放点积

在这里插入图片描述
首先声明,上面的左右两幅图中的QKA不是等同的,左边的图是直接输入的QKV,右边的图是QKV进行线性变换后,才输入到模型里面,就像是一片一片的面包片,沾了不同的酱,然后最后通过contact进行拼接,然后再通过一个线性变化压回原始的Nxd_model维里面去
在这里插入图片描述
按照上面的公式进行解读,可以看到是直接将QKV输入到里面进行head的计算,省略了线性变化的部分

解码器pad掩码、解码器sequence掩码和编码器pad掩码

在这里插入图片描述

在这里插入图片描述

上图即mask部分部分,是掩膜版作用是在使用前序序列Q时,只能看到qt位置以前的序列,后面的序列都被mask了,比如在使用我爱你时,如果比如在预测我时,Q什么也看不到,当在预测爱时,Q可以看到我,当在预测你时,Q可以看到我爱。

在这里插入图片描述
上图可以看到,前序序列是Query序列,然后kv是成对出现的

编码器pad掩码

在这里插入图片描述
长度不够,用0填充

transformer结构代码部分

在这里插入图片描述

编码定义

在这里插入图片描述
在这里插入图片描述

解码器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

掩码

在这里插入图片描述

attention代码

class BertSelfAttention(nn.Module):
    def __init__(self, config):
        super().__init__()
        if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"):
            raise ValueError(
                f"The hidden size ({config.hidden_size}) is not a multiple of the number of attention "
                f"heads ({config.num_attention_heads})"
            )

        self.num_attention_heads = config.num_attention_heads
        self.attention_head_size = int(config.hidden_size / config.num_attention_heads)
        self.all_head_size = self.num_attention_heads * self.attention_head_size

        self.query = nn.Linear(config.hidden_size, self.all_head_size)  # W_q
        self.key = nn.Linear(config.hidden_size, self.all_head_size)    # W_k
        self.value = nn.Linear(config.hidden_size, self.all_head_size)  # W_v

        self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
        self.position_embedding_type = getattr(config, "position_embedding_type", "absolute")
        if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query":
            self.max_position_embeddings = config.max_position_embeddings
            self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size)

        self.is_decoder = config.is_decoder

    def transpose_for_scores(self, x):
        new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size)
        x = x.view(*new_x_shape)            # [batch_size, seq_length, num_heads, all_hidden_size]
        return x.permute(0, 2, 1, 3)        # [batch_size, num_heads, seq_length, all_hidden_size] 第一维度和第二维度换个顺序

    def forward(
        self,
        hidden_states,
        attention_mask=None,
        head_mask=None,
        encoder_hidden_states=None,
        encoder_attention_mask=None,
        past_key_value=None,
        output_attentions=False,
    ):
        mixed_query_layer = self.query(hidden_states)   # [batch_size, seq_length, all_hidden_size]                    

        # If this is instantiated as a cross-attention module, the keys
        # and values come from an encoder; the attention mask needs to be
        # such that the encoder's padding tokens are not attended to.
        is_cross_attention = encoder_hidden_states is not None

        if is_cross_attention and past_key_value is not None:
            # reuse k,v, cross_attentions
            key_layer = past_key_value[0]
            value_layer = past_key_value[1]
            attention_mask = encoder_attention_mask
        elif is_cross_attention:
            key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) 
            value_layer = self.transpose_for_scores(self.value(encoder_hidden_states))
            attention_mask = encoder_attention_mask
        elif past_key_value is not None:
            key_layer = self.transpose_for_scores(self.key(hidden_states))
            value_layer = self.transpose_for_scores(self.value(hidden_states))
            key_layer = torch.cat([past_key_value[0], key_layer], dim=2)
            value_layer = torch.cat([past_key_value[1], value_layer], dim=2)
        else:
            key_layer = self.transpose_for_scores(self.key(hidden_states))          #[batch_size, num_heads, seq_length, all_hidden_size]
            value_layer = self.transpose_for_scores(self.value(hidden_states))      #[batch_size, num_heads, seq_length, all_hidden_size]

        query_layer = self.transpose_for_scores(mixed_query_layer)                  #[batch_size, num_heads, seq_length, all_hidden_size]

        if self.is_decoder:
            # if cross_attention save Tuple(torch.Tensor, torch.Tensor) of all cross attention key/value_states.
            # Further calls to cross_attention layer can then reuse all cross-attention
            # key/value_states (first "if" case)
            # if uni-directional self-attention (decoder) save Tuple(torch.Tensor, torch.Tensor) of
            # all previous decoder key/value_states. Further calls to uni-directional self-attention
            # can concat previous decoder key/value_states to current projected key/value_states (third "elif" case)
            # if encoder bi-directional self-attention `past_key_value` is always `None`
            past_key_value = (key_layer, value_layer)

        # Take the dot product between "query" and "key" to get the raw attention scores.
        attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2))       #[batch_size, num_heads, seq_length, all_hidden_size]

        if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query":
            seq_length = hidden_states.size()[1]
            position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1)
            position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1)
            distance = position_ids_l - position_ids_r
            positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1)
            positional_embedding = positional_embedding.to(dtype=query_layer.dtype)  # fp16 compatibility

            if self.position_embedding_type == "relative_key":
                relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding)
                attention_scores = attention_scores + relative_position_scores
            elif self.position_embedding_type == "relative_key_query":
                relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding)
                relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding)
                attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key

        attention_scores = attention_scores / math.sqrt(self.attention_head_size)       #[batch_size, num_heads, seq_length, all_hidden_size]             
        if attention_mask is not None:
            # Apply the attention mask is (precomputed for all layers in BertModel forward() function)
            attention_scores = attention_scores + attention_mask

        # Normalize the attention scores to probabilities.
        attention_probs = nn.Softmax(dim=-1)(attention_scores)                          #[batch_size, num_heads, seq_length, all_hidden_size]

        # This is actually dropping out entire tokens to attend to, which might
        # seem a bit unusual, but is taken from the original Transformer paper.
        attention_probs = self.dropout(attention_probs)                                 #[batch_size, num_heads, seq_length, all_hidden_size]

        # Mask heads if we want to
        if head_mask is not None:
            attention_probs = attention_probs * head_mask

        context_layer = torch.matmul(attention_probs, value_layer)                      #[batch_size, num_heads, seq_length, all_hidden_size]

        context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
        new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,)     #[batch_size, seq_length, num_heads, all_hidden_size]维度再进心调转会来
        context_layer = context_layer.view(*new_context_layer_shape)                    #[batch_size, seq_length, all_hidden_size]进行维度的拼接,输入是这个维度,输出还是这个维度,这就是transformer结构

        outputs = (context_layer, attention_probs) if output_attentions else (context_layer,)

        if self.is_decoder:
            outputs = outputs + (past_key_value,)
        return outputs

在这里插入图片描述

分词的代码

class WordpieceTokenizer(object):                               #word to token 拆分的方式和下面的例子是一样的,input = "unaffable"` wil return as output :obj:`["un", "##aff", "##able"]`
    """Runs WordPiece tokenization."""

    def __init__(self, vocab, unk_token, max_input_chars_per_word=100):
        self.vocab = vocab
        self.unk_token = unk_token
        self.max_input_chars_per_word = max_input_chars_per_word

    def tokenize(self, text):
        """
        Tokenizes a piece of text into its word pieces. This uses a greedy longest-match-first algorithm to perform
        tokenization using the given vocabulary.

        For example, :obj:`input = "unaffable"` wil return as output :obj:`["un", "##aff", "##able"]`.

        Args:
          text: A single token or whitespace separated tokens. This should have
            already been passed through `BasicTokenizer`.

        Returns:
          A list of wordpiece tokens.
        """

        output_tokens = []
        for token in whitespace_tokenize(text):
            chars = list(token)
            if len(chars) > self.max_input_chars_per_word:
                output_tokens.append(self.unk_token)
                continue

            is_bad = False
            start = 0
            sub_tokens = []
            while start < len(chars):
                end = len(chars)
                cur_substr = None
                while start < end:
                    substr = "".join(chars[start:end])
                    if start > 0:
                        substr = "##" + substr
                    if substr in self.vocab:
                        cur_substr = substr
                        break
                    end -= 1
                if cur_substr is None:
                    is_bad = True
                    break
                sub_tokens.append(cur_substr)
                start = end

            if is_bad:
                output_tokens.append(self.unk_token)
            else:
                output_tokens.extend(sub_tokens)
        return output_tokens

在这里插入图片描述

decoder就是将上三角形,将t1 --> t2
t1,t2—>t3
t1,t2,t3—>t4
以此类推进行预测

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值