{"record":{"id":"6cc9670b5da87779","repo":"PaddlePaddle/PaddleOCR","slug":"normalize-should-be-true-if-scale-is-passed","errorCode":null,"errorMessage":"normalize should be True if scale is passed","messagePattern":"normalize should be True if scale is passed","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"ppocr/modeling/heads/rec_can_head.py","lineNumber":107,"sourceCode":"\n        return x1, paddle.reshape(x, [b, self.out_channel, h, w])\n\n\n\"\"\"\nAttention Decoder\n\"\"\"\n\n\nclass PositionEmbeddingSine(nn.Layer):\n    def __init__(\n        self, num_pos_feats=64, temperature=10000, normalize=False, scale=None\n    ):\n        super().__init__()\n        self.num_pos_feats = num_pos_feats\n        self.temperature = temperature\n        self.normalize = normalize\n        if scale is not None and normalize is False:\n            raise ValueError(\"normalize should be True if scale is passed\")\n        if scale is None:\n            scale = 2 * math.pi\n        self.scale = scale\n\n    def forward(self, x, mask):\n        y_embed = paddle.cumsum(mask, 1, dtype=\"float32\")\n        x_embed = paddle.cumsum(mask, 2, dtype=\"float32\")\n\n        if self.normalize:\n            eps = 1e-6\n            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale\n            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale\n        dim_t = paddle.arange(self.num_pos_feats, dtype=\"float32\")\n        dim_d = paddle.expand(paddle.to_tensor(2), dim_t.shape)\n        dim_t = self.temperature ** (\n            2 * (dim_t / dim_d).astype(\"int64\") / self.num_pos_feats\n        )\n","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/ppocr/modeling/heads/rec_can_head.py#L89-L125","documentation":"PositionEmbeddingSine (used by the CAN head for contrastive character embedding) computes sine positional encodings optionally normalized to [0, scale]. The scale parameter only has meaning when cumulative coordinates are normalized, so passing scale with normalize=False is a contradictory configuration and __init__ raises ValueError('normalize should be True if scale is passed'). If scale is None it defaults to 2*pi.","triggerScenarios":"PositionEmbeddingSine(num_pos_feats=..., scale=100.0) without normalize=True; or a config that sets scale but leaves normalize at its default False.","commonSituations":"Tuning the CAN rec head config and adding a scale value copied from another codebase (DETR-style configs use normalize=True with scale=2*pi); forgetting the coupling between the two args.","solutions":["Add normalize=True when passing scale: PositionEmbeddingSine(..., normalize=True, scale=s)","Or drop scale entirely and keep the default normalize=False behavior (scale defaults to 2*pi internally)"],"exampleFix":"# before\nPositionEmbeddingSine(num_pos_feats=64, scale=2 * math.pi)  # ValueError\n\n# after\nPositionEmbeddingSine(num_pos_feats=64, normalize=True, scale=2 * math.pi)","handlingStrategy":"validation","validationCode":"if scale is not None:\n    assert normalize is True, 'set normalize=True when passing scale to PositionEmbeddingSine'\nPositionEmbeddingSine(num_pos_feats=64, normalize=normalize, scale=scale)","typeGuard":"def valid_pos_embed_args(normalize: bool, scale) -> bool:\n    return scale is None or normalize is True","tryCatchPattern":null,"preventionTips":["Treat (normalize, scale) as coupled: setting scale implies normalize=True","If unsure, omit scale and accept the internal default 2*pi","Copy positional-embedding args from the shipped CAN head config verbatim"],"tags":["config","positional-encoding","can","argument-coupling"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}