Skip to content

Setting Mixed-Precision on weights, and not only computation type for TensorRT-8.6.3 #3719

Description

@bernardrb

Description

In the code I'm basically trying to see if I can enable int8, and thereafter set the layer precision back to fp16. I want to understand how I can use mixed-precision in tensorrt.

I set computation type to FP16 using: self.network.get_layer(i).precision = trt.DataType.HALF

I try to set weights using:
for j in range(layer.num_outputs: layer.set_output_type(j, trt.DataType.FLOAT)

Using EngineInspector, I still see that all weights are in int8. As seen by the below log. I also can't find LayerType Reformat in the python tensorrt api, and I don't implement any plugin layers.

{
"Name": "Reformatting CopyNode for Input Tensor 0 to /image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"LayerType": "Reformat",
"Inputs": [
{
"Name": "input_image",
"Location": "Device",
"Dimensions": [1,3,1024,1024],
"Format/Datatype": "Row major linear FP32"
}],
"Outputs": [
{
"Name": "Reformatted Input Tensor 0 to /image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"Location": "Device",
"Dimensions": [1,3,1024,1024],
"Format/Datatype": "Row major Int8 format"
}],
"ParameterType": "Reformat",
"Origin": "REFORMAT",
"TacticValue": "0x0000000000000000",
"StreamId": 0,
"Metadata": ""
}
{
"Name": "/image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"LayerType": "CaskConvolution",
"Inputs": [
{
"Name": "Reformatted Input Tensor 0 to /image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"Location": "Device",
"Dimensions": [1,3,1024,1024],
"Format/Datatype": "Row major Int8 format"
}],
"Outputs": [
{
"Name": "/image_encoder/backbone/stages.0/op_list.0/conv/Conv_output_0",
"Location": "Device",
"Dimensions": [1,32,512,512],
"Format/Datatype": "Thirty-two wide channel vectorized row major Int8 format"
}],
"ParameterType": "Convolution",
"Kernel": [3,3],
"PaddingMode": "kEXPLICIT_ROUND_DOWN",
"PrePadding": [1,1],
"PostPadding": [1,1],
"Stride": [2,2],
"Dilation": [1,1],
"OutMaps": 32,
"Groups": 1,
"Weights": {"Type": "Int8", "Count": 864},
"Bias": {"Type": "Float", "Count": 32},
"HasSparseWeights": 0,
"HasDynamicFilter": 0,
"HasDynamicBias": 0,
"HasResidual": 0,
"ConvXAsActInputIdx": -1,
"BiasAsActInputIdx": -1,
"ResAsActInputIdx": -1,
"Activation": "NONE",
"HasBias": 1,
"HasReLU": 0,
"TacticName": "ampere_first_layer_filter3x3_imma_fwd",
"TacticValue": "0x9ae0c0d2fb3a01e5",
"StreamId": 0,
"Metadata": "[ONNX Layer: /image_encoder/backbone/stages.0/op_list.0/conv/Conv]"
}
{"Layers": [{
"Name": "Reformatting CopyNode for Input Tensor 0 to /image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"LayerType": "Reformat",
"Inputs": [
{
"Name": "input_image",
"Location": "Device",
"Dimensions": [1,3,1024,1024],
"Format/Datatype": "Row major linear FP32"
}],
"Outputs": [
{
"Name": "Reformatted Input Tensor 0 to /image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"Location": "Device",
"Dimensions": [1,3,1024,1024],
"Format/Datatype": "Row major Int8 format"
}],
"ParameterType": "Reformat",
"Origin": "REFORMAT",
"TacticValue": "0x0000000000000000",
"StreamId": 0,
"Metadata": ""
},{
"Name": "/image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"LayerType": "CaskConvolution",
"Inputs": [
{
"Name": "Reformatted Input Tensor 0 to /image_encoder/backbone/stages.0/op_list.0/conv/Conv",
"Location": "Device",
"Dimensions": [1,3,1024,1024],
"Format/Datatype": "Row major Int8 format"
}],
"Outputs": [
{
"Name": "/image_encoder/backbone/stages.0/op_list.0/conv/Conv_output_0",
"Location": "Device",
"Dimensions": [1,32,512,512],
"Format/Datatype": "Thirty-two wide channel vectorized row major Int8 format"
}],
"ParameterType": "Convolution",
"Kernel": [3,3],
"PaddingMode": "kEXPLICIT_ROUND_DOWN",
"PrePadding": [1,1],
"PostPadding": [1,1],
"Stride": [2,2],
"Dilation": [1,1],
"OutMaps": 32,
"Groups": 1,
"Weights": {"Type": "Int8", "Count": 864},
"Bias": {"Type": "Float", "Count": 32},
"HasSparseWeights": 0,
"HasDynamicFilter": 0,
"HasDynamicBias": 0,
"HasResidual": 0,
"ConvXAsActInputIdx": -1,
"BiasAsActInputIdx": -1,
"ResAsActInputIdx": -1,
"Activation": "NONE",
"HasBias": 1,
"HasReLU": 0,
"TacticName": "ampere_first_layer_filter3x3_imma_fwd",
"TacticValue": "0x9ae0c0d2fb3a01e5",
"StreamId": 0,
"Metadata": "[ONNX Layer: /image_encoder/backbone/stages.0/op_list.0/conv/Conv]"
}

def set_mixed_precision(self):

    """
    Experimental precision mode.
    Enable mixed-precision mode. When set, the layers defined here will be forced to FP16 to maximize
    INT8 inference accuracy, while having minimal impact on latency.
    """

    self.config.set_flag(trt.BuilderFlag.OBEY_PRECISION_CONSTRAINTS)

    # All convolution operations in the first four blocks of the graph are pinned to FP16.
    # These layers have been manually chosen as they give a good middle-point between int8 and fp16
    # accuracy in COCO, while maintining almost the same latency as a normal int8 engine.
    # To experiment with other datasets, or a different balance between accuracy/latency, you may
    # add or remove blocks.
    for i in range(self.network.num_layers):
        layer = self.network.get_layer(i)
        if layer.precision != trt.DataType.FLOAT and layer.type != trt.LayerType.SHAPE and layer.type != trt.LayerType.NORMALIZATION and "norm/" not in layer.name: 
            self.network.get_layer(i).precision = trt.DataType.HALF
            for j in range(layer.num_outputs):
                layer.set_output_type(j, trt.DataType.FLOAT)
        logger.info("Mixed-Precision Layer {} set to HALF STRICT data type".format(layer.name))
        if "norm/" in layer.name or layer.type == trt.LayerType.NORMALIZATION:
            self.network.get_layer(i).precision = trt.DataType.FLOAT
            logger.info("Mixed-Precision Layer {} set to FLOAT STRICT data type".format(layer.name))

def create_engine(self, engine_path, precision, img_size, calib_input=None, calib_cache=None, calib_num_images=5000,
                  calib_batch_size=8):
    """
    Build the TensorRT engine and serialize it to disk.
    :param engine_path: The path where to serialize the engine to.
    :param precision: The datatype to use for the engine, either 'fp32', 'fp16', 'int8', or 'mixed'.
    :param calib_input: The path to a directory holding the calibration images.
    :param calib_cache: The path where to write the calibration cache to, or if it already exists, load it from.
    :param calib_num_images: The maximum number of images to use for calibration.
    :param calib_batch_size: The batch size to use for the calibration process.
    """
    engine_path = os.path.realpath(engine_path)
    engine_dir = os.path.dirname(engine_path)
    os.makedirs(engine_dir, exist_ok=True)
    logger.info("Building {} Engine in {}".format(precision, engine_path))

    inputs = [self.network.get_input(i) for i in range(self.network.num_inputs)]

    if precision in ["fp16", "int8", "mixed"]:
        if not self.builder.platform_has_fast_fp16:
            log.warning("FP16 is not supported natively on this platform/device")
        self.config.set_flag(trt.BuilderFlag.FP16)
    if precision in ["int8", "mixed"]:
        if not self.builder.platform_has_fast_int8:
            log.warning("INT8 is not supported natively on this platform/device")
        self.config.set_flag(trt.BuilderFlag.INT8)
        self.config.int8_calibrator = EngineCalibrator(calib_cache)
        logger.debug("int8_calibrator.get_algorithm(): {}".format(self.config.int8_calibrator.get_algorithm()))
        if calib_cache is None or not os.path.exists(calib_cache):
            calib_shape = [calib_batch_size] + list(inputs[0].shape[1:])
            calib_dtype = trt.nptype(inputs[0].dtype)
            logger.debug("Calibration shape: {}".format(calib_shape))
            logger.debug("Calibration dtype: {}".format(calib_dtype))
            self.config.int8_calibrator.set_image_batcher(
                ImageBatcher(calib_input, calib_shape, calib_dtype, img_size=img_size, max_num_images=calib_num_images,
                             exact_batches=True, shuffle_files=True))
    self.config.profiling_verbosity  = trt.ProfilingVerbosity.DETAILED

    engine_bytes = None
    try:
        engine_bytes = self.builder.build_serialized_network(self.network, self.config)
    except AttributeError:
        engine = self.builder.build_engine(self.network, self.config)
        engine_bytes = engine.serialize()
        del engine
    assert engine_bytes
    with open(engine_path, "wb") as f:
        logger.info("Serializing engine to file: {:}".format(engine_path))
        f.write(engine_bytes)

def main(args):
builder = EngineBuilder(args.verbose, args.workspace)
builder.create_network(args.onnx, args.batch_size, args.dynamic_batch_size)
if args.precision == "mixed":
builder.set_mixed_precision()
builder.create_engine(args.engine, args.precision, args.img_size, args.calib_input, args.calib_cache, args.calib_num_images,
args.calib_batch_size)

Environment

TensorRT Version: 8.6.3

NVIDIA GPU: RTX 3090

NVIDIA Driver Version:

CUDA Version: 12.0

CUDNN Version:

Baremetal or Container (if so, version): 24.02-py3

Metadata

Metadata

Assignees

Labels

triagedIssue has been triaged by maintainers

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions