Home

Awesome

DFQ

PyTorch implementation of Data Free Quantization Through Weight Equalization and Bias Correction with some ideas from ZeroQ: A Novel Zero Shot Quantization Framework.

Results

Int8**: Fake quantization; 8 bits weight, 8 bits activation, 16 bits bias
Int8*: Fake quantization; 8 bits weight, 8 bits activation, 8 bits bias
Int8': Fake quantization; 8 bits weight(symmetric), 8 bits activation(symmetric), 32 bits bias
Int8: Int8 Inference using ncnn; 8 bits weight(symmetric), 8 bits activation(symmetric), 32 bits bias

On classification task

<table> <tr><th>MobileNetV2 </th><th>ResNet-18</th></tr> <tr><td>
model/precisionFP32Int8**Int8*Int8'Int8<br>(FP32-69.19)
Original71.810.1020.10.0620.082
+ReLU71.780.1020.0960.0940.082
+ReLU+LE71.7870.3268.7867.565.21
+ReLU+LE +DR--70.4768.87----
+BC--57.070.1226.255.57
+BC +clip_15--65.370.1365.9645.13
+ReLU+LE+BC--70.7968.1768.6562.19
+ReLU+LE+BC +DR--70.968.41----
</td><td>
model/precisionFP32Int8**Int8*
Original69.7669.1369.09
+ReLU69.7669.1369.09
+ReLU+LE69.7669.269.2
+ReLU+LE +DR--67.7467.75
+BC--69.0468.56
+BC +clip_15--69.0468.56
+ReLU+LE+BC--69.0468.56
+ReLU+LE+BC +DR--67.6567.62
</td></tr> </table>

On segmentation task

<table> <tr><th>Pascal VOC 2012 val set (mIOU) </th><th>Pascal VOC 2007 test set (mIOU)</th></tr> <tr><td>
model/precisionFP32Int8**Int8*
Original70.8160.0359.31
+ReLU70.7260.058.98
+ReLU+LE70.7266.2266.0
+ReLU+LE +DR--67.0467.23
+ReLU+BC--69.0468.42
+ReLU+BC +clip_15--66.9966.39
+ReLU+LE+BC--69.4669.22
+ReLU+LE+BC +DR--70.1269.7
</td><td>
model/precisionFP32Int8**Int8*
Original74.5462.3661.21
+ReLU74.3561.6661.04
+ReLU+LE74.3569.4769.6
+ReLU+LE +DR--70.2869.93
+BC--72.170.97
+BC +clip_15--70.1670.76
+ReLU+LE+BC--72.8472.58
+ReLU+LE+BC +DR--73.573.04
</td></tr> </table>

On detection task

<table> <tr><th>Pascal VOC 2012 val set (mAP with 12 metric) </th><th>Pascal VOC 2007 test set (mAP with 07 metric) </th></tr> <tr><td>
model/precisionFP32Int8**Int8*
Original78.5177.7177.86
+ReLU75.4275.7475.58
+ReLU+LE75.4275.3275.37
+ReLU+LE +DR--74.6574.32
+BC--77.7377.78
+BC +clip_15--77.7377.78
+ReLU+LE+BC--75.6675.66
+ReLU+LE+BC +DR--74.9274.65
</td><td>
model/precisionFP32Int8**Int8*
Original68.7068.4768.49
+ReLU65.4765.3665.56
+ReLU+LE65.4765.3665.27
+ReLU+LE +DR--64.5364.46
+BC--68.3265.33
+BC +clip_15--68.3265.33
+ReLU+LE+BC--65.6365.58
+ReLU+LE+BC +DR--64.9264.42
</td></tr> </table>

Usage

There are 6 arguments, all default to False

  1. quantize: whether to quantize parameters and activations.
  2. relu: whether to replace relu6 to relu.
  3. equalize: whether to perform cross layer equalization.
  4. correction: whether to apply bias correction
  5. clip_weight: whether to clip weights in range [-15, 15] (for convolution and linear layer)
  6. distill_range: whether to use distill data for setting min/max range of activation quantization

run the equalized model by:

python main_cls.py --quantize --relu --equalize

run the equalized and bias-corrected model by:

python main_cls.py --quantize --relu --equalize --correction

run the equalized and bias-corrected model with distilled data by:

python main_cls.py --quantize --relu --equalize --correction --distill_range

export equalized and bias-corrected model to onnx and generage calibration table file:

python convert_ncnn.py --equalize --correction --quantize --relu --ncnn_build path_to_ncnn_build_folder

Note

Distilled Data (2020/02/03 updated)

According to recent paper ZeroQ, we can distill some fake data to match the statistics from batch-normalization layers, then use it to set the min/max value range of activation quantization.
It does not need each conv followed by batch norm layer, and should produce better and more stable results using distilled data (the method from DFQ sometimes failed to find a good enough value range).

Here are some modifications that differs from original ZeroQ implementation:

  1. Initialization of distilled data
  2. Early stop criterion

Also, I think it can be applied to optimizing cross layer equalization and bias correction. The results will be updated as long as I make it to work.
Using distilled data to do LE or BC did not perform as good as using estimation from batch norm layers, probably because of overfitting.

Fake Quantization

The 'Int8' model in this repo is actually simulation of 8 bits, the actual calculation is done in floating points.
This is done by quantizing-dequantizing parameters in each layer and activation between 2 consecutive layers;
Which means each tensor will have dtype 'float32', but there would be at most 256 (2^8) unique values in it.

  Weight_quant(Int8) = Quant(Weight)
  Weight_quant(FP32) = Weight_quant(Int8*) = Dequant(Quant(Weight))

16-bits Quantization for Bias

Somehow I cannot make Bias-Correction work on 8-bits bias quantization for all scenarios (even with data dependent correction).
I am not sure how the original paper managed to do it with 8 bits quantization, but I guess they either use some non-uniform quantization techniques or use more bits for bias parameters as I do.

Int8 inference

Refer to ncnn, pytorch2ncnn, ncnn-quantize, ncnn-int8-inference for more details.
You will need to install/build the followings:
ncnn
onnx-simplifier

Inference_cls.cpp only implements mobilenetv2. Basic steps are:

  1. Run convert_ncnn.py to convert pytorch model (with layer equalization or bias correction) to ncnn int8 model and generate calibration table file. The name of out_layer will be printed to console.
  python convert_ncnn.py --quantize --relu --equalize --correction
  1. compile inference_cls.cpp
  mkdir build
  cd build
  cmake ..
  make
  1. Inference! link
  ./inference_cls --images=path_to_imagenet_validation_set --param=../modeling/ncnn/model_int8.param --bin=../modeling/ncnn/model_int8.bin --out_layer=name_from_step1

TODO

Acknowledgment