(self, file_path: str, graph: BaseGraph, config_path: str = None,
input_shapes: List[List[int]] = [[1, 3, 224, 224]], write_weight=False)
| 247 | |
| 248 | class PPLDSPCaffeExporter(CaffeExporter): |
| 249 | def export(self, file_path: str, graph: BaseGraph, config_path: str = None, |
| 250 | input_shapes: List[List[int]] = [[1, 3, 224, 224]], write_weight=False): |
| 251 | # PPL3 DSP do not need a json config file, all quantization configuration will be merged into protobuf |
| 252 | caffe_model, caffe_proto = self.prepare_model(graph, input_shapes) |
| 253 | for idx in range(len(caffe_proto.layer)): |
| 254 | layer = caffe_proto.layer[idx] |
| 255 | layer_name = layer.name |
| 256 | |
| 257 | assert isinstance(layer, ppl_caffe_pb2.LayerParameter) |
| 258 | assert isinstance(layer_name, str) |
| 259 | # layer is a caffe data structure, corresponding to operation in ppq. |
| 260 | # following code write ppq quantization configuration to caffe layer. |
| 261 | |
| 262 | # step - 1, find corresponding op |
| 263 | if layer_name not in graph.operations: |
| 264 | # PATCH FOR Slice |
| 265 | if layer.type == 'Slice': |
| 266 | # slice0 --> (ppq parse, export, combine) --> slice0_0_0, everything else |
| 267 | # is the same with original model |
| 268 | # layer_name is slice0_0_0, obtain original_name slice0 |
| 269 | original_name = ''.join(layer_name.split('_')[:-2]) |
| 270 | for bottom in layer.bottom: |
| 271 | var = graph.variables.get(bottom, None) |
| 272 | if var is not None and isinstance(var, QuantableVariable) and not var.is_parameter: |
| 273 | cfg = None |
| 274 | for dest_op, dest_cfg in zip(var.dest_ops, var.dest_op_configs): |
| 275 | # dest_op.name is slice0_0 |
| 276 | if ''.join(dest_op.name.split('_')[:-1]) == original_name: |
| 277 | cfg = dest_cfg |
| 278 | break |
| 279 | assert cfg is not None |
| 280 | qt_min = convert_value(cfg.scale * (cfg.quant_min - cfg.offset), True, DataType.FP32) |
| 281 | qt_max = convert_value(cfg.scale * (cfg.quant_max - cfg.offset), True, DataType.FP32) |
| 282 | layer.quantize_param.add(type='bottom', range_min=qt_min, range_max=qt_max) |
| 283 | |
| 284 | for top in layer.top: |
| 285 | var = graph.variables.get(top, None) |
| 286 | if var is not None and isinstance(var, QuantableVariable) and not var.is_parameter: |
| 287 | cfg = var.source_op_config |
| 288 | assert cfg is not None |
| 289 | qt_min = convert_value(cfg.scale * (cfg.quant_min - cfg.offset), True, DataType.FP32) |
| 290 | qt_max = convert_value(cfg.scale * (cfg.quant_max - cfg.offset), True, DataType.FP32) |
| 291 | layer.quantize_param.add(type='top', range_min=qt_min, range_max=qt_max) |
| 292 | continue |
| 293 | else: |
| 294 | raise KeyError(f'Can not find operation {layer_name} with current graph.') |
| 295 | |
| 296 | op = graph.operations[layer_name] |
| 297 | if not isinstance(op, QuantableOperation): continue |
| 298 | |
| 299 | # step - 2, dump parameter quantization config |
| 300 | if layer.type in {'Convolution', 'Deconvolution'}: |
| 301 | for cfg, var in op.config_with_variable: |
| 302 | if not var.is_parameter: continue |
| 303 | if cfg.num_of_bits > 8: continue # skip bias |
| 304 | |
| 305 | if cfg.policy.has_property(QuantizationProperty.PER_CHANNEL): |
| 306 | qt_min = convert_value(cfg.scale * (cfg.quant_min - cfg.offset), False, DataType.FP32) |
nothing calls this directly
no test coverage detected