chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import numpy as np
|
||||
import paddle
|
||||
|
||||
|
||||
def cal_ratio(m, v, eps=1e-8):
|
||||
"""
|
||||
cal part adam update ratio.
|
||||
Args:
|
||||
m (`paddle.Tensor`):
|
||||
moment in Adam optimizer.
|
||||
v (`paddle.Tensor`):
|
||||
variance in Adam optimizer.
|
||||
eps (`int`):
|
||||
epsilon in Adam optimizer.
|
||||
"""
|
||||
return 1 / (np.sqrt(v) + eps)
|
||||
|
||||
|
||||
def group_wise_quant_dequant(
|
||||
inputs,
|
||||
mins=None,
|
||||
maxs=None,
|
||||
quant_bits=4,
|
||||
group_size=32,
|
||||
quant=True,
|
||||
tp_rank=-1,
|
||||
tp_degree=1,
|
||||
use_pd=False,
|
||||
symmetry=False,
|
||||
):
|
||||
"""
|
||||
group-wise quantization (support symmetry, asymmetry).
|
||||
Args:
|
||||
inputs (`paddle.Tensor`):
|
||||
The tensor to quantize.
|
||||
mins (`paddle.Tensor`):
|
||||
Min scales tensor in asymmetry quantization.
|
||||
maxs (`paddle.Tensor`):
|
||||
Max scales tensor in asymmetry quantization, or Abs max tensor in symmetry quantization.
|
||||
quant_bits (`int`):
|
||||
Quantization bits.
|
||||
group_size (`int`):
|
||||
Group size of group-wise quantization.
|
||||
quant (`bool`):
|
||||
True when quantization, False in dequantization.
|
||||
tp_rank (`int`):
|
||||
Tensor parallel rank.
|
||||
tp_degree (`int`):
|
||||
Tensor parallel world size.
|
||||
use_pd (`bool`):
|
||||
Whether to use paddle calculation. If False will use numpy.
|
||||
symmetry (`bool`):
|
||||
Whether to use symmetry quantization.
|
||||
"""
|
||||
|
||||
qmax = (1 << (quant_bits)) - 1
|
||||
qmin = 0
|
||||
shape = inputs.shape
|
||||
|
||||
if quant:
|
||||
inputs_processed = inputs.reshape([shape[0] // group_size, group_size, shape[1]])
|
||||
if symmetry:
|
||||
bnt = (1 << (quant_bits - 1)) - 1
|
||||
scales = np.max(np.abs(inputs_processed), axis=1)
|
||||
new_scales = np.repeat(scales, repeats=group_size, axis=0)
|
||||
quant_tensor = np.clip(np.round(inputs / new_scales * bnt), -bnt - 1, bnt)
|
||||
return quant_tensor.astype("int8"), scales
|
||||
|
||||
# scales: [shape[0] // group_size, shape[1]]
|
||||
maxs = np.max(inputs_processed, axis=1)
|
||||
mins = np.min(inputs_processed, axis=1)
|
||||
scales = maxs - mins
|
||||
# new_scales: [shape[0], shape[1]]
|
||||
new_scales = np.repeat(scales, repeats=group_size, axis=0)
|
||||
new_mins = np.repeat(mins, repeats=group_size, axis=0)
|
||||
# add eps to avoid devide zero
|
||||
quant_tensor = np.clip(np.round((inputs - new_mins) / (new_scales) * qmax), qmin, qmax)
|
||||
quant_tensor = np.nan_to_num(quant_tensor)
|
||||
return quant_tensor.astype("uint8"), mins, maxs
|
||||
else:
|
||||
if symmetry:
|
||||
scales = mins
|
||||
bnt = (1 << (quant_bits - 1)) - 1
|
||||
if use_pd:
|
||||
new_scales = paddle.repeat_interleave(scales, group_size, 0)
|
||||
else:
|
||||
new_scales = np.repeat(scales, repeats=group_size, axis=0)
|
||||
|
||||
if tp_rank == -1:
|
||||
dequant_tensor = inputs.astype("float32") * new_scales / bnt
|
||||
elif len(new_scales.shape) == 0 or inputs.shape[-1] == new_scales.shape[-1]:
|
||||
# input tensor was row parallel in tp.
|
||||
dequant_tensor = (
|
||||
inputs.astype("float32")
|
||||
* new_scales[
|
||||
tp_rank * new_scales.shape[0] // tp_degree : (tp_rank + 1) * new_scales.shape[0] // tp_degree
|
||||
]
|
||||
/ bnt
|
||||
)
|
||||
else:
|
||||
# input tensor was column parallel in tp.
|
||||
dequant_tensor = (
|
||||
inputs.astype("float32")
|
||||
* new_scales[
|
||||
:,
|
||||
tp_rank
|
||||
* new_scales.shape[-1]
|
||||
// tp_degree : (tp_rank + 1)
|
||||
* new_scales.shape[-1]
|
||||
// tp_degree,
|
||||
]
|
||||
/ bnt
|
||||
)
|
||||
return dequant_tensor
|
||||
|
||||
scales = maxs - mins
|
||||
if use_pd:
|
||||
new_scales = paddle.repeat_interleave(scales, group_size, 0)
|
||||
new_mins = paddle.repeat_interleave(mins, group_size, 0)
|
||||
else:
|
||||
new_scales = np.repeat(scales, repeats=group_size, axis=0)
|
||||
new_mins = np.repeat(mins, repeats=group_size, axis=0)
|
||||
|
||||
if tp_rank == -1:
|
||||
dequant_tensor = (inputs.astype("float32") / qmax * new_scales) + new_mins
|
||||
elif len(new_scales.shape) == 0 or inputs.shape[-1] == new_scales.shape[-1]:
|
||||
# input tensor was row parallel in tp.
|
||||
dequant_tensor = (
|
||||
inputs.astype("float32")
|
||||
/ qmax
|
||||
* new_scales[
|
||||
tp_rank * new_scales.shape[0] // tp_degree : (tp_rank + 1) * new_scales.shape[0] // tp_degree
|
||||
]
|
||||
) + new_mins[tp_rank * new_mins.shape[0] // tp_degree : (tp_rank + 1) * new_mins.shape[0] // tp_degree]
|
||||
else:
|
||||
# input tensor was column parallel in tp.
|
||||
dequant_tensor = (
|
||||
inputs.astype("float32")
|
||||
/ qmax
|
||||
* new_scales[
|
||||
:, tp_rank * new_scales.shape[-1] // tp_degree : (tp_rank + 1) * new_scales.shape[-1] // tp_degree
|
||||
]
|
||||
) + new_mins[
|
||||
:, tp_rank * new_mins.shape[-1] // tp_degree : (tp_rank + 1) * new_mins.shape[-1] // tp_degree
|
||||
]
|
||||
return dequant_tensor
|
||||
|
||||
|
||||
def merge_int4(x, y):
|
||||
"""
|
||||
merge 2 signed int4 to 1 int8
|
||||
Args:
|
||||
x (`numpy.array`):
|
||||
4bits signed int x.
|
||||
y (`numpy.array`):
|
||||
4bits signed int y.
|
||||
"""
|
||||
int4_high = x << 4
|
||||
int4_low = y & 0x0F
|
||||
final = int4_high | int4_low
|
||||
return final.astype("int8")
|
||||
|
||||
|
||||
def split_int8(final):
|
||||
"""
|
||||
split an int8 to 2 int4 elems
|
||||
Args:
|
||||
final (`numpy.array`):
|
||||
8bits signed int.
|
||||
"""
|
||||
int4_high = final >> 4
|
||||
int4_low = final & 0x0F
|
||||
|
||||
int4_high = np.where(int4_high > 8, int4_high - 16, int4_high)
|
||||
|
||||
high_tensor = paddle.Tensor(int4_high)
|
||||
low_tensor = paddle.Tensor(int4_low)
|
||||
|
||||
return high_tensor, low_tensor
|
||||
|
||||
|
||||
def cal_abs_min_max_channel(inputs, quant_axis=1):
|
||||
"""
|
||||
channel-wise min max scales calculation
|
||||
Args:
|
||||
inputs (`numpy.array`):
|
||||
input tensor for quantization.
|
||||
quant_axis (`int`):
|
||||
dimension where calculating inputs' abs min and max scales on.
|
||||
"""
|
||||
eps = 1e-8
|
||||
reduce_axis = tuple([i for i in range(len(inputs.shape)) if i != quant_axis])
|
||||
abs_max_values = np.max(inputs, axis=reduce_axis)
|
||||
abs_min_values = np.min(inputs, axis=reduce_axis)
|
||||
abs_max_values = np.where(
|
||||
abs_max_values == np.array(0, dtype=inputs.dtype), np.array(eps, dtype=inputs.dtype), abs_max_values
|
||||
)
|
||||
abs_min_values = np.where(
|
||||
abs_min_values == np.array(0, dtype=inputs.dtype), np.array(eps, dtype=inputs.dtype), abs_min_values
|
||||
)
|
||||
return abs_max_values, abs_min_values
|
||||
|
||||
|
||||
def asymmetry_qdq_weight(
|
||||
x, quant_bit=8, quant_axis=-1, mins=None, maxs=None, dequant=False, tp_rank=-1, tp_degree=1, use_pd=False
|
||||
):
|
||||
"""
|
||||
channel-wise asymmetry quantization
|
||||
Args:
|
||||
x (`paddle.Tensor`):
|
||||
The tensor to quantize.
|
||||
quant_bits (`int`):
|
||||
Quantization bits.
|
||||
quant_axis (`int`):
|
||||
Scales calculation axis.
|
||||
mins (`paddle.Tensor`):
|
||||
Min scales tensor in asymmetry quantization.
|
||||
maxs (`paddle.Tensor`):
|
||||
Max scales tensor in asymmetry quantization.
|
||||
dequant (`bool`):
|
||||
True when dequantization, False in quantization.
|
||||
tp_rank (`int`):
|
||||
Model parallel rank.
|
||||
tp_degree (`int`):
|
||||
Model parallel world size.
|
||||
use_pd (`bool`):
|
||||
Whether to use paddle calculation. If False will use numpy.
|
||||
"""
|
||||
|
||||
if mins is None:
|
||||
maxs, mins = cal_abs_min_max_channel(x)
|
||||
bnt = (1 << (quant_bit)) - 1
|
||||
scales = maxs - mins
|
||||
if not dequant:
|
||||
# quant
|
||||
quant_x = np.clip(np.round((x - mins) / scales * bnt), 0, bnt)
|
||||
return quant_x.astype(np.uint8), mins, maxs
|
||||
else:
|
||||
quant_x = x
|
||||
# dequant
|
||||
if not use_pd:
|
||||
if len(scales.shape) == 0 or quant_x.shape[-1] == scales.shape[-1]:
|
||||
# input tensor was row parallel in tp.
|
||||
qdq_x = (quant_x / bnt * scales) + mins
|
||||
else:
|
||||
# input tensor was column parallel in tp.
|
||||
qdq_x = (
|
||||
quant_x
|
||||
/ bnt
|
||||
* scales[tp_rank * scales.shape[0] // tp_degree : (tp_rank + 1) * scales.shape[0] // tp_degree]
|
||||
) + mins[tp_rank * mins.shape[0] // tp_degree : (tp_rank + 1) * mins.shape[0] // tp_degree]
|
||||
return qdq_x.astype(np.float32), scales
|
||||
else:
|
||||
if len(scales.shape) == 0 or quant_x.shape[-1] == scales.shape[-1]:
|
||||
# input tensor was row parallel in tp.
|
||||
qdq_x = (quant_x / bnt * scales.unsqueeze(0).expand(quant_x.shape)) + mins
|
||||
else:
|
||||
# input tensor was column parallel in tp.
|
||||
qdq_x = (
|
||||
quant_x
|
||||
/ bnt
|
||||
* scales[tp_rank * scales.shape[0] // tp_degree : (tp_rank + 1) * scales.shape[0] // tp_degree]
|
||||
.unsqueeze(0)
|
||||
.expand(quant_x.shape)
|
||||
) + mins[tp_rank * mins.shape[0] // tp_degree : (tp_rank + 1) * mins.shape[0] // tp_degree]
|
||||
return qdq_x.astype(paddle.float32), scales
|
||||
|
||||
|
||||
def cal_abs_max_channel(inputs, quant_axis=1):
|
||||
"""
|
||||
channel-wise abs max calculation
|
||||
Args:
|
||||
inputs (`numpy.array`):
|
||||
input tensor for quantization.
|
||||
quant_axis (`int`):
|
||||
dimension where calculating inputs' abs max scales on.
|
||||
"""
|
||||
epsilon = 1e-8
|
||||
reduce_axis = tuple([i for i in range(len(inputs.shape)) if i != quant_axis])
|
||||
abs_max_values = np.max(np.abs(inputs), axis=reduce_axis)
|
||||
# maybe all elements are zero in one group,
|
||||
# so set the scales from those group to an actual number
|
||||
# from divide 0.
|
||||
abs_max_values = np.where(
|
||||
abs_max_values == np.array(0, dtype=inputs.dtype), np.array(epsilon, dtype=inputs.dtype), abs_max_values
|
||||
)
|
||||
return abs_max_values
|
||||
|
||||
|
||||
def qdq_weight(x, quant_bit=8, quant_axis=-1, scales=None, dequant=False, tp_rank=-1, tp_degree=1, use_pd=False):
|
||||
"""
|
||||
channel-wise symmetry quantization
|
||||
Args:
|
||||
x (`paddle.Tensor`):
|
||||
The tensor to quantize.
|
||||
quant_bits (`int`):
|
||||
Quantization bits.
|
||||
quant_axis (`int`):
|
||||
Scales calculation axis.
|
||||
scales (`paddle.Tensor`):
|
||||
Abs max scales tensor in symmetry quantization.
|
||||
dequant (`bool`):
|
||||
True when dequantization, False in quantization.
|
||||
tp_rank (`int`):
|
||||
Model parallel rank.
|
||||
tp_degree (`int`):
|
||||
Model parallel world size.
|
||||
use_pd (`bool`):
|
||||
Whether to use paddle calculation. If False will use numpy.
|
||||
"""
|
||||
|
||||
if scales is None:
|
||||
scales = cal_abs_max_channel(x)
|
||||
bnt = (1 << (quant_bit - 1)) - 1
|
||||
if not dequant:
|
||||
# quant
|
||||
quant_x = np.clip(np.round(x / scales * bnt), -bnt - 1, bnt)
|
||||
return quant_x.astype(np.int8), scales
|
||||
else:
|
||||
quant_x = x
|
||||
# dequant
|
||||
if not use_pd:
|
||||
if len(scales.shape) == 0 or quant_x.shape[-1] == scales.shape[-1]:
|
||||
# input tensor was row parallel in tp.
|
||||
qdq_x = quant_x / bnt * scales
|
||||
else:
|
||||
# input tensor was column parallel in tp.
|
||||
qdq_x = (
|
||||
quant_x
|
||||
/ bnt
|
||||
* scales[tp_rank * scales.shape[0] // tp_degree : (tp_rank + 1) * scales.shape[0] // tp_degree]
|
||||
)
|
||||
# fp32 , int8, int, fp32 or fp64
|
||||
return qdq_x.astype(np.float32), scales
|
||||
else:
|
||||
if len(scales.shape) == 0 or quant_x.shape[-1] == scales.shape[-1]:
|
||||
# input tensor was row parallel in tp.
|
||||
qdq_x = quant_x / bnt * scales.unsqueeze(0).expand(quant_x.shape)
|
||||
else:
|
||||
# input tensor was column parallel in tp.
|
||||
qdq_x = (
|
||||
quant_x
|
||||
/ bnt
|
||||
* scales[tp_rank * scales.shape[0] // tp_degree : (tp_rank + 1) * scales.shape[0] // tp_degree]
|
||||
.unsqueeze(0)
|
||||
.expand(quant_x.shape)
|
||||
)
|
||||
# fp32 , int8, int, fp32 or fp64
|
||||
return qdq_x.astype(paddle.float32), scales
|
||||
Reference in New Issue
Block a user