参考:
真的从头到尾弄懂量化之GPTQ - 知乎
LLM 推理加速技术 —— GPTQ 量化技术演进 - 知乎
原理和推导公式可见上面的几篇文章
下面主要看下autogptq的源码
初始化海森矩阵
$$
H = 2XX^T
$$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| def add_batch(self, inp, out): if os.environ.get("DEBUG"): self.inp1 = inp self.out1 = out if len(inp.shape) == 2: inp = inp.unsqueeze(0) tmp = inp.shape[0] if isinstance(self.layer, nn.Linear) or isinstance(self.layer, transformers.Conv1D): if len(inp.shape) == 3: inp = inp.reshape((-1, inp.shape[-1])) inp = inp.t() if isinstance(self.layer, nn.Conv2d): unfold = nn.Unfold( self.layer.kernel_size, dilation=self.layer.dilation, padding=self.layer.padding, stride=self.layer.stride, ) inp = unfold(inp) inp = inp.permute([1, 0, 2]) inp = inp.flatten(1) self.H *= self.nsamples / (self.nsamples + tmp) self.nsamples += tmp inp = math.sqrt(2 / self.nsamples) * inp.float() self.H += inp.matmul(inp.t())
|
fasterquant
1 2 3 4 5 6 7
|
H = self.H del self.H dead = torch.diag(H) == 0 H[dead, dead] = 1 W[:, dead] = 0
|
Cholesky 分解
为什么分解可以看LLM 推理加速技术 —— GPTQ 量化技术演进 - 知乎
Cholesky 分解是将一个正定矩阵分解为一个下三角矩阵和其转置的乘积。
1 2 3 4 5
| H = torch.linalg.cholesky(H) H = torch.cholesky_inverse(H) H = torch.linalg.cholesky(H, upper=True) Hinv = H
|
Cholesky 分解:
1
| H = torch.linalg.cholesky(H)
|
这一行将矩阵 H
进行 Cholesky 分解,即将 H
分解为一个下三角矩阵。对于一个正定矩阵 H
,其 Cholesky 分解会产生一个下三角矩阵 L
,满足:
$$
H = LL^T
$$
Cholesky 逆矩阵
1
| H = torch.cholesky_inverse(H)
|
接下来,调用 torch.cholesky_inverse()
来计算 Cholesky 分解矩阵 H
的逆矩阵。注意,H
在这之前已经是一个下三角矩阵(通过 Cholesky 分解得到)。该函数会计算 L^{-1}
,即 L
的逆矩阵,满足:
$$
H^{-1} = (L^{-1})(L^{-1})^T
$$
再次进行 Cholesky 分解(上三角)
1
| H = torch.linalg.cholesky(H, upper=True)
|
现在,H
是 $L^{-1}$,即 L
的逆矩阵。
这里,我们对 $L^{-1}$ 进行 Cholesky 分解,并且指定 upper=True
,这意味着我们得到的是一个 上三角矩阵 T
,满足:
$$
L^{−1}=TT^{T}
$$
因此,H
在这一行变成了一个上三角矩阵 T
,它是 $L^{-1}$ 的 Cholesky 分解。
$$
\delta W = - \frac{W_{:,q} - \text{quant}(W_{:,q})}{C_q T_{qq}} C_q T_{q,q:}
$$
迭代量化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| Losses = torch.zeros_like(W) Q = torch.zeros_like(W)
damp = percdamp * torch.mean(torch.diag(H)) diag = torch.arange(self.columns, device=self.dev) H[diag, diag] += damp H = torch.linalg.cholesky(H) H = torch.cholesky_inverse(H) H = torch.linalg.cholesky(H, upper=True) Hinv = H
for i1 in range(0, self.columns, blocksize): i2 = min(i1 + blocksize, self.columns) count = i2 - i1
W1 = W[:, i1:i2].clone() Q1 = torch.zeros_like(W1) Err1 = torch.zeros_like(W1) Losses1 = torch.zeros_like(W1) Hinv1 = Hinv[i1:i2, i1:i2]
for i in range(count): w = W1[:, i] d = Hinv1[i, i] if group_size != -1: if not static_groups: if (i1 + i) % group_size == 0: self.quantizer.find_params(W[:, (i1 + i) : (i1 + i + group_size)], weight=True)
if ((i1 + i) // group_size) - now_idx == -1: scale.append(self.quantizer.scale) zero.append(self.quantizer.zero) now_idx += 1 else: idx = i1 + i if actorder: idx = perm[idx] self.quantizer = groups[idx // group_size] q = self.quantizer.quantize(w.unsqueeze(1)).flatten() Q1[:, i] = q Losses1[:, i] = (w - q) ** 2 / d**2
err1 = (w - q) / d W1[:, i:] -= err1.unsqueeze(1).matmul(Hinv1[i, i:].unsqueeze(0)) Err1[:, i] = err1
Q[:, i1:i2] = Q1 Losses[:, i1:i2] = Losses1 / 2 W[:, i2:] -= Err1.matmul(Hinv[i1:i2, i2:])
|
量化结束将scale, zero等量化参数返回,而W还是原值