700 lines
27 KiB
Python
700 lines
27 KiB
Python
# Copyright (c) 2025 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 paddle
|
|
import paddle.nn as nn
|
|
from paddle.autograd import PyLayer
|
|
from paddle.distributed import fleet
|
|
from paddle.distributed.fleet.base import topology as tp
|
|
from paddle.distributed.fleet.layers.mpu import mp_ops
|
|
from paddle.distributed.fleet.utils.sequence_parallel_utils import (
|
|
AllGatherOp,
|
|
ReduceScatterOp,
|
|
)
|
|
from paddle.nn.quant import llm_int8_linear, weight_dequantize, weight_only_linear
|
|
|
|
from paddlenlp.utils import infohub
|
|
|
|
from .qat_utils import QATFunc
|
|
|
|
try:
|
|
from .qlora import qlora_weight_dequantize, qlora_weight_linear
|
|
except:
|
|
qlora_weight_linear = None
|
|
qlora_weight_dequantize = None
|
|
|
|
QuantMapping = {
|
|
# (quant_dtype, quant_weight_bit)
|
|
"weight_only_int8": ("int8", 8),
|
|
"weight_only_int4": ("int4", 4),
|
|
"llm.int8": ("int8", 8),
|
|
"fp4": ("fp4", 4),
|
|
"nf4": ("nf4", 4),
|
|
"a8w8linear": ("int8", 8),
|
|
"a8w4linear": ("int8", 8),
|
|
"fp8linear": ("fp8", 8),
|
|
}
|
|
|
|
|
|
def quant_weight_forward(
|
|
x,
|
|
quant_weight,
|
|
bias,
|
|
quant_scale,
|
|
quant_state,
|
|
quant_dtype,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
):
|
|
if weight_quantize_algo in ["weight_only_int8", "weight_only_int4"]:
|
|
output = weight_only_linear(
|
|
x=x,
|
|
weight=quant_weight,
|
|
bias=bias,
|
|
weight_scale=quant_scale,
|
|
weight_dtype=quant_dtype,
|
|
group_size=quantization_config.group_size,
|
|
)
|
|
elif weight_quantize_algo in ["llm.int8"]:
|
|
output = llm_int8_linear(x, quant_weight, bias, quant_scale, quantization_config.llm_int8_threshold)
|
|
elif weight_quantize_algo in ["fp4", "nf4"]:
|
|
output = qlora_weight_linear(
|
|
x=x,
|
|
quant_weight=quant_weight,
|
|
dtype=dtype,
|
|
state=quant_state if quantization_config.qlora_weight_double_quant else quant_scale,
|
|
quant_algo=weight_quantize_algo,
|
|
double_quant=quantization_config.qlora_weight_double_quant,
|
|
block_size=quantization_config.qlora_weight_blocksize,
|
|
double_quant_block_size=quantization_config.qlora_weight_double_quant_block_size,
|
|
bias=bias,
|
|
)
|
|
|
|
return output
|
|
|
|
|
|
def dequant_weight(
|
|
quant_weight,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
quant_scale,
|
|
quant_state,
|
|
input_shape,
|
|
):
|
|
if weight_quantize_algo in ["weight_only_int8", "weight_only_int4", "llm.int8"]:
|
|
quant_dequant_weight = weight_dequantize(
|
|
x=quant_weight,
|
|
scale=quant_scale,
|
|
algo=weight_quantize_algo,
|
|
out_dtype=dtype,
|
|
group_size=quantization_config.group_size,
|
|
)
|
|
elif weight_quantize_algo in ["fp4", "nf4"]:
|
|
quant_dequant_weight = (
|
|
qlora_weight_dequantize(
|
|
quant_weight=quant_weight,
|
|
quant_algo=weight_quantize_algo,
|
|
state=quant_state if quantization_config.qlora_weight_double_quant else quant_scale,
|
|
double_quant=quantization_config.qlora_weight_double_quant,
|
|
block_size=quantization_config.qlora_weight_blocksize,
|
|
double_quant_block_size=quantization_config.qlora_weight_double_quant_block_size,
|
|
)
|
|
.reshape([input_shape[-1], -1])
|
|
.cast(dtype)
|
|
)
|
|
return quant_dequant_weight
|
|
|
|
|
|
class QuantizationLinearFunc(PyLayer):
|
|
@staticmethod
|
|
def forward(
|
|
ctx,
|
|
x,
|
|
quant_weight,
|
|
bias,
|
|
quant_scale,
|
|
quant_state,
|
|
quant_dtype,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
):
|
|
|
|
output = quant_weight_forward(
|
|
x=x,
|
|
quant_weight=quant_weight,
|
|
bias=bias,
|
|
quant_scale=quant_scale,
|
|
quant_state=quant_state,
|
|
quant_dtype=quant_dtype,
|
|
quantization_config=quantization_config,
|
|
weight_quantize_algo=weight_quantize_algo,
|
|
dtype=dtype,
|
|
)
|
|
ctx.quant_dtype = quant_dtype
|
|
ctx.quantization_config = quantization_config
|
|
ctx.weight_quantize_algo = weight_quantize_algo
|
|
ctx.dtype = dtype
|
|
if ctx.weight_quantize_algo in ["fp4", "nf4"] and ctx.quantization_config.qlora_weight_double_quant:
|
|
qquant_scale, double_quant_scale, quant_scale_offset = quant_state
|
|
ctx.save_for_backward(x, quant_weight, bias, qquant_scale, double_quant_scale, quant_scale_offset)
|
|
else:
|
|
ctx.save_for_backward(x, quant_weight, bias, quant_scale)
|
|
return output
|
|
|
|
@staticmethod
|
|
def backward(ctx, grad_output):
|
|
if ctx.weight_quantize_algo in ["fp4", "nf4"] and ctx.quantization_config.qlora_weight_double_quant:
|
|
x, quant_weight, bias, qquant_scale, double_quant_scale, quant_scale_offset = ctx.saved_tensor()
|
|
quant_state = (qquant_scale, double_quant_scale, quant_scale_offset)
|
|
quant_scale = None
|
|
else:
|
|
x, quant_weight, bias, quant_scale = ctx.saved_tensor()
|
|
quant_state = None
|
|
|
|
qdq_weight = dequant_weight(
|
|
quant_weight=quant_weight,
|
|
quantization_config=ctx.quantization_config,
|
|
weight_quantize_algo=ctx.weight_quantize_algo,
|
|
dtype=ctx.dtype,
|
|
quant_scale=quant_scale,
|
|
quant_state=quant_state,
|
|
input_shape=x.shape,
|
|
)
|
|
|
|
if not x.stop_gradient:
|
|
input_grad = paddle.matmul(grad_output, qdq_weight.T)
|
|
else:
|
|
input_grad = None
|
|
|
|
if not quant_weight.stop_gradient:
|
|
if len(x.shape) == 2:
|
|
weight_grad = paddle.matmul(x.transpose([1, 0]), grad_output)
|
|
else:
|
|
weight_grad = paddle.matmul(
|
|
x.reshape([-1, x.shape[-1]]).transpose([1, 0]), grad_output.reshape([-1, grad_output.shape[-1]])
|
|
)
|
|
else:
|
|
weight_grad = None
|
|
|
|
if bias is not None and not bias.stop_gradient:
|
|
bias_grad = grad_output.sum(axis=[0, 1])
|
|
else:
|
|
bias_grad = None
|
|
|
|
return input_grad, weight_grad, bias_grad
|
|
|
|
|
|
def quant_weight_linear(
|
|
x,
|
|
quant_weight,
|
|
quant_dtype,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
quant_scale=None,
|
|
quant_state=None,
|
|
bias=None,
|
|
act_state=None,
|
|
):
|
|
if weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
|
|
|
|
state, training, act_scale, group = act_state
|
|
|
|
return QATFunc.apply(
|
|
x,
|
|
quant_weight,
|
|
bias,
|
|
quant_scale,
|
|
quantization_config,
|
|
dtype,
|
|
state,
|
|
training,
|
|
act_scale,
|
|
weight_quantize_algo,
|
|
group,
|
|
)
|
|
else:
|
|
return QuantizationLinearFunc.apply(
|
|
x,
|
|
quant_weight,
|
|
bias,
|
|
quant_scale,
|
|
quant_state,
|
|
quant_dtype,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
)
|
|
|
|
|
|
def get_act_scale_group(is_row=False):
|
|
if paddle.distributed.is_initialized():
|
|
if getattr(infohub, "scale_group") is None:
|
|
hcg = fleet.get_hybrid_communicate_group()
|
|
rank = hcg._dp_degree * hcg._sharding_degree
|
|
group_no_row = hcg.create_fuse_group(["data", "sharding"])[1] if rank > 1 else None
|
|
rank *= hcg._mp_degree
|
|
group_row = hcg.create_fuse_group(["data", "sharding", "model"])[1] if rank > 1 else None
|
|
|
|
setattr(infohub, "scale_group", [group_no_row, group_row])
|
|
group = infohub.scale_group[1] if is_row else infohub.scale_group[0]
|
|
else:
|
|
group = None
|
|
return group
|
|
|
|
|
|
class QuantizationLinear(nn.Layer):
|
|
"""Quantization Linear layer."""
|
|
|
|
def __init__(
|
|
self,
|
|
in_features,
|
|
out_features,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
bias_attr=None,
|
|
mp_moe=False,
|
|
is_distributed=False,
|
|
):
|
|
super().__init__()
|
|
self.in_features = in_features
|
|
self.out_features = out_features
|
|
self.quantization_config = quantization_config
|
|
self.weight_quantize_algo = weight_quantize_algo
|
|
self._dtype = dtype
|
|
self.quant_dtype, self.quant_weight_bit = QuantMapping[self.weight_quantize_algo]
|
|
self.state = 0
|
|
|
|
# PaddlePaddle doesn't support 4bit data type, one 8bit data represents two 4bit data.
|
|
# paddle.nn.quant.weight_quantize will transpose in_features and out_features.
|
|
if self.weight_quantize_algo in [
|
|
"weight_only_int8",
|
|
"weight_only_int4",
|
|
"llm.int8",
|
|
"a8w8linear",
|
|
"a8w4linear",
|
|
"fp8linear",
|
|
]:
|
|
self.quant_weight = self.create_parameter(
|
|
shape=[out_features // 2, in_features] if self.quant_weight_bit == 4 else [out_features, in_features],
|
|
dtype="int8",
|
|
is_bias=False,
|
|
)
|
|
if self.quantization_config.group_size == -1:
|
|
self.quant_scale = self.create_parameter(
|
|
shape=[out_features] if self.weight_quantize_algo not in ["fp8linear"] else [1],
|
|
dtype=self._dtype,
|
|
is_bias=False,
|
|
)
|
|
self.quant_scale.stop_gradient = True
|
|
else:
|
|
# TODO(lugimzzz): support groupwise in next PR
|
|
raise NotImplementedError("Not yet support grouwise weightonly quantization.")
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
|
|
self.act_scale = self.create_parameter(
|
|
shape=[1], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
|
|
)
|
|
self.act_scale.stop_gradient = True
|
|
self.group = get_act_scale_group()
|
|
|
|
elif self.weight_quantize_algo in ["fp4", "nf4"]:
|
|
if qlora_weight_linear is None:
|
|
raise ImportError(
|
|
"Please run the following commands to install: qlora related package first\n"
|
|
"1) git clone https://github.com/PaddlePaddle/PaddleSlim \n"
|
|
"2) cd PaddleSlim && pip install -e .\n"
|
|
"3) cd csrc && python ./setup_cuda.py install"
|
|
)
|
|
self.quant_weight = self.create_parameter(
|
|
shape=[out_features * in_features // 2, 1],
|
|
attr=paddle.nn.initializer.Constant(value=0),
|
|
dtype="uint8",
|
|
is_bias=False,
|
|
)
|
|
if self.quantization_config.qlora_weight_double_quant:
|
|
# quantized quant_scale
|
|
self.qquant_scale = self.create_parameter(
|
|
shape=[in_features * out_features // self.quantization_config.qlora_weight_blocksize],
|
|
dtype="uint8",
|
|
is_bias=False,
|
|
)
|
|
# double quant_scale: quant_scale of quantized quant_scale
|
|
self.double_quant_scale = self.create_parameter(
|
|
shape=[
|
|
in_features
|
|
* out_features
|
|
// self.quantization_config.qlora_weight_blocksize
|
|
// self.quantization_config.qlora_weight_double_quant_block_size
|
|
],
|
|
dtype="float32",
|
|
is_bias=False,
|
|
)
|
|
self.quant_scale_offset = self.create_parameter(
|
|
shape=[],
|
|
dtype="float32",
|
|
is_bias=False,
|
|
)
|
|
else:
|
|
self.quant_scale = self.create_parameter(
|
|
shape=[in_features * out_features // self.quantization_config.qlora_weight_blocksize],
|
|
dtype="float32",
|
|
is_bias=False,
|
|
)
|
|
else:
|
|
raise NotImplementedError(f"Not yet support weight_quantize_algo: {self.weight_quantize_algo}")
|
|
if bias_attr is False:
|
|
self.bias = None
|
|
else:
|
|
self.bias = self.create_parameter(
|
|
shape=[out_features],
|
|
attr=bias_attr,
|
|
dtype=self._dtype,
|
|
is_bias=True,
|
|
)
|
|
if mp_moe or is_distributed:
|
|
for p in self.parameters():
|
|
p.is_distributed = is_distributed
|
|
p.mp_moe = mp_moe
|
|
self.quant_weight.weight_quantize_algo = self.weight_quantize_algo
|
|
|
|
def forward(self, x):
|
|
output = quant_weight_linear(
|
|
x=x,
|
|
quant_weight=self.quant_weight,
|
|
quant_dtype=self.quant_dtype,
|
|
quantization_config=self.quantization_config,
|
|
weight_quantize_algo=self.weight_quantize_algo,
|
|
dtype=self._dtype,
|
|
quant_scale=self.quant_scale,
|
|
quant_state=(self.qquant_scale, self.double_quant_scale, self.quant_scale_offset)
|
|
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
|
|
else None,
|
|
bias=self.bias,
|
|
act_state=(self.state, self.training, self.act_scale, self.group)
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
|
|
else None,
|
|
)
|
|
if self.training:
|
|
self.state += 1
|
|
return output
|
|
|
|
|
|
class ColumnParallelQuantizationLinear(nn.Layer):
|
|
"""Quantization Linear layer with mp parallelized(column).
|
|
The code implementation refers to paddle.distributed.fleet.meta_parallel.ColumnParallelLinear.
|
|
https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/distributed/fleet/layers/mpu/mp_layers.py#L310
|
|
Different from ColumnParallelLinear, this class keeps weight in INT8/INT4 with quant scale, and supports matrix
|
|
multiplication(weight_only_linear/llm_int8_linear) for input tensor(fp16/bf16) and quantized weight(INT8/INT4)
|
|
and bias addition if provided.
|
|
Notice: quantized weight shape is transposed of weight shape in ColumnParallelLinear.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
in_features,
|
|
output_size_per_partition,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
bias_attr=None,
|
|
gather_output=True,
|
|
mp_skip_c_identity=False,
|
|
mp_group=None,
|
|
sequence_parallel=False,
|
|
):
|
|
super().__init__()
|
|
self.in_features = in_features
|
|
self.output_size_per_partition = output_size_per_partition
|
|
self.weight_quantize_algo = weight_quantize_algo
|
|
self.quantization_config = quantization_config
|
|
self._dtype = dtype
|
|
self.mp_skip_c_identity = mp_skip_c_identity
|
|
self.quant_dtype, self.quant_weight_bit = QuantMapping[self.weight_quantize_algo]
|
|
self.state = 0
|
|
self.model_parallel_group = (
|
|
tp._HYBRID_PARALLEL_GROUP.get_model_parallel_group() if mp_group is None else mp_group
|
|
)
|
|
self.world_size = (
|
|
tp._HYBRID_PARALLEL_GROUP.get_model_parallel_world_size() if mp_group is None else mp_group.nranks
|
|
)
|
|
self.is_mp = self.world_size > 1
|
|
self.gather_output = gather_output
|
|
self.sequence_parallel = sequence_parallel
|
|
if self.sequence_parallel and self.gather_output:
|
|
raise ValueError("Sequence parallel does not support gather_output")
|
|
|
|
# PaddlePaddle doesn't support Int4 data type, one Int8 data represents two Int4 data.
|
|
if self.weight_quantize_algo in [
|
|
"weight_only_int8",
|
|
"weight_only_int4",
|
|
"llm.int8",
|
|
"a8w8linear",
|
|
"a8w4linear",
|
|
"fp8linear",
|
|
]:
|
|
self.quant_weight = self.create_parameter(
|
|
shape=[self.output_size_per_partition // 2, in_features]
|
|
if self.quant_dtype == "int4"
|
|
else [self.output_size_per_partition, in_features],
|
|
dtype="int8",
|
|
is_bias=False,
|
|
)
|
|
self.quant_weight.is_distributed = True if self.is_mp else False
|
|
if self.quant_weight.is_distributed:
|
|
self.quant_weight.split_axis = 0
|
|
|
|
if self.quantization_config.group_size == -1:
|
|
self.quant_scale = self.create_parameter(
|
|
shape=[self.output_size_per_partition] if self.weight_quantize_algo not in ["fp8linear"] else [1],
|
|
dtype=self._dtype,
|
|
is_bias=False,
|
|
)
|
|
self.quant_scale.stop_gradient = True
|
|
if self.weight_quantize_algo in ["fp8linear", "a8w4linear", "a8w8linear"]:
|
|
self.quant_scale.is_distributed = False
|
|
else:
|
|
self.quant_scale.is_distributed = True if self.is_mp else False
|
|
if self.quant_scale.is_distributed:
|
|
self.quant_scale.split_axis = 0
|
|
else:
|
|
# TODO(lugimzzz): support groupwise in next PR
|
|
raise NotImplementedError("Not yet support grouwise weightonly quantization.")
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
|
|
self.act_scale = self.create_parameter(
|
|
shape=[1], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
|
|
)
|
|
self.act_scale.is_distributed = False
|
|
self.act_scale.stop_gradient = True
|
|
self.group = get_act_scale_group()
|
|
else:
|
|
raise NotImplementedError(f"Not yet support weight_quantize_algo: {self.weight_quantize_algo}")
|
|
if bias_attr is False:
|
|
self.bias = None
|
|
else:
|
|
self.bias = self.create_parameter(
|
|
shape=[self.output_size_per_partition],
|
|
attr=bias_attr,
|
|
dtype=self._dtype,
|
|
is_bias=True,
|
|
)
|
|
self.bias.is_distributed = True if self.is_mp else False
|
|
if self.bias.is_distributed:
|
|
self.bias.split_axis = 0
|
|
self.quant_weight.weight_quantize_algo = self.weight_quantize_algo
|
|
|
|
def forward(self, x):
|
|
if self.is_mp:
|
|
if self.sequence_parallel:
|
|
input_parallel = AllGatherOp.apply(x)
|
|
else:
|
|
input_parallel = mp_ops._c_identity(
|
|
x,
|
|
group=self.model_parallel_group,
|
|
skip_c_identity_dynamic=self.mp_skip_c_identity,
|
|
)
|
|
else:
|
|
input_parallel = x
|
|
|
|
output_parallel = quant_weight_linear(
|
|
x=input_parallel,
|
|
quant_weight=self.quant_weight,
|
|
quant_dtype=self.quant_dtype,
|
|
quantization_config=self.quantization_config,
|
|
weight_quantize_algo=self.weight_quantize_algo,
|
|
dtype=self._dtype,
|
|
quant_scale=self.quant_scale,
|
|
quant_state=(self.qquant_scale, self.double_quant_scale, self.quant_scale_offset)
|
|
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
|
|
else None,
|
|
bias=self.bias,
|
|
act_state=(self.state, self.training, self.act_scale, self.group)
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
|
|
else None,
|
|
)
|
|
if self.training:
|
|
self.state += 1
|
|
|
|
if self.gather_output and self.is_mp:
|
|
output = mp_ops._c_concat(output_parallel, group=self.model_parallel_group)
|
|
else:
|
|
output = output_parallel
|
|
return output
|
|
|
|
|
|
class RowParallelQuantizationLinear(nn.Layer):
|
|
"""Quantization Linear layer with mp parallelized(row).
|
|
The code implementation refers to paddle.distributed.fleet.meta_parallel.RowParallelLinear.
|
|
https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/distributed/fleet/layers/mpu/mp_layers.py#L517
|
|
Different from RowParallelLinear, this class keeps weight in INT8/INT4 with quant scale, and supports matrix
|
|
multiplication(weight_only_linear/llm_int8_linear) for input tensor(fp16/bf16) and quantized weight(INT8/INT4)
|
|
and bias addition if provided.
|
|
Notice: quantized weight shape is transposed of weight shape in RowParallelLinear.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
input_size_per_partition,
|
|
out_features,
|
|
quantization_config,
|
|
weight_quantize_algo,
|
|
dtype,
|
|
bias_attr=None,
|
|
input_is_parallel=False,
|
|
mp_skip_c_identity=False,
|
|
mp_group=None,
|
|
sequence_parallel=False,
|
|
):
|
|
super().__init__()
|
|
self.input_size_per_partition = input_size_per_partition
|
|
self.out_features = out_features
|
|
self.quantization_config = quantization_config
|
|
self.weight_quantize_algo = weight_quantize_algo
|
|
self._dtype = dtype
|
|
self.mp_skip_c_identity = mp_skip_c_identity
|
|
self.quant_dtype, self.quant_weight_bit = QuantMapping[self.weight_quantize_algo]
|
|
self.state = 0
|
|
|
|
self.model_parallel_group = (
|
|
tp._HYBRID_PARALLEL_GROUP.get_model_parallel_group() if mp_group is None else mp_group
|
|
)
|
|
self.world_size = (
|
|
tp._HYBRID_PARALLEL_GROUP.get_model_parallel_world_size() if mp_group is None else mp_group.nranks
|
|
)
|
|
self.is_mp = self.world_size > 1
|
|
self.input_is_parallel = input_is_parallel
|
|
self.sequence_parallel = sequence_parallel
|
|
if not self.input_is_parallel and self.sequence_parallel:
|
|
raise ValueError("Sequence parallel only support input_is_parallel.")
|
|
|
|
# PaddlePaddle doesn't support Int4 data type, one Int8 data represents two Int4 data.
|
|
# paddle.nn.quant.weight_quantize will transpose in_features and out_features.
|
|
if self.weight_quantize_algo in [
|
|
"weight_only_int8",
|
|
"weight_only_int4",
|
|
"llm.int8",
|
|
"a8w8linear",
|
|
"a8w4linear",
|
|
"fp8linear",
|
|
]:
|
|
self.quant_weight = self.create_parameter(
|
|
shape=[out_features // 2, self.input_size_per_partition]
|
|
if self.quant_dtype == "int4"
|
|
else [out_features, self.input_size_per_partition],
|
|
dtype="int8",
|
|
is_bias=False,
|
|
)
|
|
self.quant_weight.is_distributed = True if self.is_mp else False
|
|
if self.quant_weight.is_distributed:
|
|
self.quant_weight.split_axis = 1
|
|
|
|
if self.quantization_config.group_size == -1:
|
|
self.quant_scale = self.create_parameter(
|
|
shape=[out_features] if self.weight_quantize_algo not in ["fp8linear"] else [1],
|
|
dtype=self._dtype,
|
|
is_bias=False,
|
|
)
|
|
self.quant_scale.stop_gradient = True
|
|
if self.weight_quantize_algo in ["fp8linear", "a8w4linear", "a8w8linear"]:
|
|
self.quant_scale.is_distributed = False
|
|
else:
|
|
self.quant_scale.is_distributed = True if self.is_mp else False
|
|
if self.quant_scale.is_distributed:
|
|
self.quant_scale.split_axis = 0
|
|
else:
|
|
# TODO(lugimzzz): support groupwise in next PR
|
|
raise NotImplementedError("Not yet support grouwise weightonly quantization.")
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
|
|
self.act_scale = self.create_parameter(
|
|
shape=[1], dtype=self._dtype, is_bias=False, default_initializer=nn.initializer.Constant(value=0.0)
|
|
)
|
|
self.act_scale.is_distributed = False
|
|
self.act_scale.stop_gradient = True
|
|
self.group = get_act_scale_group(is_row=True)
|
|
else:
|
|
raise NotImplementedError(f"Not yet support weight_quantize_algo: {self.weight_quantize_algo}")
|
|
|
|
if bias_attr is False:
|
|
self.bias = None
|
|
else:
|
|
self.bias = self.create_parameter(
|
|
shape=[out_features],
|
|
attr=bias_attr,
|
|
dtype=self._dtype,
|
|
is_bias=True,
|
|
)
|
|
|
|
self.quant_weight.weight_quantize_algo = self.weight_quantize_algo
|
|
|
|
def forward(self, x):
|
|
if self.input_is_parallel or (not self.is_mp):
|
|
input_parallel = x
|
|
else:
|
|
# split last dim
|
|
input_parallel = mp_ops._c_split(x, group=self.model_parallel_group)
|
|
|
|
# with paddle.amp.auto_cast(enable=False):
|
|
if self.is_mp:
|
|
output_parallel = quant_weight_linear(
|
|
x=input_parallel,
|
|
quant_weight=self.quant_weight,
|
|
quant_dtype=self.quant_dtype,
|
|
quantization_config=self.quantization_config,
|
|
weight_quantize_algo=self.weight_quantize_algo,
|
|
dtype=self._dtype,
|
|
quant_scale=self.quant_scale,
|
|
quant_state=(self.qquant_scale, self.double_quant_scale, self.quant_scale_offset)
|
|
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
|
|
else None,
|
|
bias=None,
|
|
act_state=(self.state, self.training, self.act_scale, self.group)
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
|
|
else None,
|
|
)
|
|
if self.sequence_parallel:
|
|
output_ = ReduceScatterOp.apply(output_parallel)
|
|
else:
|
|
output_ = mp_ops._mp_allreduce(
|
|
output_parallel,
|
|
group=self.model_parallel_group,
|
|
use_calc_stream=True,
|
|
use_model_parallel=True,
|
|
skip_c_identity_dynamic=self.mp_skip_c_identity,
|
|
)
|
|
output = output_ + self.bias if self.bias is not None else output_
|
|
else:
|
|
output = quant_weight_linear(
|
|
x=input_parallel,
|
|
quant_weight=self.quant_weight,
|
|
quant_dtype=self.quant_dtype,
|
|
quantization_config=self.quantization_config,
|
|
weight_quantize_algo=self.weight_quantize_algo,
|
|
dtype=self._dtype,
|
|
quant_scale=self.quant_scale,
|
|
quant_state=(self.qquant_scale, self.double_quant_scale, self.quant_scale_offset)
|
|
if (self.weight_quantize_algo in ["fp4", "nf4"] and self.quantization_config.qlora_weight_double_quant)
|
|
else None,
|
|
bias=self.bias,
|
|
act_state=(self.state, self.training, self.act_scale, self.group)
|
|
if self.weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]
|
|
else None,
|
|
)
|
|
if self.training:
|
|
self.state += 1
|
|
|
|
return output
|