PyTorch 2.2 中文官方教程(七)(1)https://developer.aliyun.com/article/1482515
数据获取和处理
torchtext 库提供了用于创建数据集的实用程序,可以轻松迭代,以便创建语言翻译模型。在这个例子中,我们展示了如何使用 torchtext 的内置数据集,对原始文本句子进行标记化,构建词汇表,并将标记数值化为张量。我们将使用torchtext 库中的 Multi30k 数据集,该数据集产生一对源-目标原始句子。
要访问 torchtext 数据集,请按照github.com/pytorch/data
上的说明安装 torchdata。
from torchtext.data.utils import get_tokenizer from torchtext.vocab import build_vocab_from_iterator from torchtext.datasets import multi30k, Multi30k from typing import Iterable, List # We need to modify the URLs for the dataset since the links to the original dataset are broken # Refer to https://github.com/pytorch/text/issues/1756#issuecomment-1163664163 for more info multi30k.URL["train"] = "https://raw.githubusercontent.com/neychev/small_DL_repo/master/datasets/Multi30k/training.tar.gz" multi30k.URL["valid"] = "https://raw.githubusercontent.com/neychev/small_DL_repo/master/datasets/Multi30k/validation.tar.gz" SRC_LANGUAGE = 'de' TGT_LANGUAGE = 'en' # Place-holders token_transform = {} vocab_transform = {}
创建源语言和目标语言的标记器。确保安装了依赖项。
pip install -U torchdata pip install -U spacy python -m spacy download en_core_web_sm python -m spacy download de_core_news_sm
token_transform[SRC_LANGUAGE] = get_tokenizer('spacy', language='de_core_news_sm') token_transform[TGT_LANGUAGE] = get_tokenizer('spacy', language='en_core_web_sm') # helper function to yield list of tokens def yield_tokens(data_iter: Iterable, language: str) -> List[str]: language_index = {SRC_LANGUAGE: 0, TGT_LANGUAGE: 1} for data_sample in data_iter: yield token_transformlanguage # Define special symbols and indices UNK_IDX, PAD_IDX, BOS_IDX, EOS_IDX = 0, 1, 2, 3 # Make sure the tokens are in order of their indices to properly insert them in vocab special_symbols = ['<unk>', '<pad>', '<bos>', '<eos>'] for ln in [SRC_LANGUAGE, TGT_LANGUAGE]: # Training data Iterator train_iter = Multi30k(split='train', language_pair=(SRC_LANGUAGE, TGT_LANGUAGE)) # Create torchtext's Vocab object vocab_transform[ln] = build_vocab_from_iterator(yield_tokens(train_iter, ln), min_freq=1, specials=special_symbols, special_first=True) # Set ``UNK_IDX`` as the default index. This index is returned when the token is not found. # If not set, it throws ``RuntimeError`` when the queried token is not found in the Vocabulary. for ln in [SRC_LANGUAGE, TGT_LANGUAGE]: vocab_transform[ln].set_default_index(UNK_IDX)
使用 Transformer 的 Seq2Seq 网络
Transformer 是一个 Seq2Seq 模型,介绍在“Attention is all you need”论文中,用于解决机器翻译任务。下面,我们将创建一个使用 Transformer 的 Seq2Seq 网络。该网络由三部分组成。第一部分是嵌入层。该层将输入索引的张量转换为相应的输入嵌入的张量。这些嵌入进一步与位置编码相结合,以向模型提供输入标记的位置信息。第二部分是实际的Transformer模型。最后,Transformer 模型的输出通过线性层传递,为目标语言中的每个标记提供未归一化的概率。
from torch import Tensor import torch import torch.nn as nn from torch.nn import Transformer import math DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # helper Module that adds positional encoding to the token embedding to introduce a notion of word order. class PositionalEncoding(nn.Module): def __init__(self, emb_size: int, dropout: float, maxlen: int = 5000): super(PositionalEncoding, self).__init__() den = torch.exp(- torch.arange(0, emb_size, 2)* math.log(10000) / emb_size) pos = torch.arange(0, maxlen).reshape(maxlen, 1) pos_embedding = torch.zeros((maxlen, emb_size)) pos_embedding[:, 0::2] = torch.sin(pos * den) pos_embedding[:, 1::2] = torch.cos(pos * den) pos_embedding = pos_embedding.unsqueeze(-2) self.dropout = nn.Dropout(dropout) self.register_buffer('pos_embedding', pos_embedding) def forward(self, token_embedding: Tensor): return self.dropout(token_embedding + self.pos_embedding[:token_embedding.size(0), :]) # helper Module to convert tensor of input indices into corresponding tensor of token embeddings class TokenEmbedding(nn.Module): def __init__(self, vocab_size: int, emb_size): super(TokenEmbedding, self).__init__() self.embedding = nn.Embedding(vocab_size, emb_size) self.emb_size = emb_size def forward(self, tokens: Tensor): return self.embedding(tokens.long()) * math.sqrt(self.emb_size) # Seq2Seq Network class Seq2SeqTransformer(nn.Module): def __init__(self, num_encoder_layers: int, num_decoder_layers: int, emb_size: int, nhead: int, src_vocab_size: int, tgt_vocab_size: int, dim_feedforward: int = 512, dropout: float = 0.1): super(Seq2SeqTransformer, self).__init__() self.transformer = 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, tgt_vocab_size) self.src_tok_emb = TokenEmbedding(src_vocab_size, emb_size) self.tgt_tok_emb = TokenEmbedding(tgt_vocab_size, emb_size) self.positional_encoding = PositionalEncoding( emb_size, dropout=dropout) def forward(self, src: Tensor, trg: Tensor, src_mask: Tensor, tgt_mask: Tensor, src_padding_mask: Tensor, tgt_padding_mask: Tensor, memory_key_padding_mask: Tensor): 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: Tensor, src_mask: Tensor): return self.transformer.encoder(self.positional_encoding( self.src_tok_emb(src)), src_mask) def decode(self, tgt: Tensor, memory: Tensor, tgt_mask: Tensor): return self.transformer.decoder(self.positional_encoding( self.tgt_tok_emb(tgt)), memory, tgt_mask)
在训练过程中,我们需要一个后续单词掩码,以防止模型在进行预测时查看未来的单词。我们还需要隐藏源和目标填充标记的掩码。下面,让我们定义一个函数,来处理这两个问题。
def generate_square_subsequent_mask(sz): mask = (torch.triu(torch.ones((sz, sz), device=DEVICE)) == 1).transpose(0, 1) mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0)) return mask def create_mask(src, tgt): src_seq_len = src.shape[0] tgt_seq_len = tgt.shape[0] tgt_mask = generate_square_subsequent_mask(tgt_seq_len) src_mask = torch.zeros((src_seq_len, src_seq_len),device=DEVICE).type(torch.bool) src_padding_mask = (src == PAD_IDX).transpose(0, 1) tgt_padding_mask = (tgt == PAD_IDX).transpose(0, 1) return src_mask, tgt_mask, src_padding_mask, tgt_padding_mask
现在让我们定义模型的参数并实例化。下面,我们还定义了我们的损失函数,即交叉熵损失,以及用于训练的优化器。
torch.manual_seed(0) SRC_VOCAB_SIZE = len(vocab_transform[SRC_LANGUAGE]) TGT_VOCAB_SIZE = len(vocab_transform[TGT_LANGUAGE]) EMB_SIZE = 512 NHEAD = 8 FFN_HID_DIM = 512 BATCH_SIZE = 128 NUM_ENCODER_LAYERS = 3 NUM_DECODER_LAYERS = 3 transformer = Seq2SeqTransformer(NUM_ENCODER_LAYERS, NUM_DECODER_LAYERS, EMB_SIZE, NHEAD, SRC_VOCAB_SIZE, TGT_VOCAB_SIZE, FFN_HID_DIM) for p in transformer.parameters(): if p.dim() > 1: nn.init.xavier_uniform_(p) transformer = transformer.to(DEVICE) loss_fn = torch.nn.CrossEntropyLoss(ignore_index=PAD_IDX) optimizer = torch.optim.Adam(transformer.parameters(), lr=0.0001, betas=(0.9, 0.98), eps=1e-9)
整理
如数据获取和处理
部分所示,我们的数据迭代器产生一对原始字符串。我们需要将这些字符串对转换为批量张量,以便我们之前定义的Seq2Seq
网络可以处理。下面我们定义我们的整理函数,将一批原始字符串转换为可以直接输入到我们的模型中的批量张量。
from torch.nn.utils.rnn import pad_sequence # helper function to club together sequential operations def sequential_transforms(*transforms): def func(txt_input): for transform in transforms: txt_input = transform(txt_input) return txt_input return func # function to add BOS/EOS and create tensor for input sequence indices def tensor_transform(token_ids: List[int]): return torch.cat((torch.tensor([BOS_IDX]), torch.tensor(token_ids), torch.tensor([EOS_IDX]))) # ``src`` and ``tgt`` language text transforms to convert raw strings into tensors indices text_transform = {} for ln in [SRC_LANGUAGE, TGT_LANGUAGE]: text_transform[ln] = sequential_transforms(token_transform[ln], #Tokenization vocab_transform[ln], #Numericalization tensor_transform) # Add BOS/EOS and create tensor # function to collate data samples into batch tensors def collate_fn(batch): src_batch, tgt_batch = [], [] for src_sample, tgt_sample in batch: src_batch.append(text_transformSRC_LANGUAGE)) tgt_batch.append(text_transformTGT_LANGUAGE)) src_batch = pad_sequence(src_batch, padding_value=PAD_IDX) tgt_batch = pad_sequence(tgt_batch, padding_value=PAD_IDX) return src_batch, tgt_batch
让我们定义训练和评估循环,每个时代都会调用它。
from torch.utils.data import DataLoader def train_epoch(model, optimizer): model.train() losses = 0 train_iter = Multi30k(split='train', language_pair=(SRC_LANGUAGE, TGT_LANGUAGE)) train_dataloader = DataLoader(train_iter, batch_size=BATCH_SIZE, collate_fn=collate_fn) for src, tgt in train_dataloader: src = src.to(DEVICE) tgt = tgt.to(DEVICE) tgt_input = tgt[:-1, :] src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input) logits = model(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 += loss.item() return losses / len(list(train_dataloader)) def evaluate(model): model.eval() losses = 0 val_iter = Multi30k(split='valid', language_pair=(SRC_LANGUAGE, TGT_LANGUAGE)) val_dataloader = DataLoader(val_iter, batch_size=BATCH_SIZE, collate_fn=collate_fn) for src, tgt in val_dataloader: src = src.to(DEVICE) tgt = tgt.to(DEVICE) tgt_input = tgt[:-1, :] src_mask, tgt_mask, src_padding_mask, tgt_padding_mask = create_mask(src, tgt_input) logits = model(src, tgt_input, src_mask, tgt_mask,src_padding_mask, tgt_padding_mask, src_padding_mask) tgt_out = tgt[1:, :] loss = loss_fn(logits.reshape(-1, logits.shape[-1]), tgt_out.reshape(-1)) losses += loss.item() return losses / len(list(val_dataloader))
现在我们有了训练模型所需的所有要素。让我们开始吧!
from timeit import default_timer as timer NUM_EPOCHS = 18 for epoch in range(1, NUM_EPOCHS+1): start_time = timer() train_loss = train_epoch(transformer, optimizer) end_time = timer() val_loss = evaluate(transformer) print((f"Epoch: {epoch}, Train loss: {train_loss:.3f}, Val loss: {val_loss:.3f}, "f"Epoch time = {(end_time - start_time):.3f}s")) # function to generate output sequence using greedy algorithm def greedy_decode(model, src, src_mask, max_len, start_symbol): src = src.to(DEVICE) src_mask = src_mask.to(DEVICE) memory = model.encode(src, src_mask) ys = torch.ones(1, 1).fill_(start_symbol).type(torch.long).to(DEVICE) for i in range(max_len-1): memory = memory.to(DEVICE) tgt_mask = (generate_square_subsequent_mask(ys.size(0)) .type(torch.bool)).to(DEVICE) out = model.decode(ys, memory, tgt_mask) out = out.transpose(0, 1) prob = model.generator(out[:, -1]) _, next_word = torch.max(prob, dim=1) next_word = next_word.item() ys = torch.cat([ys, torch.ones(1, 1).type_as(src.data).fill_(next_word)], dim=0) if next_word == EOS_IDX: break return ys # actual function to translate input sentence into target language def translate(model: torch.nn.Module, src_sentence: str): model.eval() src = text_transformSRC_LANGUAGE.view(-1, 1) num_tokens = src.shape[0] src_mask = (torch.zeros(num_tokens, num_tokens)).type(torch.bool) tgt_tokens = greedy_decode( model, src, src_mask, max_len=num_tokens + 5, start_symbol=BOS_IDX).flatten() return " ".join(vocab_transform[TGT_LANGUAGE].lookup_tokens(list(tgt_tokens.cpu().numpy()))).replace("<bos>", "").replace("<eos>", "")
print(translate(transformer, "Eine Gruppe von Menschen steht vor einem Iglu .")) • 1
参考
- 注意力就是你所需要的论文。
papers.nips.cc/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf
- 带注释的 Transformer。
nlp.seas.harvard.edu/2018/04/03/attention.html#positional-encoding
脚本的总运行时间:(0 分钟 0.000 秒)
下载 Python 源代码:translation_transformer.py
下载 Jupyter 笔记本:translation_transformer.ipynb
使用 Torchtext 预处理自定义文本数据集
原文:
pytorch.org/tutorials/beginner/torchtext_custom_dataset_tutorial.html
译者:飞龙
注意
点击这里下载完整示例代码
本教程演示了 torchtext 在非内置数据集上的用法。在本教程中,我们将预处理一个数据集,可以进一步用于训练用于机器翻译的序列到序列模型(类似于本教程中的内容:使用神经网络进行序列到序列学习),但不使用 torchtext 的旧版本。
在本教程中,我们将学习如何:
- 读取数据集
- 标记化句子
- 对句子应用转换
- 执行桶批处理
假设我们需要准备一个数据集来训练一个能够进行英语到德语翻译的模型。我们将使用Tatoeba Project提供的制表符分隔的德语 - 英语句对,可以从此链接下载。
其他语言的句子对可以在此链接找到。
设置
首先,下载数据集,提取 zip 文件,并记下文件 deu.txt 的路径。
确保已安装以下软件包:
在这里,我们使用 Spacy 对文本进行标记化。简单来说,标记化意味着将句子转换为单词列表。Spacy 是一个用于各种自然语言处理(NLP)任务的 Python 包。
从 Spacy 下载英语和德语模型,如下所示:
python -m spacy download en_core_web_sm python -m spacy download de_core_news_sm
让我们从导入所需模块开始:
import torchdata.datapipes as dp import torchtext.transforms as T import spacy from torchtext.vocab import build_vocab_from_iterator eng = spacy.load("en_core_web_sm") # Load the English model to tokenize English text de = spacy.load("de_core_news_sm") # Load the German model to tokenize German text
现在我们将加载数据集
FILE_PATH = 'data/deu.txt' data_pipe = dp.iter.IterableWrapper([FILE_PATH]) data_pipe = dp.iter.FileOpener(data_pipe, mode='rb') data_pipe = data_pipe.parse_csv(skip_lines=0, delimiter='\t', as_tuple=True)
在上述代码块中,我们正在做以下事情:
- 在第 2 行,我们正在创建一个文件名的可迭代对象
- 在第 3 行,我们将可迭代对象传递给 FileOpener,然后以读取模式打开文件
- 在第 4 行,我们调用一个函数来解析文件,该函数再次返回一个元组的可迭代对象,表示制表符分隔文件的每一行
DataPipes 可以被视为类似数据集对象的东西,我们可以在其上执行各种操作。查看此教程以获取有关 DataPipes 的更多详细信息。
我们可以验证可迭代对象是否包含句子对,如下所示:
for sample in data_pipe: print(sample) break
('Go.', 'Geh.', 'CC-BY 2.0 (France) Attribution: tatoeba.org #2877272 (CM) & #8597805 (Roujin)')
请注意,我们还有句子对的归属细节。我们将编写一个小函数来删除归属细节:
def removeAttribution(row): """ Function to keep the first two elements in a tuple """ return row[:2] data_pipe = data_pipe.map(removeAttribution)
上述代码块中第 6 行的 map 函数可用于在 data_pipe 的每个元素上应用某个函数。现在,我们可以验证 data_pipe 只包含句子对。
for sample in data_pipe: print(sample) break
('Go.', 'Geh.')
现在,让我们定义一些函数来执行标记化:
def engTokenize(text): """ Tokenize an English text and return a list of tokens """ return [token.text for token in eng.tokenizer(text)] def deTokenize(text): """ Tokenize a German text and return a list of tokens """ return [token.text for token in de.tokenizer(text)]
上述函数接受文本并返回如下所示的单词列表:
print(engTokenize("Have a good day!!!")) print(deTokenize("Haben Sie einen guten Tag!!!"))
['Have', 'a', 'good', 'day', '!', '!', '!'] ['Haben', 'Sie', 'einen', 'guten', 'Tag', '!', '!', '!']
构建词汇表
让我们将英语句子作为源,德语句子作为目标。
词汇可以被视为数据集中我们拥有的唯一单词集合。我们现在将为源和目标构建词汇表。
让我们定义一个函数,从迭代器中的元组元素获取标记。
def getTokens(data_iter, place): """ Function to yield tokens from an iterator. Since, our iterator contains tuple of sentences (source and target), `place` parameters defines for which index to return the tokens for. `place=0` for source and `place=1` for target """ for english, german in data_iter: if place == 0: yield engTokenize(english) else: yield deTokenize(german)
现在,我们将为源构建词汇表:
source_vocab = build_vocab_from_iterator( getTokens(data_pipe,0), min_freq=2, specials= ['<pad>', '<sos>', '<eos>', '<unk>'], special_first=True ) source_vocab.set_default_index(source_vocab['<unk>'])
上面的代码从迭代器构建词汇表。在上述代码块中:
- 在第 2 行,我们调用 getTokens()函数,并将 place=0,因为我们需要源句子的词汇表。
- 在第 3 行,我们设置 min_freq=2。这意味着该函数将跳过出现少于 2 次的单词。
- 在第 4 行,我们指定了一些特殊标记:
- 表示句子的开始
- 表示句子结束
- 表示未知单词。一个未知单词的示例是由于 min_freq=2 而被跳过的单词。
- 是填充标记。在训练模型时,我们大多数情况下是以批量的形式训练。在一个批次中,可能会有不同长度的句子。因此,我们用标记填充较短的句子,使批次中所有序列的长度相等。
- 在第 5 行,我们设置 special_first=True。这意味着将在词汇表中得到索引 0,得到索引 1,得到索引 2,将在词汇表中得到索引 3。
- 在第 7 行,我们将默认索引设置为的索引。这意味着如果某个单词不在词汇表中,我们将使用代替该未知单词。
类似地,我们将为目标句子构建词汇表:
target_vocab = build_vocab_from_iterator( getTokens(data_pipe,1), min_freq=2, specials= ['<pad>', '<sos>', '<eos>', '<unk>'], special_first=True ) target_vocab.set_default_index(target_vocab['<unk>'])
请注意,上面的示例显示了如何向我们的词汇表添加特殊标记。特殊标记可能会根据需求而变化。
现在,我们可以验证特殊标记是放在开头的,然后是其他单词。在下面的代码中,source_vocab.get_itos()返回一个基于词汇表的索引的标记列表。
print(source_vocab.get_itos()[:9])
['<pad>', '<sos>', '<eos>', '<unk>', '.', 'I', 'Tom', 'to', 'you']
使用词汇表对句子进行数字化
构建词汇表后,我们需要将我们的句子转换为相应的索引。让我们为此定义一些函数:
def getTransform(vocab): """ Create transforms based on given vocabulary. The returned transform is applied to sequence of tokens. """ text_tranform = T.Sequential( ## converts the sentences to indices based on given vocabulary T.VocabTransform(vocab=vocab), ## Add <sos> at beginning of each sentence. 1 because the index for <sos> in vocabulary is # 1 as seen in previous section T.AddToken(1, begin=True), ## Add <eos> at beginning of each sentence. 2 because the index for <eos> in vocabulary is # 2 as seen in previous section T.AddToken(2, begin=False) ) return text_tranform
现在,让我们看看如何使用上述函数。该函数返回一个 Transforms 对象,我们将在我们的句子上使用它。让我们取一个随机句子并检查转换的工作方式。
temp_list = list(data_pipe) some_sentence = temp_list[798][0] print("Some sentence=", end="") print(some_sentence) transformed_sentence = getTransform(source_vocab)(engTokenize(some_sentence)) print("Transformed sentence=", end="") print(transformed_sentence) index_to_string = source_vocab.get_itos() for index in transformed_sentence: print(index_to_string[index], end=" ")
Some sentence=I fainted. Transformed sentence=[1, 5, 2897, 4, 2] <sos> I fainted . <eos>
在上面的代码中:
- 在第 2 行,我们从在第 1 行从 data_pipe 创建的列表中取一个源句子
- 在第 5 行,我们根据源词汇表获取一个转换,并将其应用于一个标记化的句子。请注意,转换接受单词列表而不是句子。
- 在第 8 行,我们获取索引到字符串的映射,然后使用它来获取转换后的句子
现在我们将使用 DataPipe 函数来对所有句子应用转换。让我们为此定义一些更多的函数。
def applyTransform(sequence_pair): """ Apply transforms to sequence of tokens in a sequence pair """ return ( getTransform(source_vocab)(engTokenize(sequence_pair[0])), getTransform(target_vocab)(deTokenize(sequence_pair[1])) ) data_pipe = data_pipe.map(applyTransform) ## Apply the function to each element in the iterator temp_list = list(data_pipe) print(temp_list[0])
([1, 616, 4, 2], [1, 739, 4, 2])
制作批次(使用 bucket batch)
通常,我们以批量的形式训练模型。在为序列到序列模型工作时,建议保持批次中序列的长度相似。为此,我们将使用 data_pipe 的 bucketbatch 函数。
让我们定义一些将被 bucketbatch 函数使用的函数。
def sortBucket(bucket): """ Function to sort a given bucket. Here, we want to sort based on the length of source and target sequence. """ return sorted(bucket, key=lambda x: (len(x[0]), len(x[1])))
现在,我们将应用 bucketbatch 函数:
data_pipe = data_pipe.bucketbatch( batch_size = 4, batch_num=5, bucket_num=1, use_in_batch_shuffle=False, sort_key=sortBucket )
在上面的代码块中:
- 我们保持批量大小为 4。
- batch_num 是要在桶中保留的批次数
- bucket_num 是要在池中保留的桶数以进行洗牌。
- sort_key 指定一个函数,该函数接受一个桶并对其进行排序
现在,让我们将一批源句子表示为 X,将一批目标句子表示为 y。通常,在训练模型时,我们对一批 X 进行预测,并将结果与 y 进行比较。但是,在我们的 data_pipe 中,一个批次的形式是[(X_1,y_1), (X_2,y_2), (X_3,y_3), (X_4,y_4)]:
print(list(data_pipe)[0])
[([1, 11105, 17, 4, 2], [1, 507, 29, 24, 2]), ([1, 11105, 17, 4, 2], [1, 7994, 1487, 24, 2]), ([1, 5335, 21, 4, 2], [1, 6956, 32, 24, 2]), ([1, 5335, 21, 4, 2], [1, 16003, 32, 24, 2])]
因此,我们现在将把它们转换为这种形式:((X_1,X_2,X_3,X_4),(y_1,y_2,y_3,y_4))。为此,我们将编写一个小函数:
def separateSourceTarget(sequence_pairs): """ input of form: `[(X_1,y_1), (X_2,y_2), (X_3,y_3), (X_4,y_4)]` output of form: `((X_1,X_2,X_3,X_4), (y_1,y_2,y_3,y_4))` """ sources,targets = zip(*sequence_pairs) return sources,targets ## Apply the function to each element in the iterator data_pipe = data_pipe.map(separateSourceTarget) print(list(data_pipe)[0])
(([1, 6860, 23, 10, 2], [1, 6860, 23, 10, 2], [1, 29, 466, 4, 2], [1, 29, 466, 4, 2]), ([1, 20825, 8, 2], [1, 11118, 8, 2], [1, 31, 1152, 4, 2], [1, 31, 1035, 4, 2]))
现在,我们已经得到了所需的数据。
填充
如前所述,在构建词汇表时,我们需要填充批次中较短的句子,以使批次中所有序列的长度相等。我们可以按以下方式执行填充:
def applyPadding(pair_of_sequences): """ Convert sequences to tensors and apply padding """ return (T.ToTensor(0)(list(pair_of_sequences[0])), T.ToTensor(0)(list(pair_of_sequences[1]))) ## `T.ToTensor(0)` returns a transform that converts the sequence to `torch.tensor` and also applies # padding. Here, `0` is passed to the constructor to specify the index of the `<pad>` token in the # vocabulary. data_pipe = data_pipe.map(applyPadding)
现在,我们可以使用索引到字符串映射来查看序列如何以标记而不是索引的形式呈现:
source_index_to_string = source_vocab.get_itos() target_index_to_string = target_vocab.get_itos() def showSomeTransformedSentences(data_pipe): """ Function to show how the sentences look like after applying all transforms. Here we try to print actual words instead of corresponding index """ for sources,targets in data_pipe: if sources[0][-1] != 0: continue # Just to visualize padding of shorter sentences for i in range(4): source = "" for token in sources[i]: source += " " + source_index_to_string[token] target = "" for token in targets[i]: target += " " + target_index_to_string[token] print(f"Source: {source}") print(f"Traget: {target}") break showSomeTransformedSentences(data_pipe)
Source: <sos> Freeze ! <eos> <pad> Traget: <sos> Stehenbleiben ! <eos> <pad> Source: <sos> <unk> ! <eos> <pad> Traget: <sos> Zum Wohl ! <eos> Source: <sos> Freeze ! <eos> <pad> Traget: <sos> Keine Bewegung ! <eos> Source: <sos> Got it ! <eos> Traget: <sos> Verstanden ! <eos> <pad>
在上面的输出中,我们可以观察到较短的句子被填充为。现在,我们可以在编写训练函数时使用 data_pipe。
本教程的部分内容受到了这篇文章的启发。
脚本的总运行时间:(4 分钟 41.756 秒)
下载 Python 源代码:torchtext_custom_dataset_tutorial.py
下载 Jupyter 笔记本:torchtext_custom_dataset_tutorial.ipynb
后端
ONNX 简介
原文:
pytorch.org/tutorials/beginner/onnx/intro_onnx.html
译者:飞龙
注意
点击这里下载完整示例代码
ONNX 简介 || 将 PyTorch 模型导出为 ONNX || 扩展 ONNX 注册表
Open Neural Network eXchange (ONNX)是一个用于表示机器学习模型的开放标准格式。torch.onnx
模块提供了 API,用于从本机 PyTorch torch.nn.Module
模型中捕获计算图,并将其转换为ONNX 图。
导出的模型可以被支持 ONNX 的许多runtime之一消费,包括微软的ONNX Runtime。
注意
目前,有两种 ONNX 导出器 API 的风格,但本教程将专注于torch.onnx.dynamo_export
。
TorchDynamo 引擎利用 Python 的帧评估 API 并动态重写其字节码到FX 图。生成的 FX 图在最终转换为ONNX 图之前进行了优化。
这种方法的主要优势在于使用字节码分析捕获了FX 图,保留了模型的动态特性,而不是使用传统的静态追踪技术。
依赖项
需要 PyTorch 2.1.0 或更新版本。
ONNX 导出器依赖于额外的 Python 包:
- ONNX标准库
- ONNX Script库,使开发人员能够使用 Python 的子集以富有表现力且简单的方式编写 ONNX 操作符、函数和模型。
它们可以通过pip安装:
pip install --upgrade onnx onnxscript
要验证安装,请运行以下命令:
import torch print(torch.__version__) import onnxscript print(onnxscript.__version__) from onnxscript import opset18 # opset 18 is the latest (and only) supported version for now import onnxruntime print(onnxruntime.__version__)
每个导入必须成功且没有任何错误,并且必须打印出库版本。
进一步阅读
下面的列表涵盖了从基本示例到高级场景的教程,不一定按照列出的顺序。随意跳转到您感兴趣的特定主题,或者坐下来享受浏览所有内容,以了解关于 ONNX 导出器的所有内容。
1. 将 PyTorch 模型导出为 ONNX2. 扩展 ONNX 注册表
脚本的总运行时间:(0 分钟 0.000 秒)
下载 Python 源代码:intro_onnx.py
下载 Jupyter 笔记本:intro_onnx.ipynb
PyTorch 2.2 中文官方教程(七)(3)https://developer.aliyun.com/article/1482520