BERT的动态量化

简介

使用HuggingFace Transformers示例(the HuggingFace Transformers examples)中的BERT模型,在BERT模型上应用动态量化。通过这一步一步的过程,演示如何将BERT转换为动态量化模型。

  • BERT (Bidirectional Embedding Representations from Transformers)是一种预训练语言表示的方法,它在许多流行的自然语言处理(NLP)任务上取得了最先进的精度结果,如问答、文本分类等。原始论文BERT
  • PyTorch 中的动态量化支持将浮点模型转换为量化模型,其中权重为静态 int8 或 float16 数据类型,激活为动态量化。当权重量化为 int8 时,激活被动态量化(每batch)为 int8。在PyTorch中,torch.quantization.quantize_dynamic API,将指定的模块替换为动态仅权重量化的版本,并输出量化模型。
  • 在通用语言理解评估基准(GLUE)中的Microsoft Research Paraphrase Corpus (MRPC) task上展示了准确性和推理性能结果。MRPC (Dolan and Brockett, 2005)是一个自动从在线新闻源中提取的句子对语料库,并配有人工标注,以判断句子对中的句子是否语义等价。由于类别是不平衡的(正样本为68%,负样本为32%),通常的做法是使用F1指标评估。MRPC是语言对分类的一个常见NLP任务,如下图所示。
继续阅读BERT的动态量化

LSTM的动态量化

简介

量化涉及将模型的权重和激活值从float转换为int,这可以使模型大小跟小和推理的速度更快,而只损失很小的精度。

使用PyTorch示例中的单词语言模型(word language model),将最简单的量化形式,动态量化(dynamic quantization)应用于基于LSTM的下一个单词预测模型。

# imports
import os
from io import open
import time
import torch
import torch.nn as nn
import torch.nn.functional as F

定义模型

按照单词语言模型示例中的模型(model)定义LSTM模型架构。

class LSTMModel(nn.Module):
    """Container module with an encoder, a recurrent module, and a decoder."""

    def __init__(self, ntoken, ninp, nhid, nlayers, dropout=0.5):
        super(LSTMModel, self).__init__()
        self.drop = nn.Dropout(dropout)
        self.encoder = nn.Embedding(ntoken, ninp)
        self.rnn = nn.LSTM(ninp, nhid, nlayers, dropout=dropout)
        self.decoder = nn.Linear(nhid, ntoken)

        self.init_weights()

        self.nhid = nhid
        self.nlayers = nlayers

    def init_weights(self):
        initrange = 0.1
        self.encoder.weight.data.uniform_(-initrange, initrange)
        self.decoder.bias.data.zero_()
        self.decoder.weight.data.uniform_(-initrange, initrange)

    def forward(self, input, hidden):
        emb = self.drop(self.encoder(input))
        output, hidden = self.rnn(emb, hidden)
        output = self.drop(output)
        decoded = self.decoder(output)
        return decoded, hidden

    def init_hidden(self, bsz):
        weight = next(self.parameters())
        return (weight.new_zeros(self.nlayers, bsz, self.nhid),
                weight.new_zeros(self.nlayers, bsz, self.nhid))
继续阅读LSTM的动态量化

PyTorch安装

创建环境

conda create -n env_name python=3.x

可以先使用conda create创建一个虚拟环境,env_name为虚拟环境的名字,3.x为python的版本,例如conda create -n pytorch python=3.8

使用conda activate pytorch激活虚拟环境,conda deactivate退出环境。

CUDA版本

首先查看cuda的版本,nvidia-smi

cuda版本为10.2

官方安装

官方网站:https://pytorch.org/get-started/locally/

在官方网站点选需求可以显示安装的命令,如:

由于没有需要的CUDA版本,因此在以前的版本中寻找https://pytorch.org/get-started/previous-versions/

例如需要安装v1.12.1版本,使用Conda,Linux 或者Windows操作系统,使用如下命令:

# CUDA 10.2
conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=10.2 -c pytorch

使用上述方式安装的torch版本如下:

pytorch的后缀带有py3.8_cuda10.2_cudnn7.6.5_0;torchaudio、torchvision的后缀带有py38_cu102。选择y之后就会自动安装。

或者如果使用Wheel,Linux或者Windows操作系统,使用如下命令:

# CUDA 10.2
pip install torch==1.12.1+cu102 torchvision==0.13.1+cu102 torchaudio==0.12.1 --extra-index-url https://download.pytorch.org/whl/cu102

但是官方网站安装pytorch的速度较慢,因此可以选择镜像安装。把-c pytorch表示的pytorch源,更改为国内的镜像。

镜像安装

镜像地址

清华大学:https://mirrors.tuna.tsinghua.edu.cn/

阿里云:http://mirrors.aliyun.com/

镜像地址可以参考https://blog.csdn.net/djzhao627/article/details/122999240

conda install pytorch==1.12.1 torchvision==0.13.1 torchaudio==0.12.1 cudatoolkit=10.2 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/linux-64/

检验

import torch
print(torch.__version__)
print(torch.version.cuda)
print(torch.backends.cudnn.version())
torch.cuda.is_available()      # cuda是否可用;
torch.cuda.device_count()      # 返回gpu数量;
torch.cuda.get_device_name(0)  # 返回gpu名字,设备索引默认从0开始;
torch.cuda.current_device()    # 返回当前设备索引

PyTorch数据加载(二)

处理数据样本的代码可能会混乱且难以维护;理想情况下,希望数据集代码与模型训练代码解耦,以获得更好的可读性和模块化。PyTorch提供了两个数据基元torch.utils.data.DataLoader 和 torch.utils.data.Dataset,它们允许使用预加载的数据集以及自己的数据。Dataset存储样本及其对应的标签,DataLoader在Dataset周围包装了一个可迭代对象,以方便访问样本。

PyTorch提供了许多预加载的数据集(FashionMNIST),它们是torch.utils.data.Dataset的子类,并实现了特定于特定数据的函数。它们可以用于模型的原型和基准测试。可以在这里找到它们:图像数据集(Image Datasets),文本数据集(Text Datasets)和音频数据集(Audio Datasets)。

加载数据

这是一个如何从TorchVision加载Fashion-MNIST数据集的示例。Fashion-MNIST是Zalando的文章图像数据集,包含60,000个训练示例和10,000个测试示例。每个示例包含一个28×28灰度图像和10个类别之一的关联标签。

用以下参数加载FashionMNIST数据集:

root存储训练/测试数据的路径,
train指定训练或测试数据集,
download=True如果根目录下不可用,则从互联网上下载数据。
transform和target_transform指定了特征变换和标签变换

import torch
from torch.utils.data import Dataset
from torchvision import datasets
from torchvision.transforms import ToTensor
import matplotlib.pyplot as plt


training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor()
)

test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor()
)

迭代和可视化数据集

可以像列表一样手动索引数据集:training_data[index]。使用matplotlib来可视化训练数据中的一些样本。

labels_map = {
    0: "T-Shirt",
    1: "Trouser",
    2: "Pullover",
    3: "Dress",
    4: "Coat",
    5: "Sandal",
    6: "Shirt",
    7: "Sneaker",
    8: "Bag",
    9: "Ankle Boot",
}
figure = plt.figure(figsize=(8, 8))
cols, rows = 3, 3
for i in range(1, cols * rows + 1):
    sample_idx = torch.randint(len(training_data), size=(1,)).item()
    img, label = training_data[sample_idx]
    figure.add_subplot(rows, cols, i)
    plt.title(labels_map[label])
    plt.axis("off")
    plt.imshow(img.squeeze(), cmap="gray")
plt.show()

为文件创建自定义数据集

一个自定义数据集类必须实现三个函数:__init____len____getitem__。FashionMNIST图像存储在目录img_dir中,它们的标签单独存储在CSV文件annotations_file中。

import os
import pandas as pd
from torchvision.io import read_image

class CustomImageDataset(Dataset):
    def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
        self.img_labels = pd.read_csv(annotations_file)
        self.img_dir = img_dir
        self.transform = transform
        self.target_transform = target_transform

    def __len__(self):
        return len(self.img_labels)

    def __getitem__(self, idx):
        img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
        image = read_image(img_path)
        label = self.img_labels.iloc[idx, 1]
        if self.transform:
            image = self.transform(image)
        if self.target_transform:
            label = self.target_transform(label)
        return image, label

__init__

__init__函数在实例化Dataset对象时只运行一次。初始化包含图像、标注文件和两个transforms。标注文件如下:

tshirt1.jpg, 0
tshirt2.jpg, 0
……
ankleboot999.jpg, 9

def __init__(self, annotations_file, img_dir, transform=None, target_transform=None):
    self.img_labels = pd.read_csv(annotations_file)
    self.img_dir = img_dir
    self.transform = transform
    self.target_transform = target_transform

__len__

__len__函数返回数据集中的样本数量。

def __len__(self):
    return len(self.img_labels)

__getitem__

__getitem__函数从给定索引idx的数据集中加载并返回一个样本。根据索引,它确定图像在磁盘上的位置,使用read_image将其转换为张量,从csv数据中检索相应的标签。对它们调用transform函数(如果适用),并返回元组中的张量图像和相应的标签。

def __getitem__(self, idx):
    img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
    image = read_image(img_path)
    label = self.img_labels.iloc[idx, 1]
    if self.transform:
        image = self.transform(image)
    if self.target_transform:
        label = self.target_transform(label)
    return image, label

使用DataLoaders为训练准备数据

Dataset每次检索数据集一个样本的特征和标签。在训练模型时,通常希望以“minibatches”的方式传递样本,在每个epoch重新shuffle数据以减少模型过拟合,并使用Python的多进程处理来加速数据检索。DataLoader是一个可迭代对象,它将这种复杂性抽象为一个简单的API。

from torch.utils.data import DataLoader

train_dataloader = DataLoader(training_data, batch_size=64, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size=64, shuffle=True)

遍历DataLoader

已经将该数据集加载到DataLoader中,并可以根据需要迭代该数据集。下面的每次迭代都会返回一批train_features和train_labels(分别包含batch_size=64个特征和标签)。因为指定了shuffle=True,所以在遍历完所有batches后,数据会被打乱(为了更细粒度地控制数据的加载顺序,可以看一下Samplers)。

# Display image and label.
train_features, train_labels = next(iter(train_dataloader))
print(f"Feature batch shape: {train_features.size()}")
print(f"Labels batch shape: {train_labels.size()}")
img = train_features[0].squeeze()
label = train_labels[0]
plt.imshow(img, cmap="gray")
plt.show()
print(f"Label: {label}")

输出:

Feature batch shape: torch.Size([64, 1, 28, 28])
Labels batch shape: torch.Size([64])
Label: 9

PyTorch快速入门

模型训练需要的几个基础部分包括:数据、模型、优化器、损失函数,这四个部分通过迭代不断让模型学习到输入到输出的映射关系。

数据

PyTorch有两个主要的处理数据的模块,torch.utils.data.DataLoardertorch.utils.data.DatasetDataset 存储样本及其对应的标签,DataLoader 对数据集封装了一个迭代器。

import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision.transforms import ToTensor

PyTorch提供了特定领域的库,如TorchText、TorchVision和TorchAudio,这些库都包含数据集。此文将使用TorchVision数据集。

torchvision.datasets 模块包含许多真实世界视觉数据的数据集对象,如CIFAR和COCO。在此使用FashionMNIST数据集。每个TorchVision数据集都包含两个参数:transform和target_transform,分别用于修改样本和标签。

# Download training data from open datasets.
training_data = datasets.FashionMNIST(
    root="data",
    train=True,
    download=True,
    transform=ToTensor(),
)

# Download test data from open datasets.
test_data = datasets.FashionMNIST(
    root="data",
    train=False,
    download=True,
    transform=ToTensor(),
)

Dataset 作为参数传递给 DataLoader。它对数据集包装了一个迭代器,并支持自动批处理(batching)、采样(sampling)、混洗(shuffling)和多进程数据加载(multiprocess data loading)。在这里,batch size定义为64,即dataloader迭代器中的每个元素将返回64个特征和标签。

batch_size = 64

# Create data loaders.
train_dataloader = DataLoader(training_data, batch_size=batch_size)
test_dataloader = DataLoader(test_data, batch_size=batch_size)

for X, y in test_dataloader:
    print(f"Shape of X [N, C, H, W]: {X.shape}")
    print(f"Shape of y: {y.shape} {y.dtype}")
    break

上述代码输出结果为:

继续阅读PyTorch快速入门

PyTorch定义神经网络

简介

深度学习使用人工神经网络(模型),它是由许多层相互连接的单元组成的计算系统。通过这些相互连接的单元传递数据,神经网络能够学习如何近似地计算将输入转换为输出所需的计算。在PyTorch中,可以使用 torch.nn 构建神经网络。

PyTorch提供了优雅设计的模块和类,包括 torch.nn,来帮助创建和训练神经网络。nn.Module 包含层,forward(input) 方法返回输出。

步骤

  1. 导入加载数据所需的所有库
  2. 定义并初始化神经网络
  3. 指定数据如何在模型中传递
  4. [可选]将数据传递给模型进行测试

导入加载数据所需的库

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

定义并初始化神经网络

该网络用于识别图像。将使用PyTorch内置的卷积过程。卷积将图像的每个元素添加到其局部邻域,由核或小矩阵加权,这有助于从输入图像中提取某些特征(如边缘检测、清晰度、模糊度等)。

定义模型的Net类有两个要求。第一个是写一个 __init__ 函数来引用 nn.Module。这个函数用于定义神经网络中所有的连接层。
使用卷积,将定义的模型以获取1个输入图像通道,输出与目标匹配,即表示数字0到9的10个标签。将遵循标准MNIST算法。

class Net(nn.Module):
    def __init__(self):
      super(Net, self).__init__()

      self.conv1 = nn.Conv2d(1, 32, 3, 1)
      self.conv2 = nn.Conv2d(32, 64, 3, 1)

      self.dropout1 = nn.Dropout2d(0.25)
      self.dropout2 = nn.Dropout2d(0.5)

      self.fc1 = nn.Linear(9216, 128)
      self.fc2 = nn.Linear(128, 10)

my_nn = Net()
print(my_nn)

已经定义了神经网络,然后必须定义数据如何通过网络。

指定数据如何通过模型

当使用PyTorch构建模型时,需要定义 forward 函数,它将数据传递到计算图,表示前馈算法。可以在前向函数中使用任何张量运算。

class Net(nn.Module):
    def __init__(self):
      super(Net, self).__init__()
      self.conv1 = nn.Conv2d(1, 32, 3, 1)
      self.conv2 = nn.Conv2d(32, 64, 3, 1)
      self.dropout1 = nn.Dropout2d(0.25)
      self.dropout2 = nn.Dropout2d(0.5)
      self.fc1 = nn.Linear(9216, 128)
      self.fc2 = nn.Linear(128, 10)


    def forward(self, x):
      x = self.conv1(x)
      x = F.relu(x)
      x = self.conv2(x)
      x = F.relu(x)
      x = F.max_pool2d(x, 2)
      x = self.dropout1(x)
      x = torch.flatten(x, 1)  # Flatten x with start_dim=1
      x = self.fc1(x)  # Pass data through fc1
      x = F.relu(x)
      x = self.dropout2(x)
      x = self.fc2(x)
      output = F.log_softmax(x, dim=1)  # Apply softmax to x
      return output

[可选]将数据传递给模型进行测试

为了确保收到所需的输出,通过传递一些随机数据来测试模型。

# Equates to one random 28x28 image
random_data = torch.rand((1, 1, 28, 28))

my_nn = Net()
result = my_nn(random_data)
print (result)

PyTorch数据加载(一)

简介

PyTorch具有广泛的神经网络构建模块,具有简单、直观和稳定的API。PyTorch包含为模型准备和加载公共数据集的包。

PyTorch数据加载实用工具的核心是torch.utils.data.DataLoader类。它表示数据集上的Python迭代器。PyTorch在torch.utils.data.Dataset类中提供了内置的高质量数据集。它们允许使用预加载的数据集以及自己的数据。Dataset存储样本及其对应的标签,DataLoader在Dataset周围包装了一个可迭代对象,以方便访问样本。这些数据集目前包含:

使用torchaudio.datasets中的Yesno数据集演示如何高效地将数据从PyTorch Dataset 加载到PyTorch DataLoader 中。

安装

需要安装 torchaudio 以访问数据集,pip install torchaudio

要在谷歌Colab中运行,取消注释,!pip install torchaudio

步骤

  1. 导入加载数据所需的所有库
  2. 访问数据集中的数据
  3. 加载数据
  4. 遍历数据
  5. [可选]可视化数据

导入加载数据所需的所有库

在这个样例中,使用 torchtorchaudio。根据使用的内置数据集,还可以安装和导入torchvisiontorchtext

import torch
import torchaudio

访问数据集中的数据

torchaudio中的Yesno数据集有60段记录,记录的是一个人用希伯来语说“yes”或“no”;每段录音有8个单词长。torchaudio.datasets.YESNO为YesNo创建数据集

yesno_data = torchaudio.datasets.YESNO(
     root='./',
     url='http://www.openslr.org/resources/1/waves_yesno.tar.gz',
     folder_in_archive='waves_yesno',
     download=True)

数据集中的每一项都是元组:(waveform, sample_rate, labels)。对Yesno数据集必须设置root参数,用于存储训练和测试数据集。其他参数是可选的,显示了它们的默认值。

加载数据

现在可以访问数据集了,将它传递给torch.utils.data.DataLoader。DataLoader将数据集和采样器结合起来,返回一个遍历数据集的迭代器。

data_loader = torch.utils.data.DataLoader(yesno_data,
                                          batch_size=1,
                                          shuffle=True)

遍历数据

数据现在可以使用data_loader进行迭代了。当开始训练模型时,这将是必要的!现在data_loader对象中的每个数据条目都被转换为一个张量,其中包含表示waveform、sample rate和labels的张量。

for data in data_loader:
  print("Data: ", data)
  print("Waveform: {}\nSample rate: {}\nLabels: {}".format(data[0], data[1], data[2]))
  break

可视化数据

可以选择将数据可视化,以进一步理解DataLoader的输出

import matplotlib.pyplot as plt

print(data[0][0].numpy())

plt.figure()
plt.plot(waveform.t().numpy())