江大白 | 基于Pytorch框架,从零实现Transformer模型实战(建议收藏!)

本文来源公众号“江大白,仅用于学术分享,侵权删,干货满满。

原文链接:基于Pytorch框架,从零实现Transformer模型实战

以下文章来源于微信公众号:小喵学AI

作者:郭小喵

链接:https://mp.weixin.qq.com/s/XFniIyQcrxambld5KmXr6Q

本文仅用于学术分享,如有侵权,请联系后台作删文处理

导读

Transformer作为深度学习进入大模型时代的标志性模型,其强大的性能被广泛应用于各个领域。本文基于Pytorch框架从零开始搭建Transformer模型,不仅有详细的脚本说明,还涵盖了丰富了模型分析,希望对大家有帮助。

2017年Google在论文《Attention is All You Need》中提出了Transformer模型,并成功应用到NLP领域。该模型完全基于自注意力机制Attention mechanism实现,弥补了传统的RNN模型的不足。

本文笔者将详解使用Pytorch从零开始逐步实现Transformer模型。

0 回顾

首先我们先回顾一下Transformer原理。宏观层面,Transformer可以看成是一个黑箱操作的序列到序列(seq2seq)模型。例如,在机器翻译中,输入一种语言,经Transformer输出翻译后的另一种语言。

图片

拆开这个黑箱,可以看到模型本质就是一个Encoders-Decoders结构。

  • 每个Encoders中分别由6层Encoder组成。(所有Encoder结构完全相同,但是训练参数不同,每个参数是独立训练的,循环执行6次Encode,而不是只训练了一个Encoder然后复制5份)。

  • Decoders同理。

  • 这里每个Encoders包含6层Encoder,只是论文中Nx=6,实际可以自定义。

图片

Transformer整体架构如下图所示。

图片

其中,

  • 编码端:经过词向量层(Input Embedding)和位置编码层(Positional Encoding),得到最终输入,流经自注意力层(Multi-Head Attention)、残差和层归一化(Add&Norm)、前馈神经网络层(Feed Forward)、残差和层归一化(Add&Norm),得到编码端的输出(后续会和解码端进行交互)。

  • 解码端:经过词向量层(Output Embedding)和位置编码层(Positional Encoding),得到最终输入,流经掩码自注意力层(Masked Multi-Head Attention,把当前词之后的词全部mask掉)、残差和层归一化(Add&Norm)、交互注意力层(Multi-Head Attention,把编码端的输出和解码端的信息进行交互,Q矩阵来自解码端,K、V矩阵来自编码端的输出)、残差和层归一化(Add&Norm)、前馈神经网络层(Feed Forward)、残差和层归一化(Add&Norm),得到解码端的输出。

注:编码端和解码端的输入不一定等长。

1 Encoder

图片

下面还是以机器翻译("我是学生"->"I am a student")为例说明。

对于上图中,整个模型的输入即为"我是学生",目标是将其翻译为"I am a student",但是计算机是无法识别"我是学生"的,需要将其转化为二进制形式,再送入模型。

将中文转换为计算机可以识别的向量通常有两种方法:

  • One Hot编码:形成高维向量,对于中文来说,汉字的数量就是向量的维度,然后是哪个字就将对应位置变为1,其它位置变为0,以此来表示一句话。

  • Embedding词嵌入:通过网络进行训练或者通过一些训练好的模型将其转化成连续性的向量。

一般来说第二种方法使用较多,因为第一种有几个缺点,第一个就是每个字都是相互独立的,缺少语义联系信息,第二就是汉字数量太多,会导致生成的维度过大,占用系统内存。

1.1 输入Input

输入Inputs维度是[batch size,sequence length],经Word2Vec,转换为计算机可以识别的Input Embedding,论文中每个词对应一个512维度的向量,维度是[batch_size,sequence_length,embedding_dimmension]。batch size指的是句子数,sequence length指的是输入的句子中最长的句子的字数,embedding_dimmension是词向量长度。

图片

如上图所示,以机器翻译("我是学生"->"I am a student")为例,首先对输入的文字进行Word Embedding处理,每个字(词)用一个连续型向量表示(这里定义的是4维向量),称为词向量。这样一个句子,也就是嵌入后的输入向量input Embedding就可以用一个矩阵表示(4*4维,序列长度为4,每个字用4维向量表示)。input Embedding加上位置信息得到编码器的输入矩阵。

「为什么需要在input Embedding加上位置信息?」 与RNN相比,RNN是一个字一个字输入,自然可以保留每个字的顺序关系信息,而Transformer使用的是自注意力机制来提取信息,一个句子中的每个字/词是并行计算,虽然处理每个字的时候考虑到了所有字对其的影响,但是并没有考虑到各个字相互之间的位置信息,也就是上下文。所以需要引入位置信息。

Transformer中使用Positional Encoding表示每个字/词的位置信息。定义如下:

图片

这样就即可实现让自注意力机制考虑词的顺序,同时又可以输入所有的词。

1.2 输入Input代码实现

1.2.1「Word Embedding」

Word Embedding在Pytorch中通常用nn.Embedding实现。

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

        super(Embeddings, self).__init__()
 
### 使用 Transformer 进行机器翻译的实际操作教程 以下是基于 Transformer 的机器翻译实际操作教程,涵盖了从环境搭建到模型训练的全过程。 #### 1. 导入必要的库 为了实现 Transformer 模型,需要导入 PyTorch 和其他辅助库。 ```python import torch import torch.nn as nn from torchtext.data.utils import get_tokenizer from torchtext.vocab import build_vocab_from_iterator from torch.utils.data import DataLoader ``` 这些库提供了构建和训练 Transformer 所需的功能[^1]。 #### 2. 数据集准备 加载并处理数据集是关键一步。假设我们有一个双语文本文件作为数据源。 ```python def yield_tokens(data_iter, tokenizer_src, tokenizer_tgt): for src_line, tgt_line in data_iter: yield tokenizer_src(src_line) yield tokenizer_tgt(tgt_line) SRC_LANGUAGE = 'ja' TGT_LANGUAGE = 'zh' token_transform = { SRC_LANGUAGE: get_tokenizer('spacy', language='ja'), TGT_LANGUAGE: get_tokenizer('spacy', language='zh') } train_data = [("こんにちは", "你好"), ("ありがとう", "谢谢")] # 示例数据 vocab_transform = {lang: build_vocab_from_iterator(yield_tokens(train_data, token_transform[SRC_LANGUAGE], token_transform[TGT_LANGUAGE]), specials=["<unk>", "<pad>"]) for lang in [SRC_LANGUAGE, TGT_LANGUAGE]} ``` 上述代码展示了如何定义标记化函数以及生成词汇表的过程。 #### 3. 构建 Sequence-to-Sequence Transformer 模型 Transformer 结构由编码器和解码器组成。 ```python class Seq2SeqTransformer(nn.Module): def __init__(self, num_encoder_layers, num_decoder_layers, emb_size, nhead, vocab_size_src, vocab_size_tgt, dim_feedforward=512, dropout=0.1): super(Seq2SeqTransformer, self).__init__() self.transformer = nn.Transformer(d_model=emb_size, nhead=nhead, num_encoder_layers=num_encoder_layers, num_decoder_layers=num_decoder_layers, dim_feedforward=dim_feedforward, dropout=dropout) self.generator = nn.Linear(emb_size, vocab_size_tgt) self.src_tok_emb = TokenEmbedding(vocab_size_src, emb_size) self.tgt_tok_emb = TokenEmbedding(vocab_size_tgt, emb_size) self.positional_encoding = PositionalEncoding(emb_size, dropout=dropout) def forward(self, src, trg, src_mask, tgt_mask, src_padding_mask, tgt_padding_mask, memory_key_padding_mask): src_emb = self.positional_encoding(self.src_tok_emb(src)) tgt_emb = self.positional_encoding(self.tgt_tok_emb(trg)) outs = self.transformer(src_emb, tgt_emb, src_mask, tgt_mask, None, src_padding_mask, tgt_padding_mask, memory_key_padding_mask) return self.generator(outs) def encode(self, src, src_mask): return self.transformer.encoder(self.positional_encoding(self.src_tok_emb(src)), src_mask) def decode(self, tgt, memory, tgt_mask): return self.transformer.decoder(self.positional_encoding(self.tgt_tok_emb(tgt)), memory, tgt_mask) ``` 此部分描述了完整的 Transformer 模型结构及其初始化参数。 #### 4. 训练模型 通过自定义损失函数优化模型性能。 ```python loss_fn = nn.CrossEntropyLoss(ignore_index=vocab_transform[TGT_LANGUAGE]["<pad>"]) optimizer = torch.optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9) for epoch in range(num_epochs): transformer.train() losses = [] for batch in train_dataloader: src, tgt = batch.src, batch.trg tgt_input = tgt[:-1, :] src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_masks(src, tgt_input) logits = transformer(src, tgt_input, src_mask, tgt_mask, src_padding_mask, tgt_padding_mask, src_padding_mask) optimizer.zero_grad() tgt_out = tgt[1:, :] loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1)) loss.backward() optimizer.step() losses.append(loss.item()) ``` 该片段说明了如何利用交叉熵损失来调整模型权重。 #### 5. 测试模型 验证模型效果可以通过简单的输入输出对比完成。 ```python input_seq = 'ils regardent .' translate(encoder, decoder, input_seq, max_seq_len) # 输出应接近“they are watching.” ``` 这是对法语句子进行英译的一个例子[^3]。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值