PaddlePaddle/PaddleOCR · error · ValueError

normalize should be True if scale is passed

Error message

normalize should be True if scale is passed

What it means

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.

Source

Thrown at ppocr/modeling/heads/rec_can_head.py:107

        return x1, paddle.reshape(x, [b, self.out_channel, h, w])


"""
Attention Decoder
"""


class PositionEmbeddingSine(nn.Layer):
    def __init__(
        self, num_pos_feats=64, temperature=10000, normalize=False, scale=None
    ):
        super().__init__()
        self.num_pos_feats = num_pos_feats
        self.temperature = temperature
        self.normalize = normalize
        if scale is not None and normalize is False:
            raise ValueError("normalize should be True if scale is passed")
        if scale is None:
            scale = 2 * math.pi
        self.scale = scale

    def forward(self, x, mask):
        y_embed = paddle.cumsum(mask, 1, dtype="float32")
        x_embed = paddle.cumsum(mask, 2, dtype="float32")

        if self.normalize:
            eps = 1e-6
            y_embed = y_embed / (y_embed[:, -1:, :] + eps) * self.scale
            x_embed = x_embed / (x_embed[:, :, -1:] + eps) * self.scale
        dim_t = paddle.arange(self.num_pos_feats, dtype="float32")
        dim_d = paddle.expand(paddle.to_tensor(2), dim_t.shape)
        dim_t = self.temperature ** (
            2 * (dim_t / dim_d).astype("int64") / self.num_pos_feats
        )

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Add normalize=True when passing scale: PositionEmbeddingSine(..., normalize=True, scale=s)
  2. Or drop scale entirely and keep the default normalize=False behavior (scale defaults to 2*pi internally)

Example fix

# before
PositionEmbeddingSine(num_pos_feats=64, scale=2 * math.pi)  # ValueError

# after
PositionEmbeddingSine(num_pos_feats=64, normalize=True, scale=2 * math.pi)
Defensive patterns

Strategy: validation

Validate before calling

if scale is not None:
    assert normalize is True, 'set normalize=True when passing scale to PositionEmbeddingSine'
PositionEmbeddingSine(num_pos_feats=64, normalize=normalize, scale=scale)

Type guard

def valid_pos_embed_args(normalize: bool, scale) -> bool:
    return scale is None or normalize is True

Prevention

When it happens

Trigger: PositionEmbeddingSine(num_pos_feats=..., scale=100.0) without normalize=True; or a config that sets scale but leaves normalize at its default False.

Common situations: 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.

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/6cc9670b5da87779. Report an issue: GitHub.