timm's PiT models report their feature-map downsampling strides one too small at the base.
feature_info sets each stage's reduction to (stride - 1) * 2**i instead of the cumulative
network stride stride * 2**i, so a PiT built with features_only=True advertises reductions of
[7, 14, 28] (pit_ti, pit_xs, pit_s) or [6, 12, 24] (pit_b) where the true strides are
[8, 16, 32] and [7, 14, 28]. A downstream detection or segmentation neck that keys feature
maps by their reported reduction gets a non-power-of-two base stride that corresponds to no real
downsampling factor, and frameworks that require reductions in {4, 8, 16, 32} reject PiT.
timm/models/pit.py, PoolingVisionTransformer.__init__, current main (identical in 1.0.28):
self.feature_info += [dict(num_chs=prev_dim, reduction=(stride - 1) * 2**i,
module=f'transformers.{i}')] # line 235FeatureInfo.reduction is the total downsampling stride of a feature map, the cumulative network
stride, and every other backbone reports it that way (ResNet [2, 4, 8, 16, 32], Swin
[4, 8, 16, 32] with the base equal to the patch-embed stride). PiT's patch-embed convolution
has stride stride and each of the two pooling stages halves the resolution, so stage i
downsamples by stride * 2**i. The (stride - 1) subtracts one from the base and corrupts the
reported stride at every level.
Read from the real timm models at 224x224:
pit_ti_224: reported [7, 14, 28], feature spatial [27, 14, 7], true stride [8, 16, 32]
pit_xs_224: reported [7, 14, 28], feature spatial [27, 14, 7], true stride [8, 16, 32]
pit_b_224: reported [6, 12, 24], feature spatial [31, 16, 8], true stride [7, 14, 28]
control swin_tiny_patch4_window7_224: reduction [4, 8, 16, 32] (base = patch stride 4, correct)
The feature-map spatial sizes give the true downsampling: 224 / 27 = 8.3, 224 / 14 = 16,
224 / 7 = 32, so the cumulative strides are [8, 16, 32], matching the design stride of the
patch-embed convolution. The reported base 7 is neither the design stride 8 nor the realized
ratio 8.3, and it is not a power of two, so it names no real downsampling factor. The pooling
ratios in the reported list are correct (14/7 = 2, 28/14 = 2); only the absolute base is
wrong, which is exactly the signature of the stride - 1. The control confirms the harness: Swin
reports the correct base reduction equal to its patch stride, so the feature_info.reduction
convention and machinery are otherwise right, and the defect is isolated to PiT's (stride - 1).
The bug is on current main, affects every PiT variant (pit_ti, pit_xs, pit_s, pit_b, and the
distilled variants), and is reached through the standard features_only=True construction and
feature_info.reduction(), which is how a backbone is wired into a detection or segmentation
neck. It is silent: the reduction values are still monotonically increasing, so no assertion in
FeatureInfo fires, and the wrong strides propagate into anchor and coordinate scaling. The fix
is reduction=stride * 2**i.
excerpt.py: thefeature_inforeduction line quoted with the two flags, and the reduction convention (Apache-2.0).reduction.py: the reported(stride-1) * 2**iand the correctstride * 2**ias plain functions.consequence.py: the real timm PiT reductions versus the true strides from the feature sizes, and the Swin control.test_pitstride.py: the base is off by one, the real PiT models report[7,14,28]/[6,12,24]where the true strides are[8,16,32]/[7,14,28], the reported base is not a power of two, and the control backbone is correct.
python reduction.py
python consequence.py
python test_pitstride.py
The reduction line is quoted from current main; the strides are read from the real timm PiT
models. The fix is to report stride * 2**i, the cumulative network stride, as every other
backbone does.