Repository navigation
Fix ESM-C logits extraction and repair 3D logits pipeline - #24
Conversation
- Add _compute_outputs in ESMCEmbedder to extract unmasked LM-head logits (64-dim). - Remove erroneous .permute(2,0,1) in ESM2Embedder and ESMEmbedder so logits retain standard (batch, seq, vocab) shape. - Fix BaseEmbedder._extract_logits to handle 3D logits across in-memory and streaming modes without indexing vocab as layers. - Update BaseEmbedder._set_output_objects to dynamically allocate disk shape with vocab_size. - Update model_selecter report_model to recognize ESMCEmbedder logits availability. - Add and update unit & regression tests in test_esmc.py, test_esmc_modes.py, and test_streaming_roundtrip.py. - Update CLAUDE.md documenting ESM-C logits support and caveats. Resolves #23
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved logits reconstruction, custom vocabulary sizing, and OOM retry issues can corrupt or prevent logits extraction.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Fixes ESM-C logits extraction and standardizes logits handling across embedders as 3D batch/sequence/vocabulary tensors.
Changes:
- Adds ESM-C logits computation and model-inspector support.
- Repairs logits extraction, sizing, flattening, and streaming preallocation.
- Adds regression tests and documents ESM-C scoring caveats.
File summaries
| File | Summary |
|---|---|
src/tests/test_streaming_roundtrip.py |
Validates 3D logits streaming round trips. |
src/tests/test_esmc.py |
Tests ESM-C logits and model reporting. |
src/tests/test_esmc_modes.py |
Tests ESM-C logits output dimensions. |
src/pepe/model_selecter.py |
Reports ESM-C logits availability. |
src/pepe/embedders/huggingface_embedder.py |
Implements ESM-C logits computation. |
src/pepe/embedders/esm_embedder.py |
Standardizes logits layout. |
src/pepe/embedders/custom_embedder.py |
Adds custom vocabulary sizing metadata. |
src/pepe/embedders/base_embedder.py |
Updates logits extraction, reconstruction, and output sizing. |
CLAUDE.md |
Documents logits behavior and ESM-C caveats. |
Review details
Suppressed comments (4)
CLAUDE.md:71
- The caveat is documented only in
CLAUDE.md; the user-facing README still calls logits merely experimental and the CLI help has no warning. Because unmasked ESM-C logits can produce scientifically invalid variant scores, the required warning will not reach users invokingpepeor the public API. Add the masking/scoring caveat and 64-wide vocabulary note to the user documentation and CLI help.
`_set_output_objects()` defines one entry per output type — `per_token`, `mean_pooled`, `substring_pooled`, `attention_head`, `attention_layer`, `attention_model`, `logits` — each a dict of `{output_data, method (_extract_*), output_dir, shape}`. `extract_embeddings` picks which run. Adding an output type means adding both the dict entry and the matching `_extract_*` method. Files are saved as `.npy` under `<output_path>/<model_name>/<output_type>/`, plus a `*_idx.csv` mapping row index → sequence id. Note `logits` is produced by ESM-2 and ESM-C (via `AutoModelForMaskedLM`); the generic HuggingFace path drops it. (Note: ESM-C outputs 64-dimensional unmasked single-pass logits with indices 4–28 supervised; zero-shot variant effect prediction scientifically requires masked marginal scoring).
src/pepe/embedders/base_embedder.py:180
- The new 3-D preallocation/write path is covered by the ESM-2 round-trip test, but the ESM-C tests only cover in-memory output and a mocked forward pass; the real ESM-C integration is skipped unless
ESMC_TEST=1. Since ESM-C uses a distinct 64-wide model and this PR changes shared streaming memmaps, add an ESM-C streaming round-trip or focused preallocation/write test to catch shape mismatches.
(
self.num_sequences,
self.max_input_length,
self.vocab_size,
)
if not self.flatten
else (
self.num_sequences,
self.max_input_length * self.vocab_size,
)
src/pepe/embedders/huggingface_embedder.py:655
- This returns representations as a layer-keyed dict, but
BaseEmbedder._safe_compute()concatenates recursiveo[1]values withtorch.catas though they were tensors. If logits are requested together with embeddings and an OOM retry occurs, this new ESM-C path raises aTypeErrorinstead of recovering. Update the retry path to concatenate each representation layer independently.
if return_embeddings:
representations = {
layer: outputs.hidden_states[layer]
.to(self._precision_to_dtype(self.precision, "torch"))
.cpu()
for layer in self.layers
src/tests/test_streaming_roundtrip.py:155
- These assertions cover the ESM-2 33-wide path only. The PR also introduces ESM-C's 64-wide logits and changes memmap preallocation to depend on that dimension, but the ESM-C tests are in-memory and do not exercise streaming writes or round-trip shape equality. Add an opt-in ESM-C streaming/in-memory regression so the new padded-vocabulary path is tested.
if output_type == "logits":
assert reference.ndim == 3, f"Expected 3D logits, got {reference.ndim}"
assert reference.shape[-1] == 33, f"Expected ESM-2 vocab size 33, got {reference.shape[-1]}"
assert streaming.shape == reference.shape
- Files reviewed: 9/9 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if self.flatten: | ||
| tensor = tensor.flatten(start_dim=1) |
| self.logits["output_data"][layer].extend( | ||
| [ | ||
| logits[i][pooling_mask[i]] | ||
| for i in range(len(batch_labels)) |
|
|
||
| # Initialize tokenizer | ||
| tokenizer = self._initialize_tokenizer(tokenizer_path, config) | ||
| self.vocab_size = config.get("vocab_size", getattr(tokenizer, "vocab_size", 25)) |
- Apply ruff formatting and imports sorting across modified files to satisfy pre-commit lint. - Document 64-wide ESM-C logits caveats and masking notes in README.md and parse_arguments.py CLI help. - Fix BaseEmbedder._safe_compute recursive OOM recovery for dict representations and attention tensors. - Add unit tests for ESM-C disk preallocation shape and _safe_compute OOM recovery. - Add streaming vs in-memory roundtrip assertions for ESM-C in test_esmc.py.
Summary of Updates Addressing Review Comments & CI
|
Summary
Resolves #23.
This PR implements the fixes for ESM-C logits extraction and repairs the codebase-wide logits extraction pipeline.
Changes Made
ESMCEmbedder._compute_outputs:_compute_outputsinsrc/pepe/embedders/huggingface_embedder.pyto forward passes throughAutoModelForMaskedLMand return LM-head logits on CPU with(batch, seq, vocab)shape.self.vocab_size = 64(reflecting Transformer Engine / FP8 64-dimensional padded vocabulary).Removal of Erroneous
.permute(2, 0, 1):.permute(2, 0, 1)fromESM2Embedder._compute_outputsandESMEmbedder._compute_outputs. Logits now preserve standard(batch, max_input_length, vocab_size)layout across all embedders.BaseEmbedderCodebase-Wide Fixes:_set_output_objects: Added helper_get_vocab_size()to dynamically detect vocab dimension (e.g. 64 for ESM-C, 33 for ESM-2/1). Configuredself.logits["shape"]as(num_sequences, max_input_length, vocab_size)(or flattened(num_sequences, max_input_length * vocab_size))._extract_logits: Fixed extraction to operate on 3D tensors(batch, seq, vocab). Eliminatedlayer - 1indexing bug that previously mistook vocabulary indices for layer IDs._safe_computeOOM recovery: Verifieddim=0batch-halving concatenation works as intended with unpermuted tensors.export_to_disk: Updated flatten logic to support flattening logits alongside per-token embeddings when--flattenis specified.Model Inspector (
model_selecter.py):report_model()soinspect_model/pepe --check_modelreportsLogits: yesforESMCEmbedder.Tests & Documentation:
src/tests/test_esmc.pyvalidatingreport_modelreports logits availability andESMCEmbedder._compute_outputsreturns properly shaped(batch, seq, 64)logits.test_logitstosrc/tests/test_esmc_modes.pyasserting output ndim and 64-dim vocab.src/tests/test_streaming_roundtrip.pyvalidating exact match between streaming memmapped disk arrays and in-memory arrays for 3D logits(batch, seq, 33).CLAUDE.mdarchitecture reference.Scientific Caveat & Note on ESM-C Logits
[MASK]replacement during masked language modeling.