| # Torch |
| import torch |
| import torch.nn.functional as F |
| import torch.nn.quantized.functional as qF |
| |
| # Standard library |
| import numpy as np |
| |
| # Testing utils |
| from hypothesis import assume, given |
| from hypothesis import strategies as st |
| from torch.testing._internal.common_quantization import ( |
| QuantizationTestCase, |
| _make_conv_test_input, |
| ) |
| from torch.testing._internal.common_quantized import override_quantized_engine |
| from torch.testing._internal.common_utils import ( |
| IS_PPC, |
| TEST_WITH_UBSAN, |
| ) |
| |
| class TestQuantizedFunctional(QuantizationTestCase): |
| def test_relu_api(self): |
| X = torch.arange(-5, 5, dtype=torch.float) |
| scale = 2.0 |
| zero_point = 1 |
| qX = torch.quantize_per_tensor(X, scale=scale, zero_point=zero_point, dtype=torch.quint8) |
| qY = torch.relu(qX) |
| qY_hat = qF.relu(qX) |
| self.assertEqual(qY, qY_hat) |
| |
| def _test_conv_api_impl( |
| self, qconv_fn, conv_fn, batch_size, in_channels_per_group, |
| input_feature_map_size, out_channels_per_group, groups, kernel_size, |
| stride, padding, dilation, X_scale, X_zero_point, W_scale, W_zero_point, |
| Y_scale, Y_zero_point, use_bias, use_channelwise, |
| ): |
| for i in range(len(kernel_size)): |
| assume(input_feature_map_size[i] + 2 * padding[i] |
| >= dilation[i] * (kernel_size[i] - 1) + 1) |
| (X, X_q, W, W_q, b) = _make_conv_test_input( |
| batch_size, in_channels_per_group, input_feature_map_size, |
| out_channels_per_group, groups, kernel_size, X_scale, |
| X_zero_point, W_scale, W_zero_point, use_bias, use_channelwise) |
| |
| Y_exp = conv_fn(X, W, b, stride, padding, dilation, groups) |
| Y_exp = torch.quantize_per_tensor( |
| Y_exp, scale=Y_scale, zero_point=Y_zero_point, dtype=torch.quint8) |
| Y_act = qconv_fn( |
| X_q, W_q, b, stride, padding, dilation, groups, |
| padding_mode="zeros", scale=Y_scale, zero_point=Y_zero_point) |
| |
| # Make sure the results match |
| # assert_array_almost_equal compares using the following formula: |
| # abs(desired-actual) < 1.5 * 10**(-decimal) |
| # (https://docs.scipy.org/doc/numpy/reference/generated/numpy.testing.assert_almost_equal.html) |
| # We use decimal = 0 to ignore off-by-1 differences between reference |
| # and test. Off-by-1 differences arise due to the order of round and |
| # zero_point addition operation, i.e., if addition followed by round is |
| # used by reference and round followed by addition is used by test, the |
| # results may differ by 1. |
| # For example, the result of round(2.5) + 1 is 3 while round(2.5 + 1) is |
| # 4 assuming the rounding mode is round-to-nearest, ties-to-even. |
| np.testing.assert_array_almost_equal( |
| Y_exp.int_repr().numpy(), Y_act.int_repr().numpy(), decimal=0) |
| |
| @given(batch_size=st.integers(1, 3), |
| in_channels_per_group=st.sampled_from([2, 4, 5, 8, 16, 32]), |
| L=st.integers(4, 16), |
| out_channels_per_group=st.sampled_from([2, 4, 5, 8, 16, 32]), |
| groups=st.integers(1, 4), |
| kernel=st.integers(1, 7), |
| stride=st.integers(1, 2), |
| pad=st.integers(0, 2), |
| dilation=st.integers(1, 2), |
| X_scale=st.floats(1.2, 1.6), |
| X_zero_point=st.integers(0, 4), |
| W_scale=st.lists(st.floats(0.2, 1.6), min_size=1, max_size=2), |
| W_zero_point=st.lists(st.integers(-5, 5), min_size=1, max_size=2), |
| Y_scale=st.floats(4.2, 5.6), |
| Y_zero_point=st.integers(0, 4), |
| use_bias=st.booleans(), |
| use_channelwise=st.booleans(), |
| qengine=st.sampled_from(("qnnpack", "fbgemm"))) |
| def test_conv1d_api( |
| self, batch_size, in_channels_per_group, L, out_channels_per_group, |
| groups, kernel, stride, pad, dilation, |
| X_scale, X_zero_point, W_scale, W_zero_point, Y_scale, Y_zero_point, |
| use_bias, use_channelwise, qengine, |
| ): |
| # Tests the correctness of the conv1d function. |
| if qengine not in torch.backends.quantized.supported_engines: |
| return |
| if qengine == 'qnnpack': |
| if IS_PPC or TEST_WITH_UBSAN: |
| return |
| use_channelwise = False |
| |
| input_feature_map_size = (L, ) |
| kernel_size = (kernel, ) |
| stride = (stride, ) |
| padding = (pad, ) |
| dilation = (dilation, ) |
| |
| with override_quantized_engine(qengine): |
| qconv_fn = qF.conv1d |
| conv_fn = F.conv1d |
| self._test_conv_api_impl( |
| qconv_fn, conv_fn, batch_size, in_channels_per_group, |
| input_feature_map_size, out_channels_per_group, groups, |
| kernel_size, stride, padding, dilation, X_scale, X_zero_point, |
| W_scale, W_zero_point, Y_scale, Y_zero_point, use_bias, |
| use_channelwise) |
| |
| @given(batch_size=st.integers(1, 3), |
| in_channels_per_group=st.sampled_from([2, 4, 5, 8, 16, 32]), |
| H=st.integers(4, 16), |
| W=st.integers(4, 16), |
| out_channels_per_group=st.sampled_from([2, 4, 5, 8, 16, 32]), |
| groups=st.integers(1, 4), |
| kernel_h=st.integers(1, 7), |
| kernel_w=st.integers(1, 7), |
| stride_h=st.integers(1, 2), |
| stride_w=st.integers(1, 2), |
| pad_h=st.integers(0, 2), |
| pad_w=st.integers(0, 2), |
| dilation=st.integers(1, 2), |
| X_scale=st.floats(1.2, 1.6), |
| X_zero_point=st.integers(0, 4), |
| W_scale=st.lists(st.floats(0.2, 1.6), min_size=1, max_size=2), |
| W_zero_point=st.lists(st.integers(-5, 5), min_size=1, max_size=2), |
| Y_scale=st.floats(4.2, 5.6), |
| Y_zero_point=st.integers(0, 4), |
| use_bias=st.booleans(), |
| use_channelwise=st.booleans(), |
| qengine=st.sampled_from(("qnnpack", "fbgemm"))) |
| def test_conv2d_api( |
| self, batch_size, in_channels_per_group, H, W, out_channels_per_group, |
| groups, kernel_h, kernel_w, stride_h, stride_w, pad_h, pad_w, dilation, |
| X_scale, X_zero_point, W_scale, W_zero_point, Y_scale, Y_zero_point, |
| use_bias, use_channelwise, qengine, |
| ): |
| # Tests the correctness of the conv2d function. |
| |
| if qengine not in torch.backends.quantized.supported_engines: |
| return |
| if qengine == 'qnnpack': |
| if IS_PPC or TEST_WITH_UBSAN: |
| return |
| |
| input_feature_map_size = (H, W) |
| kernel_size = (kernel_h, kernel_w) |
| stride = (stride_h, stride_w) |
| padding = (pad_h, pad_w) |
| dilation = (dilation, dilation) |
| |
| with override_quantized_engine(qengine): |
| qconv_fn = qF.conv2d |
| conv_fn = F.conv2d |
| self._test_conv_api_impl( |
| qconv_fn, conv_fn, batch_size, in_channels_per_group, |
| input_feature_map_size, out_channels_per_group, groups, |
| kernel_size, stride, padding, dilation, X_scale, X_zero_point, |
| W_scale, W_zero_point, Y_scale, Y_zero_point, use_bias, |
| use_channelwise) |
| |
| @given(batch_size=st.integers(1, 3), |
| in_channels_per_group=st.sampled_from([2, 4, 5, 8, 16, 32]), |
| D=st.integers(4, 8), |
| H=st.integers(4, 8), |
| W=st.integers(4, 8), |
| out_channels_per_group=st.sampled_from([2, 4, 5, 8, 16, 32]), |
| groups=st.integers(1, 4), |
| kernel_d=st.integers(1, 4), |
| kernel_h=st.integers(1, 4), |
| kernel_w=st.integers(1, 4), |
| stride_d=st.integers(1, 2), |
| stride_h=st.integers(1, 2), |
| stride_w=st.integers(1, 2), |
| pad_d=st.integers(0, 2), |
| pad_h=st.integers(0, 2), |
| pad_w=st.integers(0, 2), |
| dilation=st.integers(1, 2), |
| X_scale=st.floats(1.2, 1.6), |
| X_zero_point=st.integers(0, 4), |
| W_scale=st.lists(st.floats(0.2, 1.6), min_size=1, max_size=2), |
| W_zero_point=st.lists(st.integers(-5, 5), min_size=1, max_size=2), |
| Y_scale=st.floats(4.2, 5.6), |
| Y_zero_point=st.integers(0, 4), |
| use_bias=st.booleans(), |
| use_channelwise=st.booleans(), |
| qengine=st.sampled_from(("fbgemm",))) |
| def test_conv3d_api( |
| self, batch_size, in_channels_per_group, D, H, W, |
| out_channels_per_group, groups, kernel_d, kernel_h, kernel_w, |
| stride_d, stride_h, stride_w, pad_d, pad_h, pad_w, dilation, X_scale, |
| X_zero_point, W_scale, W_zero_point, Y_scale, Y_zero_point, use_bias, |
| use_channelwise, qengine, |
| ): |
| # Tests the correctness of the conv3d function. |
| # Currently conv3d only supports FbGemm engine |
| |
| if qengine not in torch.backends.quantized.supported_engines: |
| return |
| |
| input_feature_map_size = (D, H, W) |
| kernel_size = (kernel_d, kernel_h, kernel_w) |
| stride = (stride_d, stride_h, stride_w) |
| padding = (pad_d, pad_h, pad_w) |
| dilation = (dilation, dilation, dilation) |
| |
| with override_quantized_engine(qengine): |
| qconv_fn = qF.conv3d |
| conv_fn = F.conv3d |
| self._test_conv_api_impl( |
| qconv_fn, conv_fn, batch_size, in_channels_per_group, |
| input_feature_map_size, out_channels_per_group, groups, |
| kernel_size, stride, padding, dilation, X_scale, X_zero_point, |
| W_scale, W_zero_point, Y_scale, Y_zero_point, use_bias, |
| use_channelwise) |