@@ -67,9 +67,9 @@ def _get_block_size(
6767 size for that specific axis
6868
6969 ``quantization_target`` distinguishes weight from activation tensors.
70- Only per-block granularity uses it (see
71- :meth :`PerBlockGranularity._handle_single_axis_block_size `); the other
72- granularities ignore it.
70+ Only per-block granularity uses it, since the two targets collapse
71+ different sets of non-blocked axes (see :class :`PerBlockGranularity`);
72+ the other granularities ignore it.
7373
7474 Example:
7575 - ``[10, 5, 2]`` with per-channel structuring on axis 1 results in
@@ -212,10 +212,15 @@ class PerBlockGranularity(QuantizationGranularity):
212212 ``Quantizer.prepare()`` automatically resolves the axis based on the module type
213213 for weight quantization.
214214
215- Single-axis mode treats weights and activations differently. For weights only
216- the two leading channel axes participate in blocking, so trailing kernel
217- dimensions span a whole block. For activations every axis other than the block
218- axis gets its own scale.
215+ Single-axis mode treats weights and activations differently:
216+
217+ - ``WEIGHT``: only the two leading channel axes take part. Whichever of them
218+ is not the block axis collapses to ``1`` (one scale per slice), while
219+ trailing dimensions — e.g. conv kernel dims — keep their full size, so each
220+ block spans the whole kernel.
221+ - ``ACTIVATION``: every axis other than the block axis collapses to ``1``, so
222+ the scale holds one entry per block *and* per position along all the other
223+ axes.
219224
220225 .. list-table::
221226 :header-rows: 1
@@ -303,15 +308,20 @@ def _handle_single_axis_block_size(
303308 block_sizes_list : list [int ],
304309 quantization_target : _CompressionTargetTensor = _CompressionTargetTensor .WEIGHT ,
305310 ) -> list [int ]:
306- """Handle blocking when self.block_size is an integer"""
311+ """Handle blocking when ``block_size`` is an integer.
312+
313+ ``axis`` may be negative and is resolved against the tensor rank.
314+
315+ ``quantization_target`` decides which of the non-blocked axes collapse to
316+ ``1``: weights keep their trailing (e.g. kernel) dimensions whole, while
317+ activations collapse every axis but the block axis. See the class
318+ docstring for examples.
319+ """
307320 # TODO: Logic to be added where if self.axis is None,
308321 # we can figure out the optimal axis for the user
309322 if self .axis is None :
310323 raise ValueError ("axis must be specified when block_size is an int" )
311324
312- # Resolve negative (Python-style) axis to a non-negative index using the
313- # tensor rank. This allows activation quantization to target the last /
314- # reduction axis via axis=-1 regardless of the tensor's rank.
315325 rank = len (block_sizes_list )
316326 axis = self .axis + rank if self .axis < 0 else self .axis
317327
@@ -327,22 +337,7 @@ def _handle_single_axis_block_size(
327337 f"is not divisible by block size { self .block_size } "
328338 )
329339
330- # How the non-block axes are treated depends on the quantization target,
331- #
332- # WEIGHT: only the two leading channel axes participate. The other
333- # channel axis becomes 1 (one scale per slice) while any trailing
334- # dimensions (index 2+, e.g. conv kernel dims) keep their full size so
335- # each block spans the whole kernel.
336- # [C_out, C_in, KH, KW], axis=0, block=16 -> [16, 1, KH, KW]
337- #
338- # ACTIVATION: blocking runs along a single axis and every other
339- # dimension gets its own scale, so all non-block axes become 1.
340- # [B, S, D], axis=-1, block=16 -> [1, 1, 16]
341- # [B, C, H, W], axis=1, block=16 -> [1, 16, 1, 1]
342- if quantization_target == _CompressionTargetTensor .ACTIVATION :
343- collapse_upto = rank
344- else :
345- collapse_upto = 2
340+ collapse_upto = rank if quantization_target == _CompressionTargetTensor .ACTIVATION else 2
346341
347342 block_sizes_list [axis ] = self .block_size
348343 for i , _ in enumerate (block_sizes_list [:collapse_upto ]):
0 commit comments