|
6 | 6 | from torch.testing._internal.common_dtype import floating_and_complex_types_and |
7 | 7 | from torch.testing._internal.common_cuda import tf32_on_and_off |
8 | 8 | from torch.testing._internal.common_mkldnn import bf32_on_and_off |
9 | | -from torch.testing._internal.common_quantization import _group_quantize_tensor, _dynamically_quantize_per_channel |
| 9 | +from torch.testing._internal.common_quantization import _dynamically_quantize_per_channel |
10 | 10 | from torch.testing import make_tensor |
11 | 11 | import unittest |
12 | 12 | import itertools |
@@ -174,9 +174,53 @@ def _test(m, k, n, transpose_a, transpose_b, test_equal=True): |
174 | 174 |
|
175 | 175 | @unittest.skipIf(IS_WINDOWS, "Skipped on Windows!") |
176 | 176 | @parametrize("m", [1]) |
177 | | -@parametrize("k", [1024, 2048]) |
178 | | -@parametrize("n", [48, 64]) |
| 177 | +@parametrize("k", [256, 512, 1024]) |
| 178 | +@parametrize("n", [32, 64]) |
179 | 179 | def _int4_mm(self, device, m, k, n): |
| 180 | + def _group_quantize_tensor(w, n_bit=4, q_group_size=16): |
| 181 | + assert w.dim() == 2 |
| 182 | + w = w.transpose(0, 1).contiguous() |
| 183 | + assert q_group_size > 1 |
| 184 | + assert w.shape[-1] % q_group_size == 0 |
| 185 | + |
| 186 | + to_quant = w.reshape(-1, q_group_size) |
| 187 | + assert torch.isnan(to_quant).sum() == 0 |
| 188 | + |
| 189 | + max_val = to_quant.amax(dim=1, keepdim=True) |
| 190 | + min_val = to_quant.amin(dim=1, keepdim=True) |
| 191 | + max_int = 2 ** n_bit - 1 |
| 192 | + min_int = 0 |
| 193 | + scales = (max_val - min_val).clamp(min=1e-6) / max_int |
| 194 | + assert torch.isnan(scales).sum() == 0 |
| 195 | + |
| 196 | + zeros = min_val + scales * (2 ** (n_bit - 1)) |
| 197 | + assert torch.isnan(zeros).sum() == 0 |
| 198 | + |
| 199 | + out = to_quant.sub(min_val).div(scales).round().clamp_(min_int, max_int) |
| 200 | + assert torch.isnan(out).sum() == 0 |
| 201 | + |
| 202 | + out = out.to(dtype=torch.int32).reshape(w.shape) |
| 203 | + if out.device.type != 'cpu' or out.device.type != 'xpu': |
| 204 | + out = (out[::, ::2] << 4 | out[::, 1::2]).to(torch.uint8) |
| 205 | + |
| 206 | + # Scales and zeros for the same q-group should be contiguous, so we can |
| 207 | + # load as a 32-bit word |
| 208 | + scales = scales.view(w.shape[0], -1) |
| 209 | + zeros = zeros.view(w.shape[0], -1) |
| 210 | + scales_and_zeros = ( |
| 211 | + torch.cat( |
| 212 | + [ |
| 213 | + scales.reshape(scales.size(0), scales.size(1), 1), |
| 214 | + zeros.reshape(zeros.size(0), zeros.size(1), 1), |
| 215 | + ], |
| 216 | + 2, |
| 217 | + ) |
| 218 | + ) |
| 219 | + |
| 220 | + if out.device.type != 'xpu': |
| 221 | + scales_and_zeros = scales_and_zeros.transpose(0, 1).contiguous() |
| 222 | + return out, scales_and_zeros |
| 223 | + |
180 | 224 | def convert_weight_to_int4pack(b): |
181 | 225 | b_tmp, b_scales_and_zeros = _group_quantize_tensor( |
182 | 226 | b, n_bit=4, q_group_size=q_group |
@@ -231,7 +275,8 @@ def weight_int4pack_mm(a, b_int4pack, b_scales_and_zeros): |
231 | 275 | b_scales_and_zeros = b_scales_and_zeros_bf16.to(dtype=dtype) |
232 | 276 | ref = torch.mm(a, b) |
233 | 277 | res = weight_int4pack_mm(a, b_int4pack, b_scales_and_zeros) |
234 | | - |
| 278 | + print(ref) |
| 279 | + print(res) |
235 | 280 | mean_err = ((res - ref).abs() / ref).mean() |
236 | 281 | self.assertTrue(mean_err < 0.05) |
237 | 282 |
|
|
0 commit comments