QConfig in Detail
Definition
Definition of QConfig
The quantization mode of the model is determined by qconfig, which needs to be set for the model before preparing the qat / calibration model.
Due to historical reasons, there are different definitions and usages of qconfig in the Plugin. Earlier versions of qconfig will be deprecated in the near future, and we only recommend that you use the qconfig usage described in this document.
A qconfig object can set three keywords: input, weight, and output, representing the quantization configuration of the operator's input, weight, and output respectively. When preparing model, these configurations determine whether to insert FakeQuantize or FakeCast nodes at the corresponding positions. None means no nodes will be inserted.
Definition of FakeQuantize
FakeQuantize is a fake quantization node that performs quantization and dequantization operations on the input. Inserting fake quantization can simulate the errors caused by quantization in the forward pass of a floating-point model. The horizon_plugin_pytorch supports three types of fake quantization: FakeQuantize, PACTFakeQuantize, and _LearnableFakeQuantize. We recommend using the statistic-based FakeQuantize. The document won't introduce PACTFakeQuantize and _LearnableFakeQuantize. If required, please read the papers before using them.
You can call the with_args method of FakeQuantize to get a constructor and use it to construct qconfig as shown in the previous section. The parameters of with_args include parameters supported by FakeQuantize and observer, theoretically allowing configuration of all parameters declared in the init method of the FakeQuantize and observer classes. However, to avoid unnecessary details, we recommend you to configure the observer-related parameters only.
Different observers have different parameters. Below are examples of constructing FakeQuantize with common used observers. For the specific usage of other observers, see the calibration section.
Definition of FakeCast
FakeCast is a fake conversion node that converts the input to float32 data type. If the data type is float16, it also simulates the truncation error caused by converting value to float16. This node is mainly used to mark operators that require floating-point computation.
The method of using FakeCast to construct qconfig is similar to FakeQuantize, but it only has one parameter.
Construct QConfig
-
Construct the QConfig object directly as introduced above. This method is flexible, allowing the configuration of any configurable parameter, but requires deep understanding of QConfig.
-
Use the get_qconfig interface. This interface is simpler and easier to use than directly constructing QConfig objects but less flexible, and cannot be used for advanced requirements.
Usage
Set QConfig Attribute
Directly set qconfig attribute. This method has the highest priority, and other methods will not override the directly set qconfig.
QConfig Template
QConfig templates get model's graph structure based on subclass trace and automatically set qconfig according to the specified rules. This is the most recommended method for setting qconfig. Specify the qconfig setter and example_inputs on the prepare interface to use qconfig template. Usage is as follows:
The template priority is lower than directly setting the qconfig attribute of the model. If the model has been configured using model.qconfig = xxx before prepare, the template will not take effect. We do not recommend mixing the two methods unless there is a special need, as this can easily cause errors. In most cases, using one of the two methods is sufficient.
Templates can be divided into three categories:
- Fixed templates. The difference between calibration / qat / qat_fixed_act_scale in fixed templates is the type of observer used and the scale updating logic, which is used for calibration, qat training, and fixed activation scale qat training, respectively. The default template ( default_calibration_qconfig_setter / default_qat_qconfig_setter / default_qat_fixed_act_qconfig_setter ) does three things: First, it will set all the high accuracy outputs that can be set, and will give a hint for outputs that don't support high accuracy; Then, it searches forward from the grid input of the grid sample operator until the first gemm-like operator or QuantStub and sets all the intermediate operators to int16. From experience, the grid here is usually expressed over a wide range, so int8 is more likely to be insufficient to meet the accuracy requirements; Finally, set the remaining operators to int8. int16 template ( qat_8bit_weight_16bit_act_qconfig_setter / qat_8bit_weight_16bit_fixed_act_qconfig_setter / calibration_8bit_weight_16bit_act_qconfig_setter ) will do two things: First, set all the high accuracy outputs that can be set, and will give a hint for outputs that don't support high accuracy; Second, set the rest of the operators to int16.
- Sensitivity templates. The sensitivity templates are sensitive_op_calibration_8bit_weight_16bit_act_qconfig_setter, sensitive_op_qat_8bit_weight_16bit_act_qconfig_setter and sensitive_op_qat_8bit_weight_16bit_fixed_act_qconfig_setter. The difference among the three is consistent with the difference among the three in the fixed template, which is also used for calibration, qat training, and fixed activation scale qat training, respectively. The first input of the sensitivity template is the sensitivity result generated by the accuracy debug tool, and the second parameter can be specified as ratio or topk, the sensitivity template will set the topk operators with the highest quantization sensitivity to int16. With the fixed template, it is easy to realize the mixed dtype tuning. If the model has multiple outputs, each output will generate a sensitivity table, and you can set multiple sensitivity templates.
- Customized templates. Customized template only has ModuleNameQconfigSetter, you need to pass in the module name and the corresponding qconfig dictionary. It is generally used for setting fixed scale and other special needs. Also, it can be used with fixed templates and sensitivity templates.
Behavior of QConfig in Different QAT Stages
This section elaborates on the behavior of qconfig in different QAT stages. It is recommended to read this section with a solid understanding of the Horizon QAT tool.
Set QConfig Attribute
After configuration, the module will have a qconfig attribute.
Operator Replacement
There are three types of Operator replacement:
-
Replacing function-based operators with modules. For example, “ + ” is replaced by “ generated_add_x ”. Since function-based operators do not have a qconfig attribute before replacement, they will not have a qconfig attribute afterward. Their qconfig can only be inherited from the parent module through propagate mechanism or set via qconfig templates.
-
Splitting module operators into several small, concatenated operators. For example, "horizon_plugin_pytorch.nn.Norm" is split into mul / sum / sqrt. If the module operator already has a qconfig attribute before splitting, the operator’s propagate_qconfig method is called to set the qconfig for the small operators.
- A combination of the first two situations: first, replace function-based operators with modules, and then split the module into several small concatenated operators. For example, "torch.norm" will be replaced by "horizon_plugin_pytorch.nn.Norm" firstly, and then split into small operators.
Operator Fusion
- If the qconfig attribute of module is set before operator fusion, the fused module will inherit the qconfig of the last module in the fusion sequence.
- If you want to directly set the qconfig of the fused module, you can set it with qconfig template.
QConfig Propagation
QConfig is propagated from the parent module to its children modules.
-
Perform a depth-first traversal and apply the same operation to all child modules.
-
For the module being traversed, if both the parent module's qconfig and the module's own qconfig exist, the module's own qconfig takes precedence. If the propagate_qconfig method (described in step 3) does not exist, the parent module's qconfig only affects child modules that do not have a qconfig.
-
The propagate_qconfig method attempts to propagate the qconfig to the child modules. This method is generally hardcoded based on past experience. Its primary function is to set the qconfig for small operators created from splitting floating-point stage operators.
QConfig Setter
The QConfig setter consists of a series of setters. The principle of these setters is to avoid changing the existing qconfig attributes. For modules that do not have a qconfig attribute, setters automatically set the qconfig according to the specific rules of each setter. Therefore, when multiple setters are used, the one earlier in the sequence takes precedence.
Since setters are applied after operator replacement and fusion, they can directly set the qconfig of replaced or fused operators.
QConfig Canonicalization
There are two primary mechanisms in QConfig canonicalization, both based on the computational graph:
-
Based on the target architecture, some qconfig settings for int16 are downgraded to int8. A special case is matmul, which supports a single int16 input. If a sensitivity setter is available, it will automatically revert the less sensitive input branch to int8. If no sensitivity setter is present, it will revert input branch later in the topology.
-
Merging consecutive quantization nodes. The logic is relatively simple. when the output quantization node of one module has the same dtype as the input quantization node of the next module, the input quantization node of the next module is removed.
Inserting Fake Quantization Nodes
By calling the QAT operator's from_float method, floating-point operators are replaced with QAT operators. In the init method of the QAT operator, quantization nodes are initialized based on the qconfig. The quantization nodes are then used in the forward method of the QAT operator.
