You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Applying ChannelStructured pruning to a 1D tensor parameter (e.g. 1D bias parameters, 1D normalization weights, or directly calling _MagnitudePruneImpl.compute_mask on a 1D tensor) crashes with:
RuntimeError: selected index k out of range
at torch.topk(channel_norms, num_keep, largest=True) in src/coreai_opt/pruning/spec/prune.py:192.
Context & Prior Art
This defect is closely related to PR #45 (fix(pruning): normalize a negative channel axis by @eyupcanakman, approved by @u-simha):
In PR fix(pruning): normalize a negative channel axis #45, negative channel indexing (axis=-1) previously failed with this exact same error message (RuntimeError: selected index k out of range) because raw negative indices never matched d in range(ndim), causing reduce_dims to include all dimensions and collapsing channel_norms to a scalar.
PR fix(pruning): normalize a negative channel axis #45 resolved negative indexing via _normalize_axis(axis, weight.ndim) and added tests for 4D (nn.Conv2d) and 2D (nn.Linear) tensors, as well as verifying normalize_axis(-1, 1) == 0.
However, 1D tensors were never tested end-to-end with ChannelStructured.
When weight.ndim == 1 and axis == 0 (or axis == -1, which normalizes to 0):
reduce_dims= [dfordinrange(1) ifd!=0] # -> []
In PyTorch ATen C++ (ReduceOps.cpp), passing an empty dimension list dim=[] to sum(dim) does not act as an identity / no-op. Instead, PyTorch reduces across all dimensions into a 0-D scalar:
When $W$ is a 1D tensor (ndim == 1, axis == 0), each channel slice is simply an individual scalar element $w_i$. The $L_1$ norm of a scalar is its absolute value:
$$
|w_i|_{1} = |w_i|
$$
Therefore, when reduce_dims is empty, no dimension reduction should occur; channel_norms is simply weight.abs().
Step-by-Step Reproduction
Minimal Reproduction Script
importtorchimporttorch.nnasnnfromcoreai_opt.pruningimportMagnitudePruner, MagnitudePrunerConfigfromcoreai_opt.pruning.configimportModuleMagnitudePrunerConfigfromcoreai_opt.pruning.specimportChannelStructured, PruningSpecfromcoreai_opt.pruning.spec.pruneimport_MagnitudePruneImpl# Case 1: Direct mask computation on a 1D tensorw=torch.tensor([1.0, 5.0, 2.0, 8.0])
_MagnitudePruneImpl._compute_channel_mask(w, sparsity=0.5, axis=0)
# -> RuntimeError: selected index k out of range# Case 2: End-to-end model pruning on a 1D parameter (e.g. bias)model=nn.Linear(4, 4, bias=True)
config=MagnitudePrunerConfig(
global_config=ModuleMagnitudePrunerConfig(
op_state_spec={
"bias": PruningSpec(
target_sparsity=0.5,
pruning_scheme=ChannelStructured(axis=0),
)
}
)
)
pruner=MagnitudePruner(model, config)
pruner.prepare((torch.randn(1, 4),))
# -> RuntimeError: selected index k out of range
Full Traceback
Traceback (most recent call last):
File "<string>", line 20, in <module>
File ".../src/coreai_opt/pruning/magnitude_pruner.py", line 124, in prepare
prepared_model(*example_inputs)
File ".../torch/nn/modules/module.py", line 1783, in _wrapped_call_impl
return self._call_impl(*args, **kwargs)
File ".../torch/nn/modules/linear.py", line 134, in forward
return F.linear(input, self.weight, self.bias)
File ".../torch/nn/utils/parametrize.py", line 440, in get_parametrized
return parametrization()
File ".../torch/nn/utils/parametrize.py", line 331, in forward
x = self[0](self.original)
File ".../src/coreai_opt/pruning/spec/prune.py", line 103, in forward
new_mask = self.compute_mask(weight, self._sparsity, self._pruning_scheme)
File ".../src/coreai_opt/pruning/spec/prune.py", line 149, in compute_mask
return _MagnitudePruneImpl._compute_channel_mask(weight, sparsity, pruning_scheme.axis)
File ".../src/coreai_opt/pruning/spec/prune.py", line 192, in _compute_channel_mask
_, keep_indices = torch.topk(channel_norms, num_keep, largest=True)
RuntimeError: selected index k out of range
Proposed Permanent Canonical Fix
In src/coreai_opt/pruning/spec/prune.py:188-189 (and src/coreai_opt/pruning/spec/scheme.py in PR #61):
- reduce_dims = [d for d in range(weight.ndim) if d != axis]- channel_norms = weight.abs().sum(dim=reduce_dims)+ reduce_dims = [d for d in range(weight.ndim) if d != axis]+ channel_norms = weight.abs().sum(dim=reduce_dims) if reduce_dims else weight.abs()
Why This Fix Is Complete and Sound:
Handles 1D Tensors: For 1D tensors, reduce_dims is empty, avoiding PyTorch's sum(dim=[]) scalar reduction and correctly setting channel_norms = weight.abs().
Preserves Multi-D Behavior: For 2D, 3D, 4D, etc., reduce_dims is non-empty, preserving identical existing behavior.
Axis Independence: Works seamlessly for axis=0 and axis=-1 after _normalize_axis.
Verification Plan
Unit Tests in tests/pruning/test_magnitude_pruner.py:
Add parametric tests for 1D tensor channel-structured pruning with positive and negative axes (axis=0, axis=-1).
Test end-to-end model preparation with 1D parameter pruning.
Quality Gates:
Run full make check (formatting, linting, docstrings, typing, tests) to ensure 100% compliance.
Summary
Applying
ChannelStructuredpruning to a 1D tensor parameter (e.g. 1D bias parameters, 1D normalization weights, or directly calling_MagnitudePruneImpl.compute_maskon a 1D tensor) crashes with:at
torch.topk(channel_norms, num_keep, largest=True)insrc/coreai_opt/pruning/spec/prune.py:192.Context & Prior Art
This defect is closely related to PR #45 (
fix(pruning): normalize a negative channel axisby @eyupcanakman, approved by @u-simha):axis=-1) previously failed with this exact same error message (RuntimeError: selected index k out of range) because raw negative indices never matchedd in range(ndim), causingreduce_dimsto include all dimensions and collapsingchannel_normsto a scalar._normalize_axis(axis, weight.ndim)and added tests for 4D (nn.Conv2d) and 2D (nn.Linear) tensors, as well as verifyingnormalize_axis(-1, 1) == 0.ChannelStructured.Add support for structured sparsityby @u-simha), mask computation was abstracted intoChannelStructured.compute_mask_implinsrc/coreai_opt/pruning/spec/scheme.py, inheriting the identical reduction logic.Root Cause Analysis
In
src/coreai_opt/pruning/spec/prune.py:188-192:weight.ndim == 1andaxis == 0(oraxis == -1, which normalizes to0):ReduceOps.cpp), passing an empty dimension listdim=[]tosum(dim)does not act as an identity / no-op. Instead, PyTorch reduces across all dimensions into a 0-D scalar:torch.topk(channel_norms, num_keep)is invoked on a 0-D scalar tensor, PyTorch fails because the tensor has 0 dimensions:Mathematical Invariant
In channel-structured pruning along$L_1$ norm of a channel slice is the sum over all non-channel dimensions:
axis, theWhen$W$ is a 1D tensor ($w_i$ . The $L_1$ norm of a scalar is its absolute value:
ndim == 1,axis == 0), each channel slice is simply an individual scalar elementTherefore, when
reduce_dimsis empty, no dimension reduction should occur;channel_normsis simplyweight.abs().Step-by-Step Reproduction
Minimal Reproduction Script
Full Traceback
Proposed Permanent Canonical Fix
In
src/coreai_opt/pruning/spec/prune.py:188-189(andsrc/coreai_opt/pruning/spec/scheme.pyin PR #61):Why This Fix Is Complete and Sound:
reduce_dimsis empty, avoiding PyTorch'ssum(dim=[])scalar reduction and correctly settingchannel_norms = weight.abs().reduce_dimsis non-empty, preserving identical existing behavior.axis=0andaxis=-1after_normalize_axis.Verification Plan
tests/pruning/test_magnitude_pruner.py:axis=0,axis=-1).make check(formatting, linting, docstrings, typing, tests) to ensure 100% compliance.