赞
踩
目录
4.构建 TorchText 词汇对象并将句子转换为 Torch 张量
6.实现Sequence-to-sequence Transformer
机器翻译是指将一段文本从一种语言自动翻译到另一种语言。
首先定义一些特殊符号,其中“<pad>”(padding)符号用来添加在较短序列后,直到每个序列等长,而“<bos>”和“<eos>”符号分别表示序列的开始和结束。
- import collections # 导入collections模块,提供了特殊的容器数据类型,如OrderedDict等。
- import os # 导入os模块,用于与操作系统进行交互。
- import io # 导入io模块,提供了对文件和流的基本操作。
- import math # 导入math模块,提供了数学运算相关的函数。
- import torch # 导入PyTorch深度学习框架的核心库。
- from torch import nn # 从torch中导入nn模块,用于构建神经网络模型。
- import torch.nn.functional as F # 导入torch.nn.functional,包含了各种神经网络的函数接口。
- import torchtext.vocab as Vocab # 导入torchtext.vocab模块,用于处理文本的词汇表。
- import torch.utils.data as Data # 导入torch.utils.data模块,用于处理数据集。
-
- import sys # 导入sys模块,提供对Python解释器的访问。
- # sys.path.append("..") # (可选)将上级目录添加到sys.path中,使得可以导入上级目录中的模块。
- import d2lzh_pytorch as d2l # 导入d2lzh_pytorch模块,这是一个封装了Deep Learning相关工具和方法的库。
-
- # 定义常量,用于在序列处理中表示填充符、序列起始符和序列结束符。
- PAD, BOS, EOS = '<pad>', '<bos>', '<eos>'
-
- # 设置环境变量,指定使用哪块GPU进行计算。这里设置为使用第一个GPU(编号为0)。
- os.environ["CUDA_VISIBLE_DEVICES"] = "0"
-
- # 检查当前是否支持CUDA(即GPU加速),并根据结果选择在CPU还是GPU上进行计算。
- device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
-
- # 打印PyTorch的版本信息和当前选择的计算设备。
- print(torch.__version__, device)
函数 process_one_seq 将一个序列中的所有词记录到 all_tokens 中,方便后续构建词典。同时,它在该序列末尾添加特殊符号 EOS 和 PAD,使得序列长度达到 max_seq_len,然后将处理后的序列保存在 all_seqs 中;函数 build_data 使用 all_tokens 构造一个词典 vocab,然后将 all_seqs 中每个词变换为该词在词典中的索引,并将这些索引构造成一个 Tensor。
- # 将一个序列中所有的词记录在all_tokens中以便之后构造词典,
- # 然后在该序列后面添加PAD直到序列长度变为max_seq_len,
- # 然后将序列保存在all_seqs中
- def process_one_seq(seq_tokens, all_tokens, all_seqs, max_seq_len):
- # 将当前序列中的所有词添加到all_tokens列表中
- all_tokens.extend(seq_tokens)
-
- # 在当前序列末尾添加EOS(序列结束符),
- # 然后添加PAD(填充符)直到序列长度达到max_seq_len
- seq_tokens += [EOS] + [PAD] * (max_seq_len - len(seq_tokens) - 1)
-
- # 将处理后的序列添加到all_seqs列表中
- all_seqs.append(seq_tokens)
-
- # 使用所有的词来构造词典。并将所有序列中的词变换为词索引后构造Tensor
- def build_data(all_tokens, all_seqs):
- # 使用collections.Counter计算每个词的频率,并基于此构造词典。
- # 值得注意的是,这里指定了特别的词汇:PAD、BOS和EOS。
- vocab = Vocab.Vocab(collections.Counter(all_tokens),
- specials=[PAD, BOS, EOS])
-
- # 将所有序列中的每个词替换为它在词典中的索引,得到一个索引序列的列表
- indices = [[vocab.stoi[w] for w in seq] for seq in all_seqs]
-
- # 将索引序列列表转换为PyTorch的Tensor类型,用于后续的模型输入
- return vocab, torch.tensor(indices)
为了演示方便,我们在这里使用一个很小的法语—英语数据集。在这个数据集里,每一行是一对法语句子和它对应的英语句子,中间使用'\t'
隔开。在读取数据时,我们在句末附上“<eos>”符号,并可能通过添加“<pad>”符号使每个序列的长度均为max_seq_len
。我们为法语词和英语词分别创建词典。法语词的索引和英语词的索引相互独立;将序列的最大长度设成7,然后查看读取到的第一个样本。该样本分别包含法语词索引序列和英语词索引序列。
- def read_data(max_seq_len):
- # in和out分别是input和output的缩写
- in_tokens, out_tokens, in_seqs, out_seqs = [], [], [], []
- with io.open('fr-en-small.txt') as f:
- lines = f.readlines() # 读取文件中的所有行
- for line in lines:
- in_seq, out_seq = line.rstrip().split('\t') # 将每行按制表符'\t'分割成输入序列和输出序列
- in_seq_tokens, out_seq_tokens = in_seq.split(' '), out_seq.split(' ') # 将输入和输出序列分割成词汇列表
- if max(len(in_seq_tokens), len(out_seq_tokens)) > max_seq_len - 1:
- continue # 如果加上EOS后长于max_seq_len,则忽略掉此样本
- process_one_seq(in_seq_tokens, in_tokens, in_seqs, max_seq_len) # 处理输入序列并添加到对应的列表中
- process_one_seq(out_seq_tokens, out_tokens, out_seqs, max_seq_len) # 处理输出序列并添加到对应的列表中
- in_vocab, in_data = build_data(in_tokens, in_seqs) # 构建输入序列的词典并转换为Tensor数据
- out_vocab, out_data = build_data(out_tokens, out_seqs) # 构建输出序列的词典并转换为Tensor数据
- return in_vocab, out_vocab, Data.TensorDataset(in_data, out_data)
-
- max_seq_len = 7
- in_vocab, out_vocab, dataset = read_data(max_seq_len)
- dataset[0]
结果输出如下:
在编码器中,我们将输入语言的词索引通过词嵌入层得到词的表征,然后输入到一个多层门控循环单元中。我们创建一个批量大小为4、时间步数为7的小批量序列输入。设门控循环单元的隐藏层个数为2,隐藏单元个数为16。编码器对该输入执行前向计算后返回的输出形状为(时间步数, 批量大小, 隐藏单元个数)。门控循环单元在最终时间步的多层隐藏状态的形状为(隐藏层个数, 批量大小, 隐藏单元个数)。对于门控循环单元来说,state
就是一个元素,即隐藏状态;如果使用长短期记忆,state
是一个元组,包含两个元素即隐藏状态和记忆细胞。
- class Encoder(nn.Module):
- def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
- drop_prob=0, **kwargs):
- super(Encoder, self).__init__(**kwargs)
- # 初始化编码器的各个层
- self.embedding = nn.Embedding(vocab_size, embed_size) # 定义词嵌入层
- self.rnn = nn.GRU(embed_size, num_hiddens, num_layers, dropout=drop_prob) # 定义GRU循环神经网络层
-
- def forward(self, inputs, state):
- # 前向计算函数,描述了数据在模型中的传播过程
- # 输入形状是(批量大小, 时间步数)。将输出互换样本维和时间步维
- embedding = self.embedding(inputs.long()).permute(1, 0, 2) # 对输入进行词嵌入并进行维度变换
- return self.rnn(embedding, state) # 返回GRU层的输出结果以及更新后的状态
-
- def begin_state(self):
- # 初始化RNN的隐藏状态
- return None
-
- encoder = Encoder(vocab_size=10, embed_size=8, num_hiddens=16, num_layers=2)
- output, state = encoder(torch.zeros((4, 7)), encoder.begin_state())
- output.shape, state.shape # GRU的state是h, 而LSTM的是一个元组(h, c)
输出结果如下:
下图描绘了注意力机制如何为解码器在时间步2计算背景变量。首先,函数声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。