当前位置: 首页 > news >正文

量化实例分析初探

一、量化介绍

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

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

针对大模型主要是以量化为主。量化是一种将预训练模型中的权重从浮点数转换成低位数的技术。通常情况下,量化的精度是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寄存器。

  • 计算数据的绝对值。

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

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

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

  • 存储量化结果。

相关文章:

  • 【linux】网络基础(3)——tcp协议
  • [数据集][目标检测]电缆钢丝绳线缆缺陷检测数据集VOC+YOLO格式1800张3类别
  • labview技巧——AMC框架安装
  • Spring自带的持久层模板类:JdbcTemplate+Spring框架声明式事务管理实战
  • C++_01
  • 【数据结构 之压栈,形参和局部变量入栈之前会发生什么?】三种解释回答 包含操作系统版
  • Word文档中公式的常用操作
  • 被裁了(9年)
  • 【C++/STL深度剖析】stack和queue的详细概念和使用(图文详解,初学者必看!!)
  • Spring Cloud 概述
  • 【鸿蒙学习笔记】@Prop装饰器:父子单向同步
  • spring boot读取yml配置注意点记录
  • [数据集][目标检测]围栏破损检测数据集VOC+YOLO格式1196张1类别
  • 封装stater时配置导入配置类提示功能
  • MacOS docker 安装与配置
  • Android 控件背景颜色处理
  • angular2 简述
  • Cumulo 的 ClojureScript 模块已经成型
  • java8 Stream Pipelines 浅析
  • Javascript 原型链
  • Java多线程(4):使用线程池执行定时任务
  • Java精华积累:初学者都应该搞懂的问题
  • Laravel核心解读--Facades
  • leetcode388. Longest Absolute File Path
  • React的组件模式
  • scrapy学习之路4(itemloder的使用)
  • webpack+react项目初体验——记录我的webpack环境配置
  • 安装python包到指定虚拟环境
  • 不上全站https的网站你们就等着被恶心死吧
  • 关于Java中分层中遇到的一些问题
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 如何进阶一名有竞争力的程序员?
  • 什么软件可以剪辑音乐?
  • 消息队列系列二(IOT中消息队列的应用)
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 用Visual Studio开发以太坊智能合约
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • FaaS 的简单实践
  • 回归生活:清理微信公众号
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • (1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)
  • (LeetCode) T14. Longest Common Prefix
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (全部习题答案)研究生英语读写教程基础级教师用书PDF|| 研究生英语读写教程提高级教师用书PDF
  • (一)WLAN定义和基本架构转
  • (转)Oracle存储过程编写经验和优化措施
  • .NET CF命令行调试器MDbg入门(二) 设备模拟器
  • .NET Core Web APi类库如何内嵌运行?
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .net php 通信,flash与asp/php/asp.net通信的方法
  • .net 反编译_.net反编译的相关问题
  • .net 流——流的类型体系简单介绍