想加速模型推理?试试用int8量化呢
int8量化详解

一、写在前面
用int8模型推理,模型大小是fp32的1/4,推理速度必然会加快,但精度会受影响,平均可能掉1-2个点,大家酌情使用。
二、int8推理的两种方式
2.1 模型转int8修改
那么,模型调为int8推理 需要做哪些修改?
将模型调整为 int8 推理 可以显著提高推理速度并降低内存占用,但需要做一些特定的修改和配置。以下是实现模型 int8 推理 的步骤和所需修改:
1. 检查硬件支持
首先确保你的硬件支持 int8 推理。许多现代 GPU 和 CPU 都支持 INT8 加速,例如:
- NVIDIA GPU:Ampere 架构及更新的 Tensor Cores 支持 INT8 加速。
- Intel CPU:支持 INT8 推理(通过 Intel MKL 或 oneDNN)。
- TPU:支持 INT8 运算。
如果你的硬件支持 INT8,便可以利用相关库(如 NVIDIA TensorRT、ONNX Runtime、PyTorch Quantization 等)优化推理。
2. 选择量化策略
在进行 INT8 推理时,量化策略是关键:
- 动态量化(Dynamic Quantization):
- 仅在推理时将权重量化为 INT8,激活值在运行时动态量化。
- 适用于 RNN/LSTM 等。
- 静态量化(Static Quantization):
- 权重和激活值都预先量化,需在量化前收集校准数据。
- 适用于 CNN/Transformer 等。
- 混合量化(Quantization Aware Training, QAT):
- 在训练时模拟量化误差,性能更高但复杂度更大。
3. 使用框架支持的量化工具
根据你的深度学习框架,选择合适的工具来实现 INT8 量化。
PyTorch
PyTorch 提供了多种量化工具,如 torch.quantization 和 torch.ao.quantization。以下是常用方法:
-
动态量化:
动态量化适合模型较大的情况,例如 Transformer。import torch from transformers import AutoModel # 加载预训练模型 model = AutoModel.from_pretrained("bert-base-uncased") model.eval() # 应用动态量化 quantized_model = torch.quantization.quantize_dynamic( model, # 模型 {torch.nn.Linear}, # 要量化的层 dtype=torch.qint8 # 量化类型 ) -
静态量化:
静态量化需要先进行校准:import torch from torchvision.models import resnet18 model = resnet18(pretrained=True).eval() # 准备量化模型 model.qconfig = torch.quantization.get_default_qconfig('fbgemm') # CPU 配置 torch.quantization.prepare(model, inplace=True) # 校准模型(使用真实数据) for images, _ in dataloader: model(images) # 转换为量化模型 torch.quantization.convert(model, inplace=True) -
QAT:
需要在训练时进行量化感知训练:import torch model = torch.quantization.prepare_qat(model) # 继续训练模型 torch.quantization.convert(model)
TensorRT
TensorRT 是 NVIDIA 提供的高性能推理引擎,支持 INT8 推理:
- 导出模型到 ONNX 格式。
- 使用 TensorRT 的校准工具生成校准表。
- 加载量化后的 TensorRT 引擎。
TensorRT INT8 校准示例:
import tensorrt as trt
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network()
# 加载 ONNX 模型
parser = trt.OnnxParser(network, TRT_LOGGER)
with open("model.onnx", "rb") as model_file:
parser.parse(model_file.read())
# 配置量化
config = builder.create_builder_config()
config.set_flag(trt.BuilderFlag.INT8)
config.int8_calibrator = trt.Int8Calibrator() # 校准器
engine = builder.build_engine(network, config)
ONNX Runtime
ONNX Runtime 支持 INT8 量化并能方便地将模型量化:
-
使用
onnxruntime.quantization.quantize_dynamic:from onnxruntime.quantization import quantize_dynamic quantize_dynamic( model_input="model.onnx", # 原始模型路径 model_output="model_int8.onnx", # 量化后模型路径 weight_type=QuantType.QInt8 # 使用 INT8 量化 ) -
静态量化:
利用onnxruntime.quantization.quantize_static进行校准并量化。
4. 处理自定义算子
如果模型包含自定义算子或不支持的操作,你需要:
- 替换或重写算子,使其支持 INT8 计算。
- 使用模拟量化的方式,避免量化对精度的过度影响。
5. 校准和评估量化模型
- 校准是静态量化必不可少的步骤,通过真实数据确定激活值的分布范围。
- 在量化后,运行验证数据集,检查精度损失是否在可接受范围内。
6. 常见优化点
- 层选择:
- 只对计算量大的层进行量化,例如
Linear和Conv层。
- 只对计算量大的层进行量化,例如
- 分组量化:
- 对部分敏感层使用更高精度(如 FP16)保留精度。
- 后处理:
- 量化后的模型可能需要对输出进行解量化。
7. 验证和调试
- 验证量化模型的性能提升(推理速度、内存占用)。
- 检查量化后的精度变化,并适当调整量化参数。
通过以上步骤,你可以将模型调整为 INT8 推理,同时最大限度地保留原始模型的精度。
2.2 直接int8推理
不转换模型 直接推理fp32的onnx模型 用int8的精度
如果你不对模型进行转换,而是希望在推理时直接使用 FP32 的 ONNX 模型,但依然希望利用 INT8 精度进行推理,那么实际做法是利用 ONNX Runtime(或其他推理框架)支持的 量化推理 功能。
简而言之,你仍然需要使用 INT8 精度 来加速推理,但你不希望修改模型文件(不转换成 INT8 模型)。你希望的是,在推理时直接将 FP32 模型以 INT8 精度运行。
要在 CUDAExecutionProvider 中启用 INT8 推理,你需要进行以下几个步骤:
-
安装支持 INT8 推理的 ONNX Runtime 版本:
- 确保你安装的是支持 INT8 推理的 ONNX Runtime 版本,通常需要安装 GPU 版本,并确保它启用了 CUDA 支持。
- 为了使用 INT8 推理,你的 ONNX Runtime 需要支持 TensorRT,因为 TensorRT 是 NVIDIA 提供的一个高性能推理库,它支持 INT8 推理加速。
-
安装 CUDA 及 TensorRT:
- 首先,确保你已经安装了 CUDA 和 TensorRT。它们是必需的,因为 ONNX Runtime 使用 TensorRT 来支持 INT8 推理。
- 可以通过以下命令安装 ONNX Runtime 与 CUDA 和 TensorRT 支持:
pip install onnxruntime-gpu- 确保你的机器上安装了合适版本的 CUDA 和 TensorRT。例如:
- CUDA: 11.2 或更高版本
- TensorRT: 8.x 或更高版本
-
准备好你的 FP32 模型:
- 你需要一个 FP32 模型来进行量化推理。如果你的模型是 FP32,那么你可以直接将它加载到 ONNX Runtime 中并进行 INT8 推理。
-
进行 INT8 推理的配置:
- ONNX Runtime 在 CUDA 上默认使用 FP32 来进行推理。如果你希望启用 INT8 精度,你需要使用 TensorRTExecutionProvider,并配置它使用 INT8 推理。
具体步骤如下:
步骤 1: 配置 TensorRTExecutionProvider 使用 INT8 精度
你可以通过 ONNX Runtime 的 ExecutionProvider 来设置 TensorRT,并启用 INT8 精度:
import onnxruntime as ort
# 加载模型
model_path = "your_model.onnx"
# 配置 ONNX Runtime 使用 TensorRT,并启用 INT8 精度
providers = ['CUDAExecutionProvider', 'TensorrtExecutionProvider']
# 选择 GPU 加速,启用 INT8 精度
# TensorRT 通过优化模型来使用 INT8 推理
# 通过设置 session_options 来启用 INT8
session_options = ort.SessionOptions()
session_options.append_execution_provider_tensorrt(provider_options={'int8': True})
# 创建 ONNX Runtime 会话
session = ort.InferenceSession(model_path, providers=providers, session_options=session_options)
# 检查是否使用 TensorRT 进行推理
print(session.get_providers()) # 查看当前使用的推理后端
步骤 2: TensorRT 的 INT8 精度配置
通过 SessionOptions 设置 append_execution_provider_tensorrt 来配置 TensorRT 使用 INT8 精度。在推理时,它会自动将模型的权重量化为 INT8 来加速推理。
步骤 3: 运行推理
import numpy as np
# 假设我们有一些输入数据
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
# 执行推理
inputs = {session.get_inputs()[0].name: input_data}
outputs = session.run(None, inputs)
print(outputs)
步骤 4: 确保你的硬件支持 INT8 推理
INT8 推理通常需要 NVIDIA Volta 或更高版本的 GPU(如 T4, V100, A100),并且你的环境中必须有 TensorRT 支持。在 CUDA 和 TensorRT 支持下,ONNX Runtime 才能真正执行 INT8 加速推理。
需要的硬件要求:
- NVIDIA GPU:支持 INT8 推理的 GPU,通常需要 Volta 架构或更高版本(如 T4, A100 等)。
- TensorRT 支持:需要安装 TensorRT(版本通常为 7.x 或更高)来支持 INT8 加速。
步骤 5: 调整和验证精度
使用 INT8 精度进行推理时,你可能会看到推理速度明显提升,但也可能会面临一些 精度损失。你可以通过与 FP32 模型的输出进行比较来验证精度变化。
- INT8 优化:TensorRT 会尝试在不显著影响精度的情况下,优化模型以提高推理速度。
- 精度验证:在使用 INT8 推理时,通常会先进行量化校准,确保精度损失最小。
总结:
- 要使用 INT8 推理,你需要启用 TensorRTExecutionProvider,并通过
session_options.append_execution_provider_tensorrt()启用 INT8 精度。 - 需要确保你的 GPU 支持 INT8 推理(如 T4, V100, A100 等)。
- 在推理时,ONNX Runtime 会使用 TensorRT 来进行量化推理,并在推理时自动将模型转换为 INT8 精度。
其他注意事项:
- TensorRT 会自动执行量化和优化,所以你不需要手动将模型转换为 INT8。
- 你可以通过对比 FP32 和 INT8 的推理结果来验证精度是否符合要求。如果精度损失较大,可以考虑进一步调整量化配置。
三、如何查看模型的数据位数
onnx是fp16还是 fp32 还是 int8的?
要判断一个 ONNX 模型是使用 FP16、FP32 还是 INT8,可以通过以下几种方法来查看模型的权重和计算数据类型。
1. 检查模型权重数据类型
权重的数据类型直接决定模型的精度,可以通过解析 ONNX 模型文件中的权重信息进行检查:
代码示例:使用 ONNX 的 onnx 模块
import onnx
import numpy as np
# 加载 ONNX 模型
model_path = "model.onnx"
model = onnx.load(model_path)
# 遍历模型中的权重
for tensor in model.graph.initializer:
weight_dtype = np.dtype(onnx.mapping.TENSOR_TYPE_TO_NP_TYPE[tensor.data_type])
print(f"Tensor Name: {tensor.name}, Data Type: {weight_dtype}")
输出示例:
- 如果是 FP32:
Data Type: float32 - 如果是 FP16:
Data Type: float16 - 如果是 INT8:
Data Type: int8
2. 检查节点的计算精度
某些节点可能在计算时使用了不同的精度(如 FP16 或 INT8),可以通过遍历 ONNX 模型中的节点属性查看。
代码示例:检查节点的类型和精度
# 遍历模型中的节点
for node in model.graph.node:
print(f"Node Name: {node.name}, Op Type: {node.op_type}")
for attr in node.attribute:
print(f" Attribute: {attr.name}, Value: {attr}")
- 对于 FP16 节点,可能会有标记为
float16的计算类型。 - 对于量化模型(如 INT8),会有
QuantizeLinear或DequantizeLinear节点。
3. 使用 onnxruntime 检查精度
在运行时,可以检查模型权重和计算的设备支持以及精度类型。
代码示例:获取权重的详细信息
import onnxruntime as ort
# 加载 ONNX 模型
session = ort.InferenceSession("model.onnx")
# 获取模型输入和输出的详细信息
for input in session.get_inputs():
print(f"Input Name: {input.name}, Type: {input.type}")
for output in session.get_outputs():
print(f"Output Name: {output.name}, Type: {output.type}")
- FP16 会显示为
tensor(float16) - FP32 会显示为
tensor(float) - INT8 会显示为
tensor(int8)
4. 使用 ONNX 可视化工具
使用图形化工具可以更直观地分析模型的数据类型:
- Netron:加载 ONNX 模型文件,权重和节点的精度会在 UI 中显示。
5. 检查量化模型的特征
量化模型通常具有以下特征:
- 节点中包含
QuantizeLinear和DequantizeLinear。 - 权重的类型通常为 INT8。
- 使用的算子如
QLinearMatMul或QLinearConv,表示量化运算。
6. 手动对比模型的性能
通过实际推理速度和精度的对比,也可以间接推断模型的精度:
- FP16 通常较 FP32 更快,显存占用减少约 50%,但精度可能略有下降。
- INT8 推理速度比 FP16 快,但可能对精度有显著影响,特别是在某些任务上。
通过这些方法,可以明确 ONNX 模型是使用 FP32、FP16 还是 INT8,选择最适合的推理方案。
更多推荐


所有评论(0)