open-mmlab/mmdetection · error · ValueError

NUM_BRANCHES({num_branches}) != NUM_CHANNELS({len(num_channe

Error message

NUM_BRANCHES({num_branches}) != NUM_CHANNELS({len(num_channels)})

What it means

HRNet's _check_branches also requires len(num_channels) == num_branches for every stage in extra; each parallel branch needs its own channel width. A mismatch raises ValueError naming NUM_CHANNELS.

Source

Thrown at mmdet/models/backbones/hrnet.py:59

        self.norm_cfg = norm_cfg
        self.conv_cfg = conv_cfg
        self.with_cp = with_cp
        self.branches = self._make_branches(num_branches, blocks, num_blocks,
                                            num_channels)
        self.fuse_layers = self._make_fuse_layers()
        self.relu = nn.ReLU(inplace=False)

    def _check_branches(self, num_branches, num_blocks, in_channels,
                        num_channels):
        if num_branches != len(num_blocks):
            error_msg = f'NUM_BRANCHES({num_branches}) ' \
                        f'!= NUM_BLOCKS({len(num_blocks)})'
            raise ValueError(error_msg)

        if num_branches != len(num_channels):
            error_msg = f'NUM_BRANCHES({num_branches}) ' \
                        f'!= NUM_CHANNELS({len(num_channels)})'
            raise ValueError(error_msg)

        if num_branches != len(in_channels):
            error_msg = f'NUM_BRANCHES({num_branches}) ' \
                        f'!= NUM_INCHANNELS({len(in_channels)})'
            raise ValueError(error_msg)

    def _make_one_branch(self,
                         branch_index,
                         block,
                         num_blocks,
                         num_channels,
                         stride=1):
        downsample = None
        if stride != 1 or \
                self.in_channels[branch_index] != \
                num_channels[branch_index] * block.expansion:
            downsample = nn.Sequential(
                build_conv_layer(

View on GitHub (pinned to cfd5d3a985)

Solutions

  1. Set num_channels to exactly num_branches entries, e.g. num_branches=3, num_channels=[48,96,192]
  2. Keep width progression consistent with in_channels of the next stage
  3. Prefer modifying pretrained official configs (w18/w32/w48) minimally

Example fix

# before
stage3=dict(num_branches=3, num_channels=[32,64), num_blocks=[4,4,4])
# after
stage3=dict(num_branches=3, num_channels=[32,64,128], num_blocks=[4,4,4])
Defensive patterns

Strategy: validation

Validate before calling

for stage in extra.values():
    assert stage['num_branches'] == len(stage['num_channels']), 'num_channels length must equal num_branches'

Type guard

def channels_match(stage: dict) -> bool:
    return stage['num_branches'] == len(stage['num_channels'])

Prevention

When it happens

Trigger: num_branches=3 with num_channels=[32,64] (len 2); editing HRNet width lists without adding an entry when adding a branch.

Common situations: Building custom HRNet-W48 variants; changing stage3/stage4 branch counts; miscounting after merging configs.

Related errors


AI-assisted analysis of open-mmlab/mmdetection@cfd5d3a985 (2026-08-27). Data as JSON: /api/errors/7d434adbebac170d. Report an issue: GitHub.