Repository navigation
docs(demos): add Natural Language Autoencoders round-trip demo - #1963
Conversation
jlarson4
left a comment
There was a problem hiding this comment.
Great work on the demo @janmenjayap, just a couple small procedural edits, nothing to crazy. Let me know when you've had a chance to look them over.
| " calls = []\n", | ||
| "\n", | ||
| " def hook(embeddings, hook):\n", | ||
| " inject = not (use_past_kv_cache and calls)\n", |
There was a problem hiding this comment.
The hook infers the prefill from its own call count and the use_past_kv_cache argument, but generate can drop caching on its own (and does for adapters without KV support). In that scenario, only the first pass is injected while every later pass recomputes the prefix with the raw placeholder. If we adjust to decide based on whether the pass covers the placeholder, embeddings.shape[1] > position, we can remove the flag and the call-count state while still producing the two call-log patterns check (b) prints.
There was a problem hiding this comment.
Thanks, good catch. Fixed in 1924bfe: the hook now injects on every pass whose embeddings reach the placeholder (embeddings.shape[1] > position), and the use_past_kv_cache argument and call-count state are gone. Besides adapters without KV support, this also covers generate's cached branch when no cache was captured, which recomputes the full prefix. Check (b) prints the same two call-log patterns, and since the hook is no longer told the caching mode, the uncached run also shows the case where generate disables the cache itself. To keep a guard, verbalize now asserts that the prefill injected.
| } | ||
| ], | ||
| "source": [ | ||
| "TINY = \"trl-internal-testing/tiny-Qwen2ForCausalLM-2.5\"\n", |
There was a problem hiding this comment.
The released models are pinned to commits, but the tiny checkpoint loads its moving head. The outputs print token counts and the placeholder position derived from them, so a Hub update would cause this cell to error in CI unexpectedly. This checkpoint has been updated in the past, so to avoid this issue, could you please pin a revision= for the tiny checkpoint as well?
There was a problem hiding this comment.
Agreed, thanks. Pinned in 3328f10 to ce8d0bf270b28c8fab026ced69fe8aa14b0f0eda, the current head and the revision that produced the committed outputs, so nothing printed changes.
|
Hi @jlarson4, one more update. After my previous push, the NLA notebook check started failing on cell 26. That's because #1964 landed on dev and changed stop_at_layer=n_layers to stop after the last block instead of returning logits, and the notebook had relied on the old behaviour. To fix it, I merged the latest dev into the branch (283bd2b). Then in d71407e I updated check (c): it now asserts that stop_at_layer=L + 1 equals the block-L read-out instead of printing the logits width, and the two markdown cells that described it are updated. I re-executed the whole notebook, including the released 7B section, on the merged code. Parity with plain Hugging Face is still exact (max abs difference 0.0), and the parse statuses, explanations, and cosines are identical to before. Only the timings changed, with peak memory now 14.1 GiB. nbval passes twice in a row (16 passed, 7 skipped). Thank you for your patience! |
|
Thanks for putting those fixes together @janmenjayap, looks great, merging now |
Description
Adds
demos/Natural_Language_Autoencoders_Demo.ipynb. It runs the released NaturalLanguage Autoencoder (NLA) round trip for Qwen2.5-7B-Instruct at block 20 throughTransformerBridge, with no model server:This is the demo-only reframe of #1884 that @jlarson4 approved (comment), following the pattern from #1907. It includes the three requested adjustments: the released section runs in bf16 and reports measured memory, its cells are
# NBVAL_SKIPwith committed outputs, and the scope is this demo only.No package code changes. Nothing changes in
transformer_lens/,pyproject.toml, oruv.lock. The notebook uses only existing public Bridge methods:run_with_cache,hooks,generate, andboot_transformers(hf_model=...).pyyamlandsafetensorsare already dependencies throughtransformers.Fixes #1884
What the notebook contains
Helpers, defined inline. Each is short and is followed by a cell that uses it.
check_sidecarnla_meta.yamlagainst its role and the live tokenizer: schema, layer, width, placeholder token and neighbors, injection norm, templates, and the AR's prompt suffix.extractrun_with_cachefiltered toblocks.{L}.hook_outonly. Returns a detached CPU float32 vector and rejects compatibility-mode Bridges.make_injection_hookhook_embed. Injects on every forward pass that reaches the placeholder: the prefill, and each full-prefix pass when generation runs uncached, including when generate disables the cache itself. Cached decode steps embed only the newest token and are left unchanged.render_av_prompt,verbalizegenerateinsidebridge.hooks(...)with explicit, bounded sampling settings.parse_explanation<explanation>region. Otherwise it returnsNonewith a named status:missing,multiple,nested,incomplete, orempty. The notebook says that this is stricter than the reference client.reconstructscoreMechanics section, run in CI. It runs on
trl-internal-testing/tiny-Qwen2ForCausalLM-2.5, pinned to revision ce8d0bf270b28c8fab026ced69fe8aa14b0f0eda and shares Qwen2.5's tokenizer and chat template. Synthetic sidecars follow the released schema. It asserts:extractequals HFhidden_states[L + 1], and Bridge prefill logits with the injection equal HF logits frominputs_embedswith the same row replaced. Both are exact (torch.equal).hidden_states[L + 1]. It also shows two shortcuts that give the wrong vector: HF's lasthidden_statesentry has the final norm applied, andstop_at_layer=L + 1doesn't stop early on a truncated stack.reconstruct.verbalize. The caller's own hook survives until the caller removes it.Released section, run locally. All of it is
# NBVAL_SKIP, opt-in throughTL_RUN_RELEASED_NLA=1, with outputs committed. It uses pinned revisions:Qwen/Qwen2.5-7B-Instructa09a35458c702b33eeacc393d103063234e8bc28kitft/nla-qwen2.5-7b-L20-avb88469162777ae6553bc14208eb0cb579336f8f4kitft/nla-qwen2.5-7b-L20-ar(+value_head.safetensors)e2c9e57eac213d37a31612087f645ab6332c1bb6The section loads one model at a time in bf16. Each stage compares the Bridge with plain Hugging Face at the same dtype, device, tokens, and eager attention: the HF reference runs first, then the same module is wrapped with
boot_transformers(hf_model=...). The AR reference follows the authors'NLACriticread-out. Every prompt is reported, including any parse failure.Docs and CI registration:
docs/make_docs.pyanddocs/source/index.mdregister the notebook.docs/source/content/analysis_tools.mdgets a short pointer with the faithfulness caveat, anddocs/source/content/tutorials.mdmentions it..github/workflows/checks.ymladds the notebook to thenotebook-checksmatrix.tests/unit/test_make_docs.pyadds it to the expected list of copied demos.Measured results (released section)
Apple M3 Max, 48 GiB, CPU only, bf16, torch 2.11.0. Checkpoints were already cached.
hidden_states[21]inputs_embeds(argmax agrees at every position)NLACritic-style HFThe sidecar checks all pass: injection norm 150, placeholder U+320E (token 149705),$\sqrt{3584}$ , AR suffix
mse_scale=[1318, 29, 366, 1708, 29], and a 3584×3584 bf16 value head with no bias. HF reports exactlymodel.norm.weightandlm_head.weightas missing from the AR.fibonacci, 48 tokens (" return")Peak RSS stays flat after the target stage, so each 7B model is released before the next one loads. Downloads total 38.6 GiB and are not included in the times.
The notebook also shows the faithfulness caveat in practice. The explanations get the prompt's format and the final token's role right, but they also contain claims that aren't in the prompts: Brussels and Belgium for the travel prompt, a nonexistent
math.fibfor the code prompt, and a pregnancy for the narrative. These appear alongside cosines of 0.90 and 0.91. "Reading these results" and the interpretation checklist state that a high reconstruction score does not certify individual claims.Notes for review
parse_explanationis deliberately stricter than the reference client, which takes the first match or falls back to the raw text. The notebook states this, because parse-failure rates from the two are not directly comparable.get_tokenizer_with_bosraisesKeyErrorfor in-memory tokenizers. It is filed separately as [Bug Report]get_tokenizer_with_bosraises KeyError for in-memory tokenizers #1961, with a fix in fix: handle in-memory tokenizers in get_tokenizer_with_bos #1962. This demo does not depend on it.Commits
docs(demos): add Natural Language Autoencoders demo with tiny Qwen2 mechanics checks: background, helpers, the CI-executed mechanics checks, and the interpretation checklist.docs(demos): add released Qwen2.5-7B NLA round trip in bf16 with HF parity: the skipped, opt-in released section with committed outputs and measured resources.docs: register NLA demo in docs build and notebook checks: docs build, index, analysis-tools pointer, tutorials mention,notebook-checksmatrix, and thetest_make_docsexpectation.Each commit passes nbval on its own.
Validation
uv run pytest --nbval-sanitize-with demos/doc_sanitize.cfg demos/Natural_Language_Autoencoders_Demo.ipynbTL_RUN_RELEASED_NLA=1 uv run jupyter nbconvert --to notebook --execute --inplace demos/Natural_Language_Autoencoders_Demo.ipynbuv run build-docsmake check-formatuv run mypy .uv run pytest tests/unit/test_make_docs.pymake test-pr, unit tiertest_copy_demos_creates_generated_dir_when_absent, which pins the list of copied demos; it is fixed in commit 3 and now passes.make docstring-testmake acceptance-testmake integration-testtest_get_params[google/gemma-2-2b-it]andtest_TransformerBridge_gemma2_forwardraisedGatedRepoError(401) because no HF token was set. Rerun with a token:uv run pytest tests/integration/model_bridge/test_bridge_integration.py -k gemmagives 2 passed.The released cells were not re-executed for commits 4 and 5: the only change there removes an argument whose injection decision is identical, so the saved outputs are unchanged.
make test-prran before the final notebook-only revision. After that revision, only the notebook changed, and nbval,test_make_docs,check-format, andbuild-docswere rerun and passed.Type of change
Screenshots
Not applicable. The rendered notebook is in the docs build artifact.
Checklist:
All unit, docstring, acceptance, and integration tests pass locally. The feature's tests are the notebook's CI-executed mechanics section, plus the updated
test_make_docsexpectation.