ai-edge-quantizer-nightly 0.0.1.dev20250115__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. ai_edge_quantizer/__init__.py +19 -0
  2. ai_edge_quantizer/algorithm_manager.py +167 -0
  3. ai_edge_quantizer/algorithm_manager_api.py +271 -0
  4. ai_edge_quantizer/algorithm_manager_api_test.py +210 -0
  5. ai_edge_quantizer/algorithms/__init__.py +15 -0
  6. ai_edge_quantizer/algorithms/nonlinear_quantize/__init__.py +15 -0
  7. ai_edge_quantizer/algorithms/nonlinear_quantize/float_casting.py +273 -0
  8. ai_edge_quantizer/algorithms/nonlinear_quantize/float_casting_test.py +664 -0
  9. ai_edge_quantizer/algorithms/uniform_quantize/__init__.py +15 -0
  10. ai_edge_quantizer/algorithms/uniform_quantize/naive_min_max_quantize.py +666 -0
  11. ai_edge_quantizer/algorithms/uniform_quantize/naive_min_max_quantize_test.py +184 -0
  12. ai_edge_quantizer/algorithms/uniform_quantize/uniform_quantize_tensor.py +371 -0
  13. ai_edge_quantizer/algorithms/uniform_quantize/uniform_quantize_tensor_test.py +357 -0
  14. ai_edge_quantizer/algorithms/utils/__init__.py +15 -0
  15. ai_edge_quantizer/algorithms/utils/min_max_quantize_utils.py +1067 -0
  16. ai_edge_quantizer/algorithms/utils/min_max_quantize_utils_test.py +512 -0
  17. ai_edge_quantizer/calibrator.py +288 -0
  18. ai_edge_quantizer/calibrator_test.py +297 -0
  19. ai_edge_quantizer/conftest.py +22 -0
  20. ai_edge_quantizer/default_policy.py +310 -0
  21. ai_edge_quantizer/model_modifier.py +176 -0
  22. ai_edge_quantizer/model_modifier_test.py +130 -0
  23. ai_edge_quantizer/model_validator.py +357 -0
  24. ai_edge_quantizer/model_validator_test.py +354 -0
  25. ai_edge_quantizer/params_generator.py +361 -0
  26. ai_edge_quantizer/params_generator_test.py +1041 -0
  27. ai_edge_quantizer/qtyping.py +483 -0
  28. ai_edge_quantizer/quantizer.py +372 -0
  29. ai_edge_quantizer/quantizer_test.py +532 -0
  30. ai_edge_quantizer/recipe.py +67 -0
  31. ai_edge_quantizer/recipe_manager.py +245 -0
  32. ai_edge_quantizer/recipe_manager_test.py +815 -0
  33. ai_edge_quantizer/recipe_test.py +97 -0
  34. ai_edge_quantizer/transformation_instruction_generator.py +584 -0
  35. ai_edge_quantizer/transformation_instruction_generator_test.py +1082 -0
  36. ai_edge_quantizer/transformation_performer.py +278 -0
  37. ai_edge_quantizer/transformation_performer_test.py +344 -0
  38. ai_edge_quantizer/transformations/__init__.py +15 -0
  39. ai_edge_quantizer/transformations/dequant_insert.py +87 -0
  40. ai_edge_quantizer/transformations/dequant_insert_test.py +304 -0
  41. ai_edge_quantizer/transformations/emulated_subchannel.py +363 -0
  42. ai_edge_quantizer/transformations/emulated_subchannel_test.py +212 -0
  43. ai_edge_quantizer/transformations/quant_insert.py +100 -0
  44. ai_edge_quantizer/transformations/quant_insert_test.py +284 -0
  45. ai_edge_quantizer/transformations/quantize_tensor.py +156 -0
  46. ai_edge_quantizer/transformations/quantize_tensor_test.py +227 -0
  47. ai_edge_quantizer/transformations/transformation_utils.py +132 -0
  48. ai_edge_quantizer/transformations/transformation_utils_test.py +162 -0
  49. ai_edge_quantizer/utils/__init__.py +15 -0
  50. ai_edge_quantizer/utils/calibration_utils.py +86 -0
  51. ai_edge_quantizer/utils/calibration_utils_test.py +77 -0
  52. ai_edge_quantizer/utils/test_utils.py +107 -0
  53. ai_edge_quantizer/utils/tfl_flatbuffer_utils.py +317 -0
  54. ai_edge_quantizer/utils/tfl_flatbuffer_utils_test.py +200 -0
  55. ai_edge_quantizer/utils/tfl_interpreter_utils.py +312 -0
  56. ai_edge_quantizer/utils/tfl_interpreter_utils_test.py +332 -0
  57. ai_edge_quantizer/utils/validation_utils.py +125 -0
  58. ai_edge_quantizer/utils/validation_utils_test.py +87 -0
  59. ai_edge_quantizer_nightly-0.0.1.dev20250115.dist-info/LICENSE +201 -0
  60. ai_edge_quantizer_nightly-0.0.1.dev20250115.dist-info/METADATA +32 -0
  61. ai_edge_quantizer_nightly-0.0.1.dev20250115.dist-info/RECORD +63 -0
  62. ai_edge_quantizer_nightly-0.0.1.dev20250115.dist-info/WHEEL +5 -0
  63. ai_edge_quantizer_nightly-0.0.1.dev20250115.dist-info/top_level.txt +1 -0
@@ -0,0 +1,245 @@
1
+ # Copyright 2024 The AI Edge Quantizer Authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+
16
+ """Manages model quantization recipe (configuration) for the quantizer."""
17
+
18
+ import collections
19
+ import dataclasses
20
+ import re
21
+ from typing import Any, Optional
22
+ from absl import logging
23
+ from ai_edge_quantizer import algorithm_manager
24
+ from ai_edge_quantizer import qtyping
25
+
26
+ # A collection of quantization configuration.
27
+ # Key: scope regex.
28
+ # Value: list of OpQuantizationRecipe in dictionary format.
29
+ ModelQuantizationRecipe = list[dict[str, Any]]
30
+ # Expose algorithm names to users.
31
+ AlgorithmName = algorithm_manager.AlgorithmName
32
+
33
+ _TFLOpName = qtyping.TFLOperationName
34
+ _OpQuantizationConfig = qtyping.OpQuantizationConfig
35
+ _TensorQuantizationConfig = qtyping.TensorQuantizationConfig
36
+
37
+
38
+ @dataclasses.dataclass
39
+ class OpQuantizationRecipe:
40
+ """Dataclass for quantization configuration under a scope.
41
+
42
+ This class is the main entry point to a recipe schema. There could be a single
43
+ instance of this associated with a model (with `regex=.*` if the full model is
44
+ to be quantized with the same spec), or multiple instances targeting different
45
+ `regex` or `operation`.
46
+
47
+ Attributes:
48
+ regex: Regular expression for scope name matching. Any op that matches
49
+ `regex` will be quantized according to this instance. The narrowest scope
50
+ would be the full output tensor name of an op. The widest scope would be
51
+ '.*' which applies to the full model.
52
+ operation: Target TFL operation. * for any supported TFLite operation.
53
+ algorithm_key: Algorithm key to be applied. This can be any one of the
54
+ strings as enumerated in `AlgorithmName`.
55
+ op_config: Quantization configuration to be applied for the op.
56
+ """
57
+
58
+ regex: str
59
+ operation: _TFLOpName
60
+ algorithm_key: str
61
+ op_config: _OpQuantizationConfig = dataclasses.field(
62
+ default_factory=_OpQuantizationConfig
63
+ )
64
+
65
+
66
+ class RecipeManager:
67
+ """Sets the quantization recipe for target model.
68
+
69
+ This class is internal to Quantizer to help manage loading recipes and
70
+ resolving conflicts between recipes input by the user.
71
+ """
72
+
73
+ def __init__(self):
74
+ """Scope name config.
75
+
76
+ Key: scope regex. ".*" for all scopes.
77
+ Value: list of operator quantization settings under the scope.
78
+ The priority between rules are determined by the order they entered: later
79
+ one has higher priority.
80
+ """
81
+ self._scope_configs: collections.OrderedDict[
82
+ str, list[OpQuantizationRecipe]
83
+ ] = collections.OrderedDict()
84
+
85
+ # TODO: b/335254997 - Check if an op quantization config is supported.
86
+ def add_quantization_config(
87
+ self,
88
+ regex: str,
89
+ operation_name: _TFLOpName,
90
+ op_config: Optional[_OpQuantizationConfig] = None,
91
+ algorithm_key: str = algorithm_manager.AlgorithmName.MIN_MAX_UNIFORM_QUANT,
92
+ ) -> None:
93
+ """Adds a quantization configuration.
94
+
95
+ Conflict arises when we are trying to set an operation under a certain regex
96
+ which is already existed in the config dictionary. Under such circumstance,
97
+ the new config is used to replace the previous one.
98
+
99
+ We also have special treatment for _TFLOperationKey.ALL. If the new config
100
+ is on _TFLOperationKey.ALL and there are existing op configs inside the same
101
+ scope, we clear the previous configs and use _TFLOperationKey.ALL.
102
+
103
+ Args:
104
+ regex: Regular expression for layer name matching.
105
+ operation_name: Target TFLite operation. * for all supported TFLite
106
+ operation.
107
+ op_config: Quantization configuration which will be used to update the
108
+ default configuration. None or empty dict means the default
109
+ configuration will be used.
110
+ algorithm_key: Algorithm key to be applied.
111
+ """
112
+ if op_config is None:
113
+ op_config = _OpQuantizationConfig()
114
+
115
+ config = OpQuantizationRecipe(
116
+ regex, operation_name, algorithm_key, op_config
117
+ )
118
+ # Special care if trying to set all ops to some config.
119
+ if config.operation == _TFLOpName.ALL_SUPPORTED:
120
+ self._scope_configs[regex] = [config]
121
+ return
122
+
123
+ if algorithm_key != AlgorithmName.NO_QUANTIZE:
124
+ algorithm_manager.check_op_quantization_config(
125
+ algorithm_key, operation_name, op_config
126
+ )
127
+
128
+ if regex not in self._scope_configs:
129
+ self._scope_configs[regex] = [config]
130
+ else:
131
+ # Reiterate configs to avoid duplication on op settings.
132
+ configs = []
133
+ is_new_op = True
134
+ for existing_config in self._scope_configs[regex]:
135
+ if existing_config.operation == config.operation:
136
+ is_new_op = False
137
+ op_config = config
138
+ logging.warning(
139
+ 'Overwrite operation %s config under scope_regex %s with %s.',
140
+ existing_config.operation,
141
+ regex,
142
+ config,
143
+ )
144
+ else:
145
+ op_config = existing_config
146
+ configs.append(op_config)
147
+ if is_new_op:
148
+ configs.append(config)
149
+ self._scope_configs[regex] = configs
150
+
151
+ def get_quantization_configs(
152
+ self,
153
+ target_op_name: _TFLOpName,
154
+ scope_name: str,
155
+ ) -> tuple[str, _OpQuantizationConfig]:
156
+ """Gets the algorithm key and quantization configuration for an op.
157
+
158
+ We respect the latest valid config and fall back to no quantization.
159
+ Specifically, we search the quantization configuration in the order of the
160
+ scope configs. If there are two or more matching settings, the latest one
161
+ will be used.
162
+
163
+
164
+ Args:
165
+ target_op_name: Target TFLite operation. * for all supported TFLite
166
+ operation.
167
+ scope_name: Name of the target scope.
168
+
169
+ Returns:
170
+ A tuple of quantization algorithm, and quantization configuration.
171
+ """
172
+ result_key, result_config = (
173
+ AlgorithmName.NO_QUANTIZE,
174
+ _OpQuantizationConfig(),
175
+ )
176
+ for scope_regex, recipes in self._scope_configs.items():
177
+ if re.search(scope_regex, scope_name):
178
+ for recipe in recipes:
179
+ if (
180
+ recipe.operation != _TFLOpName.ALL_SUPPORTED
181
+ and recipe.operation != target_op_name
182
+ ):
183
+ continue
184
+ selected_recipe = recipe
185
+ if selected_recipe.algorithm_key != AlgorithmName.NO_QUANTIZE:
186
+ # The selected recipe must contain a supported config.
187
+ try:
188
+ algorithm_manager.check_op_quantization_config(
189
+ recipe.algorithm_key, target_op_name, recipe.op_config
190
+ )
191
+ except ValueError:
192
+ continue # Skip the recipe if it is not supported.
193
+ result_config = selected_recipe.op_config
194
+ result_key = selected_recipe.algorithm_key
195
+
196
+ return result_key, result_config
197
+
198
+ def get_quantization_recipe(self) -> ModelQuantizationRecipe:
199
+ """Gets the full quantization recipe from the manager.
200
+
201
+ Returns:
202
+ A list of quantization configs in the recipe.
203
+ """
204
+ ret = []
205
+ for _, scope_config in self._scope_configs.items():
206
+ for quant_config in scope_config:
207
+ config = dict()
208
+ config['regex'] = quant_config.regex
209
+ config['operation'] = quant_config.operation
210
+ config['algorithm_key'] = quant_config.algorithm_key
211
+ config['op_config'] = quant_config.op_config.to_dict()
212
+ ret.append(config)
213
+ return ret
214
+
215
+ def load_quantization_recipe(
216
+ self, quantization_recipe: ModelQuantizationRecipe
217
+ ) -> None:
218
+ """Loads the quantization recipe to the manager.
219
+
220
+ Args:
221
+ quantization_recipe: A configuration dictionary which is generated by
222
+ get_full_config.
223
+ """
224
+ self._scope_configs = collections.OrderedDict()
225
+ for config in quantization_recipe:
226
+ self.add_quantization_config(
227
+ config['regex'],
228
+ config['operation'],
229
+ _OpQuantizationConfig.from_dict(config['op_config'])
230
+ if config['algorithm_key'] != AlgorithmName.NO_QUANTIZE
231
+ else None,
232
+ config['algorithm_key'],
233
+ )
234
+
235
+ def need_calibration(self) -> bool:
236
+ """Check if the recipe requires calibration."""
237
+ # At the moment, only SRQ requires calibration.
238
+ for op_quant_config in self.get_quantization_recipe():
239
+ if (
240
+ op_quant_config['op_config']['compute_precision']
241
+ == qtyping.ComputePrecision.INTEGER
242
+ and 'activation_tensor_config' in op_quant_config['op_config']
243
+ ):
244
+ return True
245
+ return False