Skip to content

Commit dca17b3

Browse files
Fix for models (#2555)
* Handle zero dimensions corner cases for the torch tenson assign * expose calibration_op_group_size arg
1 parent 052291d commit dca17b3

File tree

4 files changed

+91
-13
lines changed

4 files changed

+91
-13
lines changed

coremltools/converters/mil/frontend/torch/ops.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4836,17 +4836,23 @@ def _internal_op_tensor_inplace_fill(context, node):
48364836

48374837
data, update_values = promote_input_dtypes([data, update_values])
48384838

4839-
updated_x = _translate_torch_tensor_assign(
4840-
x=data,
4841-
updates=update_values,
4842-
begin=begin,
4843-
end=end,
4844-
stride=stride,
4845-
begin_mask=begin_mask,
4846-
end_mask=end_mask,
4847-
squeeze_mask=squeeze_mask,
4848-
name=node.name,
4849-
)
4839+
if 0 in update_values.shape:
4840+
# if the update_values contains zero dimensions, this will be a noop
4841+
updated_x = mb.identity(x=data, name=node.name)
4842+
else:
4843+
# otherwise we translate it into a tensor assignment op
4844+
updated_x = _translate_torch_tensor_assign(
4845+
x=data,
4846+
updates=update_values,
4847+
begin=begin,
4848+
end=end,
4849+
stride=stride,
4850+
begin_mask=begin_mask,
4851+
end_mask=end_mask,
4852+
squeeze_mask=squeeze_mask,
4853+
name=node.name,
4854+
)
4855+
48504856
context.add(updated_x)
48514857

48524858

coremltools/converters/mil/frontend/torch/test/test_torch_ops.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9523,6 +9523,31 @@ def forward(self, x):
95239523
assert "slice_update" in get_op_types_in_program(prog)
95249524

95259525

9526+
@pytest.mark.parametrize(
9527+
"compute_unit, backend, minimum_deployment_target",
9528+
itertools.product(
9529+
compute_units,
9530+
backends,
9531+
[None, ct.target.iOS18],
9532+
),
9533+
)
9534+
def test_tensor_assign_no_op(self, compute_unit, backend, minimum_deployment_target):
9535+
# if the resulting update has `0` dimensions, and it is a noop
9536+
class TensorAssignModel(torch.nn.Module):
9537+
def forward(self, x):
9538+
x[:, -8:0, :] = 0
9539+
return x + 1
9540+
9541+
shape = (2, 10, 3)
9542+
model = TensorAssignModel()
9543+
res = self.run_compare_torch(
9544+
shape,
9545+
model,
9546+
backend=backend,
9547+
compute_unit=compute_unit,
9548+
)
9549+
9550+
95269551
class TestSelectScatter(TorchBaseTest):
95279552
@pytest.mark.parametrize(
95289553
"compute_unit, backend, frontend, minimum_deployment_target, input_shape, dynamic",

coremltools/optimize/coreml/_post_training_quantization.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,11 @@ def linear_quantize_activations(
601601
"compression::insert_prefix_quantize_dequantize_pair"
602602
]
603603
insert_prefix_quantize_dequantize_pair.set_options([PassOption("config", config)])
604-
activation_stats = _get_activation_calibration_stats(mlmodel, sample_data)
604+
activation_stats = _get_activation_calibration_stats(
605+
mlmodel,
606+
sample_data,
607+
calibration_op_group_size,
608+
)
605609
insert_prefix_quantize_dequantize_pair.set_options(
606610
[PassOption("activation_stats", activation_stats)]
607611
)

coremltools/optimize/coreml/experimental/test_post_training_quantization.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55

66
import numpy as np
77
import torch
8+
from io import StringIO
9+
import sys
810

911
import coremltools as ct
1012
from coremltools.optimize.coreml.experimental._post_training_quantization import (
1113
_get_activation_calibration_stats,
1214
)
15+
from coremltools.test.optimize.coreml.test_passes import TestCompressionPasses
16+
import coremltools.optimize as cto
1317

1418

1519
class TestActivationQuantization:
@@ -72,6 +76,45 @@ def forward(self, img): # convert + flatten
7276

7377

7478
class TestGetActivationStats(TestActivationQuantization):
79+
80+
def test_activation_quantization(self):
81+
"""
82+
Test the usage of linear_quantize_activations.
83+
"""
84+
sample_data = []
85+
for _ in range(3):
86+
input_data = np.random.rand(5, 10, 4, 4)
87+
sample_data.append({"data": input_data})
88+
89+
mlmodel = self._get_test_mlmodel_conv_relu()
90+
activation_quant_config = cto.coreml.OptimizationConfig(
91+
global_config=cto.coreml.experimental.OpActivationLinearQuantizerConfig(
92+
mode="linear_symmetric", weight_threshold=10
93+
)
94+
)
95+
96+
def _run_quantization_with_group_size(group_size, expected_batch_size):
97+
buffer = StringIO()
98+
original_stderr = sys.stderr
99+
sys.stderr = buffer
100+
mlmodel_activation_quantized = cto.coreml.experimental.linear_quantize_activations(
101+
mlmodel,
102+
activation_quant_config,
103+
sample_data,
104+
calibration_op_group_size=group_size,
105+
)
106+
sys.stderr = original_stderr
107+
output = buffer.getvalue()
108+
assert f"tensors batch-by-batch: {expected_batch_size} batches" in output
109+
110+
# when setting group size to -1, all intermediate outputs are in the same batch,
111+
# hence we will only get 1 batch
112+
_run_quantization_with_group_size(-1, 1)
113+
# when setting group size to 1, all intermediate outputs are split into different batches,
114+
# hence there will be 3 batches
115+
_run_quantization_with_group_size(1, 3)
116+
117+
75118
def test_get_activation_calibration_stats_basic(self):
76119
"""
77120
Calibration a floating point model with sample data.
@@ -113,7 +156,7 @@ def test_get_activation_calibration_stats_concat_surrounding_ops(self):
113156
sample_data.append({"data_0": input_data})
114157

115158
# Loading a floating point mlmodel
116-
mlmodel = self._get_test_mlmodel_conv_concat()
159+
mlmodel = TestCompressionPasses._get_test_mlmodel_conv_concat()
117160

118161
activation_stats = _get_activation_calibration_stats(mlmodel, sample_data)
119162

0 commit comments

Comments
 (0)