open-mmlab/mmdetection · error · ValueError

NUM_BRANCHES({num_branches}) != NUM_BLOCKS({len(num_blocks)}

Error message

NUM_BRANCHES({num_branches}) != NUM_BLOCKS({len(num_blocks)})

What it means

HRNet's _check_branches validates per-stage config consistency: num_branches must equal len(num_blocks) for each stage in extra. A mismatch means the stage definition is internally inconsistent and raises ValueError with this exact message.

Source

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

        self.in_channels = in_channels
        self.num_branches = num_branches

        self.multiscale_output = multiscale_output
        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

View on GitHub (pinned to cfd5d3a985)

Solutions

  1. For each stage in extra, make len(num_blocks) == num_branches (e.g. num_branches=2 with num_blocks=[4,4])
  2. Start from an official hrnet config and change only one field at a time
  3. Also verify num_channels and in_channels lengths match num_branches (they are checked next)

Example fix

# before
extra=dict(stage2=dict(num_branches=2, num_blocks=[4,4,4], num_channels=[18,36])))
# after
extra=dict(stage2=dict(num_branches=2, num_blocks=[4,4], num_channels=[18,36])))
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

def stage_consistent(stage: dict) -> bool:
    n = stage['num_branches']
    return len(stage['num_blocks']) == n and len(stage['num_channels']) == n and len(stage['in_channels']) == n

Prevention

When it happens

Trigger: In extra['stage2'], setting num_branches=2 but num_blocks=[4,4,4] (len 3); editing an HRNet config's stage dict and forgetting to keep list lengths equal to num_branches.

Common situations: Customizing HRNet-W18/W32/W48 configs; reducing branch counts for ablation while leaving num_blocks lists unchanged; typos when hand-editing nested stage dicts.

Related errors


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