手工量化

功能介绍

量化是指对模型的参数和数据进行低比特处理,让最终生成的网络模型更加轻量化,从而达到节省网络模型存储空间、降低传输时延、提高计算效率达到性能提升与优化的目标。

用户可通过自己的框架和工具完成量化,并将这些量化参数(scaled、scalew、offsetd)在模型构建时注入到模型中。

  • 当前仅Conv2D/DepthwiseConv2D/FullyConnection算子支持量化。
  • 网络中Conv2D/DepthwiseConv2D/FullyConnection算子输入数据的Channel维度小于等于16时,由于Padding补齐,INT8量化无性能受益。因此量化要求Conv2D/DepthwiseConv2D/FullyConnection算子输入数据的Channel维度大于16,否则无法进行量化。

以Conv2D算子进行INT8量化为例,通过在Conv2D算子前插入AscendQuant量化算子,在Conv2D算子后插入AscendDequant反量化算子实现模型量化,如图1所示。

AscendQuant量化算子的作用是将float类型的数据转换为int8类型,即:dataint8=round[(datafloat*scale)+offset],其中scale=1/scaled,offset=offsetd。此处的round算法类似于C语言rint取整模式中的FE_TONEAREST模式。

AscendDequant反量化算子的作用是将int32类型的数据转换为float16类型,即:datafloat=dataint32*deq_scale,其中deq_scale=scaled*scalew

图1 量化示意图

在Conv2D算子前插入AscendQuant算子

AscendQuant算子原型定义:

1
2
3
4
5
6
7
8
REG_OP(AscendQuant)
    .INPUT(x, TensorType({DT_FLOAT16, DT_FLOAT32}))
    .OUTPUT(y, TensorType({DT_INT8, DT_INT4}))
    .REQUIRED_ATTR(scale, Float)
    .REQUIRED_ATTR(offset, Float)
    .ATTR(sqrt_mode, Bool, false)
    .ATTR(round_mode, String, "Round")
    .OP_END_FACTORY_REG(AscendQuant)

可以看到AscendQuant算子有1个输入x,两个必选属性scale和offset,两个可选属性sqrt_mode和round_mode,参数含义如下:

根据AscendQuant算子原型定义创建AscendQuant算子实例:

1
2
3
4
auto quant = op::AscendQuant("quant")
  .set_input_x(data)
  .set_attr_scale(1.00049043)      //指定量化系数
  .set_attr_offset(-128.0);        //指定偏移量

Conv2D算子

Conv2D算子的输入为AscendQuant,并设置输出type为int32。

 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
// const op: conv2d weight
auto weight_shape = ge::Shape({ 5,17,1,1 });
TensorDesc desc_weight_1(weight_shape, FORMAT_NCHW, DT_INT8);
Tensor weight_tensor(desc_weight_1);
uint32_t weight_1_len = weight_shape.GetShapeSize();
bool res = GetConstTensorFromBin(PATH+"const_0.bin", weight_tensor, weight_1_len);
if(!res) {
    std::cout << "GetConstTensorFromBin Failed!" << std::endl;
    return -1;
}
auto conv_weight = op::Const("const_0")
    .set_attr_value(weight_tensor);

// const op: conv2d bias
auto bias_shape = ge::Shape({ 5 });
TensorDesc desc_bias(bias_shape, FORMAT_NCHW, DT_INT32);
Tensor bias_tensor(desc_bias);
uint32_t bias_len = bias_shape.GetShapeSize() * sizeof(int32_t);
res = GetConstTensorFromBin(PATH + "const_1.bin", bias_tensor, bias_len);
if(!res) {
    std::cout << "GetConstTensorFromBin Failed!" << std::endl;
    return -1;
}
auto conv_bias = op::Const("const_1")
    .set_attr_value(bias_tensor);

// conv2d op
auto conv2d = op::Conv2D("Conv2d")
    .set_input_x(quant)                                                 //AscendQuant作为Conv2D算子的输入
    .set_input_filter(conv_weight)
    .set_input_bias(conv_bias)
    .set_attr_strides({ 1, 1, 1, 1 })
    .set_attr_pads({ 0, 0, 0, 0 })
    .set_attr_dilations({ 1, 1, 1, 1 });

TensorDesc conv2d_input_desc_x(ge::Shape(), FORMAT_NCHW, DT_INT8);        //量化后,输入x的type为INT8
TensorDesc conv2d_input_desc_filter(ge::Shape(), FORMAT_NCHW, DT_INT8);   
TensorDesc conv2d_input_desc_bias(ge::Shape(), FORMAT_NCHW, DT_INT32);    
TensorDesc conv2d_output_desc_y(ge::Shape(), FORMAT_NCHW, DT_INT32);      
conv2d.update_input_desc_x(conv2d_input_desc_x);
conv2d.update_input_desc_filter(conv2d_input_desc_filter);
conv2d.update_input_desc_bias(conv2d_input_desc_bias);
conv2d.update_output_desc_y(conv2d_output_desc_y);

在Conv2D算子后插入AscendDequant算子

AscendDequant算子原型定义:

1
2
3
4
5
6
7
8
REG_OP(AscendDequant)
    .INPUT(x, TensorType({DT_INT32}))
    .INPUT(deq_scale, TensorType({DT_FLOAT16, DT_UINT64}))
    .OUTPUT(y, TensorType({DT_FLOAT16, DT_FLOAT}))
    .ATTR(sqrt_mode, Bool, false)
    .ATTR(relu_flag, Bool, false)
    .ATTR(dtype, Int, false, DT_FLOAT)
    .OP_END_FACTORY_REG(AscendDequant)

可以看到AscendDequant算子有2个输入x和deq_scale,两个可选属性sqrt_mode和relu_flag,参数含义如下:

根据AscendDequant算子原型定义创建AscendDequant算子实例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//构造dequant_scale 
TensorDesc desc_dequant_shape(ge::Shape({ 5 }), FORMAT_NCHW, DT_UINT64);
Tensor dequant_tensor(desc_dequant_shape);
uint32_t dequant_scale_len = 5 * sizeof(uint64_t);
res = GetConstTensorFromBin(PATH + "const_2.bin", dequant_tensor, dequant_scale_len);
if(!res) {
    std::cout << "GetConstTensorFromBin Failed!" << std::endl;
    return -1;
}
auto dequant_scale = op::Const("dequant_scale")
    .set_attr_value(dequant_tensor);

//定义AscendDequant算子
auto dequant = op::AscendDequant("dequant")
  .set_input_x(conv2d)                                  //Conv2D作为AscendDequant算子的输入
  .set_input_deq_scale(dequant_scale);

将AscendDequant的输出作为其他算子的输入,或者作为整个graph的输出。

1
2
3
4
auto bias_add_1 = op::BiasAdd("bias_add_1")
   .set_input_x(dequant)
   .set_input_bias(bias_weight_1)
   .set_attr_data_format("NCHW");