一文掌握大模型常用量化方法

📅 2026/7/29 3:56:25 👁️ 阅读次数 📝 编程学习
一文掌握大模型常用量化方法

01LLM主流浮点精度

随着网络层数深度的增加,我们希望前向传播的值和反向传播的值都比较稳定,不随着网络层数的加深把这种不稳定不断放大。如果每一模型越做越大,显卡越来越贵,量化的本质就是因为穷。那大模型有哪些主流的浮点精度?

千问14b就相当于28G的显存,b对应的是G,一个小数浮点数默认用float 16 bit,一共14b的数,一个数用2个字节,因此14 × 2 = 28G显存。

float 16这16位数分成三类:

第一位是符号位Sign,正数还是负数。

第二位是指数位Exponent,在这里表示2的四次方。指数位是2^1,左边是低位,右边是高位。

第三位是小数位Significand/Mantissa。这里是2^-1+ 2^-4 + 2^-7 就是0.5703。

它的数字表达就是,(-1)^0 * 2^1 * (2^0+0.5703125)=3.140625这个就是一个浮点数的表示。

非常像十进制的表达,比如103.56,它的十进制表示如上所示,只不过这里是以10为底,FFloat16(FP16)是以2为底。

Float16(FP16)它的指数部分决定了数值的范围,小数位决定它的精度,就是小数点能够后几位,这是FP16比较标准的。

除了FP16,还有很多类型的数据,比如FP32、TF32、FP16、BFLOAT16等各种各样的,FP32相比FP16是指数位多了范围就更大,小数位多了精度就更强,但是它一个数占4个字节,存储空间就会翻倍。比如FP16和BF16,这两个都是16位,但对于FP 16它侧重于精度,对于BF16它侧重于范围,表达方式其实是不一样的,一般来说在推理的时候FP16就足够用了,但是在训练的时候一定要FP32,因为在训练的时候,每次改变的东西都非常小,每次改变梯度都特别小。如果说精度不够的话,那很有可能就被忽略掉了,导致学习训练不动,因为并不是真的训练不动,而是精度不够,那些很小的变化体验不到。

比如有FP32的数,在训练的时候用FP32的数,训练这么高的精度。从Huggingface上能下载的模型,在推理的时候一般标准都是FP16。 3.14159…把这个数的精度直接给降下来,把后这一节全都给抛弃,只要前面这一部分,因此就变成了FP16,精度就降下来了,这个在推理上其实够的,但这种转化叫精度变低,就是比较小的位数表示一些精度比较高的数值,这个不叫量化,只是叫做牺牲精度。

上图为Qwen的量化版本,一个float四个字节来表示,INT4四位半个字节。一个float,INT8也就是一个字节来表示。

  • Qwen72B,用INT4来量化,对应36B的模型
  • Qwen还有一个14B的模型,正常来说是28B

假如这两个基本接近,量化的模型会更厉害。

02参数量化

参数量化跟直接降精度有什么区别,比如一个高精度的数值,4bit一共能表示2^4也就是16个数,拿低精度的16个点,跟高精度的这些点一一对应,这个过程就叫做量化,只不过这个对应的关系叫做量化的映射,由不同的量化算法就会产生不同的map。比如100、200、400、800这4个数,不需用int8表示,只需用二位做个map映射00 -> 100、01 -> 200、10 -> 400、11 -> 800即可,这就是量化。

对于浮点数也是一样的,比如原始图像的巧克力,量化是把巧克力给变模糊了点,量化肯定会带来损失,比如用INT 8来量化,一共只能表示2^8就是256个数。但是FP 16有可能1000个数,那就必然会出现几个数对应的一个数,相对于说他们之间的差异,可能是0.003和0.0031,0.0029对应量化后是一个数,相对说精度受到了一定的损失。直接降精度FP32直接降到FP16,它相当于是把图给缩小了,而量化并没有缩小图,但是能让图的分辨率变低了。

下图这个是FP32到FP16直接粗暴的截断。

但是在深度学习中,做量化的时候比如用INT 8,一位表示符号位,7位,就是2^7就是128,从-127到127,一共有256个数,把FP32里的每一个数都映射到它对应里来,以前需要4个字节,现在变到了1个字节,存储空间就会减少,但是效果会打折。因此不同的量化方法就是根据数值特点尽可能减少损失,不同的量化方法主要的目的就是尽量减少量化的损失。量化损失说白了就是原来数多,现在用少的数来表示,根据鸽笼原理原来有二个一样的东西现在变成一样的了。

03最大值量化

比如以前是FP16,可能是-5、-0.3、-0.2、1、2.1、3等。

找出一个绝对值最大的absmax=5,x/absmax值域在[-1,1],要量化成8位,就是[-127,127],[(x/absmax)*127]取整。比如说absmax=5,x=2.46、x=2.49等,量化完之后都是63,这个叫做量化的一个损失,数值之间没有区分度了。

这是一个最简单的量化方法,标准的一个公式,但是这种量化方法其实有很大的问题。损失是一个问题,但是损失这个问题其实在量化里头是不需要拿出来讲的,因为你所有方法你都是有损失的,除了这个问题外,它其实还有很多别的问题。

第一个问题,比如原来在FP32里头,原来可能有1、2、3、4、500,它的absmax=500,左边这四个数除以500都非常小,可能映射出来都是0,500对应的是127,但是中间的1 2 3 4一直到126,这些数都被浪费了,这个问题的本质是500叫做异常值。

第二个问题也比较类似,我们知道他的表达是-127到127,假如FP32都是正数,那一半的空间也被浪费,只有右边挤在了正数空间,这个就是最大值量化的一个问题。

比如有一个向量,它的最大值是5.4,进行量化之后,把每个数给量化成-127到127,反量化就是反过来,比如量化y= [x/absmax],取整,反量化就是x=absmax*y,每个乘以23.5,就是反量化回来了,误差就是因为取整符号造成的,小数点后面都舍弃掉了,所以导致误差,取整之后就发现这是个比较好的一个例子,原来是多少,返回来也是多少,但是这种例子其实是比较少见的。

因为原来的精度也不是很高,如果原来到了小数点后两位,这个5.47折腾完以后再回来就是5.44,3.08量化完后再回来3.06,拿量化前的和反量化后的,相减,它其实是有一些误差的。这个就是量化所带来的量化误差。

04零点量化

除了最大值量化以外,还有零点量化,它要解决的问题是刚才说的那两个问题,分布稀疏和有一半没有的问题。

比如x: -5、0、10,absmax=10,x/absmax,主要目的是让它变成一个有限级,变成一个-1到1的一个区间,压缩到有限级的空间,压缩完之后,再乘以INT 8的127,变成-127到127,这个压缩其实不一定要用最大值压缩,说白了就里需要知道跨度是多少。

还有个方法可以压缩,比如最小值min、最大值max,max-min就是这个跨度,任何一点x减最小值除以它(x-min)/(max-min),就是这段长度占整个长度的比例,仍然是一个0到1的范围。只要搞成了一个范围有限的,就可以进行量化了。

比如先计算一个幅度比例因子scale,原始数据是FP16(α-β)和现代数据(127-(-128))是INT 8,跨度是0.07212。

(Xi/(α-β))* 255,范围是0到255的数,让int_value之后的值有正有负,再加一个偏移量zero_point,就是让min最小的值被量化之后变成-128。零点值zero_point就是拿最小的值除以这个幅度,最后得到结果,就可以完成一个量化,zero_point是保证让最小值β能够对应到-128,这样的话整个的值域就是最大值,就是α对应的值是127。这个叫做零点量化。

对称的时候,原来的值域是-10.8 ~ 10.8,因为α=0,它能保证0点。但是不对称的,比如正数要大一些,有可能中间的一半,相当于它是中间的一半刚好对应零点,因为α大了,所以它中间的一半其实也是个大于0的数,因此0对应的是一个负数,量化之后是它,是典型的非对称量化,刚最大值量化是一个典型的对称量化。这两种方法都叫做线性量化。

对称和不对称在实际推理中会有什么影响,在大模型里头,常用的函数是relu函数,就是y = x 或 0 ,x大于0的时候为x,小于0的时候为0。

如果是对称量化,Relu函数一样通过量化之后的结果,判别大于0还是小于0,一样能够迅速的得到这个值。但是不对称量化,有可能量化之后的数它跟0的关系,很难从量化之后的一眼能看出来,必须通过一个转换一个运算才能看过来,没有那么直接,因此增大了对函数推理的计算量。所以这个就是不对称量化所带来的一个坏处。比如要是对称量化

  • x=10(int 8),肯定能够判断以前的z(量化前的是FP32),肯定可以判断出z大于0,
  • x=-10,肯定可以判断z<0。
  • 如果是非对称的,因为有个零点平移的问题,所以x=10的时候,以前z是大于0还是小于0是未知的。

零点量化的好处,比如原来的数这里都在大于0的这一块,那量化的时候就会把-128到127,把这些数值都用满,而小于0的部分不给它留空位了,就量化到什么位置,这样不会造成量化的一个浪费、存储的浪费其实就是带来误差。 因为存储东西不够,所以会带来误差,本身就不够,再浪费一些,那误差肯定会更大了。

05量化中的异常值和解决方案

量化的时候有异常值怎么处理,最典型的异常值就是突然来了一个特别大的数,就让这些全是0。刚那两个方法,不管哪个方法都有这个问题,全是0,这个是127,其他位数全部浪费掉,因此对于这种方法非常粗暴。

比如设一个Max和min,比如设一个-5和5,凡是大于5的,全当5来算,凡是小于-5的全当-5来算,直接粗暴,但是这里就会造成256这个数跟5这个数对应的都是127,也就是说损失一个幸福所有人,这叫做手动裁剪。

06如何解决最大值量化/零点量化存在的问题

这个问题主要就是离群点,既然有了离群点以后,其实是可以把离群点直接去掉,或者直接把它搞成一个-5到5之间的一个数,那这个时候离群点,这个点其实会有损失的,因为离群点它可能未必是错的,它有可能反而是一个比较重要的信息。因此有一个做法其实比较巧妙。

量化最终是矩阵相乘,把这些数值给统计一下,这些标黄色的数在X里头都算是比较大的,40多30多,其他的都是2 0 1 3,最终会跟w相乘,拎出来的这两列是黄色的第二列和第四列,跟w的第二行和第四行相乘,相对于说把这两个矩阵拆成两部分,一个是对应的是离群点。下面这一块它并不离群,它的数值还是比较正常的,但是因为要跟离群的点进行运算,把它也拉出来,下面黄色这两个就不量化了,做正常的运算,对于上面蓝色这些非离群的这些值,做完量化以后就可以进行量化了,量化完之后可以进行相乘,输入结果,再反量化,最后相加,得到一个最终的结果。

量化的东西最后都是矩阵运算,矩阵拆解,把离群的那一些和不离群的正常的给分开来搞好,这个就是常用大模型的一些计算方法。

线量化带来的很大的问题就是离群点的问题,还有一个问题其实也比较严重,叫做数据分布不均匀,所以就有一个叫分位数量化。

07分位数量化(非线性量化)

数据分布不均匀,比如一堆数比较密集,量化的时候就量化到128,128到128必然会造成,比如这段是可以的,隔一段不行,有两个相对来说比较空洞的,造成存储空间浪费,分数量化是一个典型的非线性量化,它可以很好的来解决这种分布不均匀的一个问题。

比如有10个数从小到大排0 1 2 3 4…13等,分位数就是画一个十分位,均匀的切九份,划成十份,每一分位对应一个数。

比如原来有n个数,n=1024,把这1024个数a1 a2 直到a1024,从小到大进行排序,用INT8来量化,那么INT 8它的值就是-128到127,一共有256个数。

  • a1 a2 a3 a4对应-128,
  • a5 a6 a7 a8对应-127,
  • 最后几个就是a1024,a1023,a1022, a1021,对应127。

每个在INT 8里头,每一个数都能够均匀的对应上四个原来的数,而且它根据原来数大小其实是不敏感的,对大小不敏感,只对排序敏感,只关心排序,这个就是分位数排序。

但是这个地方有个小问题,首先它是非线性,不像刚才那样乘除一个数,加减个偏移量就能进行量化、反量化。需要有一个映射表来进行一个完整的保存,这信息就全部给保存起来,所以它的运算其实没有那么简单。

还有一个很小的问题,比如说a4和a5可能一个是0.111,一个是0.112,可能只差这一点点,刚好被分到了两个不同的分位,这个也是有可能的,这个是它一个小小的一个瑕疵,但最大好处是能够保证INT 8空间充分利用,这是它最大的一个优势。

08量化参数在推理时如何使用

量化参数在推理的时候如何使用,在推理的时候,矩阵的乘法、加法,还有激活函数,这三种运算。

线性量化就是最大值量化和和零点量化,比如说矩阵新的元素,就是之前的元素对应元素相乘累加。量化前的是FP16,把FP16的数用INT 8来表示。这里有两个矩阵,m1*m2=m3,每个矩阵都单独进行量化。

m1对应s1z1,m2对应s2z2,m3对应s3z3,直接拿量化后的结果就能算出来m3量化之后的结果,相当于说原来计算的是拿FP16来计算,FP16计算可以直接转成INT 8之间数的一个计算。

拿到模型量化后的参数M1 M2,要计算M3,正常思路是把m1、m2,反量化算成m3,但是通过简单数学推导,不需要这一步了,直接算就会把M3给算出来,因此不需要反量化过程,直接把M3相乘的结果给算出来,这个是利用了线性量化的一个优点,这个加法不受量化影响。

激活函数relu,以前这是FP16量化之前的,如果是最大值量化,直接判断大于0还是小于0就行了。但是如果是零点量化,零点会飘动,因此相对来说稍微做个转机,原来要判断r1>=0,现在只需要判断这个就可以了,把这个直接带进去,因此也相对零点漂移了,所以主要是零点漂移的问题,它通过这个方法就可以解决了,这是线性函数。

线性函数就是量化的比例放大,对应的结果也成比例的放大。比较麻烦的是非线性激活函数如sigmoid函数,这种非线性函数成比例放大,对应的值不是成比例的,比如线性函数f(3)=3*f(1)+C,线性它肯定有这种特点,非线性不具备这个特点,这个比较麻烦的是sigmoid这种非线性函数。

把非线性先近似成一个线性的状态。比如随机找8个点,它是一个线性的,对于这一段里面的函数,把它当线性来进行一个插值计算,这个点取的越密,肯定就是精确的越准,但同时带来算力消耗就会越大。

首先量化范围是0到256,把它分成128份,相对说第一个就是0,1,第二个是1,2,第三个是2,3等。把这个量化之后的数值,先把它反算成量化前的int 8,把它反成FP 32。

这个量化方法可以是任一种分位数量化,零点量化等,先进行一个量化,就知道了量化的方法和反量化的方法,首先把INT 8量化成FP 32,然后反量化回来,要反量化成FP 32,比如这里对应的就是x1 x2 x3 x4等。反量化之后代入sigmoid,得到y1,y2,直到y256 ,把求出来的值再进行量化,变成了z1 z2…zn。假如新来的一个数x,进行一个量化,量化之后它刚好在这个区间,比如刚好是0,对应就直接查表,做一个表。

表对应x为int 8,y为int8,a1 b1对应z1 z2,a2 b2对应的z2 z3, a3 b3对应z3 z4等。因为分了区间,b1=a2,b2=a3,a1最小值对应z1、b1最大值对应z2。把a1 b1反量化成FP16,计算完结果以后再量化回来。比如说突然来了个x,经过量化以后,比如a2=10 b2=14,量化之后它是在13这个空间,x其实属于这个区间,这个区间里头最小的值是z2,最大的值是z3,因为这一块区间其实可以把它进行一个简单线性化,那就是z2+((z3-z2)/4)*3,就是x所对应量化后的结果,就直接在这里进行查值。

先有一个分位区间,把int 8 x量化分成n份,a1 b1 a2 b2 a3 b3,因为它是连续段,所以b1=a2,b2=a3。把这个量化后的先转成FP16,再经过sigmoid函数算出y,再进行一次量化变成int8的z1 z2、z2 z3、z3 z4。新来一个数,先确认它在哪个区间,在区间范围内让它有一个近似的关系,直接来进行一个线性的运算。无论是什么量化方法,还是函数是线性还是非线性,都可以这样做。如果说函数是线性的,量化方法也是线性的,就可以用刚才这种相对来说比较简单的方法来计算。这个方法比较麻烦的是就是需要有一个映射表来保存这些关系。

09动态量化

动态量化,大模型训练完了以后,w就可以进行量化了,已经都知道它分布了,但是有个很大的问题,比如y=wx, wx有个很大的问题。x是输入,x输入会直接影响到y,但是输入用户在用的时候,根本就不知道用户会输入什么x,y的这个数值范围是不确定的,前面的几种量化方法,无论哪种方法都是利用了数的值域,这个时候根本不知道它的值域是什么。

如果想量化y,就很难去直接量化,比如拿个模型以后,怎么量化这个y,因为连x都不知道是什么,所以就没有办法去量化。所以就出来一个方法叫动态量化。

输入x,通过矩阵进行运算,算出来一个新的向量y,y就动态的来算,把这个向量值都拿出来,动态的进行量化,边计算边量化,y会随着x的变化,或者α和β会随着x的变化而变化,这个叫动态量化。

但动态量化最大的问题是计算量太大了,每次都进行一个现算,计算量太大。

10静态量化

为了解决这个问题,出现了静态量化。不知道x是什么,所以没有办法量化y,拿一些训练数据的子集来验证数据,或者随便找一些数据或合成一些数据,或混合或随机搞一些数据,把y的值域大概估算出来。

静态的把α β z给算好,就是动态量化,边来数据边量化,但是它带来的问题是运算量太大,动态算,静态量化是拿些数据来模拟真实场景中的输入来进行一个运算,带来的问题是模拟的这个跟真实的越近效果越好,如果模拟的跟真实的差异很大,那么效果肯定不理想,这就是静态量化。

011量化感知训练

量化感知训练。经典的方法是,有一个模型训练好了之后,得到一个参数进行量化,量化成新的参数。但是量化必然带来误差,这样的话,模型的w是最好的,但是因为量化以后损失了一些东西,可带来误差,有可能量化的模型效果会打一些折扣。

量化感知训练就是说训练的时候就考虑了量化误差。

训练的时候输入经过第一层,然后输出,再经过第二层,以前是Layer1直接连到Layer2,正常神经网络,在这里先进行一次量化,再进行一次反量化,把这个误差直接注入给模型,这样的话模型的loss就包含了这个误差所带来的信息,在梯度下降的时候就会考虑这些误差,这样训练的时候它的效果就会比较好。

在训练的时候就把这个误差直接给引入进来。

站在一个更加宏观的角度来看这个问题,比如说有两个局部极小。一个局部极小为①,另外一个是②,右边这个明显低一点,但是正常思路就认为这个点可能会更好,这个点它有个很大的问题,w稍微偏一点损失迅速变大,它这个地方就比较窄,w稍微变一下损失迅速变大,左边这个点虽然没有右边那么低,但有个好处是w稍微变一下,影响并不是很大。

所以量化的时候,更希望找到左边这样一个点,哪怕量化受了点损失,偏移过来不会造成效果有多大的折扣,这是第一种理解方式。

第二种理解方式就是说,相当于说在训练的时候引入这个误差,以前是训练θ,现在直接是 c 塔扰了,就是参数加了一个随机干扰项,这随机干扰项就是量化带来的,这样训练的时候就直接有误差,这样能够避免这个问题。

012GPTQ量化

GPTQ的量化,要理解量化的思维方式和思路。

GPTQ量化,这个量化首先w求逆矩阵,w的逆矩阵W^-1,这个数值逆矩阵对应的数值跟它误差的强度是有关系的,这个地方的h这些数值跟模型的量化误差是有关联的。首先会把0.5量化,再反量化变成0.297,这是绝对误差再除以一个系数就变成了一个相对误差。

相对误差再算第二个元素,这样就可以算出来一个误差修正,误差修正就是相对误差,0.203这个误差修正,算第二个元素的时候,把第一个元素的误差修正加上去,这样就有效的解决w各元素相互耦合、相互影响带来的误差。

013Qlora原理和特点

Qlora的原理和特点,Lora是一个w+外挂两个矩阵,训练的时候不训练w,只训练这两个矩阵,Qlora就是lora的量化版本。它量化的是参数w,外挂的这两个矩阵仍然是FP 32。w这块其实不参加训练,所以随便怎么量化都没问题。

如果做一个分位数量化,FP32 -> int4,有可能原来的0被量化之后就不等于0了,很容易出现这种情况,但是它又认为0这个数很重要。Qlora的做法是:正负数分别进行分位数量化,FP16 -> int4(正数[0,7],负数[-8, 0])

所有大于0的正数进行量化,因为0在FP16正数里头它肯定是最小的,所以0肯定对应int4中的0。

在负数里头,负数的量化空间是-8到0,0在负数里头是最大的,所以它肯定能量化到0,所以可以保证0一定量化到0。

就是通过正负两个区间分别做分位数量化,一定能保证0一定能量化到0。就是QLORA第一个技术。

第二个就是分块,absmax量化,它会再做一个double双重量化。比如FP16的数x量化到int8,absmax不做量化,因为这个数FP16保存很大,直接把它再进行量化,变成INT 8,因为它做了两次量化,这个就叫双重量化。

第三个是一个分页存储,这个跟量化关系倒不是很大,它就是GPU资源相对比较紧张的时候,会把一些梯度转移到CPU内存,这个跟量化关系不是很大。

最后就是在做模型训练的时候,把这个真正要量化的东西,源模型再进行一次量化,这个是Lora,它用的都是BF 16,不做任何量化,相当于它是在训练推理的时候,训练和推理的时候一部分做了量化,然后一部分不做量化,这就是所谓的QLora。

学AI大模型的正确顺序,千万不要搞错了

🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!

有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!

就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇

学习路线:

✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经

以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!

我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费