Skip to content

[BugFix] Fix SyncBatchNorm.convert_sync_batchnorm dropping use_global_stats=True - #79944

Open
sibisrinivasb wants to merge 2 commits into
PaddlePaddle:developfrom
sibisrinivasb:fix-convert-syncbn-use-global-stats
Open

sibisrinivasb wants to merge 2 commits into
PaddlePaddle:developfrom
sibisrinivasb:fix-convert-syncbn-use-global-stats

Conversation

@sibisrinivasb

@sibisrinivasb sibisrinivasb commented Nov 11, 2026 •

Copy link
Copy Markdown

PR Category

User Experience

PR Types

Bug fixes

Description

Fixes #79943

SyncBatchNorm.convert_sync_batchnorm replaced every BatchNorm*d layer, including layers created with use_global_stats=True. SyncBatchNorm does not support use_global_stats, so after the conversion such a layer normalized with batch statistics in train mode and overwrote its stored mean / variance on every forward pass, with no warning. A partially frozen model silently changed behaviour.

This PR keeps a layer with use_global_stats=True unchanged in the conversion. It always normalizes with its stored statistics, so it has no batch statistics to synchronize. All other layers are converted as before.

Tests added to test_sync_batch_norm_op_convert.py:

  • a BatchNorm2D(use_global_stats=True) is the same layer after the conversion, while a plain BatchNorm2D in the same model becomes a SyncBatchNorm;
  • its stored mean / variance do not change after a train-mode forward pass, and the train-mode output equals the eval-mode output.

Both tests fail before the change and pass after it. They do not run a SyncBatchNorm forward, so they also pass on CPU.

An alternative is to support use_global_stats inside SyncBatchNorm and carry it through the conversion. I chose the smaller change; I can switch to the other one if you prefer.

Review follow-up (4723bca): paddle.sparse.nn.SyncBatchNorm.convert_sync_batchnorm also keeps a dense BatchNorm*d(use_global_stats=True) layer (test added to test_sparse_norm_op.py). Sparse BatchNorm layers are still always converted, because their forward rewrites _use_global_stats on the first call; that needs a separate fix.

是否引起精度变化

否 - no operator or kernel computation is changed. The only behaviour change is the bug path described above: a BatchNorm*d(use_global_stats=True) layer is no longer converted, so it keeps using its stored statistics.

…_stats=True

A BatchNorm*d layer created with use_global_stats=True is kept unchanged by the conversion: it always normalizes with its stored mean and variance, so it has no batch statistics to synchronize, and SyncBatchNorm does not support use_global_stats.
@CLAassistant

CLAassistant commented Nov 11, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@Paddle-Review-Bot

Paddle-Review-Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Paddle-Bot Review Board (review完成)

序号 位置 优先级 规则来源 状态
1 稀疏版转换入口未同步修复 P2 默认规则 ✅

可维护性建议(总分 89.8 / 100,只给建议,不影响评审结论)

序号 位置 维度 状态
1 文档返回值说明与新增注释矛盾 契约一致性 ✅
各维度得分(仅评本 PR 改动,点击展开)
86 高层优雅度
87 跨模块架构
87 中层优雅度
88 抽象适配度
88 API 表面一致性
88 约定偏离
88 底层优雅度
88 测试策略
89 逻辑清晰度
90 AI 生成债务
90 契约一致性
1
90 设计一致性
92 错误处理一致性
92 未完成迁移
92 命名质量
92 类型安全
94 初始化耦合
95 包组织结构
— 鉴权一致性
— 依赖健康度

Powered by Nyanpasu claude with Opus 5.5 默认推理级别, please check the suggestions carefully.

# its stored mean and variance and never updates them, so it has no
# batch statistics to synchronize. SyncBatchNorm does not support
# use_global_stats, so such a layer is kept as it is.
if isinstance(layer, _BatchNormBase) and not layer._use_global_stats:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 同一缺陷在 sparse 入口仍未修复。

paddle.sparse.nn.SyncBatchNorm.convert_sync_batchnorm(python/paddle/sparse/nn/layer/norm.py:376)是本方法的近似拷贝,仍只判断 isinstance(layer, _BatchNormBase)。它同样会转换 dense BatchNorm*d(else 分支构造 paddle.nn.SyncBatchNorm),而 sparse BatchNorm 也保存 use_global_stats(同文件 117/145 行)。因此用户改用 sparse 入口转换含 BatchNorm2D(use_global_stats=True) 或 paddle.sparse.nn.BatchNorm(use_global_stats=True) 的模型时,仍会被替换为 SyncBatchNorm,训练时改用 batch 统计量并覆盖 mean/variance,与本 PR 的修复和新增 Note 不一致。

建议在 sparse 版的判断上同步加 and not layer._use_global_stats,补同样的 docstring Note,并在 test/legacy_test/test_sparse_norm_op.py 增加对应用例。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done for dense layers, in 4723bca: paddle.sparse.nn.SyncBatchNorm.convert_sync_batchnorm now keeps a dense BatchNorm*d(use_global_stats=True) layer, with the same Note and a test in test_sparse_norm_op.py (it fails before the change and passes after).

I did not apply the condition to paddle.sparse.nn.BatchNorm layers, for this reason: its forward writes self._use_global_stats = not self.training on the first call (python/paddle/sparse/nn/layer/norm.py, lines 145-146). On 3.3.1, a layer built without the option reads None when new, True after one forward in eval mode and False after one forward in train mode. So at conversion time the attribute does not tell whether the layer was created with use_global_stats=True, and the same condition would also skip layers that only ran once in eval mode. The Note states that sparse BatchNorm layers are always converted (unchanged behaviour).

That side effect looks like a separate problem: a sparse BatchNorm whose first forward was in eval mode no longer updates its statistics in train mode (stored mean moved by 0.0, against 0.0121 for a layer whose first forward was in train mode). I can open a separate issue / PR for it, after which the sparse case can use the same condition. If you prefer it in this PR, tell me.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已确认 4723bca 的修改:sparse 入口现在会保留 dense BatchNorm*d(use_global_stats=True),并有对应用例。关于 paddle.sparse.nn.BatchNorm,forward 在 _use_global_stats is None 时会改写该属性(python/paddle/sparse/nn/layer/norm.py:145-146),转换时确实无法区分构造参数,保持转换并在 Note 中说明是合理的。这个副作用可以单独开 issue/PR 跟进。此问题标记为已解决。

Helper function to convert :class: `paddle.nn.BatchNorm*d` layers in the model to :class: `paddle.nn.SyncBatchNorm` layers.

Note:
A `BatchNorm*d` layer created with ``use_global_stats=True`` is kept unchanged. It always

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 可维护性建议(契约一致性,不阻塞合入):新增 Note 说明 use_global_stats=True 的层保持不变,但同一 docstring 的 Returns(1812 行)仍写 “If BatchNorm*d layer in the model, use SyncBatchNorm layer instead”,即无条件替换,二者矛盾。建议同步改写 Returns,例如:“The original model with BatchNorm*d layers converted to SyncBatchNorm layers, except layers created with use_global_stats=True, which are returned unchanged.”

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4723bca, with your wording. The Returns section of the sparse entry is updated the same way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已确认 dense 与 sparse 两个入口的 Returns 都已与 Note 一致,标记为已解决。

…_sync_batchnorm; update the Returns docs

Review follow-up. The Returns section of paddle.nn.SyncBatchNorm.convert_sync_batchnorm now states the exception. paddle.sparse.nn.SyncBatchNorm.convert_sync_batchnorm keeps a dense BatchNorm*d layer created with use_global_stats=True. Sparse BatchNorm layers are still always converted: their forward rewrites _use_global_stats on the first call, so the attribute does not tell whether the layer was created with use_global_stats=True.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor External developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SyncBatchNorm.convert_sync_batchnorm silently drops use_global_stats=True: frozen BatchNorm layers start updating their statistics

3 participants