Qlora使用模板
Qlora主要是包含了三个部分:
- Normal Float 4-bit :标准4-bit浮点数表述,也就是用4-bit来表示一个浮点数。策略是用分块-分位数量化。
- 双重量化:对普通参数和量化常数进一步量化。
- 分页优化器:显存过高时,用一部分内存替代。
8-bit表示
有符号:正数最大值,pow(2, 7) - 1, 127。负数最小值,- pow(2, 7), -128
无符号:最大值:pow(2, 8) - 1, 255
127 : 0111 1111
常见量化方法
Qlora是提出了一个新的量化方法,常见的量化方法如下:
- absolute maximum quantization量化
- 这是对称量化,映射到-127 - 127范围内。
from math import pow
def abs_max_quant(x_block, n_bit):
quant_constant = (pow(2, n_bit - 1) - 1) / max(abs(x_block))
x_quant = []
for x in x_block:
x_quant.append(round(quant_constant * x))
def de_abs_max_quant(x_quant, quant_constant):
return [item / quant_constant for item in x_quant]
- asymmetric Quant
可以看到,非对称量化在反量化时需要记录两个东西:1. 量化常数,quant_constant, 2. 零点, zero_point
非对称量化是将数字映射到0-255。
from math import pow
def asy_quant(x_block, n_bit):
min_x, max_x = min(x_block), max(x_block)
quant_constant = (pow(2, n_bit) - 1) / (max_x - min_x)
zero_point = quant_constant * min_x
return [round(quant_constant * item - zero_point) for item in x_block]
def de_asy_quant(x_quant, quant_constant, zero_point):
return [(item + zero_point) / quant_constant for item in x_quant]
- 分位数量化
CDF累积概率函数::输入x, 返回截止到x的累积概率。
PPF反累积概率函数:输入累积概率, 返回x。
本质上是使用ppf返回等差序列,量化时按照最近原则,进行量化。
- 普通方式,Q不包含0,所以0不对应到0上。
import numpy as np
from scipy.stats import norm
offset = 0.99
num_bins = 16
# 17个等差序列的累积概率
pps = np.linspace(start=1-offset, stop=offset, num=num_bins+1)
quantile = norm.ppf(pps)
quantile = [(quantile[idx] +val) / 2 for idx, val in enumerate(quantile[:-1])]
#
max_quantile, min_quantile = quantile[-1], quantile[0]
scale_factor = 2 / (max_quantile - min_quantile)
zero_point = (max_quantile + min_quantile) / (min_quantile - max_quantile)
Q = [x * scale_factor + zero_point for x in quantile]
- Qlora量化方式,Q包含0,所以0对应到0。
Q的值被限制到了[-1, 1],所以在进行量化的时候,需要记录被量化的值的最大绝对值。
from scipy.stats import norm
import numpy as np
offset = 0.99
# 0.5-0.99取9个分位数
a = norm.ppf(np.linspace(start=0.5, stop=offset, num=9)).tolist()
# 0.01-0.5取8个分位数
b = norm.ppf(np.linspace(start=1-offset, stop=0.5, num=8)).tolist()
# 去掉一个0
Q = b[:-1] + a
Q = torch.tensor(Q)
# 归到[-1, 1]之间。
Q /= Q.max()
# 量化
def quantize(input_block, Q):
input_block = np.asarray(input_block, dtype=np.float64)
quantile_constant = np.max(np.abs(input_block))
input_block /= quantile_constant
# 一块保存一个量化常数,用于反量化。
return [np.abs(Q - val).argmin() for val in input_block], quantile_constant
def dequantize(input_block, Q, quantile_constant):
return [Q[idx] * quantile_constant for idx in input_block]
- Qlora双重量化
Qlora每个块保存一个块最大绝对值,也就是所谓的量化常数。
Qlora双重量化将256个块,也就是8-bit,记录量化常数。
显存节省:
- 一个块是64个数,一个数是4-bit,一个块额外保存一个8-bit量化常数, 8/(64 * 4) = 3.125%
- 256块一个32位二次量化常数,额外占用: 32/(256 * 64 * 4) = 0.049%
Qlora公式总结
将模型参数量化为NF4之后,在Qlora前向传播中,首先将参数反量化为bf16,然后计算。
Qlora: 4-bit/参数,c2: 8-bit/64参数,c1: 32/256*64参数。
通过c1将c2恢复,通过c2将参数恢复。