乐趣区

关于人工智能:模型推理量化实现分享三详解-ACIQ-对称量化算法实现

欢送关注我的公众号 [极智视界],回复 001 获取 Google 编程标准

O_o>_<   o_OO_o~_~o_O

  大家好,我是极智视界,本文分析一下 ACIQ 对称量化算法实现,以 Tengine 的实现为例。

  这是量化实现的第三篇,后面还有一、二,有趣味的同学能够查阅

  (1)《【模型推理】量化实现分享一:详解 min-max 对称量化算法实现》;

   (2)《【模型推理】量化实现分享二:详解 KL 对称量化算法实现》;

   ACIQ 和后面的量化策略相似,也是会截取一个阈值 T,而后将 [-T, T] 映射到量化值域,不同的是寻找 T 的过程,本文不止讲原理,也联合 tengine 讲讲量化策略的实现。上面开始。

1、ACIQ 量化策略原理

   ACIQ 量化策略在论文《Post training 4-bit quantization of convolutional networks for rapid-deployment》中被提出,先贴一下成果:

  上图比对对立采纳 8-bit 权值量化、4-bit 激活值量化,在量化效率上 ACIQ 比 KL 量化过程快 4000 倍 (unbelievable~),在量化精度上,能够看到除了 resnet-101,其余测试的网络量化成果均好于 KL 量化,能够说是效率和成果一个也不落。

   在文章的一开始,作者就写道 Unlike traditional approaches that focus on the quantization at the network level, in this work we propose to minimize the quantization effect at the tensor level. 能够看出 ACIQ 是从 Tensor 级别登程的量化策略,整个推导逻辑次要是:

  (1) first, derive a generic expression for any given distribution for the expected MSE as a function of clipping value;

  (2) then use this expression to develop a specifific expression for each distribution;

  (3) finally, establish the optimal clipping values by solving the equations for which the derivative with respect to the clipping value are set to zero;

  通常在量化的时候须要做裁剪,以应答原始数据的长尾问题,假如 α 为截断值,截断能够示意为:

       

  ACIQ 须要一个较强先验假如:Tensor (feature map) 遵从拉普拉斯散布或高斯分布,而后采纳最优化思维求解量化过程截断值对应的最小量化损失,整个量化过程是将遵从原始散布的值映射到 2^M 量化离散值域,M 为量化比特数,意思是将下面的 [-α, α] 的值域等分给 2^M,如下图:

       

  假如原始散布的概率密度函数为 f(x),截断值 α 以及量化函数 Q(x),则量化前后的 L2 Loss 能够这么计算:

       

  以上算式很显著能够分为三个局部:

   (1) [负无穷, -α];

   (2) [-α, α];

  (3) [α, 正无穷];

  对于高斯分布 N(0, σ^2) 或者 拉普拉斯散布 Laplace(0, b)) 这种 0 轴对称散布来说,(1) 和 (3) 是等价的,含意是 |x| 到 |α| 之间的均方误差 (mean-square-error)。在做 [-α, α] 等分映射到 2^M 后,每个量化值会取每段两头的值 q1、q2、q3 … q2^M,第 (2) 项就是两头截断的累计误差。当初整个量化过程转化为求一个使 E[(X - Q(X))^2] 最小的截断值 α (深度学习到最初都是数学问题啊~~),而后再联合先验散布,做一些公式的等价变换~ 变换~ 之后,失去最终的整体量化损失优化指标函数:

       

   数学上,要求指标函数的最小值 ==> 求偏导,令其为 0。

   对于拉普拉斯散布来说,求偏导后的表达式为:

       

   对于高斯分布来说,求偏导后的表达式为:

       

  最初不论对于拉普拉斯散布还是高斯分布来说,M 是你想量化的比特位,还有像 β (拉普拉斯散布参数)、σ (高斯分布参数) 这些都是已知值,天然能够求出咱们想要的截断值 α 了,对于对称量化来说有了截断值就 ok 了。

2、ACIQ 量化策略实现

   上面来看 ACIQ 在 tengine 中的实现。

   量化实现次要代码:

case ALGORITHM_ACIQ:{if (quant_tool.scale_file.empty()){
        quant_tool.scale_file = "table_aciq.scale";
        quant_tool.activation_quant_tool();}
    save_graph_i8_perchannel(quant_tool.model_file.c_str(), quant_tool.scale_file.c_str(), quant_tool.output_file, quant_tool.inplace, false);
    /* Evaluate quantitative losses */
    if (quant_tool.evaluate){fprintf(stderr, "[Quant Tools Info]: Step Evaluate, evaluate quantitative losses\n");
        quant_tool.assess_quant_loss(0);
    }
    break;
}

2.1 激活值量化

   激活值量化入口:

quant_tool.activation_quant_tool();

   首先就是求 min、max 值,这个过程和后面写过的量化策略是一样的逻辑,就不多说了,接着进 ACIQ 策略:

for (int i = 0; i < ir_graph->tensor_num; i++){struct tensor* t = ir_graph->tensor_list[i];
    if (t->tensor_type == TENSOR_TYPE_VAR || t->tensor_type == TENSOR_TYPE_INPUT){
        float absmax = 0.f;
        float act_scale = 1.f;
        int act_zero_point = 0;
        int emlement_num = t->elem_num;

        absmax = std::max(std::abs(max_activation[i]), std::abs(min_activation[i]));
        float threshold = compute_aciq_gaussian_clip(absmax, emlement_num, 8);
        act_scale = threshold / 127.f;

        /* the scale of softmax is always scale = 1 / 127.f */
        for (int j = 0; j < ir_graph->node_num; j++){struct node* noden = ir_graph->node_list[j];
            struct tensor* tensor_tmp = get_ir_graph_tensor(ir_graph, noden->output_tensors[0]);

            if (!(tensor_tmp->tensor_type == TENSOR_TYPE_INPUT || tensor_tmp->tensor_type == TENSOR_TYPE_VAR))
                continue;

            std::string tmp_op_name = get_op_name_from_type(noden->op.type);
            std::string cur_name = t->name;
            std::string tmp_name = tensor_tmp->name;

            if ((cur_name == tmp_name) && tmp_op_name == "Softmax"){
                act_scale = 1 / 127.f;
                break;}
        }
        fprintf(fp_aciq, "%s %f %d\n", ir_graph->tensor_list[i]->name, act_scale, act_zero_point);}
}

   要害是这个函数,tengine 里默认先验遵从高斯分布,int8 量化:

float threshold = compute_aciq_gaussian_clip(absmax, emlement_num, 8);

   来看一下它的实现:

static float compute_aciq_gaussian_clip(float absmax, int N, int num_bits)
{const float alpha_gaussian[8] = {0, 1.71063519, 2.15159277, 2.55913646, 2.93620062, 3.28691474, 3.6151146, 3.92403714};   // 当 8 -bit 量化时,α=3.92403714

    const double gaussian_const = (0.5 * 0.35) * (1 + sqrt(3.14159265358979323846 * log(4))); 

    double std = (absmax * 2 * gaussian_const) / sqrt(2 * log(N));  

    return (float)(alpha_gaussian[num_bits - 1] * std);
}

   这样就失去了截断值,而后就能够求 scale 了:

act_scale = threshold / 127.f;

   这样就实现了激活值的量化。

2.2 权值 & 偏置量化

   权值 & 偏置的量化过程和后面介绍过的 MIN-MAX 和 KL 量化的逻辑一样,这里不再赘述。

   最初实际一下,能够发现 ACIQ 的量化过程非常的快,比 KL 量化快 4000 倍不是瞎说的,次要是源于先验的高斯分布 alpha_gaussian、gaussian_const、std 这些值不须要进行搜寻。

  以上分享了 ACIQ 的量化原理和实现,心愿我的分享能对你的学习有一点帮忙。


【公众号传送】
《【模型推理】量化实现分享三:详解 ACIQ 对称量化算法实现》

退出移动版