{"record":{"id":"3a1356b420f14862","repo":"WZMIAOMIAO/deep-learning-for-image-processing","slug":"backbone-should-contain-an-attribute-out-channels","errorCode":null,"errorMessage":"backbone should contain an attribute out_channels specifying the number of output channels (assumed to be the same for all the levels)","messagePattern":"backbone should contain an attribute out_channels specifying the number of output channels \\(assumed to be the same for all the levels\\)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pytorch_object_detection/retinaNet/network_files/retinanet.py","lineNumber":304,"sourceCode":"        'proposal_matcher': det_utils.Matcher,\n    }\n\n    def __init__(self, backbone, num_classes,\n                 # transform parameters\n                 min_size=800, max_size=1333,\n                 image_mean=None, image_std=None,\n                 # Anchor parameters\n                 anchor_generator=None, head=None,\n                 proposal_matcher=None,\n                 score_thresh=0.05,\n                 nms_thresh=0.5,\n                 detections_per_img=100,\n                 fg_iou_thresh=0.5, bg_iou_thresh=0.4,\n                 topk_candidates=1000):\n        super(RetinaNet, self).__init__()\n\n        if not hasattr(backbone, \"out_channels\"):\n            raise ValueError(\n                \"backbone should contain an attribute out_channels \"\n                \"specifying the number of output channels (assumed to be the \"\n                \"same for all the levels)\"\n            )\n\n        self.backbone = backbone\n\n        assert isinstance(anchor_generator, (AnchorsGenerator, type(None)))\n\n        if anchor_generator is None:\n            # 原论文中说在每个预测特征层上除了使用给定的尺度x外，还要额外添加x*2^(1/3)和x*2^(2/3)这两个尺度\n            # 五个预测特征层采用的原始尺度分别为32， 64， 128， 256， 512\n            # 注意尺度和面积的关系，面积=尺度^2\n            anchor_sizes = tuple((x, int(x * 2 ** (1.0 / 3)), int(x * 2 ** (2.0 / 3)))\n                                 for x in [32, 64, 128, 256, 512])\n            # 对于每个预测特征层上anchors，都会使用三种比例\n            aspect_ratios = ((0.5, 1.0, 2.0),) * len(anchor_sizes)\n            anchor_generator = AnchorsGenerator(anchor_sizes, aspect_ratios)","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/blob/1ec3fe6f374fc9969973a61f819de25658595afa/pytorch_object_detection/retinaNet/network_files/retinanet.py#L286-L322","documentation":"RetinaNet.__init__ requires the backbone object to expose an integer attribute out_channels, because the FPN and head construction need the number of channels of every pyramid level (assumed uniform). torchvision-style backbones (e.g. resnet50_fpn_backbone) set it; a raw nn.Module without it cannot be wired into the detection heads.","triggerScenarios":"Instantiating RetinaNet(backbone=my_custom_cnn, ...) where my_custom_cnn class never defines self.out_channels; wrapping a backbone in a custom module that drops the attribute; using a plain torchvision resnet directly without the FPN helper.","commonSituations":"Swapping in a custom backbone (EfficientNet, Swin, etc.) copied from a classification repo; upgrading torchvision so backbone-builder APIs changed; writing a wrapper module around a backbone and forgetting to forward out_channels.","solutions":["Set self.out_channels = <channels of last FPN level> in your backbone's __init__.","Use the repo's provided builder (e.g. resnet50_fpn_backbone) which sets out_channels automatically.","If wrapping, forward the attribute: wrapper.out_channels = inner_backbone.out_channels.","Verify with hasattr(backbone, 'out_channels') before constructing RetinaNet."],"exampleFix":"// before\nclass MyBackbone(nn.Module):\n    def __init__(self):\n        super().__init__()\n        self.body = resnet50()\nretina = RetinaNet(backbone=MyBackbone(), num_classes=91)\n// after\nclass MyBackbone(nn.Module):\n    out_channels = 2048  # channels of the last feature level\n    def __init__(self):\n        super().__init__()\n        self.body = resnet50()\nretina = RetinaNet(backbone=MyBackbone(), num_classes=91)","handlingStrategy":"validation","validationCode":"if not hasattr(backbone, \"out_channels\"):\n    raise TypeError(\"backbone must define integer attribute out_channels\")\nout_channels = backbone.out_channels","typeGuard":"def is_valid_retinanet_backbone(backbone) -> bool:\n    oc = getattr(backbone, \"out_channels\", None)\n    return isinstance(oc, int) and oc > 0","tryCatchPattern":"try:\n    model = RetinaNet(backbone=backbone, num_classes=num_classes)\nexcept ValueError as e:\n    if \"out_channels\" in str(e):\n        backbone.out_channels = infer_out_channels(backbone)  # probe with dummy input\n        model = RetinaNet(backbone=backbone, num_classes=num_classes)\n    else:\n        raise","preventionTips":["Always build backbones via the repo's *_fpn_backbone helpers.","Run a forward pass with a dummy tensor during backbone development.","Set out_channels in the backbone constructor, not after the fact.","When wrapping backbones, explicitly forward out_channels."],"tags":["pytorch","object-detection","backbone","api-contract"],"backgroundTag":"missing-required-attribute","analyzedSha":"1ec3fe6f374fc9969973a61f819de25658595afa","analyzedAt":"2026-08-30T09:19:11.901Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}