自学内容网 自学内容网

Transformer是怎样处理序列数据的?

Transformer模型最初是一种广泛应用于自然语言处理(NLP)和其他序列建模任务的架构。它由编码器(encoder)和解码器(decoder)组成。
Transformer结构

以下是Transformer模型输入和输出的详细介绍:

输入

1. 输入序列(Input Sequence)

输入的内容可以是一句话,比如"Hello, how are you?"。输入序列是指用分词器(tokenizer)将文本转化为一系列标识符(token IDs)。这些标识符是一个词汇表中不同词汇的索引。Transformer模型的词汇表大小(vocabulary size)取决于具体任务和所使用的预训练模型。词汇表大小表示模型在处理输入序列时可以识别的不同词汇或子词的数量。BERT-base和BERT-large模型的词汇表大小通常为30,000。GPT-2模型的词汇表大小为50,257。常见的预训练模型的词汇表大小在30,000到50,000之间。

  • Token IDs:因此,句子“Hello, how are you?”可能会被分词为[101, 7592, 1010, 2129, 2024, 2017, 1029, 102],其中每个数字对应一个词汇或标点的唯一ID。
  • Shape:输入序列的形状通常是(batch_size, sequence_length),其中batch_size是一次输入的序列数量,sequence_length是每个序列的长度。

2. 输入嵌入(Input Embedding)

嵌入层是一个查找表,将每个token ID映射到一个固定维度的向量表示。例如,对于词汇表大小为vocab_size,嵌入维度为embedding_dim,嵌入层是一个形状为(vocab_size, embedding_dim)的矩阵。

  • Shape:形状为(batch_size, sequence_length, embedding_dim),这是通过将Token IDs通过嵌入层(embedding layer)转换得到的。

3. 位置编码(Positional Encoding)

  • Purpose:因为Transformer没有内置的顺序信息,位置编码被添加到输入的token嵌入中,以引入序列的顺序信息。
  • Shape:形状为(sequence_length, embedding_dim),位置编码会与输入嵌入相加。
    在实际操作中,位置编码通常会扩展维度,使其形状为(1, sequence_length, embedding_dim),这样可以方便地与输入嵌入进行广播相加。

4. 注意力掩码(Attention Mask)

  • Purpose:掩码用于指示哪些位置的token应该被注意力机制忽略(例如,填充token)。
  • Shape:形状通常是(batch_size, sequence_length),每个位置上是0或1。

5. 目标序列(Target Sequence,解码器输入)

  • Token IDs:对于训练解码器时,目标序列也会被转化为token IDs,形状类似于输入序列。
  • Shape:形状通常是(batch_size, target_sequence_length)。

输出

1. 编码器输出(Encoder Output)

  • Contextualized Representations:编码器输出每个输入token的上下文表示,是一个三维张量。
  • Shape:通常是(batch_size, sequence_length, hidden_size),其中hidden_size是编码器的隐藏层大小。

2. 解码器输出(Decoder Output)

  • Logits:解码器在每个时间步输出的是未归一化的概率分布,即logits。
  • Shape:形状通常是(batch_size, target_sequence_length, vocab_size),vocab_size是词汇表的大小。

3. 注意力权重(Attention Weights)

  • Attention Scores:有时我们需要注意力机制的权重,表示模型在每个时间步对输入序列中每个位置的关注程度。
  • Shape:通常是(batch_size, num_heads, sequence_length, sequence_length),其中num_heads是多头注意力机制的头数。

Transformer的结构

编码器(Encoder)

  • 输入嵌入(Input Embedding):将输入token IDs转化为嵌入向量。
  • 位置编码(Positional Encoding):将位置编码添加到嵌入向量中。
  • 多头自注意力机制(Multi-head Self Attention):每个token与其他token进行交互以获得上下文表示。
  • 前馈神经网络(Feed-forward Neural Network):非线性变换每个位置的表示。
  • 层归一化(Layer Normalization)和残差连接(Residual Connection):提高训练稳定性和深度表示。

解码器(Decoder)

  • 输入嵌入和位置编码:与编码器类似,但输入是目标序列。
  • 多头自注意力机制:处理目标序列自身的依赖关系。
  • 编码器-解码器注意力(Encoder-Decoder Attention):将编码器的上下文表示整合到解码器中。
  • 前馈神经网络:类似于编码器。
  • 输出层(Output Layer):生成词汇表中每个词的概率分布。

Transformer的工作流程

  1. 编码器处理输入序列

    • 将输入token IDs通过嵌入层转化为嵌入向量,并添加位置编码。
    • 多头自注意力机制计算每个位置的上下文表示。
    • 前馈神经网络对上下文表示进行变换。
  2. 解码器生成输出序列

    • 解码器接收目标序列的前缀(训练时是完整的目标序列,推理时是已经生成的部分)。
    • 多头自注意力机制处理目标序列自身的依赖关系。
    • 编码器-解码器注意力将编码器的输出整合到解码器中。
    • 前馈神经网络对解码器的表示进行变换,生成logits。
    • 通过softmax层将logits转化为概率分布。

这种结构使得Transformer能够高效地处理长序列并捕捉远距离依赖关系,成为目前许多NLP任务的首选模型架构。
下面是一个简化的Transformer模型的编码器和解码器的实现代码,使用了PyTorch框架。这段代码实现了一个基础的Transformer架构,包括编码器和解码器部分。

Transformer编码器和解码器代码

import torch
import torch.nn as nn
import torch.nn.functional as F
import math

# 定义位置编码类
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding, self).__init__()
        self.pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        self.pe[:, 0::2] = torch.sin(position * div_term)
        self.pe[:, 1::2] = torch.cos(position * div_term)
        self.pe = self.pe.unsqueeze(0).transpose(0, 1)

    def forward(self, x):
        return x + self.pe[:x.size(0), :]

# 定义编码器层类
class EncoderLayer(nn.Module):
    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1):
        super(EncoderLayer, self).__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.linear1 = nn.Linear(d_model, dim_feedforward)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(dim_feedforward, d_model)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)

    def forward(self, src, src_mask=None, src_key_padding_mask=None):
        src2 = self.self_attn(src, src, src, attn_mask=src_mask, key_padding_mask=src_key_padding_mask)[0]
        src = src + self.dropout1(src2)
        src = self.norm1(src)
        src2 = self.linear2(self.dropout(F.relu(self.linear1(src))))
        src = src + self.dropout2(src2)
        src = self.norm2(src)
        return src

# 定义编码器类
class Encoder(nn.Module):
    def __init__(self, num_layers, d_model, nhead, dim_feedforward=2048, dropout=0.1):
        super(Encoder, self).__init__()
        self.layers = nn.ModuleList([EncoderLayer(d_model, nhead, dim_feedforward, dropout) for _ in range(num_layers)])
        self.norm = nn.LayerNorm(d_model)

    def forward(self, src, mask=None, src_key_padding_mask=None):
        for layer in self.layers:
            src = layer(src, src_mask=mask, src_key_padding_mask=src_key_padding_mask)
        return self.norm(src)

# 定义解码器层类
class DecoderLayer(nn.Module):
    def __init__(self, d_model, nhead, dim_feedforward=2048, dropout=0.1):
        super(DecoderLayer, self).__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.multihead_attn = nn.MultiheadAttention(d_model, nhead, dropout=dropout)
        self.linear1 = nn.Linear(d_model, dim_feedforward)
        self.dropout = nn.Dropout(dropout)
        self.linear2 = nn.Linear(dim_feedforward, d_model)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout1 = nn.Dropout(dropout)
        self.dropout2 = nn.Dropout(dropout)
        self.dropout3 = nn.Dropout(dropout)

    def forward(self, tgt, memory, tgt_mask=None, memory_mask=None, tgt_key_padding_mask=None, memory_key_padding_mask=None):
        tgt2 = self.self_attn(tgt, tgt, tgt, attn_mask=tgt_mask, key_padding_mask=tgt_key_padding_mask)[0]
        tgt = tgt + self.dropout1(tgt2)
        tgt = self.norm1(tgt)
        tgt2 = self.multihead_attn(tgt, memory, memory, attn_mask=memory_mask, key_padding_mask=memory_key_padding_mask)[0]
        tgt = tgt + self.dropout2(tgt2)
        tgt = self.norm2(tgt)
        tgt2 = self.linear2(self.dropout(F.relu(self.linear1(tgt))))
        tgt = tgt + self.dropout3(tgt2)
        tgt = self.norm3(tgt)
        return tgt

# 定义解码器类
class Decoder(nn.Module):
    def __init__(self, num_layers, d_model, nhead, dim_feedforward=2048, dropout=0.1):
        super(Decoder, self).__init__()
        self.layers = nn.ModuleList([DecoderLayer(d_model, nhead, dim_feedforward, dropout) for _ in range(num_layers)])
        self.norm = nn.LayerNorm(d_model)

    def forward(self, tgt, memory, tgt_mask=None, memory_mask=None, tgt_key_padding_mask=None, memory_key_padding_mask=None):
        for layer in self.layers:
            tgt = layer(tgt, memory, tgt_mask=tgt_mask, memory_mask=memory_mask, tgt_key_padding_mask=tgt_key_padding_mask, memory_key_padding_mask=memory_key_padding_mask)
        return self.norm(tgt)

# 定义完整的Transformer模型
class Transformer(nn.Module):
    def __init__(self, src_vocab_size, tgt_vocab_size, d_model=512, nhead=8, num_encoder_layers=6, num_decoder_layers=6, dim_feedforward=2048, dropout=0.1, max_len=5000):
        super(Transformer, self).__init__()
        self.src_embedding = nn.Embedding(src_vocab_size, d_model)
        self.tgt_embedding = nn.Embedding(tgt_vocab_size, d_model)
        self.positional_encoding = PositionalEncoding(d_model, max_len)
        self.encoder = Encoder(num_encoder_layers, d_model, nhead, dim_feedforward, dropout)
        self.decoder = Decoder(num_decoder_layers, d_model, nhead, dim_feedforward, dropout)
        self.fc_out = nn.Linear(d_model, tgt_vocab_size)

    def forward(self, src, tgt, src_mask=None, tgt_mask=None, src_key_padding_mask=None, tgt_key_padding_mask=None):
        src_embedded = self.positional_encoding(self.src_embedding(src))
        tgt_embedded = self.positional_encoding(self.tgt_embedding(tgt))
        memory = self.encoder(src_embedded, mask=src_mask, src_key_padding_mask=src_key_padding_mask)
        output = self.decoder(tgt_embedded, memory, tgt_mask=tgt_mask, memory_mask=src_mask, tgt_key_padding_mask=tgt_key_padding_mask, memory_key_padding_mask=src_key_padding_mask)
        return self.fc_out(output)

# 示例用法
# 假设我们有源语言和目标语言的词汇表大小分别为SRC_VOCAB_SIZE和TGT_VOCAB_SIZE
SRC_VOCAB_SIZE = 10000
TGT_VOCAB_SIZE = 10000
src = torch.randint(0, SRC_VOCAB_SIZE, (10, 32))  # (序列长度, 批次大小)
tgt = torch.randint(0, TGT_VOCAB_SIZE, (20, 32))  # (序列长度, 批次大小)

model = Transformer(SRC_VOCAB_SIZE, TGT_VOCAB_SIZE)
output = model(src, tgt)
print(output.shape)  # 输出形状应该为 (目标序列长度, 批次大小, 目标词汇表大小)

代码说明

  • PositionalEncoding:位置编码,用于给序列中每个标记提供位置信息。
  • EncoderLayer:编码器层,包含一个多头自注意力层和一个前馈神经网络。
  • Encoder:编码器,由多个编码器层堆叠而成。
  • DecoderLayer:解码器层,包含一个自注意力层、一个交叉注意力层和一个前馈神经网络。
  • Decoder:解码器,由多个解码器层堆叠而成。
  • Transformer:完整的Transformer模型,包括嵌入层、位置编码、编码器和解码器,以及最终的线性层用于生成输出。

示例用法

代码示例展示了如何初始化Transformer模型,并用随机生成的源序列和目标序列进行前向传播。输出的形状应该为[目标序列长度, 批次大小, 目标词汇表大小]。

这个实现是基础版本,实际应用中可能需要更多的细节处理,如掩码的正确设置、优化器和损失函数的定义等。


原文地址:https://blog.csdn.net/SisterRu/article/details/140531880

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!