南阳网站排名优化价格,打开微信公众号,西安哪里有做网站的,制作网页要钱吗作为一款高性能的推理引擎框架#xff0c;MNN高度关注Transformer模型在移动端的部署并持续探索优化大模型在端侧的推理方案。本文介绍权重量化的模型在MNN CPU后端的推理方案#xff1a;动态量化。动态量化指在运行时对浮点型feature map数据进行8bit量化#xff0c;然后与… 作为一款高性能的推理引擎框架MNN高度关注Transformer模型在移动端的部署并持续探索优化大模型在端侧的推理方案。本文介绍权重量化的模型在MNN CPU后端的推理方案动态量化。动态量化指在运行时对浮点型feature map数据进行8bit量化然后与8bit/4bit的权重数据做矩阵乘法并将结果反量化为浮点类型输出。 虽然动态量化方案设计的初衷是在移动端高效地推理LLM模型但方案本身适用于所有模型的各个推理场景例如语音识别类模型TTS、ASR等进行权重量化后也能在端侧实现高效推理推理耗时和运行时内存占用都能降低50%和浮点模型相比最大相对误差可以控制在10%以内。文章首先从数学表达式开始描述动态量化的计算原理接着从模型推理的角度拆解计算过程然后介绍MNN CPU后端的矩阵乘逻辑最后以Qwen2-1.5B模型为例介绍在CPU后端使用动态量化进行LLM模型推理的详细步骤并测试MNN CPU后端对LLM模型和传统CV模型的推理性能。 基础知识 ▐ 动态量化计算原理的解析式 ▐ 符号解释 x: 动态量化之后的 int8_t 类型输入数据[E, L]x0: 动态量化Input数据时的量化参数zeropoint值;sx: 动态量化Input数据时的量化参数scale值;y: 在转模型时就已经量化过的int8_t类型权重[L, H];y0: 直接从模型文件中读取权重的量化参数zeropointsy: 直接从模型文件中读取权重的量化参数scalec: 常量值下文称做bias值[1, H] 注 下文中x,y 的反量化zeropoint特指 x0, y0但x,y的反量化bias则指(-x0 * sx)-y0 * sy;x的反量化过程是xf (x - x0) * sx x * sx (-x0 * sx)y的反量化同理 ▐ 拆解计算过程 可离线计算的项 在L维度对y reduce sum, shape: [1, H] y的量化参数bias向量 [1, H] 需在线计算的项 int8_t 类型数据矩阵乘法累加到int32_t类型, [E, H] x和y的量化参数Scale相乘[1, H] 在L维度对 x reduce sum, shape: [E, 1] x 的量化参数bias, 标量 在矩阵乘Kernel 内部计算的项 在矩阵乘Kernel内部计算的项一定属于1.3.2节“需在线计算的项” int8_t 类型数据矩阵乘法累加到int32_t类型, [E, H] kernel外计算之后传入矩阵乘Kernel的项 离线计算的项和一些需在线计算的项会通过 QuanPostTreatParameters 结构体传入矩阵乘kernel中。 struct QuanPostTreatParameters {const float* scale;const float* biasFloat;int32_t maxValue;int32_t minValue;int32_t useInt8 1; // Save result as int8_t dataType; otherwise float32.float roundValuePos 0.5f;float roundValueNeg -0.5f;float* srcKernelSum;float* weightQuanBias;float* fp32minmax;ssize_t blockNum 1;const int32_t* bias;const float* extraScale nullptr;const float* extraBias nullptr;
};
// (sx * sy): x和y的量化参数Scale相乘[1, H]
QuanPostTreatParameters.scale
// (x * sx): 在L维度对 x reduce sumshape: [E, 1]
QuanPostTreatParameters.srcKernelSum
// (-y0*sy): 权重反量化的参数biasshape: [1, H]
QuanPostTreatParameters.weightQuanBias
// (-x0*sx)(y-y0)*syc: 原矩阵乘bias与计算过程中“新增项”相加之后的biasshape: [1, H]
QuanPostTreatParameters.biasFloat
/* 该结构体中的其他项暂时不要关注后续会有说明。*/ MNN CPU 矩阵乘计算逻辑 本节分为两个方面第一MNN CPU的矩阵乘的分块计算逻辑第二MNN CPU的矩阵乘算子在各平台的计算加速方法。 ▐ 矩阵乘的分块计算 分块计算的优点是降低读取Input和weight数据时的cache miss率各CPU后端Arm、x86_x64)可使用的汇编指令不同决定了各后端的分块大小也不同。 符号说明 input shape: [E, L] weight shape [L, H]output shape [E, H]L是reduce维度。 EP, LP, HP 分别表示 E, L, H 维度的分块大小 Input和weight分块后在内存中的排列逻辑 以weight分块为例如图1。在L和H维度分别按照LP和HP分块如果L或H不能被LP或HP整除会在最后一个子块补0以保证分块之后的每一个小方块的大小都是(HP*LP)。分块之后weight矩阵的12个子块之间会按照图示中的“123...12”的顺序在内存中排列。在各子块内部如编号为“1”的块中有16个数据其排列顺序按照图1. 中的“123...16”的顺序在内存中排列。 矩阵乘内部分块计算的顺序 下文中描述shape时默认在内存中的排列顺序是括号内从右到左的顺序。图2. 描述了矩阵乘kernel内部input和weight分块后的计算逻辑 最小计算单元是EP, 4*LP)的输入数据和(HP, 4*LP)的weight数据做乘加操作得到(EP, HP)的输出子块。按照图2. 中output矩阵的子块编号依次计算123号子块矩阵乘kernel内部最多计算得到output矩阵中(EP * H)个数据。 矩阵乘的外部调用逻辑 如果一个ONNX模型中含有MatMul算子并且该算子的两个输入中的一个是常量那么MNN转模型工具MNNConvert会将该MatMul算子转换成MNN模型中的卷积算子。MNN的矩阵乘Kernel通常在卷积算子中被调用而MNN CPU 的卷积算子的输入输出数据在内存中的排布都是C/4, N, H, W, 4其中C表示ChannelN表示BatchH和W分别表示Height和Width。上文中E、 L、 H分别对应卷积中的N * OW * OH、OC * IC * KH * KW、OC。 做矩阵分块的过程中会将Input 数据从 IC/4, N, IH, IW, 4重排为E/EP, L/LP, EP, LPweight 数据从OC, IC, KH, KW) 重排为H/HP, L/LP, HP, LP。每一个矩阵乘kernel完成的计算量最多是EP*H所以要完成E*H的计算量需要调用矩阵乘kernel ((EEP)/EP) 次。如下图3. 所示要得到最终的output矩阵的结果则需要分2次调用矩阵乘kernel. 虽然得到的输出子块是(EP, HP)的大小在往目标地址写入输出数据时直接按照C/4, N, H, W, 4的顺序。 ▐ MNN CPU的矩阵乘算子在各平台的计算加速 下图4. 表示用int8_t类型的输入数据和权重数据得到浮点输出的计算过程该过程可分为三个阶段 第一阶段基于 int8_t 类型的feature map和weight做矩阵乘法得到int32_t类型output再将int32_t output 反量化到float32_t output 第二阶段基于 float32_t output 加上 weight_dequant_bias * intput_reduce_sum_by_axis_L 第三阶段最后加上 bias (维度H这里的bias 来自于矩阵乘A*BC中的C和input数据的量化bias乘以weight_reduce_sum_by_axis_L之后的和。 第一阶段的计算耗时是矩阵乘kernel计算耗时中占比最大的部分在不同CPU后端对矩阵乘的加速几乎都集中在该阶段。诸如Armv8.6和v8.2后端都有指令专门针对int8_t类型数据的内积进行计算加速另外x86_x64的AVX512后端提供了VNNI指令集也对int8_t类型数据的计算提供了优化。 CPU Arm后端的矩阵乘优化 1. 在不支持SMMLA和SDOT指令的Armv8/v7后端只能使用最基础的smull和smlal指令做int8_t数据的内积操作。为了提高每次读取数据的效率设置 LP16保证一次读取16个int8_t数据填满向量寄存器。另外EP4, HP4. 2. Armv8.6后端支持Arm汇编指令SMMLAArmv8.2后端支持汇编指令SDOT计算原理分别如图5. 上下图所示。 SMMLA指令的输入数据是两个128位的向量寄存器将第一个输入向量寄存器中的128位数据看作一个(2x8)的矩阵其中每个数据类型是int8_t. 将第二个输入向量寄存器中的128位数据看作一个(8x2)的矩阵其中每个数据类型也是int8_t. smmla指令将两个源向量寄存器做矩阵乘法得到一个(2x2)的输出矩阵并存储在一个128位的向量寄存器中该输出寄存器中的数据由4个int32_t类型数据组成。SDOT指令的输入数据被看做是两个一维的向量每4个对应位置的int8_t数据做内积得到1个int32_t的数据最终结果是4个int32_t的数据。 根据Armv8.6和v8.2后端指令特点矩阵乘的分块大小分别是 Armv8.6EP10, LP8, HP8因为SMMLA指令对每8个int8_t数据做内积所以LP8 Armv8.2EP12, LP4, HP8因为SDOT指令对每4个int8_t数据做内积所以LP4 从把向量寄存器用满的角度分别设置EP10和12从最大程度降低读取weight值的cache miss率的角度设置HP8 CPU x86_x64后端的矩阵乘优化 AVX512后端的VNNI指令集专门针对int8_t整数运算进行了优化其他后端因为没有该指令集故基于int8_t数据的矩阵乘法性能一般比基于float数据的矩阵乘差。这里直接列举出x86_x64个后端的分块大小 不使用SSE、AVX2、AVX512等指令的C版本EP2LP16HP4 SSE: EP4LP16HP4 AVX2: EP4LP4HP8 AVX512: EP4LP4HP64 MNN CPU后端对端侧LLM模型推理的精度优化 ▐ weight分组量化 转模型阶段的支持 通常权重矩阵[OC, IC, KH, KW]的量化参数维度是[1, OC]即每IC*KH*KW个数据共享一个量化scale和zeropoint最后求得OC * 2个量化参数包含scale和zeropoint。IC*KH*KW个数据分布差异较大会影响权值量化的精度因此在转模型阶段会对将IC*KH*KW个数据分为m组进行量化即最后得到的量化参数有OC*m*2个。转模型工具MNNConvert 接受可选参数 --weightQuantBlock 来指定共享量化参数的元素个数如 --weightQuantBlock 128 会得到 OC*IC*KW*KH/128*2个量化参数。当且仅当weightQuantBlock参数设置的值能整除 (IC * KH * KW时才会生效否则依然得到(OC * 2)个量化参数。 CPU后端支持 一些计算项shape的变化当采取weight分组量化后等价于 reduce axis L被分成m组上文中结构体QuanPostTreatParameters.blockNumm. 相应地一些项的shape也发生如下改变。和上文保持一致该项在内存中按照方括号内从右往左的顺序排列。 shape 变化[1, H] - [m, H] y的量化参数bias向量 [m, H] x和y的量化参数Scale相乘[m, H] 在L维度对 x reduce sum, shape: [(EEP-1)/EP, m, EP]在内存中如下图方块编号顺序排列。 1. 矩阵乘kernel调用逻辑的变化得到一个output数据的子块(EP, H)之前只需要调用一次矩阵乘kernel在对weight进行分组量化后由于每组的量化参数不同需要在外层调用m次矩阵乘kernel。 2. 矩阵乘内部的计算逻辑变化首次调用矩阵乘kernel会将结果数据直接写入目标地址后面的m-1次调用都需要将本次计算的结果在目标地址中的已有值基础上进行累加之后再写入目标地址。 ▐ 在LLM推理的prefill阶段分sequence量化 为提高端侧LLM模型权重量化后的推理精度MNN在动态量化推理时prefill阶段对不同sequence的数据分别求解量化参数进行动态量化。在MNN CPU的动态量化卷积算子中sequence对应的是卷积输入输出数据的Batch值后文表述中会把在prefill阶段对输入数据分sequence量化简称为“Batch quant”或“Batch量化”。 当前仅支持input-width1input-height1output-width1 output-height1的数据做Batch量化。虽然在非Batch量化中对输入数据是采取非对称量化的方式但为了降低矩阵乘算子内部计算的复杂度Batch量化中对输入数据是采取对称量化的方式即x00. MNN CPU后端支持Batch量化应用在权重进行分组量化的模型中即与3.1节所述是不冲突的。 为了快速适配现有的矩阵乘kernel和CPU动态量化算子在QuanPostTreatParameters.extraScale中存储Input数据不同batch所对应的dequant scale值。此时矩阵乘kernel外部不会计算项直接将权重的反量化Scale传入QuanPostTreatParameters.scale。同时在矩阵乘kernel内部根据QuanPostTreatParameters.extraScale是否为空指针决定是否乘以该项。 代码逻辑 CPU 动态量化算子DenseConvInt8TiledExecutor CPU 动态量化算子会根据不同后端来选择矩阵乘算子和矩阵乘的分块大小目前支持ARM后端Armv8.6、Armv8.2、Armv8、Armv7和x86_x64后端SSE、AVX2、AVX512。 CPU 动态量化算子支持输出float32或float16由用户指定精度precision low表示使用输出float16precision normal/high表示输出float32。 MNN中有两个结构体 CoreInt8Functions 和 CoreFunctions 与后端紧密联系在模型加载阶段会根据推理后端为结构体中的各函数指针赋值内容。CoreInt8Functions 包含的通常是与int8_t类型相关的kernel或经常在量化场景下使用的kernelCoreFunctions 包含的通常是浮点类型相关的kernel. // CPU 后端结构体指针如果当前推理使用的是ARM V8.2后端
// core和gcore指向ARM V8.2的函数结构体
auto core static_castCPUBackend*(backend())-int8Functions();
auto gcore static_castCPUBackend*(backend())-functions();
// 例如获取ARM V8.2后端的Int8矩阵乘分块
int HP, LP, EP;
core-MNNGetGemmUnit(HP, LP, EP);
// 例如获取ARM V8.2后端的Int8矩阵乘kernel
auto mGemmKernel core-Int8GemmKernel; MNN中有结构体 CoreInt8Functions 包含与int8_t类型相关的各类函数指针其中在动态量化算子中被调用的矩阵乘kernel函数指针有 Int8GemmKernelweight是8bit量化的输出结果是float32Int8GemmKernel_W4weight是4bit量化的输出结果是float32MNNGemmInt8AddBiasScale_Unit_FP16weight是8bit量化的输出结果是float16MNNGemmInt8AddBiasScale_w4_Unit_FP16weight是4bit量化的输出结果是float16 不同后端的矩阵乘分块大小也通过CoreInt8Functions中的函数指针MNNGetGemmUnit得到。 MNN CPU动态量化的使用及性能加速效果 重要使用MNN CPU进行权重量化模型推理时当且仅当打开编译宏 MNN_LOW_MEMORY并且设置推理时memorylow才会使用动态量化进行推理。 ▐ 使用MNN CPU动态推理教程以安卓手机测试LLM模型为例 关于MNN中指定推理时memory的办法 自行编写demo时设置 BackendConfig backendConfig; backendConfig.memoryMemory_Low;若使用MNN 测试工具llm_demo只需要在config.json文件中指定memory: low在llm.mnn和llm.mnn.weight的同一个目录下新建推理时需要的config.json文件 必须包含的内容 llm_model大语言模型文件名llm_weight: 与llm_model对应的大语言模型的权值文件 可选项 backend_type: 推理后端默认0表示CPU推理thread_num推理时使用的线程数默认4线程precision推理精度默认nomal。low表示动态量化输出数据是float16类型使用CPU推理时 normal/high都表示动态量化输出数据是float32类型memory推理时内存默认normal。要使用CPU动态量化特性需设置成low. 在安卓手机上的编译和demo运行 git clonehttps://github.com/alibaba/MNN.gitcd MNNmkdir buildcd build命令行设置编译选项编译 cmake .. \
-DCMAKE_TOOLCHAIN_FILE$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABIarm64-v8a \
-DANDROID_STLc_static \
-DANDROID_NATIVE_API_LEVELandroid-21 \
-DMNN_BUILD_FOR_ANDROID_COMMANDtrue \
-DCMAKE_BUILD_TYPERelease \
-DMNN_LOW_MEMORYON \
-DMNN_SUPPORT_TRANSFORMER_FUSEON \
-DMNN_ARM82ON \
-DMNN_BUILD_LLMON
make -j4 运行./llm_demo Qwen2-1.5B-Instruct/config.json ./prompt.txt ▐ LLM 模型在MNN CPU后端使用动态量化推理时的性能 测试机器小米14骁龙8Gen3测试模型: Qwen2-1.5B-Instruct线程数4config.json和prompt.txt内容如下为了保持各配置下输出token一致这里设置了max_new_tokens: 300测试包含float32和float16的性能数据只需要更改config.json文件中precison: low 或 high. low表示float16high表示float32。memory必须设置为low才能使用动态量化推理否则会在模型加载阶段将权重反量化为浮点然后使用浮点型矩阵乘推理严重影响LLM模型推理速度。 {llm_model: llm.mnn,llm_weight: llm.mnn.weight,max_new_tokens: 300,backend_type: cpu,thread_num: 4,precision: low,memory: low
} In recent years, with the rapid development of technology and the deepening of globalization, the digital economy has become a new engine driving the growth of the world economy. The digital economy has not only changed peoples way of life and promoted the rapid flow of information and resources, but also reshaped the business models and competitive landscape of traditional industries. Although the development of the digital economy has provided new momentum for global economic growth, it has also brought a series of challenges, such as data security, privacy protection, digital divide, and market monopolies. In light of these background, please analyze in detail the role of the digital economy in promoting world economic growth, including but not limited to its contribution to improving productivity, creating employment opportunities, and promoting sustainable development. At the same time, discuss how to address the challenges that arise during the development of the digital economy, including how to protect personal data security and privacy, narrow the digital divide to ensure the inclusiveness and fairness of the digital economy, and how to formulate effective policies to avoid the emergence of market monopolies, ultimately achieving the healthy and sustainable development of the digital economy. 表格中的第一个数据表示Prefill速度第二个数据表示Decode速度。 权重量化方法Precison4 线程, Armv8.6权重4bit量化high302.49 tok/s - 41.42 tok/s ▐ CV 模型在MNN CPU后端使用动态量化推理时的性能 第三列括号内的红色数据是动态量化较浮点模型推理的加速比。 测试机器小米14骁龙8Gen3 模型推理参数动态量化推理浮点模型mobilenetv3fp32 - 四线程2.63 ms 加速比1.463.86 msmobilenetv3fp32 - 单线程4.03 ms加速比1.696.85 msmobilenetv3fp16 - 四线程2.29 ms加速比1.052.41 msmobilenetv3fp16 - 单线程3.19 ms加速比1.143.65 msmobilenetv2fp32 - 四线程2.55 ms加速比1.624.13 msmobilenetv2fp32 - 单线程4.42 ms 加速比1.848.15 msmobilenetv2fp16 - 四线程2.13 ms加速比1.112.37 msmobilenetv2fp16 - 单线程3.58 ms加速比1.144.08 msresnet50fp32 - 四线程13.09 ms 加速比3.3143.29 msresnet50fp32 - 单线程23.09 ms 加速比3.0871.18 msresnet50fp16 - 四线程11.82 ms 加速比1.8722.09 msresnet50fp16 - 单线程21.71 ms 加速比1.6335.38 msyolov4fp32 - 四线程181.43 ms加速比1.99362.68 msyolov4fp32 - 单线程383.78 ms加速比1.68644.92 msyolov4fp16 - 四线程193.74 ms加速比1.21235.59 msyolov4fp16 - 单线程454.80 ms加速比1.10501.23 ms 结语 MNN CPU动态量化方案在Transformer类模型和传统CV模型的推理中都有不错的表现虽然文章中Transformer模型的性能数据来自于LLM模型的测试但语音类模型使用动态量化推理的性能较浮点模型相比都有显著的提升。采用动态量化方法的好处不仅提升推理性能和降低运行时内存算法同学从浮点模型得到权重量化模型的过程也比PTQ方案简单许多模型转换过程更加高效模型的精度理论上要高于PTQ方式得到的量化模型。 团队介绍 我们是大淘宝技术Meta Team负责面向消费场景的3D/XR基础技术建设和创新应用探索通过技术和应用创新找到以手机及XR 新设备为载体的消费购物3D/XR新体验。团队在端智能、商品三维重建、3D引擎、XR引擎等方面有深厚的技术积累。团队在OSDI、MLSys、CVPR、ICCV、NeurIPS、TPAMI等顶级学术会议和期刊上发表多篇论文。 ¤ 拓展阅读 ¤ 3DXR技术 | 终端技术 | 音视频技术 服务端技术 | 技术质量 | 数据算法