[ARM][feat]: Add 4 bit dynamic quantization matmuls & KleidiAI Backend (#134124)

Description:
1. Quantize Linear Layer Weights to 4-bits:
Quantize the weights of the Linear layer to 4 bits, using symmetric quantization.
Pack two 4-bit weights into one uint8 container.
Choose a quantization scheme (channel-wise or group-wise), with the group size being a multiple of 32.

2. Prepare Quantized Weights, Scales, and Optional Bias:
After quantizing, obtain the quantized_weights, scales, and groupsize.
If the original Linear layer has a bias, prepare it as well.

3. Pack the Weights Efficiently:
Use torch.ops.aten._dyn_quant_pack_4bit_weight to optimally pack the weights, scales, and optional bias.
```python
packed_weights = torch.ops.aten._dyn_quant_pack_4bit_weight(weight, scales_and_zeros, bias, groupsize, in_features, out_features)
```
Input parameters should include:
in_features and out_features (the same as the Linear layer’s corresponding parameters).

4. Perform Dynamic Quantized Matrix Multiplication:
Use torch.ops.aten._dyn_quant_matmul_4bit to perform matrix multiplication with quantized weights.
```python
output = torch.ops.aten._dyn_quant_matmul_4bit(input, packed_weights,  groupsize, in_features, out_features)
```
Inputs required include:
The input tensor, packed_weights , groupsize, and the in_features and out_features.

API Usage: https://github.com/pytorch/pytorch/issues/143289

Model Perf :
7B Transformer model:
Prefill : 340 t/s
Decode  : 40  t/s
2B Transformer model
Prefill : 747 t/s
Decode  : 80  t/s

Tests:
python test/test_linalg.py -k test__dyn_quant_pack_4bit_weight
Ran 1 test in 0.016s

OK

python test/test_linalg.py -k test__dyn_quant_matmul_4bit
Ran 8 tests in 0.077s

OK

python test/test_linalg.py -k test_compile_dyn_quant_matmul_4bit
Ran 8 tests in 11.454s

Change-Id: Ia1672bad5e6ec94e64d8bb1971395d60f4b3a452

Fixes #ISSUE_NUMBER

Pull Request resolved: https://github.com/pytorch/pytorch/pull/134124
Approved by: https://github.com/digantdesai, https://github.com/malfet
This commit is contained in:
Nikhil Gupta
2024-12-19 18:51:23 +00:00
committed by PyTorch MergeBot
parent c5ddf5dd90
commit 4b82251011
37 changed files with 1898 additions and 23 deletions

View File

@ -498,6 +498,39 @@ def _group_quantize_tensor(w, n_bit=4, q_group_size=16):
return out, scales_and_zeros
def _group_quantize_tensor_symmetric(
w, n_bit=4, groupsize=32
):
# W is of shape [K x N]
# We transpose W as Quantization is applied on [N x K]
w = w.transpose(0, 1).contiguous()
assert w.dim() == 2
assert groupsize > 1
assert w.shape[-1] % groupsize == 0
# Calculate scale and zeros
to_quant = w.reshape(-1, groupsize)
max_val = to_quant.abs().amax(dim=1, keepdim=True)
eps = torch.finfo(max_val.dtype).eps
max_int = 2 ** (n_bit - 1) - 1 # For 4-bit, this is 7
scales = max_val.clamp(min=eps) / max_int
zeros = torch.zeros_like(scales)
# Quantize the weight
scales = scales.to(torch.float32).reshape(w.shape[0], -1)
zeros = zeros.to(torch.float32).reshape(w.shape[0], -1)
scales = scales.reshape(-1, 1)
zeros = zeros.reshape(-1, 1)
max_int = 2**n_bit - 1
w_int8 = to_quant.div(scales).add(8.5).to(torch.int8).clamp(max=max_int)
# We pack 2 signed int4 values in unsigned uint8 container.
# This reduces the weight size by half and improves load perf
out_uint8 = (w_int8[::, 1::2] << 4 | w_int8[::, ::2]).to(torch.uint8)
scales_and_zeros = scales.squeeze().contiguous()
return out_uint8, scales_and_zeros
def _dynamically_quantize_per_channel(x, quant_min, quant_max, target_dtype):
# source: https://github.com/pytorch-labs/gpt-fast/blob/main/quantize.py
# default setup for affine quantization of activations
@ -530,7 +563,6 @@ def _dynamically_quantize_per_channel(x, quant_min, quant_max, target_dtype):
return quant, scales.to(x_dtype), zero_points
# QuantizationTestCase used as a base class for testing quantization on modules
class QuantizationTestCase(TestCase):
def setUp(self):