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

datawhale 2411组队学习:模型压缩4 模型量化理论(数据类型、int8量化方法、PTQ和QWT)

文章目录

    • 一、数据类型
      • 1.1 整型
      • 1.2 定点数
      • 1.3 浮点数
        • 1.3.1 正规浮点数(fp32)
        • 1.3.2 非正规浮点数(fp32)
        • 1.3.3 其它数据类型
        • 1.3.4 浮点数误差
        • 1.3.5 浮点数导致的模型训练问题
    • 二、量化基本方法
      • 2.1 int8量化
        • 2.1.1 k-means 量化
        • 2.1.2 线性量化
          • 2.1.2.1 零点量化(Zero-Point Quantization)
          • 2.1.2.2 绝对最大( absmax )量化
      • 2.2 INT4 和 FP4量化
      • 2.3 二值量化 (Binarization)
      • 2.4 三值量化 (Ternary Quantization)
      • 2.5 混合精度量化
    • 三、 训练后量化 (Post-Training Quantization)
      • 3.1 量化粒度
        • 3.1.1 逐张量量化(Per-Tensor Quantization)
        • 3.1.2 逐通道量化(Per Channel Quantization)
        • 3.1.3 组量化(Group Quantization)
      • 3.2 动态量化参数的计算 ( Cliping )
        • 3.2.1 指数移动平均(EMA)
        • 3.2.2 Min-Max
        • 3.2.3 KL 量化
        • 3.2.4 均方误差(MSE)
      • 3.3 Rounding
    • 四、 量化感知训练(Quantization-Aware Training)
      • 4.1 前向传播
      • 4.2 反向传播

  • 《datawhale2411组队学习之模型压缩技术1:模型剪枝(上)》:介绍模型压缩的几种技术;模型剪枝基本概念、分类方式、剪枝标准、剪枝频次、剪枝后微调等内容
  • 《datawhale11月组队学习 模型压缩技术2:PyTorch模型剪枝教程》:介绍PyTorch的prune模块具体用法
  • 《datawhale11月组队学习 模型压缩技术3:2:4结构稀疏化BERT模型》:介绍基于模式的剪枝——2:4结构稀疏化及其在BERT模型上的测试效果
  • 项目地址:awesome-compression、在线阅读
  • bilibli视频《【精选课程】LLM大模型之精度问题(FP16,FP32,BF16)详解》
  • 《Hugging Face高性能技术五:Transformer高效推断(bitsandbytes、FlashAttention、 BetterTransformer)》

  语言模型一直在变大。例如PaLM 有 5400 亿参数,OPT、GPT-3 和 BLOOM 有大约 1760 亿参数,而且我们仍在继续朝着更大的模型发展。下图总结了最近的一些语言模型的尺寸。
在这里插入图片描述

  迄今为止,市面上显存最大的 GPU 芯片是 80GB 显存的 A100,因此这些模型无法在单个设备上运行。举个例子,如果我们使用 BLOOM-176B 模型的 Bfloat16 版本,其大小为 176 × 1 0 9 × 2 b y t e = 352 G B 176 \times 10^9 \times 2 byte=352GB 176×109×2byte=352GB。仅推理 BLOOM-176B 模型,就需要 8 个 80GB A100 。而如果要微调的话,则需要更多。

  移动端的硬件资源是有限的,比如内存和算力。而量化可以最直观的减少模型的大小,从而减少内存、硬盘和算力的占用。同时,量化可以提高模型的推理速度。下图为不同数据类型的加法和乘法操作的耗时对比。

在这里插入图片描述
  以最常见的int8模型量化来说,其零花过程可以分为两部分:将模型从 fp32 转换为 int8 ;使用 int8 进行推理,整个量化过程都和数据类型的转换息息相关,所以我们先讲解最基础的数据类型。

一、数据类型

1.1 整型

  如下图所示,整型数据可以分为无符号整型(Unsigned Integer)和有符号整型(Signed Integer)。

  • 无符号整型:数据范围为 0 到 2 n − 1 2^{n-1} 2n1,n 为数据位数。
  • 有符号整型:
    • 原码表示(Sign-Magnitude Representation):其实现的原理是取二进制数的最高位(左起第一位)为符号位,约定符号位为0时表示正数,符号位为1时表示负数,其余二进制位则用于待表示数值的绝对值。数据范围为 − 2 n − 1 − 1 -2^{n-1}-1 2n11 2 n − 1 − 1 2^{n-1}-1 2n11,n 为数据位数。
    • 补码表示(Two's Complement Representation):为了弥补原码表示,有 +0 和 -0 两种表示的缺点,最高位除了具有符号表示的功能,也具有权重值。数据范围为 − 2 n − 1 -2^{n-1} 2n1 2 n − 1 − 1 2^{n-1}-1 2n11,n 为数据位数。
      在这里插入图片描述

1.2 定点数

  定点数,即在表示小数数据时,把小数点的位置已经约定好固定在某个位置。与之对应的是浮点数,其小数点的位置不是固定的。如下图所示,指定蓝色部分为符号位,绿色部分为整数位,橙色部分为小数位。
在这里插入图片描述
  图中,蓝色格表示 − 2 3 -2^3 23,绿色格依次表示 2 0 , 2 1 , 2 2 2^0,2^1,2^2 20,21,22,橙色格依次表示 2 − 1 , 2 − 2 , 2 − 3 , 2 − 4 2^{-1},2^{-2},2^{-3},2^{-4} 21,22,23,24。将每个格数字(0或1)乘以对应scale就是最终结果。

1.3 浮点数

  定点数表示法采用固定的位数来表示整数和小数部分,这样能很容易地进行加减运算,但范围有限。而浮点数的计算方式不同于定点数中的简单相加,它利用指数来扩展数值的动态范围。这样的计算方式允许浮点数在有限的位数下表示极大的数值范围,例如从非常小的数(接近于零)到极大的数(如 1 0 38 10^{38} 1038 )。

1.3.1 正规浮点数(fp32)

在这里插入图片描述

  在1EEE 754标准中,我们用fraction表示小数部分,exponent表示指数部分。二者的的位数分别决定了数据的精度表示范围大小。对于FP32浮点数来说,其计算方式为:
f p 32 = ( − 1 ) s i g n ⋅ ( 1 + f r a c t i o n ) ⋅ 2 e x p o n e n t − 127 fp32 = (-1)^{sign}·(1+fraction)·2^{exponent-127} fp32=(1)sign(1+fraction)2exponent127

  如上图所示,FP32格式使用8位来表示指数,因此它的无符号整数范围是0到255。如果我们希望指数可以表示负值,就需要一个偏置。通过设定偏置为127,指数范围可以表示为:

指数实际值 = 存储的指数值 − 127 指数实际值 = 存储的指数值 - 127 指数实际值=存储的指数值127

  选择 2 ( 8 − 1 ) − 1 = 127 2^{(8-1)}-1=127 2(81)1=127作为偏置的原因是为了确保实际指数范围对称。正规浮点数中,存储的指数范围是从 1 到 254(即存储值从 00000001 到 11111110,全0和全1是特特殊情况),对应的实际指数范围是:−126到+127,可以均匀表示小于1的数和大于1的数。

   对于一个标准化(正规)浮点数,通常表示为:

value = ( − 1 ) sign × 1. fraction × 2 exponent \text{value} = (-1)^{\text{sign}} \times 1.\text{fraction} \times 2^{\text{exponent}} value=(1)sign×1.fraction×2exponent

   1. fraction 1.\text{fraction} 1.fraction 是尾数部分,其中 1 是隐含的,也就是说,虽然在存储尾数时,这个1并没有明确存储在内存中,但它默认存在,而 fraction 是实际存储的尾数部分。

  例如对于数值 6.25,它可以表示为:

6.25 = 1.100 1 2 × 2 2 6.25 = 1.1001_2 \times 2^2 6.25=1.10012×22

在内存中,它会存储为:

value = ( − 1 ) 0 × 1.100 1 2 × 2 2 \text{value} = (-1)^{0} \times 1.1001_2 \times 2^2 value=(1)0×1.10012×22

但在内存存储时,不会显式存储前面的 1,只存储 . 100 1 2 .1001_2 .10012,这样就省去了1位的存储空间。

1.3.2 非正规浮点数(fp32)

  非正规浮点数是指数部分的值为最小值(即 0),但尾数部分不为零的浮点数。在这种情况下,尾数部分的表示不再以 1 开头,而是以 0 开头,所以叫非正规浮点数。数学表达式为:

( − 1 ) sign × 0. fraction × 2 exponent − bias (-1)^{\text{sign}} \times 0.\text{fraction} \times 2^{\text{exponent} - \text{bias}} (1)sign×0.fraction×2exponentbias

此时,指数部分强制为 1-bias = -126 。此时,非正规浮点数的有效数值范围是非常小的。

在这里插入图片描述

  • 正常浮点可表示的最小正值为 fraction = 0,exponent = 1,结果为 2 − 126 2^{-126} 2126
    ( 1 + 0 ) ⋅ 2 1 − 127 = 2 − 126 (1+0)·2^{1-127} = 2^{-126} (1+0)21127=2126
  • 正常浮点数可表示的最大值为 fraction = 2 − 23 2^{-23} 223,结果为 ( 1 + 1 − 2 − 23 ) ⋅ 2 127 (1+1-2^{-23})·2^{127} (1+1223)2127
  • 非正规浮点可表示的最小正值为 fraction = 2 − 23 2^{-23} 223,结果为 2 − 149 2^{-149} 2149
    2 − 23 ⋅ 2 1 − 127 = 2 − 149 2^{-23}·2^{1-127} = 2^{-149} 22321127=2149
  • 非正规浮点可表示的最大值为 fraction 部分全为1 ,结果为 2 − 126 − 2 − 149 2^{-126}-2^{-149} 21262149
  • 正规浮点数的小数部分全为0,指数部分全为1时分别表示正无穷和负无穷(inf-inf)。

  所以,非正规浮点数主要用于表示非常小的数值,确保在接近零时,尽可能保持一定的精度,提高数值稳定性和减少精度丢失问题。

1.3.3 其它数据类型

机器学习中常用的数据类型还有:

  • 当expontent位数为5,fraction位数为11时,为fp16。
    f p 16 = ( − 1 ) s i g n ⋅ ( 1 + f r a c t i o n ) ⋅ 2 e x p o n e n t − 15 fp16 = (-1)^{sign}·(1+fraction)·2^{exponent-15} fp16=(1)sign(1+fraction)2exponent15

指数的值从-14到15,共30个区间。能表示的最大数是 2 ( 30 − 15 ) = 65504 2^{(30-15)}=65504 2(3015)=65504,最小的非零数是 2 − 14 2^{-14} 214

  • 当expontent位数为8,fraction位数为7时,为bf16。
    b f 16 = ( − 1 ) s i g n ⋅ ( 1 + f r a c t i o n ) ⋅ 2 e x p o n e n t − 127 bf16 = (-1)^{sign}·(1+fraction)·2^{exponent-127} bf16=(1)sign(1+fraction)2exponent127
  • 当expontent位数为4,fraction位数为3时,为fp8(E4M3)。
    f p 8 = ( − 1 ) s i g n ⋅ ( 1 + f r a c t i o n ) ⋅ 2 e x p o n e n t − 7 fp8 = (-1)^{sign}·(1+fraction)·2^{exponent-7} fp8=(1)sign(1+fraction)2exponent7
  • 当expontent位数为5,fraction位数为2时,为fp8(E5M2)。
    f p 8 = ( − 1 ) s i g n ⋅ ( 1 + f r a c t i o n ) ⋅ 2 e x p o n e n t − 15 fp8 = (-1)^{sign}·(1+fraction)·2^{exponent-15} fp8=(1)sign(1+fraction)2exponent15

在这里插入图片描述

1.3.4 浮点数误差

  浮点数在计算机中的表示会涉及到精度误差,原因在于其有限的表示方式。

  在计算机中,所有数字都通过二进制表示。对于一个十进制小数如9.23,计算机需要将其映射到二进制表示中,并确定其所在的范围和精确度。以fp16类型来说,正整数部分被划分为多个区间:

[1,2)
[2,4)
[4,8)
[8,16)
...

  所以9.23被划分在 [ 8 , 16 ) [8, 16) [8,16)区间内(即 2 3 2^3 23 2 4 2^4 24之间)。这种区间划分使得9.23可以通过以下方式表示:

  • 确定区间起点(8,即2的3次方)
  • 确定在此区间内的相对位置(9.23比8多1.23)

  为了表示9.231.23部分,该区间被细分为1024个等份(fp16的尾数部分为10bit),每个等分大小为 s = 8 / 1024 = 0.0078125 s=8/1024=0.0078125 s=8/1024=0.0078125

  由于每个区间段的大小有限,计算机只能在这些细分点中找到最接近的数值,而1.23落在区间157s到158s之间,所以1.23只能被近似表示为 157 × s = 1.2265625 ≈ 1.2266 157\times s=1.2265625\approx 1.2266 157×s=1.22656251.2266,这意味着浮点数9.23在fp16中会被表示为8 + 1.2266 = 9.2266,这种近似导致精度损失。总结就是:

  • 二进制无法精确表示某些十进制小数(如1.23),因其需被转化为有限的二进制数。
  • 细分区间的有限分辨率带来了约数值的逼近误差。

  显而易见的是, 随着数值范围的增大,精度的损失越严重,因为尾数是固定划分为1024个区间的。数学上来说,就是 f r a c t i o n ⋅ 2 e x p o n e n t − b i a s fraction·2^{exponent-bias} fraction2exponentbias扩大了精度损失。

1.3.5 浮点数导致的模型训练问题

  FP16在深度学习模型训练中是一种精度较低的表示方式,容易出现浮点数溢出与精度损失问题。

  1. 上溢出

  FP16的最大正数为65504。当试图表示超过这个数的值(如65505)时,浮点数不会发生溢出,但会发生精度损失。当浮点数超过fp16能表示的最大值才会发生上溢出,,通常表示为无穷大inf

  1. 下溢出

  在softmax,sigmoid,attention等涉及指数运算的过程中,容易导致上溢出问题。一个常见的优化方法是在计算前先进行归一化,比如对输入图片(0-255的ndarray表示)归一化到[-1,1];或者是训练时使用fp32类型。

  如果数值非常小,则可能发生下溢出,即数值被“舍入”到零,这对于一些非常小的数值表示时会有问题。比如模型后期梯度计算中梯度等于e-7,或者是在标准化(如Batch Normalization、Layer Normalization)操作中需要计算 x ′ = x − μ σ 2 {x}'=\frac{x-\mu }{\sigma^{2} } x=σ2xμ,当batch size很小或者数值都很接近时,方差过小,也可能导致数值下溢出。

  1. nan
    指数计算和均值计算中,当数值很大时,都有可能出现nan
m1=torch.tensor(100000,dtype=torch.float16).cuda()
m2=torch.tensor(200000,dtype=torch.float16).cuda()
print(torch.exp(m1)/(torch.exp(m1)+torch.exp(m2)))
tensor(nan,device='cuda:0,dtype=torch.float16)

最简单的避免方式是减去最大值,有严格的数学推导:

print(torch.exp(m1-max(m1,m2))/(torch.exp(m1-max(m1,m2))+torch.exp(m2-max(m1,m2)))

均值计算中也容易出现nan,

s=torch.tensor(500000*[1000],dtype=torch.float16)
print(torch.mean(s))

  另外,模型输入的异常值可能导致数值溢出或不稳定,进而导致模型计算产生NaN值;有时,当batch size过大时,可能会导致计算过程中的溢出问题。

  1. 相加精度损失
    当一个较小的数值加到一个较大的数值时,可能会因为精度限制导致“加了等于没加”的情况。
import torchpi_16=torch.tensor(3.141,dtype=torch.float16)
print("3.141的float16表示",pi_16)
pi_32=torch.tensor(3.141,dtype=torch.float32)
print("3.141的float32表示",pi_32)
print("3.141的float16转float32表示",pi_16.float())
3.141的float16表示 tensor(3.1406, dtype=torch.float16)
3.141的float32表示 tensor(3.1410)
3.141的float16转float32表示 tensor(3.1406)
s=torch.tensor(0.0005,dtype=torch.float16)
print(pi_16+s)
print(pi_32+s)
tensor(3.1406, dtype=torch.float16)
tensor(3.1415)

  在训练过程中,尤其是模型微调时,更新的梯度通常会很小,学习率也很小。参数更新时,由 W ( t + 1 ) = W ( t ) − η ⋅ g r a d i e n t W^{(t+1)} = W^{(t)} - \eta \cdot gradient W(t+1)=W(t)ηgradient可知,可能会因为精度不足导致“加了等于没加”的情况,从而无法更新参数,训练过程停滞,即因为数值精度丢失,导致模型无法有效更新。下图为 SSD 网络在训练过程中的梯度统计,有67%的值下溢出变成 0。
在这里插入图片描述
为了避免梯度消失(即“下溢出”),可以放大损失函数(Loss function):

  • 在损失计算后,使用FP32表示来存储损失函数,防止精度丢失。
  • 放大损失函数的数值,使其足够大,从而避免下溢出。
  • 然后使用FP16表示的梯度进行更新,并在更新后将结果缩小到原始范围。

在这里插入图片描述

二、量化基本方法

2.1 int8量化

  INT8量化 是一种常见的低位量化方法,它将浮点数(通常是32位浮点数)映射到8位整数(INT8)表示。量化后的数值范围通常是 [-128, 127],但不包括 -128,这有助于优化推理。以下是 INT8 量化的基本步骤:

  • 计算量化步长(scale)和零点(zero-point)。
  • 将浮点数值映射到整数,通常使用线性公式: q ( x ) = round ( x − zero_point scale ) q(x) = \text{round}\left(\frac{x - \text{zero\_point}}{\text{scale}}\right) q(x)=round(scalexzero_point) 这样可以将浮动值转化为低精度整数表示,减少计算资源消耗。

模型量化对象主要包括以下几个方面:

  • 权重(Weights):量化权重是最常见和流行的方法,它可以减少模型大小、内存使用和空间占用。
  • 激活(Activations):在实践中,激活通常占内存使用的大部分。因此,量化激活不仅可以大大减少内存使用,而且与权重量化结合时,可以充分利用整数计算来实现性能提升。
  • KV缓存(KV cache):量化KV缓存对于提高长序列生成的吞吐量至关重要。
  • 梯度(Gradients):与上面相比,梯度稍微不常见,因为它们主要用于训练。训练深度学习模型时,梯度通常是浮点数。量化梯度主要用于减少分布式计算中的通信开销,也可以减少后向传递过程中的成本。
2.1.1 k-means 量化

  K-means 量化是一种用于压缩和加速神经网络模型的技术,通过对权重进行聚类到不同的簇,将其近似为一组有限的中心值(质心),从而降低存储和计算复杂度。下图展示了整个过程:

在这里插入图片描述

  1. weights (32-bit float):神经网络中的原始权重矩阵,数据以32位浮点数存储。不同颜色的方块代表分到不同的簇。

  2. cluster index (2-bit int):权重经过 k-means 聚类,被分配到不同的簇,得到每个权重的簇索引矩阵。这样权重只需要使用2位整型就可以进行表示,显著减少存储所需的位数。

  3. centroids:计算每个簇的质心,这些质心是簇内权重的平均值。此时,每个簇内的权重都可以用一个质心来近似表示。例如:
    c l u s t e r 0 = m e a n ( − 0.98 , − 1.08 , − 0.91 , − 1.03 ) = − 1 cluster_0=mean(-0.98, -1.08, -0.91, -1.03)=-1 cluster0=mean(0.98,1.08,0.91,1.03)=1

  4. gradient:原始的梯度值

  5. group by和reduce:根据 cluster index 将梯度矩阵中的梯度值按所属簇分组。每个簇内的梯度被视为一组。对每个簇内的梯度进行汇总求和,得到簇的梯度更新值。

  6. fine-tuned centroids:利用 reduce 得到的簇梯度更新值和学习率,更新每个簇的质心。新的质心用于更新权重,使其在量化后仍能接近最佳解。
    c i ′ = c i − η ⋅ g i c_i' = c_i - \eta \cdot g_i ci=ciηgi

  最终,存储占用从 32bit x 16 = 512 bit = 64 B => 2bit x 16 + 32 bit x 4 = 32 bit + 128 bit = 160 bit = 20 B。当weight矩阵更大时,压缩比例将会更大。

  • 推理时,我们读取转换表,根据索引值获取对应的值。
  • 训练时,我们将gradient按照weights的聚类方式进行聚类相加,反向传播到转换表,更新转换表的值。

以下是将上一节的剪枝和k-means 量化结合起来的压缩流程。

  • 循环进行微调和剪枝,得到最优的剪枝模型。
  • k-means 量化将剪枝后的参数进行聚类,将聚类的索引值存储在模型中,并构建相应的索引表,并使用哈夫曼编码进一步压缩。
    在这里插入图片描述
2.1.2 线性量化

  线性量化是将原始浮点数据和量化后的定点数据之间建立一个简单的线性变换关系,因为卷积、全连接等网络层本身只是简单的线性计算,因此线性量化中可以直接用量化后的数据进行直接计算。

2.1.2.1 零点量化(Zero-Point Quantization)

  我们用 r r r 表示浮点实数, q q q 表示量化后的定点整数。浮点和整型之间的换算公式为:

r = S ( q − Z ) r = S(q - Z) r=S(qZ)

q = r o u n d ( r / S + Z ) q = round(r / S + Z) q=round(r/S+Z)
  其中, S S S 是量化放缩的尺度,表示实数和整数之间的比例关系, Z Z Z 是偏移量,表示浮点数中的 0 经过量化后对应的数(量化偏移),根据偏移量 Z Z Z是否为0,可以将浮点数的线性量化分为对称量化( Z Z Z=0)和非对称量化( Z Z Z≠0)。大多数情况下量化是选用无符号整数,比如INT8的值域为[0,255],这种情况下需要要用非对称量化。 S S S Z Z Z的计算方法为:
S = r m a x − r m i n q m a x − q m i n S = \frac{r_{max} - r_{min}}{q_{max} - q_{min}} S=qmaxqminrmaxrmin
Z = r o u n d ( q m a x − r m a x S ) Z = round(q_{max}-\frac{r_{max}}{S}) Z=round(qmaxSrmax)
在这里插入图片描述

  其中, r m a x r_{max} rmax r m a x r_{max} rmax分别表示浮点数中的最小值和最大值, q m a x q_{max} qmax q m i n q_{min} qmin分别表示定点数中的最小值和最大值。

下面举一个例子来详细说明。如下图所示,给定一个矩阵,可以通过上面的公式计算出Z和S。
在这里插入图片描述

可进一步利用上面的公式计算出量化后的矩阵。
在这里插入图片描述

2.1.2.2 绝对最大( absmax )量化

  absmax 量化通过找到输入数据或权重的绝对值最大值来计算缩放因子,将数据映射到指定的整数范围内。在绝对最大量化(absmax 量化)中,整个计算过程可以用以下公式来描述:

  1. 确定缩放因子 S S S: 计算输入矩阵 X X X 的绝对最大值:

    S = absmax ( X ) Q max S = \frac{\text{absmax}(X)}{Q_{\text{max}}} S=Qmaxabsmax(X)

    其中, absmax ( X ) = max ⁡ ( ∣ X i , j ∣ ) \text{absmax}(X) = \max(|X_{i,j}|) absmax(X)=max(Xi,j) 表示 X X X 中的绝对值最大元素, Q max Q_{\text{max}} Qmax 是量化整数的最大绝对值。例如,对于 8 位量化, Q max = 127 Q_{\text{max}} = 127 Qmax=127

  2. 量化映射公式: 将原始输入矩阵 X X X 转换为量化后的矩阵 X quant X_{\text{quant}} Xquant

    X quant = round ( X S ) X_{\text{quant}} = \text{round}\left(\frac{X}{S}\right) Xquant=round(SX)

    这里, round \text{round} round 表示对结果进行四舍五入,以确保 X quant X_{\text{quant}} Xquant 是整数。

  3. 反量化公式(从量化值恢复原始浮点值):

    X dequant = r o u n d ( X quant × S ) X_{\text{dequant}} = round(X_{\text{quant}} \times S) Xdequant=round(Xquant×S)

假设输入矩阵 X X X 为:

X = [ 0.5 − 1.2 2.4 − 0.7 ] X = \begin{bmatrix} 0.5 & -1.2 \\ 2.4 & -0.7 \end{bmatrix} X=[0.52.41.20.7]

步骤 1:计算 S S S

absmax ( X ) = 2.4 , S = 2.4 127 ≈ 0.0189 \text{absmax}(X) = 2.4, \quad S = \frac{2.4}{127} \approx 0.0189 absmax(X)=2.4,S=1272.40.0189

步骤 2:计算 X quant X_{\text{quant}} Xquant

X quant = round ( X S ) = round ( [ 0.5 0.0189 − 1.2 0.0189 2.4 0.0189 − 0.7 0.0189 ] ) = round ( [ 26.5 − 63.5 127 − 37 ] ) X_{\text{quant}} = \text{round}\left(\frac{X}{S}\right) = \text{round}\left(\begin{bmatrix} \frac{0.5}{0.0189} & \frac{-1.2}{0.0189} \\ \frac{2.4}{0.0189} & \frac{-0.7}{0.0189} \end{bmatrix}\right) = \text{round}\left(\begin{bmatrix} 26.5 & -63.5 \\ 127 & -37 \end{bmatrix}\right) Xquant=round(SX)=round([0.01890.50.01892.40.01891.20.01890.7])=round([26.512763.537]) X quant = [ 27 − 64 127 − 37 ] X_{\text{quant}} = \begin{bmatrix} 27 & -64 \\ 127 & -37 \end{bmatrix} Xquant=[271276437]

步骤 3:反量化 X dequant X_{\text{dequant}} Xdequant

X dequant = X quant × S = [ 27 − 64 127 − 37 ] × 0.0189 ≈ [ 0.51 − 1.21 2.4 − 0.7 ] X_{\text{dequant}} = X_{\text{quant}} \times S = \begin{bmatrix} 27 & -64 \\ 127 & -37 \end{bmatrix} \times 0.0189 \approx \begin{bmatrix} 0.51 & -1.21 \\ 2.4 & -0.7 \end{bmatrix} Xdequant=Xquant×S=[271276437]×0.0189[0.512.41.210.7]

区别绝对最大量化(Absmax 量化)零点量化(Zero-point 量化)
核心思想基于绝对最大值进行量化引入零点,使数据能更好适应非对称分布
缩放因子计算 S = absmax ( X ) Q max S = \frac{\text{absmax}(X)}{Q_{\text{max}}} S=Qmaxabsmax(X) S = max ( X ) − min ( X ) Q max − Q min S = \frac{\text{max}(X) - \text{min}(X)}{Q_{\text{max}} - Q_{\text{min}}} S=QmaxQminmax(X)min(X)
零点计算无需计算零点 Z = Q max − max ( X ) S Z = Q_{\text{max}} - \frac{\text{max}(X)}{S} Z=QmaxSmax(X)
量化公式 X quant = round ( X S ) X_{\text{quant}} = \text{round}\left(\frac{X}{S}\right) Xquant=round(SX) X quant = round ( X S ) + Z X_{\text{quant}} = \text{round}\left(\frac{X}{S}\right) + Z Xquant=round(SX)+Z
反量化公式 X dequant = X quant × S X_{\text{dequant}} = X_{\text{quant}} \times S Xdequant=Xquant×S X dequant = ( X quant − Z ) × S X_{\text{dequant}} = (X_{\text{quant}} - Z) \times S Xdequant=(XquantZ)×S
实现复杂度简单,计算和存储开销小较复杂,需要额外计算零点
适用场景由于这种方法使用对称量化(即数据在正负区间使用相同的范围),它适用于输入数据中零点(即数据中心)接近 0 的情况数据偏移明显,需更高精度的动态推理任务,比如量化神经网络的中间激活值或在输入数据非对称的情况下,提升模型的表达能力。

在这里插入图片描述

2.2 INT4 和 FP4量化

  • INT4量化将权重和激活值从高位宽(如INT8或FP32)压缩到4位整数(INT4,表示范围为 -8 到 7)
  • FP4量化是将浮点数(例如FP32)压缩为4位浮点数表示的方法,其表示的范围根据不同的指数位和小数位而有所不同。这是一个相对较新的量化策略,保留了浮点数的结构,包括符号位、指数位和尾数位。

在这里插入图片描述

2.3 二值量化 (Binarization)

  在二值量化中,模型的权重或激活值被限制为两个离散值,通常是 -1 和 1 。这样可以大幅减少模型的存储需求,因为每个参数只需要一位 bit 来表示。二值神经网络的计算也可以大幅加速,因为二值运算比浮点运算要简单得多。

  二值化(Binarization)的具体实现有两种方法:确定性二值化(Deterministic Binarization)随机二值化(Stochastic Binarization)

  1. 确定性二值化(Deterministic Binarization)

    • 直接根据一个阈值(通常是0)计算位值,结果为符号函数:
      q = sign ( r ) = { + 1 , r ≥ 0 − 1 , r < 0 q = \text{sign}(r) = \begin{cases} +1, & r \geq 0 \\ -1, & r < 0 \end{cases} q=sign(r)={+1,1,r0r<0
    • 即,如果输入大于等于0,则输出1;否则输出-1。
  2. 随机二值化(Stochastic Binarization)

    • 使用全局统计或输入数据的值来确定输出为 -1 或 +1 的概率。例如,在 Binary Connect (BC) 方法中,概率由 sigmoid 函数 σ ( r ) \sigma(r) σ(r) 确定:
      q = { + 1 , with probability  p = σ ( r ) − 1 , with probability  1 − p q = \begin{cases} +1, & \text{with probability } p = \sigma(r) \\ -1, & \text{with probability } 1 - p \end{cases} q={+1,1,with probability p=σ(r)with probability 1p
      其中, σ ( r ) = min ⁡ ( max ⁡ ( r + 1 2 , 0 ) , 1 ) \sigma(r) = \min(\max(\frac{r+1}{2}, 0), 1) σ(r)=min(max(2r+1,0),1)
    • 这种方法的实现较为困难,因为量化时需要硬件生成随机比特。

在这里插入图片描述

  上图展示了在二值化中最小化量化误差的方法。为了更好地逼近原始权重,二值权重 W B W^\mathbb{B} WB 乘以一个缩放因子 α \alpha α,计算后得到缩放后的二值权重 α W B \alpha W^\mathbb{B} αWB,其中 n 为矩阵中元素的个数。
α = 1 n ∥ W ∥ 1 \alpha = \frac{1}{n} \| W \|_1 α=n1W1
  通过引入缩放因子,误差从9.28减少到9.24,显示出缩放对减小误差的效果。

2.4 三值量化 (Ternary Quantization)

  在三值量化中,模型的权重或激活值被限制为三个离散值,通常是 -1 、 0 和 1 。相比二值量化,三值量化允许模型拥有一个额外的零值,这可以压缩模型参数的同时保留模型的精度。

  三值量化的具体规则如下:
q = { r t , r > Δ 0 , ∣ r ∣ ≤ Δ − r t , r < − Δ q = \begin{cases} r_t, & r > \Delta \\ 0, & |r| \leq \Delta \\ -r_t, & r < -\Delta \end{cases} q= rt,0,rt,r>ΔrΔr<Δ
其中, Δ = 0.7 × E ( ∣ r ∣ ) \Delta = 0.7 \times \mathbb{E}(|r|) Δ=0.7×E(r) r t = E ∣ r ∣ > Δ ( ∣ r ∣ ) r_t = \mathbb{E}_{|r|>\Delta}(|r|) rt=Er>Δ(r)

在这里插入图片描述

  如上图所示为三值量化的具体示例,展示了一个权重矩阵 $ W $ 如何被量化为三值权重矩阵。

  • 量化的阈值 Δ \Delta Δ 被计算为:
    Δ = 0.7 × 1 16 ∥ W ∥ 1 = 0.73 \Delta = 0.7 \times \frac{1}{16} \|W\|_1 = 0.73 Δ=0.7×161W1=0.73
    其中, ∥ W ∥ 1 \|W\|_1 W1 是原始权重矩阵 W W W 的 L1 范数,即所有元素绝对值的平均值。

  • 确定非零权重的值 r t r_t rt
    r t = 1 11 ∥ W W T ≠ 0 ∥ 1 = 1.5 r_t = \frac{1}{11} \|W_{W^T \neq 0}\|_1 = 1.5 rt=111WWT=01=1.5
    其中, ∥ W W T ≠ 0 ∥ 1 \|W_{W^T \neq 0}\|_1 WWT=01 是非零权重的 L1 范数。

  二值量化和三值量化可以显著减小模型的尺寸和加速推理速度,但通常也会导致模型精度的下降。因此,这些方法常用于对精度要求不太高的应用场景或需要在低计算资源环境下运行的场景中。

2.5 混合精度量化

  混合精度量化是结合多种量化方法的技术,例如在不同的层或不同的模型部分使用不同精度的量化方法。例如,某些计算密集型的层可能使用 INT8 量化,而其他层使用 INT16 或 FP16 量化,以达到精度和效率之间的平衡。

三、 训练后量化 (Post-Training Quantization)

  训练后量化是指在训练完成后,对模型进行量化,因此也叫做离线量化,可分为对称量化和非对称量化(量化零点是否为 0)。根据量化粒度区分,训练后量化又分为逐张量量化和逐通道量化以及组量化。

  量化误差来自两方面,一个是clip操作,一个是round操作。如何选取量化时所用参数(如scaling factor,zero point)可以尽可能地减少对准确率的影响呢?这也是我们需要关注的地方。

3.1 量化粒度

量化通常会导致模型精度下降,不同的量化粒度下降程度不一样。

3.1.1 逐张量量化(Per-Tensor Quantization)

  逐张量量化(Per-Tensor Quantization)是指对整个张量使用相同的量化参数(缩放因子和零点)来进行量化。但是在张量之间应用相同的参数会导致精度下降,因为张量内参数值的范围可能会有所不同。如下图的红框所示,3个channel共享一个量化参数。但是我们可以看到不同channel的数据范围是不同的。因此当 Layer-wise 量化效果不好时,需要对每个channel进行分别量化。
在这里插入图片描述

逐张量量化

3.1.2 逐通道量化(Per Channel Quantization)

  逐通道量化是将数据按照通道维度进行拆分,分别对每一通道的数据进行量化。由于现阶段模型越来越大,每个通道的参数也原来越多,参数的数值范围也越来越大,因此我们需要更细粒度的量化方式。逐通道量化可以更准确地捕获不同通道中的变化,比起逐张量量化,精度损失更少,但需要更多的存储空间(存储多个r和S)。

  下图演示了逐张量量化和逐通道量化过程。最终对比原始矩阵 W 与量化之后重建矩阵之间的误差 ∥ W − W ′ ∥ F = ∑ i , j ( W i , j − W i , j ′ ) 2 \|W - W'\|_F = \sqrt{\sum_{i,j} (W_{i,j} - W'_{i,j})^2} WWF=i,j(Wi,jWi,j)2 可以看出,逐通道量化量化损失更小,但需要的存储空间更大。

在这里插入图片描述

逐张量量化与逐通道量化对比

3.1.3 组量化(Group Quantization)

  与逐通道量化或逐张量量化不同,组量化将一个通道内的数据拆分为多个较小的向量组,然后进行两级量化:

  • 先对较小粒度的组(如向量)使用较简单的整数缩放因子 S q S_q Sq进行缩放:
    r 向量 = S q ⋅ ( q − Z ) r_{\text{向量}} = S_q \cdot (q - Z) r向量=Sq(qZ)

  • 再对较大粒度的整体(如通道)使用更复杂的浮点数缩放因子 γ \gamma γ进行缩放:
    r = γ ⋅ r 向量 = γ ⋅ S q ⋅ ( q − Z ) r = \gamma \cdot r_{\text{向量}} = \gamma \cdot S_q \cdot (q - Z) r=γr向量=γSq(qZ)

  第一步目的是在较细粒度的范围内进行初步量化,适应不同的向量分布特性,减少量化误差;第二步将先前量化的结果进行全局调整。这种方法通过结合不同粒度的缩放因子,实现了精度和硬件效率的平衡。

  存储开销分析:假设我们使用 4-bit 量化,即每个元素都被量化为一个 4-bit 整数。组量化中,为了更高的精度,每 16 个元素(即一个组)共享一个 4-bit 的向量缩放因子 S q S_q Sq。这个缩放因子需要额外存储,但它是每个组共享的,因此只需为每组存储一次。下面计算有效位宽(元素本身的位宽和每个向量缩放因子带来的额外存储):
4 bits + 4 bits 16 个元素 = 4.25 bits 4 \text{ bits} + \frac{4 \text{ bits}}{16 \text{ 个元素}} = 4.25 \text{ bits} 4 bits+16 个元素4 bits=4.25 bits
通过适当分配粒度和缩放因子,存储开销得到了优化。

  Microscaling(MX) 是一种具体实现两级缩放因子的方案,从微软的浮点(MSFP)数据类型演化而来。MX 系列(如 MX4、MX6、MX9)在不同的数据类型、缩放因子设计和组大小上有所区别,以便根据不同应用优化性能。每个向量的浮点比例因子被分成整数和浮点逐通道的分量,进一步提高量化的灵活性和精度。

3.2 动态量化参数的计算 ( Cliping )

  上面介绍的都是静态量化,即使用固定的量化参数进行量化。动态量化参数是指在量化过程中,随着输入数据或模型运行情况的变化而实时更新的量化参数(例如,量化的范围或缩放因子)。这种动态更新使得量化更能适应不同的数据分布,减少精度损失,提高模型在推理过程中的性能。

  动态量化一般应用于已经训练好的模型,用来优化推理过程中的速度和内存占用。它通过将模型的权重和激活值(在推理时)转换为低精度的整数(如8位),减少了存储需求和计算负担,从而提高推理效率,特别适用于推理设备(如移动设备、嵌入式系统)对存储和计算资源有限制的场景,也适合在生产环境中快速部署模型进行推理。

3.2.1 指数移动平均(EMA)

  指数移动平均(Exponential Moving Average, EMA)是一种常用的统计方法,用于计算数据的指数移动平均值。

  EMA 收集了训练过程中激活函数的取值范围 r m i n r_{min} rmin r m a x r_{max} rmax,然后在每个 epoch 对这些取值范围进行平滑处理。EMA的计算公式如下:
r m i n , m a x t + 1 = α r m i n , m a x t + ( 1 − α ) r m i n , m a x t + 1 r^{t+1}_{min,max} = \alpha r^{t}_{min,max} + (1-\alpha) r^{t+1}_{min,max} rmin,maxt+1=αrmin,maxt+(1α)rmin,maxt+1

其中, r m i n , m a x t r^{t}_{min,max} rmin,maxt 表示第 t t t 步的取值范围, α \alpha α 表示平滑系数。

3.2.2 Min-Max

  Min-Max 是一种常用的校准方法,通过在训练好的 fp32 模型上跑少量的校准数据。统计校准数据的 r m i n , m a x r_{min,max} rmin,max 并取平均值作为量化参数。

3.2.3 KL 量化

  KL 量化是用 KL 散度来衡量数据和量化后的数据之间的相似性;这种方法不是直接将 [ m i n , m a x ] [min, max] [min,max]v映射到 [ − 127 , 128 ] [-127,128] [127,128],而是去寻找一个阈值 ∣ T ∣ < m a x ( ∣ m a x ∣ , ∣ m i n ∣ ) |T| < max(|max|, |min|) T<max(max,min) ,将 [ − T , T ] [-T, T] [T,T] 映射到 [ − 127 , 128 ] [-127, 128] [127,128] 。并假设只要阈值选取得当,使得两个数据之间的分布相似,就不会对精度损失造成影响。

D K L ( P ∣ ∣ Q ) = ∑ i = 1 n P ( x i ) log ⁡ P ( x i ) Q ( x i ) D_{KL}(P||Q) = \sum_{i=1}^nP(x_i)\log\frac{P(x_i)}{Q(x_i)} DKL(P∣∣Q)=i=1nP(xi)logQ(xi)P(xi)

3.2.4 均方误差(MSE)

  均方误差量化是指通过最小化输入数据 X X X 和量化后的数据 Q ( X ) Q(X) Q(X) 之间的均方误差,计算得到最合适的量化参数。

m i n ∣ r ∣ m a x E ∣ ( X − Q ( X ) ) 2 ∣ min_{|r|_{max}}E|(X-Q(X))^2| minrmaxE(XQ(X))2

  通过动态调整 | r | m a x |r|_{max} rmax 来最小化均方误差。

  • EMA方法能适应变化的数据分布,适用于实时或在线推理任务。
  • Min-Max方法通过简单的最大最小值计算,速度快,适合较为稳定的数据分布,常见于硬件加速支持的低精度推理中,如嵌入式设备。
  • KL量化侧重于保留数据的原始分布信息,适用于需要对数据分布高度保真、精度要求高的任务,比如文本生成或图像生成等任务中。
  • MSE量化则关注量化后的误差最小化,适合科学计算等对误差控制严格的应用。

3.3 Rounding

  在量化过程中,通常会将浮点数值(如32位浮点数)转化为整数(如8位整数),然而,舍入过程会引入误差,影响模型的预测准确性。传统的舍入方法(如向最近整数舍入)并不会考虑多个权重之间的舍入影响,可能导致某些权重在量化后与真实值的差异较大,影响最终输出的精度。

在这里插入图片描述
比如上图中,对每个权重的最近舍入不一定是对整个张量的最好舍入。

  AdaRound 是一种改进的舍入策略,会根据量化误差的积累情况来动态调整舍入策略,从而减少累计的误差。简单来说就是在舍入时引入了一个 偏差项 σ \sigma σ ,会根据之前的舍入结果来调整下一次舍入的方向,而不是单纯地对每个权重进行四舍五入,用数学表示就是:
W ^ = ⌊ ⌊ W ⌋ + σ ⌉ \hat{W} = \lfloor \lfloor W \rfloor + \sigma \rceil W^=⌊⌊W+σ

其中:

  • W W W 是原始的浮动权重。
  • ⌊ W ⌋ \lfloor W \rfloor W 是权重 W W W 向下取整后的值。
  • σ ∈ [ 0 , 1 ] \sigma \in [0, 1] σ[0,1] 是一个在 [ 0 , 1 ] [0, 1] [0,1] 区间内的偏差项,用来决定是向上舍入还是向下舍入。具体而言, σ \sigma σ 会影响最终舍入的方向,优化量化后误差的大小。

四、 量化感知训练(Quantization-Aware Training)

论文《Quantization and Training of Neural Networks for Efficient Integer-Arithmetic-Only Inference》

  传统的量化模型做法,是先用浮点数训练网络,然后进行量化。这种方法对于大规模模型有效,但对于小型模型常常会导致显著的精度下降。常见的失败模式包括:

  • 权重范围差异过大:某些输出通道的权重范围差异超过100倍,导致量化时,范围较小的通道会有较大的相对误差。
  • 异常权重值:某些权重值异常,影响量化后的精度。

  量化感知训练(QAT)通过在训练的前向传播中模拟量化效果(对模型的权重和激活值进行量化),来让模型更适应低精度计算,避免直接的后期量化带来的问题,从而提高了最终量化后的模型精度。

  量化操作(特别是低精度量化)会导致数据精度损失,这会影响模型的性能。如果在训练时不考虑这些量化带来的误差,训练出的模型可能在量化后表现不佳。因此,模拟量化的目的就是让模型在训练过程中感知到量化的影响,从而让模型调整参数,使得它可以更好地适应低精度计算环境。这样,训练得到的模型会在量化后保留较好的精度。

4.1 前向传播

  QAT训练时所有计算是在高精度下完成的(FP32),但是在前向传播过程中加入了伪量化节点(quantization+dequantization),用于模拟模型量化时引起的误差。以INT8量化为例,QAT处理流程为:

  1. 在数据集上以FP32精度进行模型训练,得到训练好的baseline模型;

  2. 在baseline模型中插入伪量化节点(fake quantization),以便在训练期间模拟推理时的量化效果,其前向过程为:

    • 输入量化 L a y e r N − 1 Layer_{N-1} LayerN1 层的输出 Q ( X ) Q(X) Q(X) 作为下一层( L a y e r N Layer_{N} LayerN)的输入。 Q ( X ) Q(X) Q(X) 表示已经过量化和反量化的输入数据。
    • 权重量化:在与输入进行卷积前,先对权重进行量化。当前层的权重 W W W 会通过量化反量化操作得到 Q ( W ) Q(W) Q(W),然后使用 Q ( W ) Q(W) Q(W) 计算得到输出 Y Y Y
    • 输出量化/激活值量化:在激活函数应用后,或者在像ResNet这样的跳跃连接中,量化激活值(输出 Y Y Y 经过量化反量化,得到 Q ( Y ) Q(Y) Q(Y)),作为下一层 L a y e r N + 1 Layer_{N+1} LayerN+1 的输入。
      在这里插入图片描述
  3. 在模拟量化模式下训练直到收敛(即微调量化模型)。这一过程会考虑量化误差的影响,从而让模型适应低位宽(例如8位)表示。

  4. 训练完成后,模型的权重将被真正量化,并且在实际硬件上进行推理。

Tips:训练过程中,偏置不需要进行量化,它的量化参数是根据权重和激活的量化参数推断出来的。

  论文中是在weights输入conv之前(weight quantization)以及activation之后(activation quantizaion)插入了伪量化节点,伪量化节点插入位置就是需要进行量化操作的位置。在这里插入图片描述
量化过程通过以下函数实现:

  1. 将输入值 r 限制在区间 [a, b] 内:
    clamp ( r ; a , b ) = min ⁡ ( max ⁡ ( r , a ) , b ) \text{clamp}(r; a, b) = \min(\max(r, a), b) clamp(r;a,b)=min(max(r,a),b)
  2. 计算量化步长,即在给定的量化范围 [a, b] 和量化级数 n 下(8bit量化时,n=256),量化的间隔:
    s ( a , b , n ) = b − a n − 1 s(a, b, n) = \frac{b - a}{n - 1} s(a,b,n)=n1ba
  3. 将实数值 r 量化到 [a, b] 范围内的离散级别。首先使用 clamp 限制 r[a, b] 范围内,然后根据步长 s(a, b, n) 将其映射到 n 个量化级别中的一个,并进行四舍五入。其公式为:
    q ( r ; a , b , n ) = ( ⌊ clamp ( r ; a , b ) − a s ( a , b , n ) ⌉ ) ⋅ s ( a , b , n ) + a q(r; a, b, n) = \left( \left\lfloor \frac{\text{clamp}(r; a, b) - a}{s(a, b, n)} \right\rceil \right) \cdot s(a, b, n) + a q(r;a,b,n)=(s(a,b,n)clamp(r;a,b)a)s(a,b,n)+a

其中, ⌊ ⋅ ⌉ \left\lfloor \cdot \right\rceil 表示四舍五入到最近的整数。

  • 对于权重量化,通常通过取权重的最小值和最大值来确定量化范围,并对这些范围进行微调,确保零点值能够精确表示。
  • 对于激活量化,通过在训练过程中使用指数加权平均(EMA)平滑激活范围。另外为了避免训练初期激活量化范围不稳定,通常在训练的前50,000到200万步内禁用激活量化。

  量化过程中的学习会调整量化的边界[a, b],确保在量化后能精确表示零值。最终,学习到的量化参数会映射到公式中的比例因子S和零点Z,公式为:

S = s ( a , b , n ) , Z = z ( a , b , n ) S = s(a, b, n),Z = z(a, b, n) S=s(a,b,n),Z=z(a,b,n)

4.2 反向传播

  量化感知训练的损失函数与普通训练的损失函数类似,但是量化后的权重是离散值。如图所示为 W W W Q ( W ) Q(W) Q(W) 的关系。

在这里插入图片描述

  由于 Q ( W ) Q(W) Q(W)是离散值,故:
∂ Q ( W ) ∂ W = 0 \frac{\partial Q(\mathbf{W})}{\partial \mathbf{W}}=0 WQ(W)=0

  梯度计算公式为:

g W = ∂ L ∂ W = ∂ L ∂ Q ( W ) ⋅ ∂ Q ( W ) ∂ W = 0 g_{\mathbf{W}}=\frac{\partial L}{\partial \mathbf{W}}=\frac{\partial L}{\partial Q(\mathbf{W})} \cdot \frac{\partial Q(\mathbf{W})}{\partial \mathbf{W}}=0 gW=WL=Q(W)LWQ(W)=0

  如果按照上述式子进行梯度计算,这样的话梯度就永远为 0,无法进行梯度更新。因此人们提出了一个修正的方式,被称为直通估计器(Straight-Through Estimator,STE)。在STE中,在STE中,假设量化和反量化操作不会改变权重,此时 W = Q ( W ) W = Q(W) W=Q(W) ∂ Q ( W ) ∂ W = 1 \frac{\partial{Q(W)}}{\partial{W}}=1 WQ(W)=1 ,梯度公式可以转换为如下式子:

g W = ∂ L ∂ W = ∂ L ∂ Q ( W ) g_{\mathbf{W}}=\frac{\partial L}{\partial \mathbf{W}}=\frac{\partial L}{\partial Q(\mathbf{W})} gW=WL=Q(W)L

  这样,量化感知训练就能够顺利地进行反向传播,进行权重更新。

相关文章:

datawhale 2411组队学习:模型压缩4 模型量化理论(数据类型、int8量化方法、PTQ和QWT)

文章目录 一、数据类型1.1 整型1.2 定点数1.3 浮点数1.3.1 正规浮点数&#xff08;fp32&#xff09;1.3.2 非正规浮点数&#xff08;fp32&#xff09;1.3.3 其它数据类型1.3.4 浮点数误差1.3.5 浮点数导致的模型训练问题 二、量化基本方法2.1 int8量化2.1.1 k-means 量化2.1.2 …...

数据分析-48-时间序列变点检测之在线实时数据的CPD

文章目录 1 时间序列结构1.1 变化点的定义1.2 结构变化的类型1.2.1 水平变化1.2.2 方差变化1.3 变点检测1.3.1 离线数据检测方法1.3.2 实时数据检测方法2 模拟数据2.1 模拟恒定方差数据2.2 模拟变化方差数据3 实时数据CPD3.1 SDAR学习算法3.2 Changefinder模块3.3 恒定方差CPD3…...

POD-Transformer多变量回归预测(Matlab)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现POD-Transformer多变量回归预测&#xff0c;本征正交分解数据降维融合Transformer多变量回归预测&#xff0c;使用SVD进行POD分解&#xff08;本征正交分解&#xff09;&#xff1b; 2.运行环境Matlab20…...

Hadoop生态圈框架部署(七)- MySQL安装与配置教程

文章目录 前言一、MySQL安装与配置&#xff08;手动部署&#xff09;1. 下载MySQL2. 上传安装包3. 解压HBase安装包4. 配置4.1 配置 MySQL 的主配置文件 my.cnf4.2 配置 MySQL 服务的脚本 5. 初始化MySQL数据库6. 创建快捷方式7. 启动MySQL服务8. 修改MySQL登录密码8.1 使用临时…...

视频直播5G CPE解决方案:ZX7981PG/ZX7981PMWIFI6网络覆盖

方案背景 视频直播蓬勃发展的当下&#xff0c;传统直播网络联网方式的局限性越来越明显。目前传统直播的局限性主要集中在以下几个方面&#xff1a; 传统直播间网络架构条件有限&#xff0c;可连接WIFI数量少&#xff0c;多终端同时直播难以维持&#xff1b;目前4G网络带宽有限…...

技术周刊 |Google 2024 年首届 Web AI 峰会回顾

大家好&#xff0c;我是童欧巴。见字如面&#xff0c;万事胜意&#xff0c;欢迎来到第 134 期周刊。 大厨推荐 Google 2024 年首届 Web AI 峰会回顾 不仅包括来自谷歌团队的演讲者&#xff0c;如 Chrome 和 MediaPipe&#xff0c;还包括第三方代表&#xff0c;如英特尔、Hug…...

web——upload-labs——第十二关——%00截断

查看源码 分析源码我们可以知道&#xff0c;这里是基于白名单过滤&#xff0c;只允许上传jpg,png,gif&#xff0c;但是这里注意第八行&#xff0c;上传路径是可以控制的&#xff0c;所以可以利用%00截断&#xff0c;来达到上传木马的目的。这里要注意一下&#xff0c;%00截断想…...

ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值。ojdbc8版本23.2.0.0驱动BUG【已解决】

问题描述 JDK8使用ojdbc8驱动操作oracle11g数据库&#xff0c;使用JDBC复用 PreparedStatement 对象执行Insert操作时&#xff0c;报错java.sql.SQLException: ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值。&#xff0c;经测试发现&#xff0c;是预编译对象某个占位符号被赋…...

win10 自带 directx 修复工具怎么用?最新 directx 修复工具使用方法介绍

DirectX 是一组用于处理多媒体&#xff0c;特别是游戏和图形相关任务的技术和接口。当 DirectX 出现问题&#xff0c;可能会导致游戏运行不畅、图像显示异常、声音故障等。 系统自带的 directx 修复工具能够检测 DirectX 的组件是否完整、版本是否正确、配置是否合理&#xff…...

报错java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not ...解决方法

在运行项目时出现java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field com.sun.tools.javac.tree.JCTree qualidzz这样的报错 解决方法 1.第一步&#xff1a;在pom文件中将lombok的版本改成最新的 此时1.18.34是新…...

前端三大件之CSS

引言 CSS&#xff08;层叠样式表&#xff0c;Cascading Style Sheets&#xff09;是一种用于描述网页文档外观和格式的样式表语言。它主要用于控制网页的布局、颜色、字体、间距等视觉效果&#xff0c;使开发者能够将内容与设计分离。 一&#xff0c;CSS的基本概念 选择器&…...

C语言 | 指针 | 野指针 | 数组指针 | 指针数组 | 二级指针 | 函数指针 | 指针函数

文章目录 1.指针的定义2.指针的加减运算3.野指针4.指针 & 数组 & 传参 & 字符数组5.数组指针 & 指针数组6.二级指针7.指针函数 & 函数指针 & 回调函数8.函数指针数组 & 指向函数指针数组的指针 1.指针的定义 指针是内存中一个最小单元的编号&…...

mysql 的乐观锁和 mvcc 是一回事吗

MySQL 的乐观锁和 MVCC&#xff08;多版本并发控制&#xff09;是两个不同的概念&#xff0c;尽管它们都涉及到并发控制和数据的一致性&#xff0c;但在设计目的和实现方式上存在本质区别。 1. 乐观锁 概念 乐观锁是一种用于解决并发更新冲突的控制机制。它假设数据在大部分情况…...

redis的击穿和雪崩

Redis 是一个高性能的键值存储数据库&#xff0c;广泛用于缓存、会话管理等场景。然而&#xff0c;Redis 在高并发场景下可能会遇到一些问题&#xff0c;比如“击穿”和“雪崩”。下面详细解释这两个概念&#xff1a; 击穿&#xff08;Hotspot&#xff09; 击穿是指某个热点数…...

java中创建多线程的4种方式

目录 一、继承 Thread 类创建线程 步骤 示例代码 原理 二、实现 Runnable 接口创建线程 步骤 示例代码 原理 三、实现 Callable 接口创建线程 步骤 示例代码 原理 与Runnable接口相比的不同之处 四、使用线程池创建线程 步骤 示例代码&#xff08;使用 Executo…...

MATLAB深度学习(二)——如何训练一个卷积神经网路

2.1 基本概念 从数学的角度看&#xff0c;机器学习的目标是建立输入和输出的函数关系&#xff0c;相当于 y F&#xff08;x&#xff09;的过程。F&#xff08;x&#xff09;就是我们所说的模型&#xff0c;对于使用者来说&#xff0c;这个模型就是一个黑箱&#xff0c;我们不知…...

删除k8s 或者docker运行失败的脚本

vi delete_exited_containers.sh#!/bin/bash# 列出所有停止的容器并存储到数组 list_exited_containers() {echo -e "\nStopped containers:"containers()# 获取停止的容器信息并存入数组while IFS read -r line; docontainers("$line")done < <(do…...

重置docker版本的octoprint管理员账号密码

我的情况是octoprint安装在HiNAS系统的机顶盒上&#xff0c;只有一个账号&#xff0c;但是忘记了用户名和密码。有两个选择&#xff1a; 可以试试先找回用户名&#xff0c;然后尝试你的常用密码。直接重置所有账号。 1.找回用户名&#xff1a; 使用使用 docker exec -it <…...

prometheus监控数据远程写入Kafka集群

文章目录 前言一、环境简介1.1 环境简介1.2 部署清单1.3 组件版本 二、部署步骤2.1 prometheus部署2.2 kafka集群部署2.3 prometheus-kafka-adapter部署 三、数据验证四、总结 前言 根据项目要求&#xff0c;需将prometheus监控数据存储到kafka中。前面为了图方便就搭建了单机…...

Excel使用-弹窗“此工作簿包含到一个或多个可能不安全的外部源的链接”的发生与处理

文章目录 前言一、探讨问题发生原因1.引入外部公式2.引入外部数据验证二、问题现象排查及解决1.排查公式2.排查数据验证3.特殊处理方式总结前言 作为一种常用的办公软件,Excel被大家所熟知。尽管使用了多年,有时候在使用Excel时候也会发生一些不太常见的现象,需要用心核查下…...

C++小白实习日记——Day 2 TSCNS怎么读取当前时间

和老板问了一下&#xff0c;今天就可以自己上手了&#xff1a; 用TSCNS写了一个cpp,运行出来老板说让我去看看另一个项目是怎么做的 用TSCNS和std库获取当前时间 #include <iostream> #include <iomanip> #include "tscns.h"using namespace std;TSCN…...

【Pythonr入门第二讲】你好,世界

"Hello, World!" 是一种传统的编程入门示例&#xff0c;通常是程序员学习一门新编程语言时编写的第一个程序。这个程序的目标非常简单&#xff1a;在屏幕上输出 "Hello, World!" 这个字符串。尽管它非常简单&#xff0c;但具有重要的象征意义和实际价值。 …...

3D Streaming 在线互动展示系统:NVIDIA RTX 4090 加速实时渲染行业数字化转型

随着科技的飞速发展&#xff0c;实时渲染正逐步成为游戏与实时交互领域的重要驱动力。与离线渲染不同&#xff0c;实时渲染需要极高的计算性能&#xff0c;对硬件设备尤其是GPU的性能要求极高。随着 RTX 4090 显卡的问世&#xff0c;其强大的算力和创新技术&#xff0c;为实时渲…...

Oracle 单机及 RAC 环境 db_files 参数修改

Oracle 数据库中 DB_FILES 定义了数据库中数据文件的个数&#xff0c;默认值为200&#xff0c;如果创建数据库文件时超过DB_FILES 定义的值就会报 ORA-00059 错误。 下面分别演示单机及 RAC 环境下修改 db_files 参数的操作步骤。 一、单机环境 1.查询当前参数值 SQL> sh…...

消息中间件分类

消息中间件&#xff08;Message Middleware&#xff09;是一种在分布式系统中实现跨平台、跨应用通信的软件架构。它基于消息传递机制&#xff0c;允许不同系统、不同编程语言的应用之间进行异步通信。 常见的消息中间件类型包括&#xff1a; 1. JMS&#xff08;Java Message S…...

讯飞、阿里云、腾讯云:Android 语音合成服务对比选择

在 移动端 接入语音合成方面&#xff0c;讯飞和腾讯云等都是优秀的选择&#xff0c;但各有其特点和优势。咱们的需求是需要支持普通话/英语/法语三种语言&#xff0c;以下是对各个平台的详细比较&#xff1a; 一、讯飞语音合成介绍 与语音听写相反&#xff0c;语音合成是将一段…...

SpringBoot开发——整合AJ-Captcha实现安全高效的滑动验证码

文章目录 一、什么是AJ-Captcha二、项目配置1、Maven依赖配置2、滑动验证码的基本原理3、 后端实现3.1 生成滑动验证码图片代码解释3.2 校验滑块位置代码解释4、前端部分代码解释5、Redis 缓存滑动验证码信息5.1 Redis配置5.2使用Redis缓存验证码数据5.3 校验时从Redis获取总结…...

Spring Security 核心组件

Spring Security 是一个功能全面的安全框架&#xff0c;用于处理基于 Spring 应用程序的身份验证和授权。 它提供了开箱即用的支持&#xff0c;采用行业标准的做法和机制来保护你的应用。 无论你是开发简单的 Web 应用还是复杂的微服务架构&#xff0c;理解 Spring Security …...

聚焦 AUTO TECH 2025华南展:探索新能源汽车发展新趋势

随着“新四化”浪潮的推进&#xff0c;汽车行业正经历前所未有的变革。中国新能源汽车正逐渐走向世界。国内汽车制造巨头如比亚迪、吉利、奇瑞、长安等&#xff0c;已经将出口提升至核心战略地位。中国新能源汽车的发展&#xff0c;不仅推动了全球汽车产业的电动化转型&#xf…...

Python-简单病毒程序合集(一)

前言&#xff1a;简单又有趣的Python恶搞代码&#xff0c;往往能给我们枯燥无味的生活带来一点乐趣&#xff0c;激发我们对编程的最原始的热爱。那么话不多说&#xff0c;我们直接开始今天的编程之路。 编程思路&#xff1a;本次我们将会用到os,paltform,threading,ctypes,sys,…...

沈阳室内设计公司/seo包年优化平台

一.数据库的特点:a.实现数据共享 b.采用特定的数据类型. c.具有较高的数据独立性 d.具有统一的数据控制功能.二.mysql的优势:a.速度:运行速度快b.价格:mysql对多数个人来说是面费的.c.容易使用:与其他大型数据库的设置和管理相比,其复杂程度较低,易于学习.d.可移植性:能够工作…...

杭州网站建设找思创网络/百度收录入口提交查询

为什么80%的码农都做不了架构师&#xff1f;>>> 一、SocketChannel和KafkaChannel有什么区别&#xff1f; 上篇文章说道KafkaSelector在创建一个连接的时候和普通的nioSelector并没有什么不同&#xff0c;它是基于nioSelector的封装。我们知道创建连接的一系列操作…...

改善网站建设/如何建网站不花钱

我有一长串用户输入,并希望将它们存储在一个对象中,而不是用HTML拼写出来.我想将这些值绑定到另一个存储用户/客户数据的对象.由于其简单性和功能性,优选使用ngModel.谁知道我怎么能做到这一点&#xff1f;以下示例(不工作).Component({template: NgModel Example{{ input.labe…...

温岭哪里有做网站的/网站内部seo

SUSE的zypper本地源配置起来跟yum的配置很相似&#xff0c;它们的配置文件有很多相似之处。不过&#xff0c;个人觉得zypper这个工具稍微强大些。在SUSE下&#xff0c;可以通过一条zypper的命令&#xff0c;即可完成zypper源的配置。一、zypper源配置我这里内部搭建了一台源服务…...

wordpress主题安装后图片找不到/脚本外链平台

应用&#xff1a;可以将相同的画面数据高速地(大约每64KB数据为两秒)传送到多台F940GOT中。当使用编程软件GT Designer2的情况下&#xff0c;可以以Intel的HEX格式创建文件FX3U-4AD-ADP使用案例。使用通信软件将数据传送到ROM写入器中。适用系列&#xff1a;F940GOT。输入输出点…...

关键词网站建设优化/百度搜索引擎营销如何实现

&#xff08;PS&#xff1a;个人课下整理的操作系统笔记&#xff0c;OneNote直接拷贝过来的&#xff0c;图片看不了就将就一下&#xff0c;配合张伟老师的PPT看效果更好&#xff09; 本章讨论一种机制&#xff0c;该机制能确保共享同一逻辑地址空间的协作进程能有序执行并维护…...