DYNAMIC QUANTIZATION

Tutorials >PyTorch Recipes > Dynamic Quantization

doc : Dynamic Quantization — PyTorch Tutorials 1.11.0+cu102 documentation

2022年5月24日

tag : 翻译学习

topic : Pytorch 量化


0 Dynamic Quantization

在本配方中,将介绍如何利用动态量化来加速 LSTM 风格的 递归神经网络 上的推理

减小了模型权重的大小并加快了模型的执行速度。

1 Introduction

在设计神经网络时,进行许多权衡。在模型开发和训练期间,可以更改递归神经网络中的层数和参数数,并根据模型大小和/或模型延迟或吞吐量权衡准确性。此类更改可能需要大量时间和计算资源,因为正在迭代模型训练。量化提供了一种在训练完成后使用已知模型在性能和模型准确性之间进行类似权衡的方法。

可在单个会话中进行尝试,肯定可以看到模型大小显著减小,并且可以在不损失很多准确性的情况下显著减少延迟。

2 What is dynamic quantization?

量化网络意味着将其转换为对权重和/或激活使用精度较低的整数表示。这样可以降低模型大小,并允许在 CPU 或 GPU 上使用更高吞吐量的数学运算。

从浮点值转换为整数值时,实质上是将浮点值乘以某个比例因子,并将结果舍入为整数。各种量化方法的不同之处在于它们确定该比例因子的方式。

此处所述的动态量化的关键思想是,将根据运行时观察到的数据范围动态确定激活的比例因子。这可确保“调整”比例因子,以便保留有关每个观测数据集的尽可能多的信号。

另一方面,模型参数在模型转换期间是已知的,它们会提前转换并以INT8形式存储。

量化模型中的算术是使用矢量化的INT8指令完成的。通常使用 INT16 或 INT32 进行累积,以避免溢出。如果量化下一层或转换为FP32以进行输出,则此高精度值将缩小到INT8。

**动态量化相对不需要调整参数,**这使得它非常适合作为将LSTM模型转换为部署的标准部分添加到生产pipeline中。

注意
此处所采用方法的限制

此配方简要介绍了 PyTorch 中的动态量化功能以及使用它的工作流程。我们的重点是解释用于转换模型的特定函数。为了简洁明了,我们将进行一些重大的简化
  1. 从最小的LSTM网络开始
  2. 只需使用随机隐藏状态初始化网络即可
  3. 将使用随机输入测试网络
  4. 在本教程中,并不打算从新训练网络
  5. 将看到该网络的量化形式比我们开始的浮点网络更小,运行速度更快。
  6. 将看到输出值通常与FP32网络的输出值处于相同的大致值,但我们在这里不演示真实训练网络上的预期精度损失。

将看到如何完成动态量化,并能够看到内存使用和延迟时间的潜在减少。证明该技术可以在经过训练的LSTM上保持高水平的模型准确性,留给更高级的教程。如果想立即进行更高级的处理,请继续学习高级动态量化教程 advanced dynamic quantization tutorial.

3 Steps

这个食谱有5个步骤。

  1. 设置 - 在这里定义一个非常简单的LSTM,导入模块,并建立一些随机输入张量。
  2. 执行量化 - 在这里,您可以实例化浮点模型,然后创建其量化版本。
  3. 查看模型大小 - 在这里,显示模型大小变小。
  4. 查看延迟 - 在此处运行两个模型并比较模型运行时(延迟)。
  5. 查看准确性 - 在这里,您将运行两个模型并比较输出。
1: Set Up

这是为配方的其余部分设置的简单代码。

此处导入的唯一模块是 torch.quantization,其中包括 PyTorch 的量化运算符和转换函数。 我们还定义了一个非常简单的 LSTM 模型并设置了一些输入。

# import the modules used here in this recipe
import torch
import torch.quantization
import torch.nn as nn
import copy
import os
import time

# define a very, very simple LSTM for demonstration purposes
# in this case, we are wrapping nn.LSTM, one layer, no pre or post processing
# inspired by
# https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html, by Robert Guthrie
# and https://pytorch.org/tutorials/advanced/dynamic_quantization_tutorial.html
class lstm_for_demonstration(nn.Module):
  """Elementary Long Short Term Memory style model which simply wraps nn.LSTM
     Not to be used for anything other than demonstration.
  """
  def __init__(self,in_dim,out_dim,depth):
     super(lstm_for_demonstration,self).__init__()
     self.lstm = nn.LSTM(in_dim,out_dim,depth)

  def forward(self,inputs,hidden):
     out,hidden = self.lstm(inputs,hidden)
     return out, hidden


torch.manual_seed(29592)  # set the seed for reproducibility

#shape parameters
model_dimension=8
sequence_length=20
batch_size=1
lstm_depth=1

# random data for input
inputs = torch.randn(sequence_length,batch_size,model_dimension)
# hidden is actually is a tuple of the initial hidden state and the initial cell state
hidden = (torch.randn(lstm_depth,batch_size,model_dimension), torch.randn(lstm_depth,batch_size,model_dimension))
2: Do the Quantization

现在我们进入有趣的部分。首先创建一个名为float_lstm模型实例,然后对其进行量化。将使用

torch.quantization.quantize_dynamic()

此处的函数(see documentation) 获取模型,然后是我们希望量化的子模块的列表(如果它们出现),然后是我们的目标数据类型。 此函数将原始模型的量化版本作为新模块返回。

这就是它所需要的一切。

# here is our floating point instance
float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth)

# this is the call that does the work
quantized_lstm = torch.quantization.quantize_dynamic(
    float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8
)

# show the changes that were made
print('Here is the floating point version of this module:')
print(float_lstm)
print('')
print('and now the quantized version:')
print(quantized_lstm)
3. Look at Model Size

现在已经量化好了模型带来了什么好处?

  • first 已经用INT8值(以及一些记录的比例因子)替换了FP32模型参数。
  • 这意味着要存储和移动的数据减少了约 75%。使用默认值时,下面显示的减少将小于75%,但是如果增加上面的模型大小(例如,您可以将模型尺寸设置为类似80的值),这将收敛到小4倍,因为存储的模型大小越来越多地由参数值主导。
def print_size_of_model(model, label=""):
    torch.save(model.state_dict(), "temp.p")
    size=os.path.getsize("temp.p")
    print("model: ",label,' \t','Size (KB):', size/1e3)
    os.remove('temp.p')
    return size

# compare the sizes
f=print_size_of_model(float_lstm,"fp32")
q=print_size_of_model(quantized_lstm,"int8")
print("{0:.2f} times smaller".format(f/q))
4. Look at Latency
  • **second 好处是量化模型通常运行得更快。**这是各种影响的组合,至少包括:
  • 减少在 中移动参数数据所花费的时间
  • 更快的 INT8 操作

正如将看到的,这个超级简单网络的量化版本运行得更快。这通常适用于更复杂的网络,但正如他们所说,““你的旅费可能会 不同”,这取决于许多因素,包括模型的结构和您正在运行的硬件。

# compare the performance
print("Floating point FP32")
# %timeit float_lstm.forward(inputs, hidden)

print("Quantized INT8")
# %timeit quantized_lstm.forward(inputs,hidden)
5: Look at Accuracy

不打算在这里详细对比准确性,因为我们使用的是随机初始化的网络,而不是经过适当训练的网络。但是,我认为值得快速证明的是,量化网络确实产生了与原始网络“in the same ballpark” 的输出张量。

更详细的分析,请参阅本配方末尾引用的更高级教程。

# run the float model
out1, hidden1 = float_lstm(inputs, hidden)
mag1 = torch.mean(abs(out1)).item()
print('mean absolute value of output tensor values in the FP32 model is {0:.5f} '.format(mag1))

# run the quantized model
out2, hidden2 = quantized_lstm(inputs, hidden)
mag2 = torch.mean(abs(out2)).item()
print('mean absolute value of output tensor values in the INT8 model is {0:.5f}'.format(mag2))

# compare them
mag3 = torch.mean(abs(out1-out2)).item()
print('mean absolute value of the difference between the output tensors is {0:.5f} or {1:.2f} percent'.format(mag3,mag3/mag1*100))

4 Learn More

解释了什么是动态量化,它带来了什么好处,并且使用该函数快速量化了一个简单的LSTM模型。

torch.quantization.quantize_dynamic()