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
| import torch import torch.nn as nn import onnxruntime as ort import numpy as np from typing import Tuple
class DMSModelQuantizer: """ DMS模型量化部署器 支持的量化方案: 1. 训练后量化 (PTQ) - INT8 2. 混合精度量化 - FP16/INT8 3. 量化感知训练 (QAT) """ def __init__(self, model: nn.Module): self.model = model self.calibration_data = None def prepare_for_quantization(self): """准备模型量化""" self.model = torch.quantization.fuse_modules( self.model, [['conv', 'bn', 'relu'], ['fc', 'relu']], inplace=False ) qconfig = torch.quantization.get_default_qconfig('fbgemm') self.model.qconfig = qconfig return self.model def calibrate( self, calibration_loader, num_batches: int = 100 ) -> dict: """ 校准量化参数 Args: calibration_loader: 校准数据加载器 num_batches: 校准批次数量 Returns: stats: 校准统计信息 """ self.model.eval() self.prepare_for_quantization() self.model = torch.quantization.prepare(self.model) min_vals, max_vals = [], [] with torch.no_grad(): for i, (data, _) in enumerate(calibration_loader): if i >= num_batches: break _ = self.model(data) self.quantized_model = torch.quantization.convert(self.model) original_size = sum( p.nelement() * p.element_size() for p in self.model.parameters() ) quant_size = sum( p.nelement() * p.element_size() for p in self.quantized_model.parameters() ) return { 'original_size_mb': original_size / 1024**2, 'quantized_size_mb': quant_size / 1024**2, 'compression_ratio': original_size / quant_size } def export_onnx( self, output_path: str, input_shape: Tuple[int, ...] = (1, 3, 224, 224) ) -> str: """ 导出ONNX格式(用于NPU部署) Args: output_path: 输出路径 input_shape: 模型输入形状 Returns: output_path: ONNX文件路径 """ dummy_input = torch.randn(*input_shape) torch.onnx.export( self.quantized_model, dummy_input, output_path, opset_version=17, input_names=['input'], output_names=['output'], dynamic_axes={ 'input': {0: 'batch'}, 'output': {0: 'batch'} } ) return output_path
if __name__ == "__main__": model = nn.Sequential( nn.Conv2d(3, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(), nn.AdaptiveAvgPool2d((1, 1)), nn.Flatten(), nn.Linear(64, 10) ) quantizer = DMSModelQuantizer(model) cal_data = [(torch.randn(4, 3, 224, 224), 0) for _ in range(50)] stats = quantizer.calibrate(cal_data) print(f"原始大小: {stats['original_size_mb']:.2f} MB") print(f"量化大小: {stats['quantized_size_mb']:.2f} MB") print(f"压缩比: {stats['compression_ratio']:.1f}x")
|