AutoAWQ源码

 获得设置的参数

调用的是awq/models/_config.py中的from_dict类方法

哪些模块不需要转化,这里没有设置就是空

调用AWQ量化

其中

calib_data 数据集
split 要进行拆分的数据集(train)
text_column 文本列名
duo_scaling Whether to scale using both w/x or just x. 
modules_to_not_convert 默认设置None
export_compatible 这个论点通过仅应用缩放因子而不将数值量化到FP16,从而避免了真正的量化
apply_clip 在量化过程中是否对模型clip。有些模型在将此设置为 False 时可能表现更好。默认True
n_parallel_calib_samples 模型并行运行样本数,默认None,意思为全部运行
max_calib_samples 采样的样本数
max_calib_seq_len 被采样的样本最大token数
max_chunk_memory 损失计算和每通道均值已经优化为分块计算。调整此参数可以增加或减少这些计算的内存使用量。默认值为 1GB(1024 * 1024 * 1024)

AWQ量化类的属性

利用数据集获得模型的模型、模块参数和输入

qwen2.5-3b有35个模块

获得数据,共获取max_calib_samples个数据,然后把这些样本的token放在一起除以max_chunk_memory,获得量化时使用的数据

利用模型的第一个模块,获得隐藏层状态和模型参数

隐藏层状态

模型参数

样本没用了 删除掉样本

clear_memory()清除模型的缓存,最后返回获得的模型模块、模块参数、输入


 调用AWQ 

遍历模型的模块,再把数据放到GPU上

提取线性层

 有7个线性层

筛选出不想提取的线性层 

这里没有不想提取的还是7个

获得特征

cache_input_hook 这是一个函数,用于处理模型的输入。在这里,它被注册为 hook,用来处理每个线性层的输入特征。
为每个 named_linears 中的线性层注册一个 forward hook。当这些层的前向传播被调用时,会触发 cache_input_hook,并将层的名称和输入特征传递给它。同时,返回的 hook 句柄被存储,以便未来可以移除这些 hooks。 

清理 kwargs,以防我们使用的 transformers 版本包含模块无法处理的 kwargs。
这对于信任远程代码的模型很有用。获得模型参数,然后把handles删除

前向,这里的并行设置的None就一次性处理所有数据

返回特征

 然后计算应用缩放列表

 

输入包括模型的第i个模块,输入特征,模型参数

字典记录:

prev_op表示当前层的前置操作(LayerNorm)
自注意力机制中的查询、键和值投影层
输入数据与这些层的映射关系
当前模块(自注意力)的引用,供后续检查和调试
附加参数(kwargs),通常用于配置或控制行为

inp=input_feat["self_attn.q_proj"]
inp 表示输入特征,这里从 input_feat 字典中提取了与查询投影(q_proj)相关的输入数据。

module2inspect=module.self_attn
这个键将当前的自注意力模块(module.self_attn)保存到 module2inspect。通常这样做是为了以后调试或可视化时检查这个自注意力模块的细节。

MLP(多层感知机)的第一部分,包含门控投影和上升投影。
MLP(多层感知机)的第二部分,包含下降投影层。
这些层在模型中形成了一个顺序的处理管道,每一层的输出都会作为下一层的输入进行进一步的处理。

这里v_proj和o_proj的shape不同,if中的内容不会运行

 

从刚才保存的模块中,依次开始搜索缩放比例

 第一步:对模型中的多个层权重进行处理,具体步骤包括将权重归一化(按量化组进行),计算每个输出通道的归一化权重的平均值

把q,k,v三个线性层特征的各个维度合在一起,然后按组进行划分,缩放到(0,1)之间

按权重维度组成的组将权重缩放到(0,1)之间

 对每个输出通道求平均值

对q,k,v三个线性层的每个输出通道求平均值

 第二步:

num_elements:样本数
num_channels:通道数
element_size_bytes:fp32:4bit

计算最大内存一次能加载多少样本,能加载全部就一次加载全部,不能就截断用for循环加载

初始化保存的tensor

所有q线性层输入数据,对每条数据求平均值 

按通道,对所有输入数据求平均值(fp32)

第三步:计算模块的输出

inp是计算q线性层的输入,计算注意力机制时计算q,k,v的输入都是一个

module2inspect: module.self_attn

模型参数还是之前获得几个 删除了attention_mask和use_cache

获得模块的输出

第四步:计算loss

输入包括

inp:q_porj层的输入数据(即q,k,v三个线性层的输入数据)
w_mean:按权重维度组成的组将权重缩放到(0,1)之间,对q,k,v三个线性层的每个输出通道求平均值
x_mean:按通道,对所有输入数据求平均值(fp32)
layers:q,k,v三个线性层
fp16_output:模块的fp16输出
module_kwargs:模型参数

G哥对损失函数的解释

先把q,k,v的参数放在cpu上保存下来,最后算WX的时候使用

下面这段代码G哥给的解释是这样的


 

将inf和Nan替换为1

将q,k,v三层的权重乘以scale_x进行缩放


mul_ 是一个原地操作,意味着它直接修改 fc.weight 的值,不会返回新的张量。
fc.weight: 形状为 (out_features, in_features) 的二维张量,表示线性层的权重矩阵,其中 out_features 是输出特征的数量,in_features 是输入特征的数量。
scales_view: 这是一个与 fc.weight 中的元素逐一对应的张量,是一个缩放因子数组。形状是广播(broadcast)的,以适应 fc.weight 的形状。
例如
fc.weight 的形状为 (m, n),表示有 m 行和 n 列。
scales_view 的形状是 m(广播的情况)。

伪量化:
fc.weight.data = self.pseudo_quantize_tensor(fc.weight.data)[0] / scales_view 通过调用 pseudo_quantize_tensor 方法对 fc.weight.data(权重矩阵)进行量化处理。
量化后的权重再通过除以 scales_view 恢复到原始的尺度(因为上一步中已经做了缩放)。

保留w原来的形状,然后将W按组进行变形


当计算零点偏移量z时:
【量化】量化原理浅析_模型量化原理-CSDN博客

求每个分组的最大值和最小值
计算每组最大值和最小值之间的范围,除以(2^量化位数)-1获得量化scale(这里是针对W求的scale和前面x求的scale_x不是一个)

比如max_int=15,max_val=30.0,min_val=15.0

(30.0-15.0)/15=1

偏移量(或零点/最小值对应的量化数值),又被称为 Zero Point

比如又有一个例子

(45.0-30.0)/15=1

但是两个缩放比例相同,零点值不同:

15.0/15=1

30.0/15=2

量化与反量化的过程

w = {Q[(w/s)+z]-z}*s

 

不计算零点偏移量的时候:

w = Q[(w/s)]*s

最后返回 w,scales,zeros

量化后并反量化的权重再除去针对x求得的缩放比列scale_x,对权重进行还原

计算量化后并反量化的权重经过模块后获得的输出

利用fp16输出和反量化输出共同计算loss

计算MSE然后除以元素个数 

记录loss,加载原来的参数继续计算 下一n_grid,针对x的scale,量化并反量化的loss

最后把最小loss针对x的scale返回

把当前模块的相关信息返回:进入模块的操作名称,模块中的层名,最好的scale_x

 再对后面的mlp.gate_proj计算最好的scale_x

以及mlp.sown_proj计算最好的scale_x

 scale_list包括进入模块之前的操作名,模块中的层名,以及模块最好的scale_x

 

对所在的大模块和scales_list应用缩放

对scales_list进行遍历,放到GPU上 

然后判定属于哪种类型

1.前置操作是线性层且包装层的类型为list,第一层神经网络为线性层,使用scale_fc_fcs
2.前置操作是线性层且整个包装层只有一层,使用scale_fc_fc
3.前置操作在允许的归一化层的范围或RMSNORM,使用scale_ln_fc
4.前置操作在允许的激活层的范围,使用scale_gelu_fc

然后将输入除以scale_x

这几个函数都是一个意思,前置操作除以scale_x,剩下的层乘scale_x,有偏置的话偏置也除,

 scale_fc_fcs

 

scale_fc_fc

scale_ln_fc

scale_gelu_fc 

开始计算截断的列表,输入包括遍历第i个模块,和模块中的各个层,以及q线性层的输入

q,k不取,计算最好的阶段输入是权重还有输入数据,q,k,v共用一个输入数据

首先进行维度的变换

v的输入层是2048维,输出是256维

inp的shape

是[B,最大token数,维度]

n_grid默认为20

max_shrink默认为0.5

n_sample_tokem每条样本的最大token数

 [B,最大token数,维度]

然后输入数据的shape变形为[1,总token数,维度分成的组数,组的大小]

 总token数除每个样本的最大token数即多少个样本,然后每隔样本数个数据取一条数据

样本的shape为[1,每个样本的最大token数,维度分成的组数,组的大小]

 

权重按照[输出维度,1,维度组数,组的大小]进行变换 

求权重每组的最大值

将权重和输入数据相乘对每组的数据进行求和[输出维度,每个样本的最大token数,维度组数]

通过线性地减小 max_val,即 max_val 会在每一轮迭代中按照 (1 - i_s / n_grid) 的比例逐渐缩小,从原始的 org_max_val 开始

将权重 w 限制在新的量化范围内 

量化权重

计算当前的输出 cur_out,即输入特征 input_feat 和量化权重 q_w 的逐元素乘积的和

计算方式是当前输出 cur_out 和原始输出 org_out 之间的平方差的均值。(cur_out - org_out).pow(2) 计算每个元素的平方差,然后通过 mean(dim=1) 对每行的误差进行平均

找到那些当前误差 err 小于最小误差 min_errs 的索引。
min_errs[cur_best_idx] = err[cur_best_idx]:更新最小误差,确保 min_errs 中记录的是当前最小的误差值。
best_max_val[cur_best_idx] = max_val[cur_best_idx]:更新对应的最佳 max_val,记录当前最佳的量化范围(max_val)。

将结果添加到clip_list 

其他层按照相同的程序最终获得全部的clip_list

将clip_list进行应用


遍历 clip_list 中的每一项。每一项包含层的名称 (name) 和对应的最大裁剪值 (max_val)

操作将层的权重重塑为将原本维度按组进行分配,进行权重截断

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值