PyTorch 2.2 中文官方教程(六)(2)

简介: PyTorch 2.2 中文官方教程(六)

PyTorch 2.2 中文官方教程(六)(1)https://developer.aliyun.com/article/1482505


使用 Better Transformer 进行快速 Transformer 推理

原文:pytorch.org/tutorials/beginner/bettertransformer_tutorial.html

译者:飞龙

协议:CC BY-NC-SA 4.0

作者Michael Gschwind

本教程将 Better Transformer(BT)作为 PyTorch 1.12 版本的一部分进行介绍。在本教程中,我们展示了如何使用 Better Transformer 进行 torchtext 的生产推理。Better Transformer 是一个生产就绪的快速路径,可加速在 CPU 和 GPU 上部署具有高性能的 Transformer 模型。快速路径功能对基于 PyTorch 核心nn.module或 torchtext 的模型透明地工作。

可以通过 Better Transformer 快速路径执行加速的模型是使用以下 PyTorch 核心torch.nn.moduleTransformerEncoderTransformerEncoderLayerMultiHeadAttention的模型。此外,torchtext 已更新为使用核心库模块以从快速路径加速中受益。 (未来可能会启用其他模块以进行快速路径执行。)

Better Transformer 提供了两种加速类型:

  • 为 CPU 和 GPU 实现的原生多头注意力(MHA)以提高整体执行效率。
  • 利用 NLP 推理中的稀疏性。由于输入长度可变,输入标记可能包含大量填充标记,处理时可以跳过,从而实现显著加速。

快速路径执行受一些标准的限制。最重要的是,模型必须在推理模式下执行,并且在不收集梯度磁带信息的输入张量上运行(例如,使用 torch.no_grad 运行)。

要在 Google Colab 中查看此示例,请点击这里

本教程中的 Better Transformer 功能

  • 加载预训练模型(在 PyTorch 版本 1.12 之前创建,没有 Better Transformer)
  • 在 CPU 上运行和基准推理,使用 BT 快速路径(仅原生 MHA)
  • 在(可配置的)设备上运行和基准推理,使用 BT 快速路径(仅原生 MHA)
  • 启用稀疏性支持
  • 在(可配置的)设备上运行和基准推理,使用 BT 快速路径(原生 MHA + 稀疏性)

附加信息

有关 Better Transformer 的更多信息可以在 PyTorch.Org 博客A Better Transformer for Fast Transformer Inference中找到。

  1. 设置

1.1 加载预训练模型

我们通过按照torchtext.models中的说明从预定义的 torchtext 模型中下载 XLM-R 模型。我们还将设备设置为在加速器测试上执行。(根据需要启用 GPU 执行环境。)

import torch
import torch.nn as nn
print(f"torch version: {torch.__version__}")
DEVICE = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
print(f"torch cuda available: {torch.cuda.is_available()}")
import torch, torchtext
from torchtext.models import RobertaClassificationHead
from torchtext.functional import to_tensor
xlmr_large = torchtext.models.XLMR_LARGE_ENCODER
classifier_head = torchtext.models.RobertaClassificationHead(num_classes=2, input_dim = 1024)
model = xlmr_large.get_model(head=classifier_head)
transform = xlmr_large.transform() 

1.2 数据集设置

我们设置了两种类型的输入:一个小输入批次和一个带有稀疏性的大输入批次。

small_input_batch = [
               "Hello world",
               "How are you!"
]
big_input_batch = [
               "Hello world",
               "How are you!",
  """`Well, Prince, so Genoa and Lucca are now just family estates of the
Buonapartes. But I warn you, if you don't tell me that this means war,
if you still try to defend the infamies and horrors perpetrated by
that Antichrist- I really believe he is Antichrist- I will have
nothing more to do with you and you are no longer my friend, no longer
my 'faithful slave,' as you call yourself! But how do you do? I see
I have frightened you- sit down and tell me all the news.`
It was in July, 1805, and the speaker was the well-known Anna
Pavlovna Scherer, maid of honor and favorite of the Empress Marya
Fedorovna. With these words she greeted Prince Vasili Kuragin, a man
of high rank and importance, who was the first to arrive at her
reception. Anna Pavlovna had had a cough for some days. She was, as
she said, suffering from la grippe; grippe being then a new word in
St. Petersburg, used only by the elite."""
] 

接下来,我们选择小批量或大批量输入,预处理输入并测试模型。

input_batch=big_input_batch
model_input = to_tensor(transform(input_batch), padding_value=1)
output = model(model_input)
output.shape 

最后,我们设置基准迭代次数:

ITERATIONS=10 
  1. 执行

在 CPU 上运行和基准推理,使用 BT 快速路径(仅原生 MHA)

我们在 CPU 上运行模型,并收集性能信息:

  • 第一次运行使用传统(“慢速路径”)执行。
  • 第二次运行通过将模型置于推理模式并使用 model.eval()启用 BT 快速路径执行,并使用 torch.no_grad()禁用梯度收集。

当模型在 CPU 上执行时,您会看到改进(其幅度取决于 CPU 模型)。请注意,快速路径概要显示大部分执行时间在本地 TransformerEncoderLayer 实现 aten::_transformer_encoder_layer_fwd 中。

print("slow path:")
print("==========")
with torch.autograd.profiler.profile(use_cuda=False) as prof:
  for i in range(ITERATIONS):
    output = model(model_input)
print(prof)
model.eval()
print("fast path:")
print("==========")
with torch.autograd.profiler.profile(use_cuda=False) as prof:
  with torch.no_grad():
    for i in range(ITERATIONS):
      output = model(model_input)
print(prof) 

在(可配置的)设备上运行和基准推理,使用 BT 快速路径(仅原生 MHA)

我们检查 BT 的稀疏性设置:

model.encoder.transformer.layers.enable_nested_tensor 

我们禁用了 BT 的稀疏性:

model.encoder.transformer.layers.enable_nested_tensor=False 

我们在设备上运行模型,并收集用于设备上原生 MHA 执行的性能信息:

  • 第一次运行使用传统的(“慢路径”)执行。
  • 第二次运行通过将模型置于推理模式并使用 model.eval()禁用梯度收集来启用 BT 快速执行路径。

在 GPU 上执行时,您应该看到显着的加速,特别是对于小输入批处理设置:

model.to(DEVICE)
model_input = model_input.to(DEVICE)
print("slow path:")
print("==========")
with torch.autograd.profiler.profile(use_cuda=True) as prof:
  for i in range(ITERATIONS):
    output = model(model_input)
print(prof)
model.eval()
print("fast path:")
print("==========")
with torch.autograd.profiler.profile(use_cuda=True) as prof:
  with torch.no_grad():
    for i in range(ITERATIONS):
      output = model(model_input)
print(prof) 

2.3 在(可配置的)DEVICE 上运行和对比推理,包括 BT 快速执行路径和不包括 BT 快速执行路径(原生 MHA + 稀疏性)

我们启用稀疏性支持:

model.encoder.transformer.layers.enable_nested_tensor = True 

我们在 DEVICE 上运行模型,并收集原生 MHA 和稀疏性支持在 DEVICE 上的执行的概要信息:

  • 第一次运行使用传统的(“慢路径”)执行。
  • 第二次运行通过将模型置于推理模式并使用 model.eval()禁用梯度收集来启用 BT 快速执行路径。

在 GPU 上执行时,您应该看到显着的加速,特别是对于包含稀疏性的大输入批处理设置:

model.to(DEVICE)
model_input = model_input.to(DEVICE)
print("slow path:")
print("==========")
with torch.autograd.profiler.profile(use_cuda=True) as prof:
  for i in range(ITERATIONS):
    output = model(model_input)
print(prof)
model.eval()
print("fast path:")
print("==========")
with torch.autograd.profiler.profile(use_cuda=True) as prof:
  with torch.no_grad():
    for i in range(ITERATIONS):
      output = model(model_input)
print(prof) 

总结

在本教程中,我们介绍了在 torchtext 中使用 PyTorch 核心 Better Transformer 支持 Transformer 编码器模型的快速变压器推理。我们演示了在 BT 快速执行路径可用之前训练的模型中使用 Better Transformer 的方法。我们演示并对比了 BT 快速执行路径模式、原生 MHA 执行和 BT 稀疏性加速的使用。

从头开始的自然语言处理:使用字符级 RNN 对名称进行分类

原文:pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.html

译者:飞龙

协议:CC BY-NC-SA 4.0

注意

点击这里下载完整的示例代码

作者Sean Robertson

我们将构建和训练一个基本的字符级循环神经网络(RNN)来对单词进行分类。本教程以及其他两个“从头开始”的自然语言处理(NLP)教程 NLP From Scratch: Generating Names with a Character-Level RNN 和 NLP From Scratch: Translation with a Sequence to Sequence Network and Attention,展示了如何预处理数据以建模 NLP。特别是这些教程不使用 torchtext 的许多便利函数,因此您可以看到如何在低级别上处理 NLP 以建模 NLP。

字符级 RNN 将单词作为一系列字符读取 - 在每一步输出一个预测和“隐藏状态”,将其先前的隐藏状态馈送到每个下一步。我们将最终预测视为输出,即单词属于哪个类别。

具体来说,我们将在来自 18 种语言的几千个姓氏上进行训练,并根据拼写预测名称来自哪种语言:

$  python  predict.py  Hinton
(-0.47)  Scottish
(-1.52)  English
(-3.57)  Irish
$  python  predict.py  Schmidhuber
(-0.19)  German
(-2.48)  Czech
(-2.68)  Dutch 

推荐准备工作

在开始本教程之前,建议您已经安装了 PyTorch,并对 Python 编程语言和张量有基本的了解:

  • pytorch.org/获取安装说明
  • 使用 PyTorch 进行深度学习:60 分钟入门以开始使用 PyTorch 并学习张量的基础知识
  • 通过示例学习 PyTorch 提供广泛和深入的概述
  • 如果您以前是 Lua Torch 用户,请参阅 PyTorch for Former Torch Users

了解 RNN 以及它们的工作原理也会很有用:

准备数据

注意

这里下载数据并将其解压缩到当前目录。

data/names目录中包含 18 个名为[Language].txt的文本文件。每个文件包含一堆名称,每行一个名称,大多数是罗马化的(但我们仍然需要从 Unicode 转换为 ASCII)。

我们最终会得到一个字典,其中包含每种语言的名称列表,{language: [names ...]}。通用变量“category”和“line”(在我们的案例中用于语言和名称)用于以后的可扩展性。

from io import open
import glob
import os
def findFiles(path): return glob.glob(path)
print(findFiles('data/names/*.txt'))
import unicodedata
import string
all_letters = string.ascii_letters + " .,;'"
n_letters = len(all_letters)
# Turn a Unicode string to plain ASCII, thanks to https://stackoverflow.com/a/518232/2809427
def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn'
        and c in all_letters
    )
print(unicodeToAscii('Ślusàrski'))
# Build the category_lines dictionary, a list of names per language
category_lines = {}
all_categories = []
# Read a file and split into lines
def readLines(filename):
    lines = open(filename, encoding='utf-8').read().strip().split('\n')
    return [unicodeToAscii(line) for line in lines]
for filename in findFiles('data/names/*.txt'):
    category = os.path.splitext(os.path.basename(filename))[0]
    all_categories.append(category)
    lines = readLines(filename)
    category_lines[category] = lines
n_categories = len(all_categories) 
['data/names/Arabic.txt', 'data/names/Chinese.txt', 'data/names/Czech.txt', 'data/names/Dutch.txt', 'data/names/English.txt', 'data/names/French.txt', 'data/names/German.txt', 'data/names/Greek.txt', 'data/names/Irish.txt', 'data/names/Italian.txt', 'data/names/Japanese.txt', 'data/names/Korean.txt', 'data/names/Polish.txt', 'data/names/Portuguese.txt', 'data/names/Russian.txt', 'data/names/Scottish.txt', 'data/names/Spanish.txt', 'data/names/Vietnamese.txt']
Slusarski 

现在我们有category_lines,一个将每个类别(语言)映射到一系列行(名称)的字典。我们还跟踪了all_categories(只是一个语言列表)和n_categories以供以后参考。

print(category_lines['Italian'][:5]) 
['Abandonato', 'Abatangelo', 'Abatantuono', 'Abate', 'Abategiovanni'] 

将名称转换为张量

现在我们已经组织好所有的名称,我们需要将它们转换为张量以便使用。

为了表示单个字母,我们使用大小为<1 x n_letters>的“one-hot 向量”。一个 one-hot 向量除了当前字母的索引处为 1 之外,其他位置都填充为 0,例如,"b" = <0 1 0 0 0 ...>

为了构成一个单词,我们将其中的一堆连接成一个 2D 矩阵

额外的 1 维是因为 PyTorch 假设一切都是批处理 - 我们这里只是使用批处理大小为 1。

import torch
# Find letter index from all_letters, e.g. "a" = 0
def letterToIndex(letter):
    return all_letters.find(letter)
# Just for demonstration, turn a letter into a <1 x n_letters> Tensor
def letterToTensor(letter):
    tensor = torch.zeros(1, n_letters)
    tensor[0][letterToIndex(letter)] = 1
    return tensor
# Turn a line into a <line_length x 1 x n_letters>,
# or an array of one-hot letter vectors
def lineToTensor(line):
    tensor = torch.zeros(len(line), 1, n_letters)
    for li, letter in enumerate(line):
        tensor[li][0][letterToIndex(letter)] = 1
    return tensor
print(letterToTensor('J'))
print(lineToTensor('Jones').size()) 
tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0.]])
torch.Size([5, 1, 57]) 

创建网络

在自动求导之前,在 Torch 中创建一个循环神经网络涉及在几个时间步上克隆层的参数。这些层保存了隐藏状态和梯度,现在完全由图本身处理。这意味着您可以以非常“纯粹”的方式实现 RNN,就像常规的前馈层一样。

这个 RNN 模块(主要是从PyTorch for Torch 用户教程中复制的)只是在输入和隐藏状态上操作的 2 个线性层,输出后是一个LogSoftmax层。

import torch.nn as nn
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(RNN, self).__init__()
        self.hidden_size = hidden_size
        self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
        self.h2o = nn.Linear(hidden_size, output_size)
        self.softmax = nn.LogSoftmax(dim=1)
    def forward(self, input, hidden):
        combined = torch.cat((input, hidden), 1)
        hidden = self.i2h(combined)
        output = self.h2o(hidden)
        output = self.softmax(output)
        return output, hidden
    def initHidden(self):
        return torch.zeros(1, self.hidden_size)
n_hidden = 128
rnn = RNN(n_letters, n_hidden, n_categories) 

要运行此网络的一步,我们需要传递一个输入(在我们的情况下,是当前字母的张量)和一个先前的隐藏状态(最初我们将其初始化为零)。我们将得到输出(每种语言的概率)和下一个隐藏状态(我们将其保留到下一步)。

input = letterToTensor('A')
hidden = torch.zeros(1, n_hidden)
output, next_hidden = rnn(input, hidden) 

为了提高效率,我们不希望为每一步创建一个新的张量,因此我们将使用lineToTensor代替letterToTensor并使用切片。这可以通过预先计算批量张量来进一步优化。

input = lineToTensor('Albert')
hidden = torch.zeros(1, n_hidden)
output, next_hidden = rnn(input[0], hidden)
print(output) 
tensor([[-2.9083, -2.9270, -2.9167, -2.9590, -2.9108, -2.8332, -2.8906, -2.8325,
         -2.8521, -2.9279, -2.8452, -2.8754, -2.8565, -2.9733, -2.9201, -2.8233,
         -2.9298, -2.8624]], grad_fn=<LogSoftmaxBackward0>) 

如您所见,输出是一个<1 x n_categories>张量,其中每个项目都是该类别的可能性(可能性越高,越可能)。

训练

为训练做准备

在进行训练之前,我们应该编写一些辅助函数。第一个是解释网络输出的函数,我们知道它是每个类别的可能性。我们可以使用Tensor.topk来获取最大值的索引:

def categoryFromOutput(output):
    top_n, top_i = output.topk(1)
    category_i = top_i[0].item()
    return all_categories[category_i], category_i
print(categoryFromOutput(output)) 
('Scottish', 15) 

我们还希望快速获取一个训练示例(一个名称及其语言):

import random
def randomChoice(l):
    return l[random.randint(0, len(l) - 1)]
def randomTrainingExample():
    category = randomChoice(all_categories)
    line = randomChoice(category_lines[category])
    category_tensor = torch.tensor([all_categories.index(category)], dtype=torch.long)
    line_tensor = lineToTensor(line)
    return category, line, category_tensor, line_tensor
for i in range(10):
    category, line, category_tensor, line_tensor = randomTrainingExample()
    print('category =', category, '/ line =', line) 
category = Chinese / line = Hou
category = Scottish / line = Mckay
category = Arabic / line = Cham
category = Russian / line = V'Yurkov
category = Irish / line = O'Keeffe
category = French / line = Belrose
category = Spanish / line = Silva
category = Japanese / line = Fuchida
category = Greek / line = Tsahalis
category = Korean / line = Chang 

训练网络

现在训练这个网络所需的全部工作就是向其展示一堆示例,让它猜测,并告诉它是否错误。

对于损失函数,nn.NLLLoss是合适的,因为 RNN 的最后一层是nn.LogSoftmax

criterion = nn.NLLLoss() 

每次训练循环将:

  • 创建输入和目标张量
  • 创建一个初始化的零隐藏状态
  • 逐个读取每个字母
  • 保留下一个字母的隐藏状态
  • 将最终输出与目标进行比较
  • 反向传播
  • 返回输出和损失
learning_rate = 0.005 # If you set this too high, it might explode. If too low, it might not learn
def train(category_tensor, line_tensor):
    hidden = rnn.initHidden()
    rnn.zero_grad()
    for i in range(line_tensor.size()[0]):
        output, hidden = rnn(line_tensor[i], hidden)
    loss = criterion(output, category_tensor)
    loss.backward()
    # Add parameters' gradients to their values, multiplied by learning rate
    for p in rnn.parameters():
        p.data.add_(p.grad.data, alpha=-learning_rate)
    return output, loss.item() 

现在我们只需运行一堆示例。由于train函数返回输出和损失,我们可以打印其猜测并跟踪损失以绘图。由于有成千上万的示例,我们仅打印每print_every个示例,并计算损失的平均值。

import time
import math
n_iters = 100000
print_every = 5000
plot_every = 1000
# Keep track of losses for plotting
current_loss = 0
all_losses = []
def timeSince(since):
    now = time.time()
    s = now - since
    m = math.floor(s / 60)
    s -= m * 60
    return '%dm %ds' % (m, s)
start = time.time()
for iter in range(1, n_iters + 1):
    category, line, category_tensor, line_tensor = randomTrainingExample()
    output, loss = train(category_tensor, line_tensor)
    current_loss += loss
    # Print ``iter`` number, loss, name and guess
    if iter % print_every == 0:
        guess, guess_i = categoryFromOutput(output)
        correct = '✓' if guess == category else '✗ (%s)' % category
        print('%d  %d%% (%s) %.4f  %s / %s  %s' % (iter, iter / n_iters * 100, timeSince(start), loss, line, guess, correct))
    # Add current loss avg to list of losses
    if iter % plot_every == 0:
        all_losses.append(current_loss / plot_every)
        current_loss = 0 
5000 5% (0m 29s) 2.6379 Horigome / Japanese ✓
10000 10% (0m 58s) 2.0172 Miazga / Japanese ✗ (Polish)
15000 15% (1m 29s) 0.2680 Yukhvidov / Russian ✓
20000 20% (1m 58s) 1.8239 Mclaughlin / Irish ✗ (Scottish)
25000 25% (2m 29s) 0.6978 Banh / Vietnamese ✓
30000 30% (2m 58s) 1.7433 Machado / Japanese ✗ (Portuguese)
35000 35% (3m 28s) 0.0340 Fotopoulos / Greek ✓
40000 40% (3m 58s) 1.4637 Quirke / Irish ✓
45000 45% (4m 28s) 1.9018 Reier / French ✗ (German)
50000 50% (4m 57s) 0.9174 Hou / Chinese ✓
55000 55% (5m 27s) 1.0506 Duan / Vietnamese ✗ (Chinese)
60000 60% (5m 57s) 0.9617 Giang / Vietnamese ✓
65000 65% (6m 27s) 2.4557 Cober / German ✗ (Czech)
70000 70% (6m 57s) 0.8502 Mateus / Portuguese ✓
75000 75% (7m 26s) 0.2750 Hamilton / Scottish ✓
80000 80% (7m 56s) 0.7515 Maessen / Dutch ✓
85000 85% (8m 26s) 0.0912 Gan / Chinese ✓
90000 90% (8m 55s) 0.1190 Bellomi / Italian ✓
95000 95% (9m 25s) 0.0137 Vozgov / Russian ✓
100000 100% (9m 55s) 0.7808 Tong / Vietnamese ✓ 

PyTorch 2.2 中文官方教程(六)(3)https://developer.aliyun.com/article/1482508

相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
相关文章
|
6天前
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(二十)(4)
PyTorch 2.2 中文官方教程(二十)
31 0
PyTorch 2.2 中文官方教程(二十)(4)
|
6天前
|
PyTorch 算法框架/工具 并行计算
PyTorch 2.2 中文官方教程(二十)(3)
PyTorch 2.2 中文官方教程(二十)
49 0
|
6天前
|
Android开发 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(二十)(2)
PyTorch 2.2 中文官方教程(二十)
49 0
PyTorch 2.2 中文官方教程(二十)(2)
|
6天前
|
iOS开发 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(二十)(1)
PyTorch 2.2 中文官方教程(二十)
51 0
PyTorch 2.2 中文官方教程(二十)(1)
|
6天前
|
PyTorch 算法框架/工具 并行计算
PyTorch 2.2 中文官方教程(十九)(4)
PyTorch 2.2 中文官方教程(十九)
32 0
|
6天前
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(十九)(3)
PyTorch 2.2 中文官方教程(十九)
30 0
PyTorch 2.2 中文官方教程(十九)(3)
|
6天前
|
异构计算 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十九)(2)
PyTorch 2.2 中文官方教程(十九)
62 0
PyTorch 2.2 中文官方教程(十九)(2)
|
6天前
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.2 中文官方教程(十九)(1)
PyTorch 2.2 中文官方教程(十九)
81 1
PyTorch 2.2 中文官方教程(十九)(1)
|
6天前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十八)(4)
PyTorch 2.2 中文官方教程(十八)
55 1
|
6天前
|
机器学习/深度学习 PyTorch 算法框架/工具
PyTorch 2.2 中文官方教程(十八)(3)
PyTorch 2.2 中文官方教程(十八)
36 1
PyTorch 2.2 中文官方教程(十八)(3)

相关实验场景

更多