大语言模型的模型量化(INT8/INT4)技术

目录

  1. 一、LLM.in8 的量化方案
    1. 1.1 模型量化的动机和原理
    2. 1.2 LLM.int8 量化的精度和性能
    3. 1.3 LLM.int8 量化的实践
  2. 二、SmoothQuant 量化方案
    1. 2.1 SmoothQuant 的基本原理
    2. 2.2 SmoothQuant 的实践
  3. 三、GPTQ 量化训练方案
    1. 3.1 GPTQ 的基本原理
    2. 3.2 GPTQ 的实践
  4. 参考资料

一、LLM.in8 的量化方案

1.1 模型量化的动机和原理

成本和准确度。

常见精度介绍:大模型涉及到的精度是啥?FP32、TF32、FP16、BF16、FP8、FP4、NF4、INT8区别

在训练时,为保证精度,主权重始终为 FP32。而在推理时,FP16 权重通常能提供与 FP32 相似的精度,这意味着在推理时使用 FP16 权重,仅需一半 GPU 显存就能获得相同的结果。那么是否还能进一步减少显存消耗呢?答案就是使用量化技术,最常见的就是 INT8 量化。

简单来说, INT8 量化即将浮点数 x f x_f xf 通过缩放因子 s c a l e scale scale 映射到范围在[-128, 127] 内的 8bit 表示 x q x_q xq ,即

x q = Clip ⁡ ( Round ⁡ ( x f ∗ scale  ) ) x_q=\operatorname{Clip}\left(\operatorname{Round}\left(x_f * \text { scale }\right)\right) xq=Clip(Round(xf scale ))

其中 Round 表示四舍五入都整数,Clip 表示将离群值(Outlier) 截断到 [-128, 127] 范围内。对于 scale 值,通常按如下方式计算得到:

a m a x = max ⁡ ( a b s ( x f ) ) scale  = 127 / a m a x \begin{aligned} &amax =\max \left(a b s\left(x_f\right)\right)\\ &\text { scale }=127/amax \end{aligned} amax=max(abs(xf)) scale =127/amax

反量化的过程为:

x f ′ = x q / s c a l e x_f^{'} = x_q/scale xf=xq/scale

下面是通过该方式实现的量化-反量化的例子:

当进行矩阵乘法时,可以通过组合各种技巧,例如逐行或逐向量量化,来获取更精确的结果。举个例子,对矩阵乘法,我们不会直接使用常规量化方式,即用整个张量的最大绝对值对张量进行归一化,而会转而使用向量量化方法,找到 A 的每一行和 B 的每一列的最大绝对值,然后逐行或逐列归一化 A 和 B 。最后将 A 与 B 相乘得到 C。最后,我们再计算与 A 和 B 的最大绝对值向量的外积,并将此与 C 求哈达玛积来反量化回 FP16。

1.2 LLM.int8 量化的精度和性能

上文说明了如何对单个向量进行量化,原理比较简单,但是可能也会出现一些问题,比如下面这个例子:

A=[-0.10, -0.23, 0.08, -0.38, -0.28, -0.29, -2.11, 0.34, -0.53, -67.0]

注意到向量A中有离群值(Emergent Features) − 67.0 -67.0 67.0 ,如果去掉该值对向量A做量化和反量化,处理后的结果是:

[-0.10, -0.23, 0.08, -0.38, -0.28, -0.28, -2.11, 0.33, -0.53]

出现的误差只有-0.29 -> -0.28。但是如果我们在保留 − 67.0 -67.0 67.0 的情况下对该向量做量化和反量化,处理后的结果是:

[ -0.00, -0.00, 0.00, -0.53, -0.53, -0.53, -2.11, 0.53, -0.53, -67.00]

可见大部分信息在处理后都丢失了。

幸运的是,Emergent Features的分布是有规律的。对于一个参数量为6.7亿的transformer模型来说,每个句子的表示中会有150000个Emergent Features,但这些Emergent Features只分布在6个维度中

基于此,可以采用混合精度分解的量化方法:将包含了Emergent Features的几个维度从矩阵中分离出来,对其做高精度的矩阵乘法;其余部分进行量化。如下图所示:

精度与性能

如下图所示的对比实验,可以看到,在模型参数量达到6.7亿时,使用vector-wise方法进行量化会使模型性能有非常大的下降,而使用LLM.int8()方法进行量化则不会造成模型性能的下降。

对 OPT-175B 模型,使用 lm-eval-harness 在 8 位和原始模型上运行了几个常见的基准测试,结果如下:

测试基准-----
测试基准名指标指标值 int8指标值 fp16标准差 fp16指标差值
hellaswagacc_norm0.78490.78490.00410
hellaswagacc0.59210.59310.00490.001
piqaacc0.79650.79590.00940.0006
piqaacc_norm0.81010.81070.00910.0006
lambadappl3.01423.01520.05520.001
lambadaacc0.74640.74660.00610.0002
winograndeacc0.71740.72450.01250.0071

LLM.int8() 方法的主要目的是在不降低性能的情况下降低大模型的应用门槛,使用了 LLM.int8() 的 BLOOM-176B 比 FP16 版本慢了大约 15% 到 23%,结果如下所示:

精度参数量硬件延迟 (ms/token,BS=1)延迟 (ms/token,BS=8)延迟 (ms/token,BS=32)
bf16176B8xA100 80GB239329.9
int8176B4xA100 80GB28237.510.2
bf16176B14xA100 40GB28536.510.4
int8176B5xA100 40GB36746.4oom
fp1611B2xT4 15GB11.71.70.5
int811B1xT4 15GB43.55.31.3
fp323B2xT4 15GB457.23.1
int83B1xT4 15GB31239.110.2

1.3 LLM.int8 量化的实践

bitsandbytes 是基于 CUDA 的主要用于支持 LLM.int8() 的库。它是torch.nn.modules的子类,你可以仿照下述代码轻松地将其应用到自己的模型中。

  1. 首先导入模块,初始化fp16 模型并保存
import torch
import torch.nn as nn
from bitsandbytes.nn import Linear8bitLtfp16_model = nn.Sequential(nn.Linear(64, 64),nn.Linear(64, 64)
).to(torch.float16).to(0)
torch.save(fp16_model.state_dict(), "model.pt")
  1. 初始化 int8 模型并加载保存的weight,此处标志变量has_fp16_weights非常重要。默认情况下,它设置为True,用于在训练时使能 Int8/FP16 混合精度。
int8_model = nn.Sequential(Linear8bitLt(64, 64, has_fp16_weights=False),Linear8bitLt(64, 64, has_fp16_weights=False)
)int8_model.load_state_dict(torch.load("model.pt"))

此时还未进行量化操作,可以查看weight值

int8_model[0].weight
Parameter containing:
Parameter(Int8Params([[ 0.1032,  0.0544,  0.1021,  ..., -0.0465,  0.1050,  0.0687],[ 0.0083,  0.0352,  0.0540,  ..., -0.0931, -0.0224,  0.0541],[ 0.0476,  0.0220, -0.0803,  ...,  0.1031,  0.1134,  0.0905],...,[-0.0523, -0.0858,  0.0330,  ...,  0.1122, -0.1082,  0.1210],[ 0.0045, -0.1019,  0.0072,  ..., -0.1069, -0.0417,  0.0365],[-0.1134,  0.0032, -0.0742,  ..., -0.1142, -0.0374,  0.0915]]))

之后将模型加载到GPU上,此时发生量化操作:

int8_model = int8_model.to(0) # Quantization happens here

此时可以查看weight值,可以看到值已经传到GPU上并转为 INT8 类型。

int8_model[0].weight
Parameter containing:
Parameter(Int8Params([[ 105,   55,  104,  ...,  -47,  107,   70],[   9,   36,   56,  ...,  -96,  -23,   56],[  49,   23,  -82,  ...,  105,  116,   92],...,[ -54,  -89,   34,  ...,  116, -112,  126],[   5, -107,    8,  ..., -112,  -44,   38],[-116,    3,  -76,  ..., -117,  -38,   94]], device='cuda:0',dtype=torch.int8))

获取 FP16 权重以便在 FP16 中执行离群值的矩阵乘,可见与原始值比较接近

(int8_model[0].weight.CB * int8_model[0].weight.SCB) / 127
tensor([[ 0.1032,  0.0532,  0.1018,  ..., -0.0453,  0.1017,  0.0682],[ 0.0088,  0.0348,  0.0548,  ..., -0.0926, -0.0219,  0.0546],[ 0.0482,  0.0223, -0.0802,  ...,  0.1012,  0.1102,  0.0897],...,[-0.0531, -0.0861,  0.0333,  ...,  0.1118, -0.1064,  0.1228],[ 0.0049, -0.1036,  0.0078,  ..., -0.1080, -0.0418,  0.0370],[-0.1140,  0.0029, -0.0744,  ..., -0.1128, -0.0361,  0.0916]],device='cuda:0')

最后比较 fp16 模型与 int8 模型输出结果

input_ = torch.randn(8, 64, dtype=torch.float16)
hidden_states_int8 = int8_model(input_.to(0))
hidden_states_fp16 = fp16_model(input_.to(0))
print(torch.max(hidden_states_fp16 - hidden_states_int8))

可以最大的绝对误差为

tensor(0.0098, device='cuda:0', dtype=torch.float16, grad_fn=<MaxBackward1>)

另外,我们也可以直接加载预训练完成的模型为 int8 类型,方式如下:

from transformers import LlamaForCausalLM
model = LlamaForCausalLM.from_pretrained(args.base_model, load_in_8bit=args.load_8bit,torch_dtype=torch.float16, device_map={"auto"}, )

其INT8转化实际是调用了如下操作,函数的具体实现参见这里

from bitsandbytes import set_module_8bit_tensor_to_device
if param.dtype == torch.int8 and param_name.replace("weight", "SCB") in state_dict.keys():fp16_statistics = state_dict[param_name.replace("weight", "SCB")]
else:fp16_statistics = Noneif "SCB" not in param_name:set_module_8bit_tensor_to_device(model, param_name, param_device, value=param, fp16_statistics=fp16_statistics)

二、SmoothQuant 量化方案

2.1 SmoothQuant 的基本原理

当模型规模更大时,单个token的值变化范围较大,难以量化,相比之下 weight 的变化范围较小,即 weight 较易量化,而 activation 较难量化。基于此,SmoothQuant 核心思想是引入一个超参,减小激活值的变化范围,增大权重的变化范围,从而均衡两者的量化难度。

由于量化前的激活值变化范围较大,即使对于同一 token,不同channel数值差异较大,对每个 token 的量化也会造成精度损失,但是不难看出较大值一般出现在同一 channel,因此作者也分析了采用 per-channel 的量化方式,这种量化方式能很好的避免精度损失,但是硬件不能高效执行,增加了计算时间,因此大多数量化仍采用 per-token 及 per-tensor 的量化方式。从下图可以看出,量化前的 activation 矩阵在某些 channel 的数值较大,weight 矩阵相对平缓,经过 SmoothQuant 之后,activation 张量中数值较大的 channel 相对减小,对应 weight 张量的 channel 处数值增大,达到均衡二者量化难度的目的。

将 activation 的量化难度转移到 weight 上,需要引入平滑因子 s \mathbf{s} s ,则

Y = ( X diag ⁡ ( s ) − 1 ) ⋅ ( diag ⁡ ( s ) W ) = X ^ W ^ \mathbf{Y}=\left(\mathbf{X} \operatorname{diag}(\mathbf{s})^{-1}\right) \cdot(\operatorname{diag}(\mathbf{s}) \mathbf{W})=\hat{\mathbf{X}} \hat{\mathbf{W}} Y=(Xdiag(s)1)(diag(s)W)=X^W^

为了减少激活的量化难度,可以让 s j = m a x ( ∣ X j ∣ ) , j = 1 , 2 , … , C j s_j = max(|X_j|), j = 1, 2, …, C_j sj=maxXj,j=1,2,,Cj ,即第 j j j 个 channel 的最大值。

但是这样 weight 的量化难度会变得难以量化,因此需要引入另一个超参转移强度 α α α

s j = max ⁡ ( ∣ X j ∣ ) α / max ⁡ ( ∣ W j ∣ ) 1 − α \mathbf{s}_j=\max \left(\left|\mathbf{X}_j\right|\right)^\alpha / \max \left(\left|\mathbf{W}_j\right|\right)^{1-\alpha} sj=max(Xj)α/max(Wj)1α

其中 α α α 可以根据 activation 和 weight 的量化难易程度进行调整,对于大多数模型 α = 0.5 α= 0.5 α=0.5 ,对于模型GLM-130B,由于其 activation 值更加难以量化,设置 α = 0.75 α= 0.75 α=0.75 ,可以更好地进行量化。

具体的计算过程下图所示,从结果可以看出,activation 值较大的 channel 数值相对减小,量化难度降低,对应channel 的 weight 量化难度增加。

得到smooth变换之后的 activation 和 weight 矩阵,可以再采用 per-token 或 per-tensor 的量化方式,即

又根据激活值是基于先验数据获得或者执行时获得分为动态量化和静态量化,作者提出了三个层次的量化方式,其中O1~O3的计算延迟依次减少。

MethodWeightActivation
W8A8per-tensorper-tensor dynamic
ZeroQuantgroup-wiseper-token dynamic
LLM.int8per-channelper-token dynamic+FP16
Outlier Suppressionper-tensorper-tensor static
SmoothQuant-O1per-tensorper-token dynamic
SmoothQuant-O2per-tensorper-tensor dynamic
SmoothQuant-O3per-tensorper-tensor static

作者将 SmoothQuant 应用到 Transformer 结构中,由于存储和计算开销大多在attention模块和前向模块,对应图中绿色部分均量化为W8A8进行计算。平滑因子S的计算也可以融合到前层 Layernorm/Softmax 中。

2.2 SmoothQuant 的实践

接下来,我们将通过一个 LLaMa-13B 的模型来体验 SmoothQuant 的效果。

  • Step 1. 加载模型和数据集, 采用标准的 LLaMa-13B 模型
import torch
from transformers import LlamaTokenizer, LlamaForCausalLM
from transformers.models.llama.modeling_llama import LlamaAttention, LlamaDecoderLayer
from smoothquant.smooth import smooth_lm
from smoothquant.fake_quant import W8A8Linear
from datasets import load_datasetmodel_fp16 = LlamaForCausalLM.from_pretrained('decapoda-research/llama-13b-hf', torch_dtype=torch.float16, device_map='auto')
tokenizer = LlamaTokenizer.from_pretrained('decapoda-research/llama-13b-hf')
dataset = load_dataset('lambada', split='validation[:1000]')
  • Step 2. 构造 Evaluator 并评估 FP16 模型
class Evaluator:def __init__(self, dataset, tokenizer, device):self.dataset = datasetself.tokenizer = tokenizerself.device = device# tokenize the datasetdef tokenize_function(examples):example = self.tokenizer(examples['text'])return exampleself.dataset = self.dataset.map(tokenize_function, batched=True)self.dataset.set_format(type='torch', columns=['input_ids'])@torch.no_grad()def evaluate(self, model):model.eval()# The task is to predict the last word of the input.total, hit = 0, 0for batch in self.dataset:input_ids = batch['input_ids'].to(self.device).unsqueeze(0)label = input_ids[:, -1]outputs = model(input_ids)last_token_logits = outputs.logits[:, -2, :]pred = last_token_logits.argmax(dim=-1)total += label.size(0)hit += (pred == label).sum().item()acc = hit / totalreturn accacc_fp16 = evaluator.evaluate(model_fp16)
print(f'Original model (fp16) accuracy: {acc_fp16}')

输出结果为:

Original model (fp16) accuracy: 0.888
  • Step 3. 使用 SmoothQuant 将模型矩阵乘部分转为W8A8并评估
def quantize_model(model, weight_quant='per_tensor', act_quant='per_tensor', quantize_bmm_input=True):for name, m in model.model.named_modules():if isinstance(m, LlamaDecoderLayer):m.mlp.gate_proj = W8A8Linear.from_float(m.mlp.gate_proj, weight_quant=weight_quant, act_quant=act_quant)m.mlp.up_proj = W8A8Linear.from_float(m.mlp.up_proj, weight_quant=weight_quant, act_quant=act_quant)m.mlp.down_proj = W8A8Linear.from_float(m.mlp.down_proj, weight_quant=weight_quant, act_quant=act_quant)elif isinstance(m, LlamaAttention):# Her we simulate quantizing BMM inputs by quantizing the output of q_proj, k_proj, v_projm.q_proj = W8A8Linear.from_float(m.q_proj, weight_quant=weight_quant, act_quant=act_quant, quantize_output=quantize_bmm_input)m.k_proj = W8A8Linear.from_float(m.k_proj, weight_quant=weight_quant, act_quant=act_quant, quantize_output=quantize_bmm_input)m.v_proj = W8A8Linear.from_float(m.v_proj, weight_quant=weight_quant, act_quant=act_quant, quantize_output=quantize_bmm_input)m.o_proj = W8A8Linear.from_float(m.o_proj, weight_quant=weight_quant, act_quant=act_quant)return modelmodel_w8a8 = quantize_model(model_fp16)
print(model_w8a8)
acc_w8a8 = evaluator.evaluate(model_w8a8)
print(f'SmoothQuant W8A8 quantized model accuracy: {acc_w8a8}')

可以看到相关模块被替换为 W8A8Linear:

(self_attn): LlamaAttention((q_proj): W8A8Linear(5120, 5120, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=per_tensor)(k_proj): W8A8Linear(5120, 5120, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=per_tensor)(v_proj): W8A8Linear(5120, 5120, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=per_tensor)(o_proj): W8A8Linear(5120, 5120, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=None)(rotary_emb): LlamaRotaryEmbedding())
(mlp): LlamaMLP((gate_proj): W8A8Linear(5120, 13824, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=None)(down_proj): W8A8Linear(13824, 5120, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=None)(up_proj): W8A8Linear(5120, 13824, bias=False, weight_quant=per_tensor, act_quant=per_tensor, output_quant=None)(act_fn): SiLUActivation())

转化后 INT8 的精度存在一定程度得下降:

 SmoothQuant W8A8 quantized model accuracy: 0.791

三、GPTQ 量化训练方案

3.1 GPTQ 的基本原理

GPTQ 的核心思想是逐一量化模型的各个层,对于每个层寻找可以满足下式的量化结果:

a r g m i n w ^ = ∣ ∣ W X − W ^ X ∣ ∣ 2 argmin_{\widehat{w}} = ||WX - \widehat{W}X||^2 argminw =∣∣WXW X2

即对于每个需要被量化的层(对应参数 W W W ),希望量化前后该层输出变化尽量小。

GPTQ的核心原理来自于OBQ(Optimal Brain Quantization), 而OBQ的思路主要来自于OBS(Optimal Brain Surgeon)。 在OBS中,假设我们要抹去一个权重记为 w q w_q wq ,使得其对整体的误差增加最少,并且同时计算出一个补偿 δ q \delta_q δq 应用于剩余的权重上,使得抹去的这个权重增加的误差被抵消。作者找到了这样的一个方法,公式为:

w q = a r g m i n q w q 2 [ H − 1 ] q q , δ q = − w q [ H q q − 1 ] ⋅ H : , q − 1 w_q = argmin_q{\dfrac{w_q^2}{[H^{-1}]_{qq}}} , \delta_q=-\dfrac{w_q}{[H^{-1}_{qq}]}·H^{-1}_{:,q} wq=argminq[H1]qqwq2,δq=[Hqq1]wqH:,q1

其中 H H H 是一个代表loss的 Hessian 矩阵。

OBQ把它推广到量化中,即可将剪枝稀疏看作量化的极端情况。OBQ里的公式是这样的:

w q = a r g m i n q ( q u a n t ( q ) − w q ) 2 [ H − 1 ] q q , δ q = − w q − q u a n t ( q ) [ H q q − 1 ] ⋅ H : , q − 1 w_q = argmin_q{\dfrac{(quant(q) - w_q)^2}{[H^{-1}]_{qq}}} , \delta_q=-\dfrac{w_q - quant(q)}{[H^{-1}_{qq}]}·H^{-1}_{:,q} wq=argminq[H1]qq(quant(q)wq)2,δq=[Hqq1]wqquant(q)H:,q1

其中 q u a n t ( q ) quant(q) quant(q) 是把 q q q 近似到最近的quant grid的点上,简单说就是四舍五入到指定位数上。可以看到假如quant(q)永远返回零其实就是原版的OBS。

OBQ 算法理论很好,但其算法复杂度是 O ( d r o w ∗ d c o l 3 ) O(d_{row}*d_{col}^3) O(drowdcol3) ,计算太慢。因此 GPTQ 在此基础上做了以下改进:

  • **取消贪心算法:**OBQ使用贪心算法,逐个找影响最小的 q q q 来剪枝/量化,经过观察发现,其实随机的顺序效果也一样好(甚至在大模型上更好)。原算法对 W W W 进行优化时,逐行计算,每一行挑选q的顺序都是不一样的。在GPTQ中直接全部都采用固定顺序,使得复杂度从 O ( d r o w ∗ d c o l 3 ) O(d_{row}*d_{col}^3) O(drowdcol3) 优化到 O ( m a x ( d r o w ∗ d c o l 2 , d c o l 3 ) ) O(max(d_{row} * d^2_{col}, d^3_{col})) O(max(drowdcol2,dcol3))
  • **批处理:**OBQ 对权重一个个进行单独更新,作者发现瓶颈实际在于GPU的内存带宽,而且同一个特征矩阵W不同列间的权重更新是不会互相影响的。因此作者提出了批处理的方法,一次处理多个(如128)列,大幅提升了计算速度。
  • **数值稳定性: **作者观察到一些操作会引入数值误差(主要是 H − 1 H^{-1} H1 相关的部分)。为了解决这个误差提出了两种方法,一是往 H − 1 H^{-1} H1里加入一个小的常数项。另一个是用数值稳定的 Cholesky 分解提前计算好所有需要的信息,避免在更新的过程中再计算。

GPTQ 算法过程如下所示:

3.2 GPTQ 的实践

此处采用 GPTQ-for-LLaMa 来说明 GPTQ 的 4bit 的 LLaMa 模型的训练过程,其命令如下所示:

python3 llama.py decapoda-research/llama-13b-hf c4 --wbits 4 --true-sequential --act-order --groupsize 128 --save llama13b-4bit-128g.pt

其中核心部分则是 QuantLinear 的实现,如下:

class QuantLinear(nn.Module):def __init__(self, bits, groupsize, infeatures, outfeatures, bias):super().__init__()if bits not in [2, 4, 8]:raise NotImplementedError("Only 2,4,8 bits are supported.")self.infeatures = infeaturesself.outfeatures = outfeaturesself.bits = bitsself.maxq = 2**self.bits - 1self.groupsize = groupsize if groupsize != -1 else infeaturesself.register_buffer('qweight', torch.zeros((infeatures // 32 * self.bits, outfeatures), dtype=torch.int32))self.register_buffer('qzeros', torch.zeros((math.ceil(infeatures / self.groupsize), outfeatures // 32 * self.bits), dtype=torch.int32))self.register_buffer('scales', torch.zeros((math.ceil(infeatures / self.groupsize), outfeatures), dtype=torch.float16))self.register_buffer('g_idx', torch.tensor([i // self.groupsize for i in range(infeatures)], dtype=torch.int32))if bias:self.register_buffer('bias', torch.zeros((outfeatures), dtype=torch.float16))else:self.bias = Nonedef pack(self, linear, scales, zeros, g_idx=None):self.g_idx = g_idx.clone() if g_idx is not None else self.g_idxscales = scales.t().contiguous()zeros = zeros.t().contiguous()scale_zeros = zeros * scalesself.scales = scales.clone().half()if linear.bias is not None:self.bias = linear.bias.clone().half()intweight = []for idx in range(self.infeatures):intweight.append(torch.round((linear.weight.data[:, idx] + scale_zeros[self.g_idx[idx]]) / self.scales[self.g_idx[idx]]).to(torch.int)[:, None])intweight = torch.cat(intweight, dim=1)intweight = intweight.t().contiguous()intweight = intweight.numpy().astype(np.uint32)qweight = np.zeros((intweight.shape[0] // 32 * self.bits, intweight.shape[1]), dtype=np.uint32)i = 0row = 0while row < qweight.shape[0]:if self.bits in [2, 4, 8]:for j in range(i, i + (32 // self.bits)):qweight[row] |= intweight[j] << (self.bits * (j - i))i += 32 // self.bitsrow += 1else:raise NotImplementedError("Only 2,4,8 bits are supported.")qweight = qweight.astype(np.int32)self.qweight = torch.from_numpy(qweight)zeros -= 1zeros = zeros.numpy().astype(np.uint32)qzeros = np.zeros((zeros.shape[0], zeros.shape[1] // 32 * self.bits), dtype=np.uint32)i = 0col = 0while col < qzeros.shape[1]:if self.bits in [2, 4, 8]:for j in range(i, i + (32 // self.bits)):qzeros[:, col] |= zeros[:, j] << (self.bits * (j - i))i += 32 // self.bitscol += 1else:raise NotImplementedError("Only 2,4,8 bits are supported.")qzeros = qzeros.astype(np.int32)self.qzeros = torch.from_numpy(qzeros)def forward(self, x):out_shape = x.shape[:-1] + (self.outfeatures, )out = QuantLinearFunction.apply(x.reshape(-1, x.shape[-1]), self.qweight, self.scales, self.qzeros, self.g_idx, self.bits, self.maxq)out = out + self.bias if self.bias is not None else outreturn out.reshape(out_shape)

使用训练完成后的模型进行推理,命令如下:

python3 llama_inference.py decapoda-research/llama-13b-hf --wbits 4 --groupsize 128 --load llama13b-4bit-128g.pt --text "Who are you?"

输出如下:

 ⁇  Who are you? I've been looking for you.
Brigitte: I've been busy.
Maria: I'm not interested in your lies. Now, who are you?
Brigitte: I'

参考资料

[1] A Gentle Introduction to 8-bit Matrix Multiplication for transformers at scale using transformers, accelerate and bitsandbytes

[2] 大规模 Transformer 模型 8 比特矩阵乘简介

[3] 模型量化(int8)知识梳理 - 知乎 (zhihu.com)

[4] https://arxiv.org/pdf/2208.07339.pdf

[5] LLM.int8() and Emergent Features — Tim Dettmers

[6] Achieving FP32 Accuracy for INT8 Inference Using Quantization Aware Training with NVIDIA TensorRT | NVIDIA Technical Blog

[7] arxiv.org/pdf/2211.10438.pdf

[8] smoothquant/smoothquant_opt_demo.ipynb at main · mit-han-lab/smoothquant · GitHub

[9] LLM大语言模型量化方法(一)

[10] [2210.17323] GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers (arxiv.org)

[11] qwopqwop200/GPTQ-for-LLaMa: 4 bits quantization of LLaMA using GPTQ (github.com)

[12] IST-DASLab/gptq: Code for the ICLR 2023 paper “GPTQ: Accurate Post-training Quantization of Generative Pretrained Transformers”. (github.com)

桥如虹,水如空,一叶飘然烟雨中。 —— 陆游《长相思》

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SD-WAN企业组网:与传统组网有何不同?

很多企业已经尝试过使用SD-WAN来进行组网。SD-WAN代表着一种新兴的网络连接技术&#xff0c;与传统的网络架构相比&#xff0c;它在许多方面都有明显的不同。 SD-WAN基于软件定义网络&#xff08;SDN&#xff09;的概念&#xff0c;提供集中化的网络控制和智能优化&#xff0c;…

macos重装系统 启动U盘制作方法 - createinstallmedia 命令使用方法总结

macos重装系统比windows要稍微复杂一些&#xff0c;不过还好&#xff0c;macos系统安装app这个Apple官方提供的系统软件里面默认就内置了一个可用为我们制作启动盘的工具 createinstallmedia 我们下载的apple安装镜像要门是 dmg/pkg/iso 的压缩档案格式的&#xff0c;要么是 x…

电子行业数字工厂管理系统解决方案

电子行业数字工厂管理系统解决方案是针对电子企业特定需求而设计的一套综合管理系统&#xff0c;旨在通过数字化手段提升生产效率、优化资源配置、降低运营成本&#xff0c;并确保高品质产品的输出。以下是一个详细的电子行业数字工厂管理系统解决方案的概述&#xff1a; 一、系…

WebAssembly最详教程

WebAssembly 是一种新的编码方式&#xff0c;可以在现代的 Web 浏览器中运行——它是一种低级的类汇编语言&#xff0c;具有紧凑的二进制格式&#xff0c;可以接近原生的性能运行&#xff0c;并为诸如 C/C、C# 和 Rust 等语言提供编译目标&#xff0c;以便它们可以在 Web 上运行…

面试 | Python 自动化测试技术面试真题

本文为面试某公司测试开发&#xff0f;自动化测试工程师时的面试题笔记。 全部笔试内容没有全部写全&#xff0c;只贴通用性的技术相关的笔试面试题&#xff0c;至于测试理论和团队管理的问题&#xff0c;都是大同小异&#xff0c;也没什么实际的参考价值。 1.直接手写一个 P…

AWS云服务器选择最佳区域

2024年&#xff0c;随着全球云计算的持续发展和AWS在全球不断扩展的数据中心网络&#xff0c;选择合适的AWS云服务器区域成为了企业和开发者需要认真考虑的问题。九河云告诉你在做出选择之前&#xff0c;需要考虑以哪些关键因素&#xff1a; 地理位置和用户分布 选择AWS云服务…

ZAN与Mysten Labs合作推进Web3基础设施开发

Mysten Labs是一家Web3基础设施公司&#xff0c;也是Sui区块链的开发公司&#xff0c;今天宣布与蚂蚁数字科技的技术品牌ZAN建立合作伙伴关系。 通过整合Sui&#xff0c;ZAN旨在加速其Web3应用程序的开发和采用。该合作将专注于为Mysten Labs在两个关键领域提供技术支持&#…

分班查询一键发布,老师们都在用

新学期的钟声即将敲响&#xff0c;校园里又将迎来一批充满好奇和期待的新生。对于老师们来说&#xff0c;这不仅仅是一个新起点&#xff0c;更是一项挑战——如何高效而准确地将新生的分班信息传达给每一位家长。传统的方法是通过私信逐一发送&#xff0c;这不仅耗时耗力&#…

opencv-python图像增强二:图像去雾(暗通道去雾)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、简介&#xff1a;二、暗通道去雾方案简述&#xff1a;三、算法实现步骤3.1最小值滤波3.2 引导滤波3.3 计算图像全局光强 四&#xff1a;整体代码实现五&#xf…

加密软件有哪些常见的安全特性

1. 数据加密 核心功能&#xff1a;加密软件的核心在于对数据进行加密处理&#xff0c;通过复杂的加密算法&#xff08;如AES、RSA等&#xff09;将明文数据转换为密文&#xff0c;确保数据在存储和传输过程中的安全性。 透明加密&#xff1a;部分加密软件支持透明加密功能&am…

代码+视频,R语言VRPM绘制多种模型的彩色列线图

列线图&#xff0c;又称诺莫图&#xff08;Nomogram&#xff09;&#xff0c;它是建立在回归分析的基础上&#xff0c;使用多个临床指标或者生物属性&#xff0c;然后采用带有分数高低的线段&#xff0c;从而达到设置的目的&#xff1a;基于多个变量的值预测一定的临床结局或者…

排队免单小程序

本文来自&#xff1a;排队免单小程序 - 源码1688 应用介绍 排队免单小程序是基于移动互联网技术开发的平台系统&#xff0c;通过小程序的形式为消费者和商家搭建了一个互动桥梁。以下是对排队免单小程序的详细介绍&#xff1a; 一、基本概念 排队免单小程序是一种创新的营销工…

拯救打工人的4款可ai生成ppt神器大PK,谁是加班狗的最爱?

在忙碌的工作日常里&#xff0c;做PPT常常让不少上班族头疼。晚上加班时&#xff0c;办公室里总能看到那些为了做出完美PPT而熬夜的人。不过现在好了&#xff0c;科技进步带来了好消息&#xff0c;有几款AI生成PPT的神器出现了&#xff0c;今天&#xff0c;咱们就来比一比&…

c++41两头堵模型

直接拷到自己的内存空间 字符串反转 递归&#xff1a;参数的入栈模型 函数的调用模型 先被调用的模型后执行 向局部变量拷贝内存 传两个参数 做函数结果

华为od统一考试B卷【AI面板识别】python实现

思路 n int(input())class Light:def __init__(self, id, x1, y1, x2, y2):self.id idself.x1 x1self.y1 y1self.x2 x2self.y2 y2self.height y2 - y1def get_lights_info(n):lights []for _ in range(n):id, x1, y1, x2, y2 map(int, input().strip().split())lights…

乡村振兴旅游休闲景观解决方案

乡村振兴旅游休闲景观解决方案摘要 2. 规划方案概览 规划核心&#xff1a;PPT展示了乡村振兴建设规划的核心区平面图及鸟瞰图&#xff0c;涵盖景观小品、设施农业、自行车道、新社区等设计元素。 规划策略&#xff1a;方案注重打造大开大合的空间感受&#xff0c;特色农产大观…

安全测试中的权限测试

安全测试中的权限测试&#xff08;或称为访问控制测试&#xff09;是一个关键的组成部分&#xff0c;它确保系统正确地实施了访问控制策略&#xff0c;防止未授权的用户或进程访问敏感信息和功能。 身份验证&#xff1a; 测试系统是否要求用户提供凭据&#xff08;如用户名和密…

负载均衡、高可用

负载均衡 负载均衡&#xff08;Load Balance&#xff09;&#xff1a;可以利用多个计算机和组合进行海量请求处理&#xff0c;从而获得很高的处理效率&#xff0c;也可以用多个计算机做备份&#xff08;高可用&#xff09;&#xff0c;使得任何一个机器坏了整个系统还是能正常…

2024靠谱的网站建设公司推荐

在现在的互联网社会&#xff0c;一个企业的网站往往是潜在客户对该品牌的第一印象来源。也正因如此&#xff0c;选择一个靠谱的网站建设公司对于确保企业在线形象和功能性至关重要&#xff0c;作为建站行业从业人员&#xff0c;我分享几个选择网站建设公司时应考虑的几个关键因…

react+taro的文字粘贴识别功能

效果图 <View className"components-page"><Textareastyle"font-size:12PX"className"textareaStyle"placeholderClass"placeholderStyle"placeholder"例&#xff1a;公司&#xff1a;xxxx公司, 电话:13*********, 地址…