Repository navigation
Conversation
RobertaForMaskedLM is BertForMaskedLM with the trunk under roberta.* and the MLM head under lm_head.*, so the adapter subclasses BertArchitectureAdapter, calls super().__init__ and rebuilds component_mapping with the new paths. token_type_embed stays (a real [1, d_model] table even at type_vocab_size 1) and so do the hook_mlp_in/out redirects (RobertaLayer keeps BertLayer's sibling intermediate/output layout). The inherited prepare_model finds no bert.pooler or cls.* heads and is a no-op. n_ctx is max_position_embeddings minus the two rows HF reserves for position ids that start at padding_idx + 1, which is what makes the W_pos accessor slice them off. Subtracting instead of pinning 512 keeps longer-table checkpoints right too. Wired into the factory, the registry sets and the report descriptions. No tokenizer flags on the adapter: <s> / </s> handling comes from each checkpoint's tokenizer at boot.
jlarson4
reviewed
Oct 13, 2026
Collaborator
There was a problem hiding this comment.
Looks good! My only request is that you trim down some of these comments. The adapter is 120 lines, but 40 of those lines are comments, making up 30% of the file.
Collaborator
|
Just one request (see above), I look forward to reviewing the rest of the commits! |
Keep the one-liners the issue notes asked to be visible (inherited prepare_model, position offset and which W_pos slices it, token_type_embed, alias redirects, n_ctx) and drop the rest. 40 comment lines down to 14.
verify_models on FacebookAI/roberta-base and distilbert/distilroberta-base: P1/P2/P3 at 100%, P4 not applicable to a masked LM, same as bert-base-cased. Entries, header counts and the verification_history records are what the tool wrote.
The unit test pins the mapping against BertArchitectureAdapter (keys, bridge types, block layout, weight conversions) instead of restating it, so a BERT-side change surfaces here too, plus the RoBERTa paths, the n_ctx adjustment, the no-op prepare_model, untouched tokenizer flags and the factory routing. test_roberta_tiny.py is the CI tier: a random-weight two-layer RobertaForMaskedLM, live wiring and tied decoder weight, W_pos / W_E_pos shapes, hook capture against the HF modules including the redirected hook_mlp_in/out aliases and ln_final, the position rows HF actually looks up (2.. for content, 1 for padding), padded and unpadded parity, and a block-output intervention.
Follows test_bridge_vs_hf_bert_parity.py against an independent eager HF reference, with the padding-mask case on pad id 1 and the boot-time <s>/</s> detection pinned. Slow-marked since the checkpoint is not in the CI cache, quarantine row added. 6/6 locally.
… ids RoBERTa builds its own pad-aware position ids inside RobertaEmbeddings (padding_idx + 1 onwards), but accepts_mask_derived_position_ids only recognised OPT-style embeddings that take the mask, so a left-padded batch got the bridge's 0-based cumsum instead and content tokens landed on the two reserved rows (max logit diff 38 vs HF on distilroberta-base; right padding was unaffected). A module exposing create_position_ids_from_input_ids now counts as owning its derivation. Regression cases on the gate, the tiny RoBERTa and the real-weights parity file.
jlarson4
reviewed
Oct 16, 2026
jlarson4
left a comment
Collaborator
There was a problem hiding this comment.
Just one additional comment and then this should be ready to merge!
|
|
||
| # max_position_embeddings counts the two reserved rows; a table of n_ctx + 2 | ||
| # rows is what makes TransformerBridge.W_pos drop them. | ||
| self.cfg.n_ctx -= 2 |
Collaborator
There was a problem hiding this comment.
This subtracts once per adapter construction, so rebuilding from an existing bridge's cfg via build_bridge_from_module(tl_config=...) lands on 510 and W_pos doesn't drop the reserved rows. Deriving it from the loaded position table in prepare_model (ast.py sets n_ctx there) would make it more robust.
1 task done
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds
RobertaArchitectureAdaptersoRobertaForMaskedLMcheckpoints (roberta-base, roberta-large, distilroberta-base and the fine-tunes that keep the MLM head) load throughTransformerBridge.boot_transformers. Before this the factory raisedUnsupported architecture: RobertaForMaskedLMeven thoughutilities/architectures.pyalready routed the family to the masked-LM loader.The adapter subclasses
BertArchitectureAdapter, callssuper().__init__(cfg)and rebuildscomponent_mappingwith RoBERTa's paths (roberta.*trunk,lm_head.*head), as discussed on the issue:token_type_embedstays mapped toroberta.embeddings.token_type_embeddings(a real[1, d_model]table attype_vocab_size = 1).hook_mlp_in/hook_mlp_outredirects onBlockBridgeare preserved, sinceRobertaLayerhas BERT's siblingintermediate.dense/output.denselayout with no MLP module. The tiny CI test caches both aliases against the HF modules.prepare_modelis a no-op here (nobert.pooler, nocls.*heads); the unit test pins that.n_ctxis reported as HF'smax_position_embeddingsminus the two rows RoBERTa reserves (position ids start atpadding_idx + 1 = 2). I went withself.cfg.n_ctx -= 2rather than pinning 512 so roberta-large (514 rows) and longer-table checkpoints get the same treatment; then_ctx + 2branch inW_posfires either way and returns the[512, d_model]table aligned to content positions, whilepos_embed.W_poskeeps the raw table. The one caveat is thatbuild_bridge_from_module(tl_config=...)expects a config that still carries the table size, as its docstring says; a pre-adjusted one would be shifted twice. Happy to pin 512 if you prefer the literal version.<s>/</s>handling comes from each checkpoint's tokenizer at boot (detect_tokenizer_bos_eossees bos = cls = 0); the parity test pins the detected flags.Registered in the factory,
supported_architectures/__init__.py,HF_SUPPORTED_ARCHITECTURES+CANONICAL_AUTHORS_BY_ARCH(FacebookAI,distilbert) andARCHITECTURE_DESCRIPTIONS(the RoBERTa string already existed there, it just was not reachable).Registry: entries for
FacebookAI/roberta-baseanddistilbert/distilroberta-base, both run throughverify_models: P1 = P2 = P3 = 100%, P4 not applicable to a masked LM (same asgoogle-bert/bert-base-cased).verification_history.jsoncarries the two matching records.Tests:
tests/unit/model_bridge/supported_architectures/test_roberta_adapter.pypins the mapping againstBertArchitectureAdapter(same keys, bridge types, block layout, weight conversions), the RoBERTa HF paths, the context-length adjustment, the no-opprepare_model, untouched tokenizer flags and the factory routing.tests/integration/model_bridge/test_roberta_tiny.pyruns in CI on a random-weight two-layerRobertaForMaskedLM: live wiring and tied decoder weight,W_pos/W_E_posshapes, hook capture against the HF modules (includinghook_mlp_in/hook_mlp_outandln_final.hook_normalized), the position rows HF actually looks up (2.. for content, 1 for padding), padded and unpadded logit parity, and a block-output intervention.tests/integration/model_bridge/test_bridge_vs_hf_roberta_parity.pyfollowstest_bridge_vs_hf_bert_parity.pyondistilbert/distilroberta-baseagainst an independent eager HF reference, including the padding-mask case. It isslow-marked (the checkpoint is not in the CI cache) with a row intests/QUARANTINES.md; passes locally, 6/6.Left padding: the bridge used to hand RoBERTa its own 0-based position ids (the gate in
utilities/position_ids.pyonly recognised OPT-style mask-consuming embeddings), so left-padded rows landed on the two reserved rows while right padding, tensor inputs andverify_modelswere unaffected. Fixed in the last commit: a module exposingcreate_position_ids_from_input_idsnow counts as owning its positions, with regression cases on the gate, the tiny model and the parity file. RoBERTa is the only registered architecture in that family, so nothing else changes behaviour.One thing I would rather get your call on than change here:
to_tokensstrips the trailing</s>because the tokenizer appends it (tokenizer_appends_eos), a policy written for causal LMs, sobridge("text")feeds RoBERTa one token fewer than HF does, while BERT keeps[SEP]. Gating the strip onadapter.supports_generationwould fix it but also changes encoder-decoder inputs.Fixes #1870
Type of change
Checklist: