边缘部署量化实践:INT8 模型压缩与部署优化

边缘部署量化实践:INT8 模型压缩与部署优化

一、边缘部署的挑战

1.1 资源约束

约束 典型值(车规级)
功耗 ≤3W
内存 ≤2GB
存储 ≤8GB
算力 ≤30 TOPS
延迟 ≤50ms

1.2 FP32 vs INT8

精度 内存占用 计算速度 精度损失
FP32 100% 基准 0%
FP16 50% <1%
INT8 25% 1-3%

二、量化原理

2.1 线性量化

公式:

1
2
3
4
5
6
7
q = round(r / S) + Z

其中:
- r: 原始浮点值
- q: 量化整数值
- S: 缩放因子(Scale)
- Z: 零点(Zero Point)

2.2 量化类型

类型 特点 适用场景
PTQ(Post-Training Quantization) 无需重训练 部署快、精度略降
QAT(Quantization-Aware Training) 训练时模拟量化 精度高、需重训练

2.3 量化实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import torch
import torch.nn as nn
import torch.quantization as quant

class QuantizableModel(nn.Module):
"""
可量化模型示例
"""

def __init__(self):
super().__init__()

# 量化配置
self.quant = quant.QuantStub()
self.dequant = quant.DeQuantStub()

# 卷积层(需指定 qconfig)
self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
self.conv2 = nn.Conv2d(32, 64, 3, padding=1)

# 全连接层
self.fc = nn.Linear(64 * 56 * 56, 10)

# ReLU
self.relu = nn.ReLU()

def forward(self, x):
# 量化输入
x = self.quant(x)

# 卷积 + ReLU
x = self.relu(self.conv1(x))
x = self.relu(self.conv2(x))

# Flatten
x = x.flatten(1)

# 全连接
x = self.fc(x)

# 反量化输出
x = self.dequant(x)

return x

def fuse_model(self):
"""
融合层(提升量化精度)
"""
torch.quantization.fuse_modules(self, ['conv1', 'relu'], inplace=True)
torch.quantization.fuse_modules(self, ['conv2', 'relu'], inplace=True)


def quantize_ptq(model, calibration_loader):
"""
PTQ 量化流程

Args:
model: 原始模型
calibration_loader: 校准数据加载器

Returns:
quantized_model: 量化后的模型
"""
# 1. 设置量化配置
model.qconfig = quant.get_default_qconfig('fbgemm')

# 2. 融合层
model.fuse_model()

# 3. 准备量化
quant.prepare(model, inplace=True)

# 4. 校准
model.eval()
with torch.no_grad():
for batch in calibration_loader:
model(batch)

# 5. 转换为 INT8
quant.convert(model, inplace=True)

return model


# 测试代码
if __name__ == "__main__":
# 创建模型
model = QuantizableModel()

# 模拟校准数据
calibration_data = torch.randn(100, 3, 224, 224)
calibration_loader = [calibration_data[i:i+10] for i in range(0, 100, 10)]

# PTQ 量化
quantized_model = quantize_ptq(model, calibration_loader)

# 测试推理
test_input = torch.randn(1, 3, 224, 224)
output = quantized_model(test_input)

print(f"输出形状: {output.shape}")

# 对比精度
model_fp32 = QuantizableModel()
output_fp32 = model_fp32(test_input)

error = (output - output_fp32).abs().mean()
print(f"量化误差: {error:.6f}")

三、TensorRT 部署优化

3.1 ONNX 导出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import torch
import torch.onnx

def export_to_onnx(model, output_path="model.onnx"):
"""
导出 ONNX 模型

Args:
model: PyTorch 模型
output_path: 输出路径
"""
model.eval()

# 模拟输入
dummy_input = torch.randn(1, 3, 224, 224)

# 导出
torch.onnx.export(
model,
dummy_input,
output_path,
input_names=['input'],
output_names=['output'],
dynamic_axes={
'input': {0: 'batch_size'},
'output': {0: 'batch_size'}
},
opset_version=11
)

print(f"ONNX 模型已导出: {output_path}")


# 验证 ONNX
import onnx

def verify_onnx(onnx_path):
"""
验证 ONNX 模型
"""
model = onnx.load(onnx_path)
onnx.checker.check_model(model)
print("ONNX 模型验证通过")

3.2 TensorRT 优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import numpy as np

class TensorRTEngine:
"""
TensorRT 推理引擎
"""

def __init__(self, onnx_path, precision='int8', max_batch_size=1):
"""
Args:
onnx_path: ONNX 模型路径
precision: 精度('fp32', 'fp16', 'int8')
max_batch_size: 最大批大小
"""
self.logger = trt.Logger(trt.Logger.WARNING)

# 创建 Builder
builder = trt.Builder(self.logger)

# 创建 Network
network = builder.create_network(
1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
)

# 创建 Config
config = builder.create_builder_config()

# 设置精度
if precision == 'fp16':
config.set_flag(trt.BuilderFlag.FP16)
elif precision == 'int8':
config.set_flag(trt.BuilderFlag.INT8)

# INT8 校准器
calibrator = FatigueCalibrator()
config.int8_calibrator = calibrator

# 解析 ONNX
parser = trt.OnnxParser(network, self.logger)
with open(onnx_path, 'rb') as f:
parser.parse(f.read())

# 构建引擎
self.engine = builder.build_engine(network, config)

# 创建 Context
self.context = self.engine.create_execution_context()

# 分配内存
self.inputs = []
self.outputs = []
self.bindings = []

for i in range(self.engine.num_bindings):
binding = self.engine[i]
shape = self.engine.get_binding_shape(binding)
dtype = trt.nptype(self.engine.get_binding_dtype(binding))

# 分配 GPU 内存
size = trt.volume(shape)
host_mem = cuda.pagelocked_empty(size, dtype)
device_mem = cuda.mem_alloc(host_mem.nbytes)

self.bindings.append(int(device_mem))

if self.engine.binding_is_input(i):
self.inputs.append({'host': host_mem, 'device': device_mem})
else:
self.outputs.append({'host': host_mem, 'device': device_mem})

def infer(self, input_data):
"""
执行推理

Args:
input_data: 输入数据, shape=(B, C, H, W)

Returns:
output: 输出结果
"""
# 复制输入到 Host 内存
np.copyto(self.inputs[0]['host'], input_data.ravel())

# H2D
cuda.memcpy_htod(self.inputs[0]['device'], self.inputs[0]['host'])

# 执行
self.context.execute_v2(self.bindings)

# D2H
cuda.memcpy_dtoh(self.outputs[0]['host'], self.outputs[0]['device'])

return self.outputs[0]['host'].reshape(-1)


class FatigueCalibrator(trt.Int8Calibrator):
"""
INT8 校准器
"""

def __init__(self, calibration_data=None):
super().__init__()
self.data = calibration_data or []
self.index = 0

def get_batch_size(self):
return 1

def get_batch(self, names):
if self.index >= len(self.data):
return None

batch = self.data[self.index]
self.index += 1

return [batch]

def read_calibration_cache(self):
return None

def write_calibration_cache(self, cache):
with open('calibration.cache', 'wb') as f:
f.write(cache)


# 使用示例
if __name__ == "__main__":
# 导出 ONNX
model = QuantizableModel()
export_to_onnx(model, "fatigue_model.onnx")

# TensorRT 优化
engine = TensorRTEngine("fatigue_model.onnx", precision='int8')

# 推理
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
output = engine.infer(input_data)

print(f"输入形状: {input_data.shape}")
print(f"输出形状: {output.shape}")

四、QCS8255 部署

4.1 Hexagon NPU 架构

graph TB
    A[应用层] --> B[SNPE SDK]
    B --> C[Hexagon NN]
    C --> D[Hexagon NPU]
    
    D --> E[HVX 加速]
    D --> F[HMX 加速]
    
    E --> G[INT8 卷积]
    F --> H[矩阵乘法]

4.2 SNPE 部署流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 模型转换(PyTorch → DLC)
snpe-pytorch-to-dlc \
--input_model fatigue_model.pt \
--input_dim input 1,3,224,224 \
--output_path fatigue_model.dlc

# 2. 模型量化
snpe-dlc-quantize \
--input_model fatigue_model.dlc \
--input_list calibration_list.txt \
--output_model fatigue_model_int8.dlc

# 3. 部署到设备
adb push fatigue_model_int8.dlc /data/local/tmp/

# 4. 测试推理
snpe-net-run \
--container fatigue_model_int8.dlc \
--input_list test_list.txt \
--output_dir output/

五、性能对比

5.1 延迟对比

平台 FP32 FP16 INT8
RTX 4090 8ms 5ms 4ms
Jetson Orin 32ms 18ms 12ms
QCS8255 45ms 25ms 18ms

5.2 精度对比

模型 FP32 准确率 INT8 准确率 损失
ResNet-18 92.5% 91.8% 0.7%
MobileNetV2 89.2% 88.5% 0.7%
EfficientNet-B0 94.3% 93.8% 0.5%

六、IMS 集成方案

6.1 量化流程

graph LR
    A[PyTorch 模型] --> B[ONNX 导出]
    B --> C[TensorRT 优化]
    C --> D[INT8 量化]
    
    D --> E[精度验证]
    E -->|精度合格| F[部署到设备]
    E -->|精度不足| G[QAT 重训练]
    
    G --> C

6.2 开发检查清单

模型准备:

  • 导出 ONNX(Opset 11+)
  • 验证 ONNX 正确性
  • 准备校准数据集

量化配置:

  • 选择量化精度(INT8)
  • 设置校准策略
  • 融合层优化

精度验证:

  • 对比 FP32/INT8 准确率
  • 损失 <1% 则通过
  • 否则启用 QAT

部署测试:

  • 测试推理延迟
  • 测试功耗
  • 测试内存占用

七、参考资源

  1. TensorRT 文档: https://docs.nvidia.com/deeplearning/tensorrt/
  2. SNPE SDK: https://developer.qualcomm.com/software/qualcomm-neural-processing-sdk
  3. PyTorch 量化: https://pytorch.org/docs/stable/quantization.html

八、总结

INT8 量化实现4× 加速、25% 内存占用,关键要点:

  1. PTQ 快速部署 - 无需重训练
  2. QAT 精度保障 - 精度损失 <1%
  3. TensorRT 优化 - 自动层融合

IMS 开发建议:

  • 优先使用 PTQ 快速验证
  • 精度不足时启用 QAT
  • 重点测试边缘设备性能

本文基于边缘部署量化实践经验总结。


边缘部署量化实践:INT8 模型压缩与部署优化
https://dapalm.com/2026/08/16/2026-08-16-09-Edge-AI-Quantization-INT8-Deployment/
作者
Mars
发布于
2026年8月16日
许可协议