Files
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

433 lines
16 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
from paddle.autograd import PyLayer
from .hadamard_utils import apply_hadamard_matmul
try:
from transformer_engine import transformer_engine_paddle as tex
from transformer_engine.paddle.constants import FP8BwdTensors, FP8FwdTensors
from transformer_engine.paddle.cpp_extensions import fp8_gemm
from transformer_engine.paddle.layer.base import get_workspace
TE_DType = {
paddle.float8_e4m3fn: tex.DType.kFloat8E4M3,
paddle.float8_e5m2: tex.DType.kFloat8E5M2,
}
SUPPORT_TE = True
except ImportError:
SUPPORT_TE = False
from paddle.linalg import fp8_fp8_half_gemm_fused
QMIN_QMAX_MAPPING = {
"a8w8linear_activation": (-128, 127),
"a8w4linear_activation": (-128, 127),
"a8w8linear_weight": (-128, 127),
"a8w4linear_weight": (-8, 7),
"float8_e4m3fn": (-488, 488),
"float8_e5m2": (-57344, 57344),
}
def quantize(
x,
weight_quantize_algo,
tensor_type,
quantization_config,
side="right",
apply_hadamard=False,
act_scale=None,
state=0,
training=False,
group=None,
):
if apply_hadamard:
target_x = apply_hadamard_matmul(x, side, quantization_config.hadamard_block_size)
hadamard_scale = quantization_config.hadamard_block_size
else:
target_x, hadamard_scale = x, 1.0
if weight_quantize_algo in ["fp8linear"]:
qmin, qmax = QMIN_QMAX_MAPPING[quantization_config.fp8_format[tensor_type]]
else:
qmin, qmax = QMIN_QMAX_MAPPING[weight_quantize_algo + "_" + tensor_type]
if tensor_type == "activation":
if act_scale is not None:
if training:
scale = (paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon).reshape([1])
if group is not None:
paddle.distributed.all_reduce(scale, op=paddle.distributed.ReduceOp.MAX, group=group, sync_op=True)
if state < quantization_config.apply_online_actscale_step:
act_scale[:] = (state * act_scale + scale) / (state + 1)
else:
scale = (
1 - quantization_config.actscale_moving_rate
) * act_scale + quantization_config.actscale_moving_rate * scale
act_scale[:] = scale
else:
scale = act_scale
else:
scale = (paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon).reshape([1])
if weight_quantize_algo in ["a8w8linear", "a8w4linear"]:
quant_x = paddle.clip((target_x / scale).round(), qmin, qmax).astype("int8")
elif weight_quantize_algo in ["fp8linear"]:
quant_x = (target_x / scale).astype(quantization_config.fp8_format[tensor_type])
else:
raise NotImplementedError(f"Unknown {weight_quantize_algo}.")
elif tensor_type == "weight":
if weight_quantize_algo in ["a8w8linear", "a8w4linear"]:
# channelwise
scale = paddle.max(paddle.abs(target_x), axis=0, keepdim=True) / qmax + quantization_config.scale_epsilon
if group is not None:
paddle.distributed.all_reduce(scale, op=paddle.distributed.ReduceOp.MAX, group=group, sync_op=True)
quant_x = paddle.clip((target_x / scale).round(), qmin, qmax).astype("int8").T
scale = scale.squeeze(0) / hadamard_scale
elif weight_quantize_algo in ["fp8linear"]:
scale = paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon
if group is not None:
paddle.distributed.all_reduce(scale, op=paddle.distributed.ReduceOp.MAX, group=group, sync_op=True)
quant_x = (target_x / scale).astype(quantization_config.fp8_format[tensor_type]).view("int8").T
scale = (scale / hadamard_scale).reshape([1])
else:
raise NotImplementedError(f"Unknown {weight_quantize_algo}.")
elif tensor_type == "grad_output":
if weight_quantize_algo in ["fp8linear"]:
scale = (paddle.max(paddle.abs(target_x)) / qmax + quantization_config.scale_epsilon).reshape([1])
quant_x = (target_x / scale).astype(quantization_config.fp8_format[tensor_type])
scale = scale / hadamard_scale
else:
raise NotImplementedError(f"Unknown {weight_quantize_algo}.")
else:
raise NotImplementedError(f"Unknown {tensor_type}.")
scale.stop_gradient = True
return quant_x, scale
def dequantize(
quant_x, scale, tensor_type, weight_quantize_algo, quantization_config, apply_hadamard=False, side="left"
):
if tensor_type == "weight":
if weight_quantize_algo in ["a8w8linear", "a8w4linear"]:
x = quant_x.T.astype(scale.dtype)
elif weight_quantize_algo in ["fp8linear"]:
x = quant_x.view(quantization_config.fp8_format[tensor_type]).T.astype(scale.dtype)
else:
raise NotImplementedError(f"Unknown weight_quantize_algo: {weight_quantize_algo}")
if apply_hadamard:
x = apply_hadamard_matmul(x, side, quantization_config.hadamard_block_size)
x *= scale
else:
raise NotImplementedError(f"Unknown {tensor_type}.")
return x
def int8_forward(
x,
quant_w,
scale_w,
weight_quantize_algo,
bias=None,
quantization_config=None,
state=0,
training=False,
act_scale=None,
group=None,
):
quant_x, scale_x = quantize(
x=x,
weight_quantize_algo=weight_quantize_algo,
tensor_type="activation",
quantization_config=quantization_config,
side="right",
apply_hadamard=quantization_config.apply_hadamard,
act_scale=act_scale,
state=state,
training=training,
group=group,
)
out = paddle.matmul(quant_x, quant_w.T).astype(scale_w.dtype) * (scale_x * scale_w)
if bias is not None:
out += bias
return out, quant_x, scale_x
def int8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale):
if not ctx.x_stop_gradient:
qdq_weight = dequantize(
quant_weight,
quant_scale,
"weight",
ctx.weight_quantize_algo,
ctx.quantization_config,
ctx.quantization_config.apply_hadamard,
"left",
)
input_grad = paddle.matmul(grad_output, qdq_weight.T)
else:
input_grad = None
if not ctx.w_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
return input_grad, weight_grad
def fp8_forward(
x,
w_fp8,
w_scale,
weight_quantize_algo,
bias=None,
dtype=None,
quantization_config=None,
state=0,
training=False,
act_scale=None,
group=None,
):
x_fp8, x_scale = quantize(
x,
weight_quantize_algo,
"activation",
quantization_config,
side="right",
apply_hadamard=quantization_config.apply_hadamard,
act_scale=act_scale,
state=state,
training=training,
group=group,
)
w_fp8 = w_fp8.view(quantization_config.fp8_format["weight"])
out = fp8_fp8_half_gemm_fused(
x_fp8,
w_fp8,
transpose_x=False,
transpose_y=True,
bias=bias,
scale=x_scale * w_scale,
output_dtype=dtype,
)
return out, x_fp8, x_scale
def fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale):
if not ctx.x_stop_gradient:
if ctx.quantization_config.quant_input_grad:
grad_output_fp8, grad_output_scale = quantize(
grad_output,
ctx.weight_quantize_algo,
"grad_output",
ctx.quantization_config,
side="left",
apply_hadamard=False,
)
quant_weight = quant_weight.view(ctx.quantization_config.fp8_format["weight"])
if SUPPORT_TE:
grad_output_shape = grad_output_fp8.shape
grad_output_fp8 = grad_output_fp8.view((-1, grad_output_fp8.shape[-1]))
fwd_scales = paddle.concat([x_scale.astype("float32"), quant_scale.astype("float32")])
bwd_scales = grad_output_scale[None].astype("float32")
input_grad, _ = fp8_gemm(
A=quant_weight.T,
A_scale_inv=fwd_scales,
A_fp8_tensor=FP8FwdTensors.GEMM1_WEIGHT,
A_dtype=TE_DType[quant_weight.dtype],
B=grad_output_fp8,
B_scale_inv=bwd_scales,
B_fp8_tensor=FP8BwdTensors.GRAD_OUTPUT1,
B_dtype=TE_DType[grad_output_fp8.dtype],
out_dtype=ctx.dtype,
workspace=get_workspace(),
use_split_accumulator=True,
)
input_grad = input_grad.view((*grad_output_shape[:-1], -1))
else:
grad_output_ = grad_output_fp8.astype(ctx.dtype) * grad_output_scale
weight_ = quant_weight.astype(ctx.dtype) * quant_scale
input_grad = paddle.matmul(grad_output_, weight_).astype(ctx.dtype)
if ctx.quantization_config.apply_hadamard:
input_grad = apply_hadamard_matmul(input_grad, "right", ctx.quantization_config.hadamard_block_size)
else:
qdq_weight = dequantize(
quant_weight,
quant_scale,
"weight",
ctx.weight_quantize_algo,
ctx.quantization_config,
apply_hadamard=ctx.quantization_config.apply_hadamard,
side="left",
)
input_grad = paddle.matmul(grad_output, qdq_weight.T)
else:
input_grad = None
if not ctx.w_stop_gradient:
if ctx.quantization_config.quant_weight_grad:
grad_output_fp8, grad_output_scale = quantize(
x=grad_output,
weight_quantize_algo=ctx.weight_quantize_algo,
tensor_type="grad_output",
quantization_config=ctx.quantization_config,
apply_hadamard=False,
)
if SUPPORT_TE:
quant_x = quant_x.view((-1, quant_x.shape[-1]))
grad_output_fp8 = grad_output_fp8.view((-1, grad_output_fp8.shape[-1]))
fwd_scales = paddle.concat([x_scale.astype("float32"), quant_scale.astype("float32")])
bwd_scales = grad_output_scale[None].astype("float32")
# FP8 gemm need k % 16 = 0
ALIGNMENT_SIZE = 16
def pad_tensor_to_multiple(tensor, dtype):
current_size = tensor.shape[0]
padding_size = ALIGNMENT_SIZE - current_size % ALIGNMENT_SIZE
# Create padding zeros with matching shape and dtype
padding_shape = [padding_size, tensor.shape[1]]
padding = paddle.zeros(padding_shape, dtype=dtype)
padded_tensor = paddle.concat([tensor, padding], axis=0)
return padded_tensor
if quant_x.shape[0] % ALIGNMENT_SIZE != 0:
quant_x = pad_tensor_to_multiple(quant_x, ctx.quantization_config.fp8_format["activation"])
grad_output_fp8 = pad_tensor_to_multiple(
grad_output_fp8, ctx.quantization_config.fp8_format["grad_output"]
)
weight_grad, _ = fp8_gemm(
A=grad_output_fp8.T,
A_scale_inv=bwd_scales,
A_fp8_tensor=FP8BwdTensors.GRAD_OUTPUT1,
A_dtype=TE_DType[grad_output_fp8.dtype],
B=quant_x.T,
B_scale_inv=fwd_scales,
B_fp8_tensor=FP8FwdTensors.GEMM1_INPUT,
B_dtype=TE_DType[quant_x.dtype],
out_dtype=ctx.dtype,
workspace=get_workspace(),
use_split_accumulator=True,
)
else:
grad_output_ = grad_output_fp8.astype(ctx.dtype) * grad_output_scale
x_ = quant_x.astype(ctx.dtype) * x_scale
if len(x_.shape) == 2:
weight_grad = paddle.matmul(x_.transpose([1, 0]), grad_output_).astype(ctx.dtype)
else:
weight_grad = paddle.matmul(
x_.reshape([-1, x_.shape[-1]]).transpose([1, 0]),
grad_output_.reshape([-1, grad_output_.shape[-1]]),
).astype(ctx.dtype)
if ctx.quantization_config.apply_hadamard:
weight_grad = weight_grad / ctx.quantization_config.hadamard_block_size
weight_grad = apply_hadamard_matmul(weight_grad, "left", ctx.quantization_config.hadamard_block_size)
else:
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
return input_grad, weight_grad
class QATFunc(PyLayer):
@staticmethod
def forward(
ctx,
x,
quant_weight,
bias,
quant_scale,
quantization_config,
dtype,
state,
training,
act_scale,
weight_quantize_algo,
group,
):
quant_x, x_scale = None, None
if weight_quantize_algo in ["fp8linear"]:
output, quant_x, x_scale = fp8_forward(
x,
quant_weight,
w_scale=quant_scale,
weight_quantize_algo=weight_quantize_algo,
bias=bias,
dtype=dtype,
quantization_config=quantization_config,
state=state,
training=training,
act_scale=act_scale,
group=group,
)
else:
output, quant_x, x_scale = int8_forward(
x,
quant_w=quant_weight,
scale_w=quant_scale,
weight_quantize_algo=weight_quantize_algo,
bias=bias,
quantization_config=quantization_config,
state=state,
training=training,
act_scale=act_scale,
group=group,
)
ctx.quantization_config = quantization_config
ctx.weight_quantize_algo = weight_quantize_algo
ctx.dtype = dtype
ctx.x_stop_gradient = x.stop_gradient
ctx.w_stop_gradient = quant_weight.stop_gradient
ctx.b_stop_gradient = bias.stop_gradient if bias is not None else True
ctx.save_for_backward(
x if not quantization_config.quant_weight_grad else None,
quant_weight,
bias,
quant_scale,
quant_x if quantization_config.quant_weight_grad else None,
x_scale if quantization_config.quant_weight_grad else None,
)
return output
@staticmethod
def backward(ctx, grad_output):
x, quant_weight, bias, quant_scale, quant_x, x_scale = ctx.saved_tensor()
if ctx.quantization_config.weight_quantize_algo in ["fp8linear"]:
input_grad, weight_grad = fp8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale)
else:
input_grad, weight_grad = int8_backward(ctx, x, grad_output, quant_weight, quant_scale, quant_x, x_scale)
if not ctx.b_stop_gradient:
bias_grad = grad_output.sum(axis=[0, 1])
else:
bias_grad = None
return input_grad, weight_grad, bias_grad