Files
paddlepaddle--paddlenlp/paddlenlp/quantization/quantization_utils.py
T
wehub-resource-sync 2aaeece67c
Codestyle Check / Lint (push) Has been cancelled
Codestyle Check / Check bypass (push) Has been cancelled
Pipelines-Test / Pipelines-Test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:37:14 +08:00

279 lines
12 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 re
import paddle
import paddle.nn as nn
from paddle.distributed.fleet.meta_parallel import (
ColumnParallelLinear,
RowParallelLinear,
)
from paddle.distributed.fleet.utils.sequence_parallel_utils import (
ColumnSequenceParallelLinear,
RowSequenceParallelLinear,
)
from paddle.incubate.nn.layer.fused_linear import FusedLinear
from paddle.nn.quant import weight_quantize
try:
from .qlora import qlora_weight_linear, qlora_weight_quantize
except:
qlora_weight_linear = None
qlora_weight_quantize = None
from ..utils.log import logger
from .qat_utils import quantize
from .quantization_linear import (
ColumnParallelQuantizationLinear,
QuantizationLinear,
RowParallelQuantizationLinear,
)
LINEAR_CLASSES = [
nn.Linear,
FusedLinear,
ColumnParallelLinear,
RowParallelLinear,
ColumnSequenceParallelLinear,
RowSequenceParallelLinear,
]
def parse_weight_quantize_algo(quantization_config, name):
if quantization_config.ignore_modules is not None and any(
re.fullmatch(ignore_module, name) for ignore_module in quantization_config.ignore_modules
):
weight_quantize_algo = None
elif isinstance(quantization_config.weight_quantize_algo, str):
weight_quantize_algo = quantization_config.weight_quantize_algo
else:
weight_quantize_algo = None
for algo in quantization_config.weight_quantize_algo:
if any(re.fullmatch(module, name) for module in quantization_config.weight_quantize_algo[algo]):
weight_quantize_algo = algo
return weight_quantize_algo
def replace_with_quantization_linear(model, quantization_config, llm_int8_threshold=6.0):
for name, child in model.named_sublayers():
weight_quantize_algo = parse_weight_quantize_algo(quantization_config, name)
if weight_quantize_algo is None:
continue
if any(isinstance(child, linear_class) for linear_class in LINEAR_CLASSES):
if child.bias is None:
bias_attr = False
else:
bias_attr = None
parent = model
*path, last = name.split(".")
for attr in path:
parent = getattr(parent, attr)
if isinstance(child, nn.Linear) or isinstance(child, FusedLinear):
if getattr(child.weight, "transpose_weight", False):
out_feature, in_features = child.weight.shape[0], child.weight.shape[1]
else:
in_features, out_feature = child.weight.shape[0], child.weight.shape[1]
quant_linear = QuantizationLinear(
in_features=in_features,
out_features=out_feature,
quantization_config=quantization_config,
weight_quantize_algo=weight_quantize_algo,
dtype=child._dtype,
bias_attr=bias_attr,
mp_moe=getattr(child.weight, "mp_moe", False),
is_distributed=getattr(child.weight, "is_distributed", False),
)
elif isinstance(child, ColumnParallelLinear):
quant_linear = ColumnParallelQuantizationLinear(
in_features=child.weight.shape[0],
output_size_per_partition=child.weight.shape[1],
quantization_config=quantization_config,
weight_quantize_algo=weight_quantize_algo,
dtype=child._dtype,
bias_attr=bias_attr,
gather_output=child.gather_output,
mp_skip_c_identity=child.mp_skip_c_identity,
)
elif isinstance(child, RowParallelLinear):
quant_linear = RowParallelQuantizationLinear(
input_size_per_partition=child.weight.shape[0],
out_features=child.weight.shape[1],
quantization_config=quantization_config,
weight_quantize_algo=weight_quantize_algo,
dtype=child._dtype,
bias_attr=bias_attr,
input_is_parallel=child.input_is_parallel,
mp_skip_c_identity=child.mp_skip_c_identity,
)
elif isinstance(child, ColumnSequenceParallelLinear):
quant_linear = ColumnParallelQuantizationLinear(
in_features=child.weight.shape[0],
output_size_per_partition=child.weight.shape[1],
quantization_config=quantization_config,
weight_quantize_algo=weight_quantize_algo,
dtype=child._dtype,
bias_attr=bias_attr,
gather_output=False,
sequence_parallel=True,
)
elif isinstance(child, RowSequenceParallelLinear):
quant_linear = RowParallelQuantizationLinear(
input_size_per_partition=child.weight.shape[0],
out_features=child.weight.shape[1],
quantization_config=quantization_config,
weight_quantize_algo=weight_quantize_algo,
dtype=child._dtype,
bias_attr=bias_attr,
input_is_parallel=True,
sequence_parallel=True,
)
setattr(parent, last, quant_linear)
del child
def convert_to_weight_quantize_state_dict(state_dict, name, quantization_config, dtype, weight_quantize_algo):
weight_name = name + ".weight"
quant_weight_name = name + ".quant_weight"
quant_scale_name = name + ".quant_scale"
act_scale_name = name + ".act_scale"
if quant_weight_name in state_dict and quant_scale_name in state_dict:
return state_dict
if weight_name in state_dict:
# gpu weight_quantize will fix in future
target_weight = state_dict.pop(weight_name).cast(dtype).cuda()
if weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
quant_weight, quant_scale = quantize(
target_weight,
weight_quantize_algo,
"weight",
quantization_config,
side="left",
apply_hadamard=quantization_config.apply_hadamard,
)
act_scale = paddle.ones([1], dtype=dtype).cuda()
act_scale.stop_gradient = True
state_dict[act_scale_name] = act_scale
else:
quant_weight, quant_scale = weight_quantize(
x=target_weight,
algo=weight_quantize_algo,
group_size=quantization_config.group_size,
)
state_dict[quant_weight_name] = quant_weight
state_dict[quant_scale_name] = quant_scale
del target_weight
return state_dict
def convert_to_qlora_state_dict(state_dict, name, quantization_config, dtype, weight_quantize_algo):
if qlora_weight_quantize 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 \n"
"3) python ./csrc/setup_cuda.py install"
)
weight_name = name + ".weight"
quant_weight_name = name + ".quant_weight"
quant_name_list = [quant_weight_name]
if not quantization_config.qlora_weight_double_quant:
quant_scale_name = name + ".quant_scale"
quant_name_list += [quant_scale_name]
else:
qquant_scale_name = name + ".qquant_scale"
double_quant_scale_name = name + ".double_quant_scale"
quant_sacle_offset_name = name + ".quant_sacle_offset"
quant_name_list += [qquant_scale_name, double_quant_scale_name, quant_sacle_offset_name]
if all(quant_name in state_dict for quant_name in quant_name_list):
return state_dict
elif weight_name in state_dict:
target_weight = state_dict.pop(weight_name).cast(dtype).cuda()
qlora_state_dict = qlora_weight_quantize(
weight=target_weight,
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,
linear_name=name,
return_dict=True,
)
state_dict.update(qlora_state_dict)
del target_weight
return state_dict
def convert_to_quantize_state_dict(state_dict, quantization_linear_list, quantization_config, dtype):
for name in quantization_linear_list:
# Get quantization algorithm
weight_quantize_algo = parse_weight_quantize_algo(quantization_config, name)
if weight_quantize_algo is None:
continue
# Convert state dict
if weight_quantize_algo in [
"weight_only_int8",
"weight_only_int4",
"llm.int8",
"a8w8linear",
"a8w4linear",
"fp8linear",
]:
convert_to_weight_quantize_state_dict(state_dict, name, quantization_config, dtype, weight_quantize_algo)
elif weight_quantize_algo in ["fp4", "nf4"]:
convert_to_qlora_state_dict(state_dict, name, quantization_config, dtype, weight_quantize_algo)
else:
raise NotImplementedError(
f"Please check the quantization_config.weight_quantize_algo: {quantization_config.weight_quantize_algo}"
)
return state_dict
def update_loaded_state_dict_keys(state_dict, quantization_linear_list, quantization_config, ignore_warning=False):
for name in quantization_linear_list:
weight_name = name + ".weight"
quant_weight_name = name + ".quant_weight"
quant_scale_name = name + ".quant_scale"
act_scale_name = name + ".act_scale"
qquant_scale_name = name + ".qquant_scale"
double_quant_scale_name = name + ".double_quant_scale"
quant_sacle_offset_name = name + ".quant_sacle_offset"
if quant_weight_name in state_dict and quant_scale_name in state_dict:
continue
elif weight_name in state_dict:
state_dict.remove(weight_name)
state_dict.append(quant_weight_name)
if quantization_config.qlora_weight_double_quant:
state_dict.append(qquant_scale_name)
state_dict.append(double_quant_scale_name)
state_dict.append(quant_sacle_offset_name)
else:
state_dict.append(quant_scale_name)
weight_quantize_algo = parse_weight_quantize_algo(quantization_config, name)
if weight_quantize_algo in ["a8w8linear", "a8w4linear", "fp8linear"]:
state_dict.append(act_scale_name)
else:
if not ignore_warning:
logger.warning(
f"Cannot find {weight_name} in state_dict or {quant_weight_name} and {quant_scale_name} in state_dict"
)
return state_dict