class APoTFakeQuantize(FakeQuantizeBase): alpha: Tensor gamma: Tensor quantization_levels: Tensor level_indices: Tensor def __init__(self, **observer_kwargs): super().__init__() self.activation_post_process = APoTObserver(**observer_kwargs) def calculate_qparams(self, signed: bool): # type: ignore[override] return self.activation_post_process.calculate_qparams(signed=signed) def forward(self, X: torch.Tensor, signed: bool): # type: ignore[override] if self.observer_enabled[0] == 1: self.activation_post_process.forward(X) self.alpha, self.gamma, self.quantization_levels, self.level_indices = \ self.activation_post_process.calculate_qparams(signed) if self.fake_quant_enabled[0] == 1: assert (self.alpha is not None and self.gamma is not None and self.quantization_levels is not None and self.level_indices is not None), "Must set qparams for fake quant" X = fake_quantize_function.apply(X, self.alpha, self.gamma, self.quantization_levels, self.level_indices) return X
def test_int_repr(self): # generate tensor with random fp values tensor2quantize = tensor2quantize = torch.tensor( [0, 0.0215, 0.1692, 0.385, 1, 0.0391]) observer = APoTObserver(b=4, k=2) observer.forward(tensor2quantize) qparams = observer.calculate_qparams(signed=False) # get apot quantized tensor result qtensor = quantize_APoT(tensor2quantize=tensor2quantize, alpha=qparams[0], gamma=qparams[1], quantization_levels=qparams[2], level_indices=qparams[3]) qtensor_data = qtensor.int_repr().int() # expected qtensor values calculated based on # corresponding level_indices to nearest quantization level # for each fp value in tensor2quantize # e.g. # 0.0215 in tensor2quantize nearest 0.0208 in quantization_levels -> 3 in level_indices expected_qtensor_data = torch.tensor([0, 3, 8, 13, 5, 12], dtype=torch.int32) self.assertTrue(torch.equal(qtensor_data, expected_qtensor_data))
def test_dequantize_dim(self): # make observer observer = APoTObserver(4, 2) # generate random size of tensor2quantize between 1 -> 20 size1 = random.randint(1, 20) size2 = random.randint(1, 20) size3 = random.randint(1, 20) # make tensor2quantize: random fp values between 0 -> 1000 tensor2quantize = 1000 * torch.rand( size1, size2, size3, dtype=torch.float) observer.forward(tensor2quantize) alpha, gamma, quantization_levels, level_indices = observer.calculate_qparams( signed=False) # make mock apot_tensor original_apot = quantize_APoT(tensor2quantize=tensor2quantize, alpha=alpha, gamma=gamma, quantization_levels=quantization_levels, level_indices=level_indices) # dequantize apot_tensor dequantize_result = dequantize_APoT(apot_tensor=original_apot) self.assertEqual(original_apot.data.size(), dequantize_result.size())
def test_quantize_APoT_k2(self): r""" given b = 4, k = 2, alpha = 1.0, we know: (from APoT paper example: https://arxiv.org/pdf/1909.13144.pdf) quantization_levels = tensor([0.0000, 0.0208, 0.0417, 0.0625, 0.0833, 0.1250, 0.1667, 0.1875, 0.2500, 0.3333, 0.3750, 0.5000, 0.6667, 0.6875, 0.7500, 1.0000]) level_indices = tensor([ 0, 3, 12, 15, 2, 14, 8, 11, 10, 1, 13, 9, 4, 7, 6, 5])) """ # generate tensor with random fp values tensor2quantize = torch.tensor([0, 0.0215, 0.1692, 0.385, 1, 0.0391]) observer = APoTObserver(b=4, k=2) observer.forward(tensor2quantize) alpha, gamma, quantization_levels, level_indices = observer.calculate_qparams(signed=False) # get apot quantized tensor result qtensor = quantize_APoT(tensor2quantize=tensor2quantize, alpha=alpha, gamma=gamma, quantization_levels=quantization_levels, level_indices=level_indices) qtensor_data = qtensor.data.int() # expected qtensor values calculated based on # corresponding level_indices to nearest quantization level # for each fp value in tensor2quantize # e.g. # 0.0215 in tensor2quantize nearest 0.0208 in quantization_levels -> 3 in level_indices expected_qtensor = torch.tensor([0, 3, 8, 13, 5, 12], dtype=torch.int32) self.assertTrue(torch.equal(qtensor_data, expected_qtensor))
def test_quantize_APoT_rand_k1(self): # generate random size of tensor2quantize between 1 -> 20 size = random.randint(1, 20) # generate tensor with random fp values between 0 -> 1000 tensor2quantize = 1000 * torch.rand(size, dtype=torch.float) observer = APoTObserver(b=8, k=1) observer.forward(tensor2quantize) observer.min_val = torch.tensor([0]) observer.max_val = torch.tensor([255]) alpha, gamma, quantization_levels, level_indices = observer.calculate_qparams(signed=False) # get apot quantized tensor result qtensor = quantize_APoT(tensor2quantize=tensor2quantize, alpha=alpha, gamma=gamma, quantization_levels=quantization_levels, level_indices=level_indices) # get uniform quantization quantized tensor result uniform_quantized = quantize_per_tensor(input=tensor2quantize, scale=1.0, zero_point=0, dtype=torch.quint8).int_repr() qtensor_data = qtensor.data.int() uniform_quantized_tensor = uniform_quantized.data.int() self.assertTrue(torch.equal(qtensor_data, uniform_quantized_tensor))
def test_forward(self): # generate a tensor of size 20 with random values # between 0 -> 1000 to quantize -> dequantize X = 1000 * torch.rand(20) observer = APoTObserver(b=4, k=2) observer.forward(X) alpha, gamma, quantization_levels, level_indices = observer.calculate_qparams(signed=False) apot_fake = APoTFakeQuantize(b=4, k=2) apot_fake.enable_observer() apot_fake.enable_fake_quant() X_reduced_precision_fp = apot_fake.forward(torch.clone(X), False) # get X_expected by converting fp -> apot -> fp to simulate quantize -> dequantize X_to_apot = quantize_APoT(X, alpha, gamma, quantization_levels, level_indices) X_expected = dequantize_APoT(X_to_apot) self.assertTrue(torch.equal(X_reduced_precision_fp, X_expected))
def test_dequantize_quantize_rand_b6(self): # make observer observer = APoTObserver(12, 4) # generate random size of tensor2quantize between 1 -> 20 size = random.randint(1, 20) # make tensor2quantize: random fp values between 0 -> 1000 tensor2quantize = 1000 * torch.rand(size, dtype=torch.float) observer.forward(tensor2quantize) alpha, gamma, quantization_levels, level_indices = observer.calculate_qparams( signed=False) # make mock apot_tensor original_apot = quantize_APoT(tensor2quantize=tensor2quantize, alpha=alpha, gamma=gamma, quantization_levels=quantization_levels, level_indices=level_indices) original_input = torch.clone(original_apot.data).int() # dequantize apot_tensor dequantize_result = dequantize_APoT(apot_tensor=original_apot) # quantize apot_tensor final_apot = quantize_APoT(tensor2quantize=dequantize_result, alpha=alpha, gamma=gamma, quantization_levels=quantization_levels, level_indices=level_indices) result = final_apot.data.int() self.assertTrue(torch.equal(original_input, result))
def test_forward(self): obs = APoTObserver(b=4, k=2) X = torch.tensor([0.0, -100.23, -37.18, 3.42, 8.93, 9.21, 87.92]) X = obs.forward(X) alpha, gamma, quantization_levels, level_indices = obs.calculate_qparams( signed=True) min_val = torch.min(X) max_val = torch.max(X) expected_alpha = torch.max(-min_val, max_val) self.assertEqual(alpha, expected_alpha)