-
Notifications
You must be signed in to change notification settings - Fork 4.9k
add quant unit test #2315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add quant unit test #2315
Changes from 11 commits
48bd182
7f56db6
e696ed3
b5a5508
0f5dc1b
dfd3ee7
5cd45c8
325b86a
caa716f
9410fcd
1ebd819
9f6fa41
798c2f0
6bc80bb
2cf46f1
4f0b71e
c5d2173
2a5b3ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| * @jeffra @samyam @tjruwase @ShadenSmith @conglongli @awan-10 @cli99 @eltonzheng @minjiaz @RezaYazdaniAminabadi @duli2012 @mrwyattii @yaozhewei @arashb @xiaoxiawu-microsoft @samadejacobs @cmikeh2 | ||
| * @jeffra @samyam @tjruwase @ShadenSmith @conglongli @awan-10 @cli99 @eltonzheng @minjiaz @RezaYazdaniAminabadi @duli2012 @mrwyattii @yaozhewei @arashb @xiaoxiawu-microsoft @samadejacobs @cmikeh2 @GuanhuaWang |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| import torch | ||
| import pytest | ||
| from deepspeed.ops import op_builder | ||
|
|
||
| quantizer_cuda_module = None | ||
|
|
||
|
|
||
| def allclose(x, y): | ||
| assert x.dtype == y.dtype | ||
| rtol, atol = {torch.float32: (2e-1, 5e-2), torch.float16: (2e-1, 5e-2)}[x.dtype] | ||
| return torch.allclose(x, y, rtol=rtol, atol=atol) | ||
|
|
||
|
|
||
| def quantize_dequantize_ref(inputs, bit, num_groups=1): | ||
| # quantize | ||
| q_range = 2**bit | ||
| input_flat = inputs.float().reshape(num_groups, -1).contiguous() | ||
| input_flat = torch.nan_to_num(input_flat, nan=0.0) | ||
| input_min = input_flat.amin(-1, keepdim=True) | ||
| input_max = input_flat.amax(-1, keepdim=True) | ||
|
|
||
| scale = q_range / (2 * torch.max(input_min.abs(), input_max.abs())) | ||
| input_flat = (input_flat * scale).round().clamp(-q_range // 2, q_range // 2 - 1) | ||
| # dequantize | ||
| dequant_flat = torch.t(input_flat.to(torch.int8)) / scale.view(-1).to(torch.float16) | ||
| return torch.t(dequant_flat).reshape(inputs.shape) | ||
|
|
||
|
|
||
| def run_quant_dequant(inputs, groups, bits): | ||
| global quantizer_cuda_module | ||
| if quantizer_cuda_module is None: | ||
| quantizer_cuda_module = op_builder.QuantizerBuilder().load() | ||
| return quantizer_cuda_module.ds_quantize_fp16(inputs, groups, bits) | ||
|
|
||
|
|
||
| @pytest.mark.inference | ||
| @pytest.mark.parametrize("tensor_shape", [(8, 8), (128, 256)]) | ||
| def test_quant_dequant(tensor_shape): | ||
| input_tensor = torch.rand((tensor_shape), dtype=torch.float16).cuda() | ||
|
|
||
| # test 8bit quant/dequant on tensor partitioned in 1 group. | ||
| ref_input_8bit_1group = input_tensor.clone().detach() | ||
| ref_out_8bit_1group = quantize_dequantize_ref(ref_input_8bit_1group, 8) | ||
| # run_quant_dequant will do quantize then dequantize and return the dequantized value. | ||
| ds_out_8bit_1group = run_quant_dequant(input_tensor, 1, 8) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the quantization process in-place? Does that have repercussions for doing both the 8bit-1group and 4bit-16group on the same Tensor? If so maybe add another detach or factor it out as another parameterized aspect?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. detached the tensor for both ds and ref now. Thx for find this bug. @cmikeh2 |
||
| assert (allclose(ds_out_8bit_1group, ref_out_8bit_1group)) | ||
|
|
||
| # test 4bit quant/dequant on tensor partitioned into 16 groups. | ||
| # Note that we have an explicit boundary for groups as ((size / groups) - 1) / 4096 + 1) <= MAX_REG. | ||
| ref_input_4bit_16group = input_tensor.clone().detach() | ||
| ref_out_4bit_16group = quantize_dequantize_ref(ref_input_4bit_16group, 4, 16) | ||
| ds_out_4bit_16group = run_quant_dequant(input_tensor, 16, 4) | ||
| assert (allclose(ds_out_4bit_16group, ref_out_4bit_16group)) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you refactor this so that the shapes come through as parameters for the test? This will help make it clearer if one of the conditions fails if one does, which will help with debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cmikeh2 done.