量化实例分析初探

news2025/1/11 21:59:26

一、量化介绍

大型语言模型通常具有数十亿乃至上百亿参数,导致存储和计算成本极高,大多数下游用户难以进行微调。为了便于进一步部署,大模型的模型压缩成为关键的解决方案。

模型压缩目标:减少模型大小,加快训练速度,保持相同精度。

针对大模型主要是以量化为主。量化是一种将预训练模型中的权重从浮点数转换成低位数的技术。通常情况下,量化的精度是8位或更低。量化可以大大减少模型的存储空间和计算量,但可能对模型的性能产生一定的影响。

  1. 对称量化:对称量化中浮点值的零点直接映射到量化值的零点,因此不需要其他参数来调整零点的映射的位置,与量化相关的参数只有缩放因子s。

  2. 非对称量化:非对称量化有一个额外的参数Z调整零点的映射,这个参数通常称为零点。非对称量化表示的范围没有严格的限制,可以根据浮点值的范围,选取任意的想要表示的范围。因此非对称量化的效果通常比对称量化好,但是需要额外存储以及推理时计算零点相关的内容。

Tmax和Tmin代表实际数据的浮点数最大值、最小值,Qmax和Qmin代表量化后的最大值和最小值。

举例: 权重范围[-2.0,6.0],即Tmax=6.0,Tmin=-2.0,用int8量化,定点量化值范围为[-128, 127],即Qmax = 127,Qmin = -127,那么S和Z的求值过程如下:

二、量化原理计算过程

2.1 量化计算

1. 计算量化系数s,和偏移z.

量化的基本原理都是一样的,就是按照下面公式将浮点数转为一个区间内的整数, 尽可能的保持数据原有的分布不变。

2. 计算量化值

根据下面的公式可以计算得到量化的值q. 和反量化的浮点数据值r.

3. 计算的技巧优化

对称量化,z=0, 简化计算。

S=(max - min) / ((1 << 4) - 1); //得到量化系数

S=(max-min)/(2^n -1)

得到量化的数值:

q= (r-min)/S。 r:量化前的真实数据。实际的量化方式:(x[i*qk + 0 + j] - min)/S;

三、量化代码分析

3.1 llamacpp量化实现

使用时group wise进行量化,也就将需要量化的数据按照某一个维度进行分组,在一个组内找到最大,最小值,然后按照量化公式,将浮点数进行量化。


void quantize_row_q4_1_reference(const float * restrict x, block_q4_1 * restrict y, int k) {
    const int qk = QK4_1;

    assert(k % qk == 0);

    const int nb = k / qk; //分组量化, qk是组大小, 比如把一行32个数值的数据分成4组,每组包含8个数据,一个组内进行求最大最小值,然后进行一个一个组量化

    for (int i = 0; i < nb; i++) {
        float min = FLT_MAX;
        float max = -FLT_MAX;

        for (int j = 0; j < qk; j++) {
            const float v = x[i*qk + j];

            if (v < min) min = v;
            if (v > max) max = v;
        }

        const float d  = (max - min) / ((1 << 4) - 1);  //得到量化系数max-min/2^4-1 (4bit量化)
        const float id = d ? 1.0f/d : 0.0f;

        y[i].d = GGML_FP32_TO_FP16(d);
        y[i].m = GGML_FP32_TO_FP16(min); //保存这一组内的最小值,计算量化值的需要使用

        for (int j = 0; j < qk/2; ++j) {
        //对称的取两个值const float x0和const float x1:分别计算两个值的量化形式。
            const float x0 = (x[i*qk + 0    + j] - min)*id;
            const float x1 = (x[i*qk + qk/2 + j] - min)*id;
//保证最小值一定在4bit 表示的范围之内。const uint8_t xi0和const uint8_t xi1:四舍五入并确保量化值在0到15的范围内。
            const uint8_t xi0 = MIN(15, (int8_t)(x0 + 0.5f));
            const uint8_t xi1 = MIN(15, (int8_t)(x1 + 0.5f));
            
// y[i].qs[j]:将两个4位量化值合并为一个字节,第一个值存储在低4位,第二个值左移4位后存储在高4位。
            y[i].qs[j]  = xi0;
            y[i].qs[j] |= xi1 << 4;
        }
    }
}

3.2 难点解析:

const float x0 = (x[i*qk + 0 + j] - min)*id; const float x1 = (x[i*qk + qk/2 + j] - min)*id;

它的作用是将浮点数数组x中的值映射到量化的范围(这里是4位量化,即0到15)内。让我们逐步分析这两行代码:

  1. const float x0 = (x[i*qk + 0 + j] - min) * id;

  2. x[i*qk + 0 + j]:从量化块的开始位置(索引为i*qk)加上当前处理的元素索引j,获取原始浮点数x0的值。

  3. min量化块中的最小值,用于将数据归一化到0附近。

  4. id:量化步长的逆,用于将归一化后的值映射到量化范围。

  5. const float x1 = (x[i*qk + qk/2 + j] - min) * id;

  6. x[i*qk + qk/2 + j]:从量化块的中间位置开始(索引为i*qk + qk/2),再加上当前处理的元素索引j,获取原始浮点数x1的值。这里假设qk是偶数,qk/2是量化块一半的位置。这两行代码的目的是将原始数据x中的值转换为相对于最小值min偏移,然后通过乘以逆量化步长id,将这些偏移量映射到量化的范围内。这样做的原因有:

  7. 归一化:通过减去min,将数据范围转换到以0为中心的范围,这有助于量化后的数据分布更均匀

  8. 量化映射:通过乘以id,将归一化后的值映射到量化的整数范围内。由于是4位量化,范围是0到15。

为什么这样取值,还有以下考虑:

  • 对称性:这段代码处理的是每对值x0x1,它们分别位于量化块的前半部分和后半部分。这种方法利用了数据的对称性,可以减少计算量。

  • 效率:通过同时处理两个值,可以减少循环迭代次数,提高量化过程的效率。

  • 量化精度:通过计算每个量化块的最小值和最大值,然后根据这些极值确定量化步长,可以尽量保持量化块内数据的原始分布特性,从而在量化过程中保持较高的精度。

最后,这两行代码是量化过程中的一个步骤,将原始浮点数映射到量化的整数表示,以便后续可以以更紧凑的形式存储和处理。

3.3 llamacpp的反量化实现示例

void dequantize_row_q4_1(const block_q4_1 * restrict x, float * restrict y, int k) {
    static const int qk = QK4_1;

    assert(k % qk == 0);

    const int nb = k / qk;

    for (int i = 0; i < nb; i++) {
        const float d = GGML_FP16_TO_FP32(x[i].d); //量化系数
        const float m = GGML_FP16_TO_FP32(x[i].m); //min 最小值

        for (int j = 0; j < qk/2; ++j) {
            const int x0 = (x[i].qs[j] & 0x0F); //得到低位的量化值
            const int x1 = (x[i].qs[j] >>   4); //得到高位的量化值

            y[i*qk + j + 0   ] = x0*d + m; //按照公式反量化
            y[i*qk + j + qk/2] = x1*d + m;
        }
    }
}

3.2 llamcpp neon 加速的量化实现

void quantize_row_q8_0(const float * restrict x, void * restrict vy, int k) {
    assert(QK8_0 == 32); // 一组32个元素
    assert(k % QK8_0 == 0);
    const int nb = k / QK8_0; // 分组量化的组数

    block_q8_0 * restrict y = vy;
#if defined(__ARM_NEON)
    for (int i = 0; i < nb; i++) {
        float32x4_t srcv [8];
        float32x4_t asrcv[8];
        float32x4_t amaxv[8];

        for (int j = 0; j < 8; j++) srcv[j]  = vld1q_f32(x + i*32 + 4*j);
        for (int j = 0; j < 8; j++) asrcv[j] = vabsq_f32(srcv[j]);

        for (int j = 0; j < 4; j++) amaxv[2*j] = vmaxq_f32(asrcv[2*j], asrcv[2*j+1]);
        for (int j = 0; j < 2; j++) amaxv[4*j] = vmaxq_f32(amaxv[4*j], amaxv[4*j+2]);
        for (int j = 0; j < 1; j++) amaxv[8*j] = vmaxq_f32(amaxv[8*j], amaxv[8*j+4]);

        const float amax = vmaxvq_f32(amaxv[0]); //这32个数据的最大值。

        const float d = amax / ((1 << 7) - 1);
        const float id = d ? 1.0f/d : 0.0f;

        y[i].d = GGML_FP32_TO_FP16(d);

        for (int j = 0; j < 8; j++) {
            const float32x4_t v  = vmulq_n_f32(srcv[j], id); //量化值
            const int32x4_t   vi = vcvtnq_s32_f32(v);// 将量化后的浮点数四舍五入到最近的整数。

            y[i].qs[4*j + 0] = vgetq_lane_s32(vi, 0);
            y[i].qs[4*j + 1] = vgetq_lane_s32(vi, 1);
            y[i].qs[4*j + 2] = vgetq_lane_s32(vi, 2);
            y[i].qs[4*j + 3] = vgetq_lane_s32(vi, 3);
        }
    }
    }

3.2.1 步骤总结:

  • 加载数据到NEON寄存器。

  • 计算数据的绝对值。

  • 通过比较操作找到最大值。

  • 根据最大值计算量化步长。

  • 使用量化步长将浮点数量化为整数。

  • 存储量化结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1892729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

dell服务器RAID5磁盘阵列出现故障的解决过程二——热备盘制作与坏盘替换过程

目录 背景方案概念全局热备&#xff08;Global Hot Spare&#xff09;&#xff1a;独立热备&#xff08;Dedicated Hot Spare&#xff09;&#xff1a; 过程8号制作成热备清除配置制作独立热备热备顶替坏盘直接rebuild 更换2号盘2号热备 注意注意事项foreign状态要先清除配置 背…

Fiddler关于Repaly的细节您了解吗?如何重复执行请求?

最近深入的使用了一下Fiddler的Repaly功能&#xff0c;没想到有这么多的细节&#xff0c;不仅可以设置Repaly的次数&#xff0c;还可以无条件地重发选中的请求&#xff0c;而不考虑之前的请求条件或缓存状态。在这里与各位小伙伴分享一下&#xff0c;希望能够帮到大家&#xff…

Unity射击游戏开发教程:(29)躲避敌人的子弹射击

在这篇文章中,我将介绍如何创建一个可以使玩家火力无效的敌人。创建的行为如下...... 当玩家向敌人开火时,敌人会向左或向右移动。向左或向右的移动是随机选择的,并在一段时间后停止敌人的移动。如果敌人移出屏幕,它就会绕到另一边。将一个精灵拖到画布上,将其缩小以匹配游…

Vue实现金钱输入框组件自动带千位逗号

新建PriceInput.vue <template><div id"bord"><el-inputv-model"inputValue"v-bind"$attrs":maxlength"maxlength"input"handleInput"focus"handleFocus"blur"handleBlur"change"h…

Springboot煤炉网站代切系统-计算机毕业设计源码06656

目 录 摘要 1 绪论 1.1 课题目的与意义 1.2国内外研究现状 1.3论文结构与章节安排 1.4 Springboot框架介绍 2 Springboot煤炉网站代切系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统功能分析 2.2.1 功能性…

9.计算机视觉—目标检测

目录 1.物体检测边缘框目标检测数据集总结边缘框代码实现2.锚框:目标检测的一种方法IoU—交并比赋予锚框标号使用非极大值抑制(NMS)输出总结代码实现1.物体检测 边缘框 一个边缘框可以通过四个数字定义 (左上x,左上y),(右下x,右下y)(左上x,左上y,宽,高)(中间x,中间y…

Llama-2 vs. Llama-3:利用微型基准测试(井字游戏)评估大模型

编者按&#xff1a; 如何更好地评估和比较不同版本的大语言模型&#xff1f;传统的学术基准测试固然重要&#xff0c;但往往难以全面反映模型在实际应用场景中的表现。在此背景下&#xff0c;本文作者别出心裁&#xff0c;通过让 Llama-2 和 Llama-3 模型进行井字游戏对决&…

轻松搞定!4个微信图片恢复方法大揭秘

在日常使用微信的过程中&#xff0c;我们经常会通过微信发送和接收大量图片&#xff0c;这些图片记录了许多重要的瞬间和信息。然而&#xff0c;误操作、手机故障或软件更新等原因可能导致我们不小心删除了这些宝贵的图片。 当这些重要的图片消失时&#xff0c;往往会让我们感…

提升学生职务执行力的智慧校园学工管理策略

智慧校园的学工管理系统匠心独运地融入了“学生职务”这一创新模块&#xff0c;它紧密贴合学生的实际需求&#xff0c;致力于在校期间的实践经验积累和个人能力的全面提升。这个模块化身为一个便捷的综合平台&#xff0c;让学生们能够轻松发掘并参与到丰富多彩的校内职务中去&a…

2. jenkins发布java项目

jenkins发布java项目 一、环境描述二、部署tomcat业务服务器三、部署git服务器&#xff0c;上传测试代码1、部署git服务器2、上传测试代码 四、jenkins对接组件1、安装必要的插件2、对接git客户端3、对接maven工具4、配置maven需要的jdk5、配置gitlab服务器的连接6、在jenkins上…

VBA 操作注册表

1. SaveSetting 1-1. 语法 SaveSetting(AppName As String, Section As String, Key As String, Setting As String) 1-2. 函数说明 在Windows注册表中创建或保存一个应用程序项。将值写入注册表中的HKEY_CURRENT_USER\Software\VBand VBA Program Settings键的子键 1-3. 参…

#BI建模与数仓建模有什么区别?指标体系由谁来搭建?

问题1&#xff1a; 指标体系是我们数仓来搭建还是分析师来做&#xff0c;如何去推动&#xff1f; 问题2&#xff1a;BI建模与数仓建模有什么区别&#xff1f; 指标体系要想做好&#xff0c;其实是分两块内容的&#xff0c;一块是顶层设计阶段&#xff0c;业务指标体系的搭建&am…

github 设置中文,亲测有效

点进去 安装 选上面第二个&#xff0c;不行再选第一个 GitHub - maboloshi/github-chinese: GitHub 汉化插件&#xff0c;GitHub 中文化界面。 (GitHub Translation To Chinese)

如何注册一个自己的公众号?详细指南

博主介绍&#xff1a;全网粉丝10w、CSDN合伙人、华为云特邀云享专家&#xff0c;阿里云专家博主、星级博主&#xff0c;51cto明日之星&#xff0c;热爱技术和分享、专注于Java技术领域 &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅…

程序员鱼皮的保姆级写简历指南第三弹,简历常见问题和建议汇总

大家好&#xff0c;我是程序员鱼皮。做知识分享这些年来&#xff0c;我看过太多简历、也帮忙修改过很多的简历&#xff0c;发现很多同学是完全不会写简历的、会犯很多常见的问题&#xff0c;不能把自己的优势充分展示出来&#xff0c;导致错失了很多面试机会&#xff0c;实在是…

香橙派AIpro测评:快速部署SLAM算法,性能与体验的完美融合

一、引言 最近收到了一块”香橙派AIpro“的开发板&#xff0c;这款开发板是香橙派联合华为精心打造的&#xff0c;他们的宣传sologen是&#xff1a;“为AI而生”。这引起了我的好奇心&#xff0c;想知道其是否可以胜任无人机的实时建图和航迹规划工作。通过实操博主成功实现了…

SprongBoot3整合Knife4j实现在线接口文档

大家好&#xff0c;我是晓凡。 写在前面 在上一篇文章&#xff0c;我们详细介绍了SpringBoot3 怎么整合SpringDoc实现在线接口文档。但是&#xff0c;有不少小伙伴 都觉得接口界面太丑了。有没有什么更美观一点的UI界面呢&#xff1f; 当然是有的了&#xff0c;毕竟这是一个…

MySQL 进阶(一)【存储引擎】

1、存储引擎 1.1、MySQL 体系结构 自上而下可以分为 连接层 接受客户端的连接&#xff0c;完成连接的处理、认证授权、安全方案和最大连接数等服务层 绝大部分的核心功能都是在服务层完成的&#xff0c;比如SQL 接口&#xff08;DDL、DML、视图、触发器、存储过程&#xff09…

【IDEA】什么maven,如何进行文件导入,并打包

maven介绍 maven是一个Java世界中&#xff0c;构建工具。 核心功能&#xff1a; 1&#xff0c;管理依赖&#xff1a; 管理文件运行的顺序逻辑依赖关系。对配置文件&#xff0c;进行构建和编译。其也是在调用jdk&#xff0c;来进行编译打包工作。 2&#xff0c;打包 通过使用…

【Python基础篇】你知道python的数据类型都有哪些吗?

文章目录 0. 前言1. 基本数据类型1.1 数值类型&#xff08;int、float&#xff09;1.1.1 整型&#xff08;int&#xff09;1.1.2 浮点型&#xff08;float&#xff09; 1.2 布尔类型&#xff08;bool&#xff09;1.3 字符串&#xff08;str&#xff09;1.4 字节串&#xff08;b…