Skip to content

Commit efb5888

Browse files
bbuschkaemperyaoyu-33claudecuichenx
authored
feat(model): add bert support. (#4737)
Signed-off-by: Björn Buschkämper <bjoern.buschkaemper@gmail.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> Co-authored-by: yaoyu-33 <yaoyu.094@gmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Chen Cui <chcui@nvidia.com>
1 parent 7213568 commit efb5888

22 files changed

Lines changed: 1626 additions & 9 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ Megatron Bridge provides out-of-the-box bridges and training recipes for a wide
230230
| Family | Supported variants |
231231
|----------------|--------------------|
232232
| [**Bailing**](docs/models/bailing/index.md) | Ling 2.0 / Ling MoE V2 (Bailing) |
233+
| [**BERT**](docs/models/bert/index.md) | Megatron-Style BERT (`MegatronBertForMaskedLM`) |
233234
| [**DeepSeek**](docs/models/deepseek/index.md) | DeepSeek V2 / V2 Lite (deprecated), DeepSeek V3, DeepSeek V4 / V4 Flash |
234235
| [**Diffusion**](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/src/megatron/bridge/diffusion/models) | FLUX, LLaDA 1.5, Nemotron-Labs Diffusion, WAN |
235236
| **Ernie** | [Ernie 4.5 MoE](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/src/megatron/bridge/models/ernie), [Ernie 4.5 VL MoE](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/src/megatron/bridge/models/ernie_vl) |

‎docs/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ bridge-tech-details.md
3636
models/README.md
3737
models/bagel/bagel.md
3838
models/bailing/index.md
39+
models/bert/index.md
3940
models/deepseek/index.md
4041
models/ernie/ernie45.md
4142
models/exaone/exaone.md

‎docs/models/bert/index.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# BERT
2+
3+
BERT model documentation is organized by model variant.
4+
5+
```{toctree}
6+
:hidden:
7+
8+
megatron-bert.md
9+
```
10+
11+
| Variant | Guide |
12+
|---------|-------|
13+
| Megatron-Style BERT | [megatron-bert.md](megatron-bert.md) |

‎docs/models/bert/megatron-bert.md‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Megatron-Style BERT
2+
3+
[Megatron BERT](https://huggingface.co/nvidia/megatron-bert-uncased-345m) is NVIDIA's original
4+
Pre-LayerNorm BERT-family encoder, published to Hugging Face as `MegatronBertForMaskedLM`
5+
(`transformers` model type `megatron-bert`). Megatron Bridge supports it through `BertBridge`,
6+
the first encoder-only (masked-LM) model bridge in this repository.
7+
8+
## Supported Variants
9+
10+
The public examples target NVIDIA's original 345M-parameter checkpoint:
11+
12+
- `nvidia/megatron-bert-uncased-345m`: https://huggingface.co/nvidia/megatron-bert-uncased-345m
13+
- `nvidia/megatron-bert-cased-345m`: https://huggingface.co/nvidia/megatron-bert-cased-345m
14+
15+
The bridge is registered for the `MegatronBertForMaskedLM` architecture and can auto-detect
16+
compatible checkpoints when their Hugging Face config uses the `megatron-bert` model type.
17+
18+
## Architecture Notes: Pre-LayerNorm Only
19+
20+
`BertBridge` targets **Pre-LayerNorm** BERT, matching Megatron-Core's
21+
`megatron.core.models.bert.bert_model.BertModel` exactly (LayerNorm is applied before
22+
self-attention/MLP, and the residual carries the un-normalized input).
23+
24+
> [!WARNING]
25+
> Vanilla `transformers.BertModel`-based checkpoints (`bert-base-uncased`, `bert-large-uncased`,
26+
> most BioBERT/SciBERT/etc. derivatives) are **Post-LayerNorm** — architecturally different from
27+
> Megatron-Core's `BertModel`. This bridge does **not** support them: forcing a Post-LN checkpoint
28+
> through it would silently load weights into the wrong computational graph and produce incorrect
29+
> outputs with no error raised. Check that a checkpoint's `model_type` is `megatron-bert`, not
30+
> `bert`, before converting.
31+
32+
Other implementation notes:
33+
34+
- `add_binary_head=False` — masked-LM checkpoints have no pooler/NSP head.
35+
- `hidden_act="gelu"` is required because Megatron-Core's BERT masked-LM head hardcodes GELU.
36+
- Decoder and cross-attention configurations are not supported.
37+
- Untied word embeddings are not supported because Hugging Face represents two independent MLM
38+
biases in that mode, while Megatron-Core has a single output-layer bias.
39+
- HF ties `cls.predictions.decoder.bias` to `cls.predictions.bias` internally. The bridge
40+
synthesizes the alias when the export target expects both keys; safe serialization normally
41+
stores only the canonical `cls.predictions.bias` key.
42+
- Megatron-Core's `BertModel.forward()` requires `tokentype_ids` to be passed explicitly whenever
43+
`num_tokentypes > 0`, and expects a raw (non-extended) `[batch, seq_len]` attention mask — it
44+
builds the extended mask internally.
45+
46+
## Examples
47+
48+
For checkpoint conversion, round-trip validation, and fill-mask inference, see the
49+
[Megatron-Style BERT examples README](https://github.com/NVIDIA-NeMo/Megatron-Bridge/blob/main/examples/models/bert/megatron-bert/README.md).
50+
51+
Note that `nvidia/megatron-bert-uncased-345m` only hosts tokenizer assets on the Hub; the weights
52+
must first be downloaded from NGC and converted with Hugging Face's own conversion script (see
53+
the examples README for the exact commands) before they can be imported with Megatron Bridge.
54+
55+
## Related Implementation
56+
57+
- Bridge implementation: [`src/megatron/bridge/models/bert`](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/src/megatron/bridge/models/bert)
58+
- Examples: [`examples/models/bert/megatron-bert`](https://github.com/NVIDIA-NeMo/Megatron-Bridge/tree/main/examples/models/bert/megatron-bert)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Megatron-Style BERT (`MegatronBertForMaskedLM`) Examples
2+
3+
This directory contains example scripts for the Megatron-Bridge `BertBridge`, which converts
4+
between Hugging Face's `MegatronBertForMaskedLM` architecture and Megatron-Core's
5+
`megatron.core.models.bert.bert_model.BertModel`.
6+
7+
The examples target `nvidia/megatron-bert-uncased-345m` (24 layers, 1024 hidden size, 345M
8+
parameters), NVIDIA's original Pre-LayerNorm BERT checkpoint.
9+
10+
## Supported architecture: Pre-LayerNorm only
11+
12+
`BertBridge` targets HF's **`MegatronBertForMaskedLM`** architecture (`transformers` model type
13+
`megatron-bert`), which is Pre-LayerNorm — LayerNorm is applied *before* self-attention and the
14+
MLP, matching Megatron-Core's `BertModel` exactly.
15+
16+
> [!WARNING]
17+
> This bridge does **not** support vanilla `transformers.BertModel`-based checkpoints (e.g.
18+
> `bert-base-uncased`, `bert-large-uncased`, most BioBERT/SciBERT/etc. variants). Those are
19+
> **Post-LayerNorm** (LayerNorm applied after the residual add, as in the original BERT paper),
20+
> which is architecturally different from Megatron-Core's `BertModel`. Converting a Post-LN
21+
> checkpoint through this bridge would silently load weights into the wrong computational graph
22+
> and produce incorrect outputs with no error. If a HF config's `model_type` is `bert` rather than
23+
> `megatron-bert`, it is not supported here.
24+
25+
`MegatronBertForMaskedLM` also has no pooler/NSP head in this bridge (`add_binary_head=False`),
26+
matching how the masked-LM checkpoints are published.
27+
28+
The bridge requires `hidden_act="gelu"` because Megatron-Core's BERT masked-LM head hardcodes
29+
GELU. Decoder and cross-attention configurations are not supported.
30+
31+
## Obtaining a convertible checkpoint
32+
33+
The `nvidia/megatron-bert-uncased-345m` Hugging Face repo only hosts tokenizer files — the
34+
weights must be downloaded from NVIDIA GPU Cloud (NGC) and converted to HF format first, using
35+
HF's own conversion script (this is a one-time step, unrelated to Megatron-Bridge):
36+
37+
mkdir -p megatron-bert-uncased-345m-hf
38+
wget --content-disposition \
39+
https://api.ngc.nvidia.com/v2/models/nvidia/megatron_bert_345m/versions/v0.1_uncased/zip \
40+
-O megatron-bert-uncased-345m-hf/checkpoint.zip
41+
git clone https://github.com/huggingface/transformers.git
42+
git -C transformers checkout fe747d88a3296bd94d426db2717f232f9d4afdb7
43+
uv run python transformers/src/transformers/models/megatron_bert/convert_megatron_bert_checkpoint.py \
44+
megatron-bert-uncased-345m-hf/checkpoint.zip
45+
46+
The upstream converter writes a pretraining checkpoint without the architecture metadata needed
47+
by `AutoBridge`. Normalize it to the masked-LM architecture and add the tokenizer assets before
48+
using the directory with Megatron Bridge:
49+
50+
uv run python - <<'PY'
51+
from transformers import AutoTokenizer, MegatronBertForMaskedLM
52+
53+
checkpoint_path = "megatron-bert-uncased-345m-hf"
54+
model = MegatronBertForMaskedLM.from_pretrained(checkpoint_path)
55+
model.save_pretrained(checkpoint_path, safe_serialization=True)
56+
tokenizer = AutoTokenizer.from_pretrained("nvidia/megatron-bert-uncased-345m")
57+
tokenizer.save_pretrained(checkpoint_path)
58+
PY
59+
60+
This produces a self-contained config, weights, and tokenizer directory that can be used as
61+
`HF_MODEL_PATH` below. See
62+
the [`nvidia/megatron-bert-uncased-345m` model card](https://huggingface.co/nvidia/megatron-bert-uncased-345m)
63+
for full details, including the `-cased` variant.
64+
65+
## Workspace Configuration
66+
67+
All scripts use a `WORKSPACE` environment variable to define the base directory for checkpoints
68+
and results. By default, this is set to `/workspace`.
69+
70+
export WORKSPACE=/your/custom/path
71+
export HF_MODEL_PATH=/path/to/converted/megatron-bert-uncased-345m-hf
72+
73+
## Checkpoint Conversion
74+
75+
See [conversion.sh](conversion.sh) for Hugging Face to Megatron import, Megatron to Hugging Face
76+
export, and round-trip validation.
77+
78+
./examples/models/bert/megatron-bert/conversion.sh
79+
80+
## Inference (fill-mask)
81+
82+
`MegatronBertForMaskedLM` has no `generate()` method, so "inference" here means masked-token
83+
prediction rather than text generation. See [inference.sh](inference.sh), which runs
84+
[fill_mask.py](fill_mask.py) against the original Hugging Face checkpoint and, if present, the
85+
round-tripped export from `conversion.sh`:
86+
87+
TEXT="Paris is the [MASK] of France." ./examples/models/bert/megatron-bert/inference.sh
88+
89+
`fill_mask.py` uses the Hugging Face `fill-mask` pipeline directly (no Megatron-Core forward
90+
pass) — its purpose is to sanity-check that a converted checkpoint still produces sensible
91+
predictions, not to benchmark Megatron inference throughput.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -xeuo pipefail
17+
18+
WORKSPACE=${WORKSPACE:-/workspace}
19+
20+
MODEL_NAME=megatron-bert-uncased-345m
21+
# `HF_MODEL_PATH` must point at a *converted* Hugging Face checkpoint directory
22+
# (config.json + weights), not the tokenizer-only `nvidia/megatron-bert-uncased-345m`
23+
# Hub repo. See README.md for how to obtain one.
24+
HF_MODEL_PATH=${HF_MODEL_PATH:-"${WORKSPACE}/models/${MODEL_NAME}-hf"}
25+
26+
uv run python examples/conversion/convert_checkpoints.py import \
27+
--hf-model "$HF_MODEL_PATH" \
28+
--megatron-path "${WORKSPACE}/models/${MODEL_NAME}"
29+
30+
uv run python examples/conversion/convert_checkpoints.py export \
31+
--hf-model "$HF_MODEL_PATH" \
32+
--megatron-path "${WORKSPACE}/models/${MODEL_NAME}/iter_0000000" \
33+
--hf-path "${WORKSPACE}/models/${MODEL_NAME}-hf-export"
34+
35+
uv run python -m torch.distributed.run --nproc_per_node=1 \
36+
examples/conversion/hf_megatron_roundtrip_multi_gpu.py \
37+
--hf-model-id "$HF_MODEL_PATH" \
38+
--megatron-load-path "${WORKSPACE}/models/${MODEL_NAME}/iter_0000000" \
39+
--tp 1 --pp 1
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env python
2+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
"""Run masked-token ("fill-mask") inference with a Megatron-Bert-family checkpoint.
17+
18+
Unlike the causal-LM examples in `examples/conversion/`, `MegatronBertForMaskedLM`
19+
does not implement `generate()`, so this example uses the Hugging Face `fill-mask`
20+
pipeline directly. This is intentionally HF-only (no Megatron-Core forward pass):
21+
its purpose is to sanity-check that a checkpoint produced by the Megatron-Bridge
22+
BERT bridge (see `conversion.sh`) still makes sensible predictions after a
23+
round trip, not to benchmark Megatron inference.
24+
25+
Run Script Example:
26+
uv run python examples/models/bert/megatron-bert/fill_mask.py \\
27+
--hf_model_path /workspace/models/megatron-bert-uncased-345m-hf-export \\
28+
--text "Paris is the [MASK] of France."
29+
"""
30+
31+
import argparse
32+
33+
from transformers import pipeline
34+
35+
36+
def main() -> None:
37+
"""Parse CLI arguments and print the top masked-token predictions."""
38+
parser = argparse.ArgumentParser(description=__doc__)
39+
parser.add_argument("--hf_model_path", type=str, required=True, help="HuggingFace model ID or local path")
40+
parser.add_argument(
41+
"--text",
42+
type=str,
43+
default="Paris is the [MASK] of France.",
44+
help="Input text containing exactly one `[MASK]` token",
45+
)
46+
parser.add_argument("--top_k", type=int, default=5, help="Number of predictions to print")
47+
args = parser.parse_args()
48+
49+
fill_mask = pipeline("fill-mask", model=args.hf_model_path)
50+
predictions = fill_mask(args.text, top_k=args.top_k)
51+
52+
print(f"Model: {args.hf_model_path}")
53+
print(f"Input: {args.text}")
54+
for prediction in predictions:
55+
print(f" {prediction['score']:.4f} {prediction['sequence']}")
56+
57+
58+
if __name__ == "__main__":
59+
main()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env bash
2+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
set -xeuo pipefail
17+
18+
WORKSPACE=${WORKSPACE:-/workspace}
19+
MODEL_NAME=megatron-bert-uncased-345m
20+
HF_MODEL_PATH=${HF_MODEL_PATH:-"${WORKSPACE}/models/${MODEL_NAME}-hf"}
21+
HF_EXPORT_PATH=${HF_EXPORT_PATH:-"${WORKSPACE}/models/${MODEL_NAME}-hf-export"}
22+
TEXT=${TEXT:-"Paris is the [MASK] of France."}
23+
24+
# `MegatronBertForMaskedLM` has no `generate()` method, so inference here means
25+
# fill-mask prediction, not text generation. This only exercises Hugging Face
26+
# code paths, so it can be used to sanity-check both the original checkpoint
27+
# and the round-tripped export from conversion.sh.
28+
uv run python examples/models/bert/megatron-bert/fill_mask.py \
29+
--hf_model_path "$HF_MODEL_PATH" \
30+
--text "$TEXT"
31+
32+
if [ -d "$HF_EXPORT_PATH" ]; then
33+
uv run python examples/models/bert/megatron-bert/fill_mask.py \
34+
--hf_model_path "$HF_EXPORT_PATH" \
35+
--text "$TEXT"
36+
fi

‎src/megatron/bridge/models/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
from megatron.bridge.models.bailing import (
2929
BailingMoeV2Bridge,
3030
)
31+
from megatron.bridge.models.bert import (
32+
BertModelProvider,
33+
)
3134
from megatron.bridge.models.conversion.auto_bridge import AutoBridge
3235
from megatron.bridge.models.conversion.mapping_registry import MegatronMappingRegistry
3336
from megatron.bridge.models.conversion.model_bridge import MegatronModelBridge
@@ -231,6 +234,7 @@
231234
"RowParallelMapping",
232235
"AutoMapping",
233236
"BailingMoeV2Bridge",
237+
"BertModelProvider",
234238
# DeepSeek Models
235239
"DeepSeekV2Bridge",
236240
"DeepSeekV3Bridge",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from megatron.bridge.models.bert.bert_bridge import BertBridge # noqa: F401
16+
from megatron.bridge.models.bert.bert_provider import BertModelProvider
17+
18+
19+
__all__ = [
20+
"BertModelProvider",
21+
]

0 commit comments

Comments
 (0)