Skip to content

Add NeoMME and NeoMME-Retriever - #47992

Merged
ArthurZucker merged 134 commits into
huggingface:mainfrom
tonywu71:add-neomme
Aug 31, 2026
Merged

ArthurZucker merged 134 commits into
huggingface:mainfrom
tonywu71:add-neomme

Conversation

@tonywu71

@tonywu71 tonywu71 commented Aug 15, 2026 •

Copy link
Copy Markdown
Contributor

CPU CI GPU run-slow

What does this PR do?

NeoMME is a family of bidirectional multilingual encoders for text and images, including natural images and documents. Text tokens and raw image patches use one shared Transformer encoder, without a separately pretrained vision encoder. The pretrained backbone returns contextual token representations for task-specific fine-tuning.

NeoMME-Retriever is a downstream model fine-tuned for text and document retrieval. It returns token embeddings for MaxSim scoring and mean-pooled embeddings for cosine similarity.

Before submitting

Who can review?

@tomaarsen and @zucchini-nlp 🤗

Technical details

Non-trivial decisions were discussed on Slack. The main decisions are:

  1. Keep NeoMMEForRetrieval so one forward pass can return dense and multi-vector embeddings. It is intentionally absent from MODEL_FOR_RETRIEVAL_MAPPING_NAMES, so AutoModel and Sentence Transformers v6 load NeoMMEModel (you need to load the backbone in ST)
  2. NeoMMEProcessor remains generic. Direct processor calls do not require a retrieval task or chat template.
  3. Retrieval checkpoints ship a Jinja template that requires an explicit task="query" or task="document". ST forwards this automatically because the template declares task.
  4. The template adds retrieval markers, fixed query expansion tokens, and validates retrieval content. Python handles work that depends on token IDs or image geometry, such as truncation checks, image token expansion, and two-axis positions.
  5. Retrieval scoring is not part of the processor. Use Sentence Transformers for MeanMaxSim or cosine scoring.
  6. The image processor uses torchvision only, preserves native resolution by default, and has no hardcoded processor _defaults.
  7. Following @zucchini-nlp's suggestion, this PR extends the shared SizeDict with min_pixels and max_pixels. These fields describe pixel-area limits without overloading the existing edge-length fields (not suited for areas!), which is why the PR modifies shared files outside the NeoMME model folder.
  8. Transformers provides one joint checkpoint, Hcompany/NeoMME-{model-size}-Retriever. Sentence Transformers provides separate dense and multi-vector wrappers that reuse the backbone weights and load NeoMMEModel.

Manual tests

  • I checked on our internal repository under tolerance 1e-3 that the outputs of NeoMME and NeoMME-Retriever match what our original implementation.
  • To validate that NeoMME-Retriever will properly work with ST-v6, I implemented a custom test on this private repo. @tomaarsen @zucchini-nlp I added you as contributors so you can check the passed tests!
🤖 mlinter review state

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Very interesting model, and good use of modular in model classes. There are a few more classes we can modularize imo, left comments where needed

The major concern atm is the processing class, I left a suggestion so lmk if that works for you/ST. I am not really following the latest ST release, would be great to clean-up redundancy where possible

Comment thread docs/source/en/model_doc/neomme.md Resolved
Comment thread docs/source/en/model_doc/neomme.md Resolved
Comment thread docs/source/en/model_doc/neomme.md Resolved

@zucchini-nlp zucchini-nlp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oke, I am not sure when we're merging (didn't get a reply from Arthur) it but overall nothing too important from comments, and all can be fixed afterwards without even changing the state dict

Lmk in slack or here if you absolutely want this merged tonight, and I'll merge. Otherwise we can wait until Monday

Comment thread docs/source/en/model_doc/neomme.md Resolved
Comment on lines +653 to +654
hidden_states = outputs.last_hidden_state
hidden_states = hidden_states @ self.model.embed_tokens.embedding_projection.weight

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what Arthur mean is prob to create an output projection layer and tie same way as you tied the lm head

_tied_weights_keys = {"lm_head.weight": "model.embed_tokens.word_embeddings.weight", "unembedding_projection.weight": "model.embed_tokens.embedding_projection.weight"}

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

small nits, let's go!

Comment on lines +785 to +786
embeddings = self.multi_vector_head(hidden_states, attention_mask) if output_multivector else None
dense_embeddings = self.dense_head(hidden_states, attention_mask, dense_dim) if output_dense else None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

the heads are dangling if outputs are not needed -> maybe another class to extract embeddings only?

Fine for now, it's clean at least!

@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@github-actions

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: auto, neomme

@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 33375260729:2
Result: success | Jobs: 16 | Tests: 184,769 | Failures: 1 | Duration: 15h 9m

@ArthurZucker
ArthurZucker merged commit e2b3550 into huggingface:main Aug 31, 2026
111 checks passed
sbucaille pushed a commit to sbucaille/transformers that referenced this pull request Sep 16, 2026
* feat: add neomme

* tests: temporarily remove integration tests (need to make the weights public first)

* chore: add aurelien as co-author

Co-authored-by: uminaty <lacaurelien@yahoo.fr>

* docs: fix neomme copyright year

* docs: simplify neomme model loading

* docs: use shared patch size description

* docs: use shared image token description

* test: use configured neomme initializer

* refactor: serialize neomme layer types

* refactor: use strict neomme config validation

* refactor: modularize neomme image processor

* refactor: inline neomme resize scale

* refactor: standardize neomme image normalization

* fix: match neomme image resize rounding

* docs: clarify neomme resize defaults

* test: reject invalid neomme template tasks

* refactor: use processor input helpers

* fix: keep neomme image grid internal

* refactor: source neomme markers from tokenizer

* test: use tokenizer markers in template

* refactor: split neomme patch projection

* refactor: reuse parameter-free rms norm

* fix: remove obsolete attention mask workaround

* refactor: use standard value embeddings

* refactor: inline neomme rotary dimensions

* refactor: store neomme residual scale

* refactor: register neomme input embeddings

* refactor: remove wrapper embedding accessors

* style: move neomme shape comments

* refactor: remove redundant config assignments

* refactor: reuse base retrieval output fields

* refactor: reuse nemotron mlp

* fix: require exactly one model input

* refactor: name retrieval outputs before return

* refactor: narrow neomme weight initialization

* refactor: remove redundant retrieval helpers

* refactor: centralize neomme prompt formatting

* refactor: name neomme retrieval heads

* style: clarify neomme shape comments

* style: inline neomme shape comments

* refactor: use standard neomme rope layout

* fix: harden neomme input handling

- Preserve chat task state while forwarding processing options
- Validate image layouts, model dimensions, and rotary settings
- Clarify public documentation and expand regression coverage

* style: tighten neomme comments

* fix: validate neomme position ID shapes

* refactor: simplify neomme processor helpers

* fix: satisfy neomme repository checks

* feat: support pixel area image size bounds

- Add min_pixels and max_pixels to the shared SizeDict schema
- Move NeoMME pixel budgets into the standard size configuration
- Preserve max-side rounding while enforcing pixel floors and caps
- Validate size keys and positive bound values
- Cover serialization, resizing, rounding, and patch-count behavior

* refactor: make neomme value embeddings mandatory

* style: group neomme code by phase

* style: clarify neomme tensor comments

* style: tighten neomme test comments

* refactor: validate neomme dimensions from field definitions

* refactor: validate neomme rotary dimensions after standardization

* docs: remove duplicate neomme image size docs

* refactor: reuse standard neomme image normalization constants

* style: compact neomme image processor defaults

* refactor: validate neomme image kwargs before preprocessing

* fix: compute neomme rotary embeddings outside autocast

* docs: clarify neomme pixel area size bounds

* refactor: rename neomme residual multiplier

* refactor: remove legacy neomme rope scaling support

* refactor: validate neomme image dimensions once

* refactor: reuse laguna rotary initialization in neomme

* refactor: configure neomme windows per layer

* docs: simplify neomme config arguments

* refactor: split neomme key and value projections

* feat: add neomme image feature helpers

* perf: batch same-shape neomme images

* style: format neomme follow-up changes

* perf: batch neomme patch conversion

* refactor: keep neomme checkpoint conversion in exporter

* refactor: reuse batched image patch conversion

* fix: validate pixel area image size keys

* refactor: remove unused neomme config defaults

* refactor: reuse laguna rope frequencies

* refactor: centralize neomme input validation

* refactor: source neomme image token from tokenizer

* refactor: use standard neomme image validators

* refactor: simplify neomme patch count helper

* refactor: iterate neomme layer configs directly

* style: sync generated neomme files

* refactor: load neomme windows from config

* refactor: reuse neomme image token placeholder

* refactor: move neomme validation into jinja

* refactor: use standard neomme image hook

* refactor: use standard neomme vlm flow

* docs: use neomme chat template processing

* docs: document neomme retrieval task

* refactor: remove neomme retrieval scoring helper

* test: remove private neomme checkpoint coverage

* docs: clarify neomme retrieval scoring

* refactor: remove neomme embedding tie flag

* feat: support direct neomme retrieval inputs

* fix: support generic neomme processor inputs

* docs: refine neomme usage examples

* refactor: reuse base neomme text processing

* refactor: simplify neomme assistant mask policy

* test: cover neomme processor without chat template

* refactor: organize neomme processor methods

* refactor: simplify neomme input finalization

* docs: clarify neomme processor helpers

* refactor: simplify NeoMME processor flow

- Route retrieval formatting through apply_chat_template and delegate tokenization, padding, and keyword handling to ProcessorMixin
- Keep only NeoMME-specific image placeholder expansion, two-axis positions, and validation for task markers and truncation
- Remove direct task routing, manual sequence padding, and the duplicate query expansion processor setting
- Update documentation and tests for the template-first API, malformed layouts, truncation, and ragged image batches
- Add the PR review notes, chat template draft, and handoff context used to resolve feedback on huggingface#47992

* docs: simplify neomme retrieval example

* style: align neomme processor variable names

* fix: enforce NeoMME input and image limits

* fix: allow task-free NeoMME chat templates

* docs: simplify NeoMME encode helper

* fix: remove hardcoded NeoMME processor defaults

* style: clarify NeoMME implementation comments

* docs: pad NeoMME retrieval batches

* feat: support untied NeoMME word embeddings

* docs: update neomme model doc

* clean up a bit

* docs: improve and fix snippets model doc

* docs: clarify neomme processor docstring

* fix: define neomme per-layer defaults

* refactor: modularize neomme retrieval heads

* refactor: modularize neomme attention output

* refactor: require input ids for neomme

* refactor: make tied neomme decoder explicit

* chore: remove stale neomme fixme

* fix: sync generated neomme model

* fix: expose neomme word embeddings

* fix: validate neomme layer types

* fix: validate neomme image options

* refactor: remove neomme image option validation

* refactor: expose neomme unembedding projection

* chore: sync neomme repository files

* refactor: simplify neomme unembedding projection

* refactor: build neomme positions with torch

* refactor: require neomme input ids

* refactor: rely on neomme forward signature

* docs: update neomme contribution date

---------

Co-authored-by: uminaty <lacaurelien@yahoo.fr>
Co-authored-by: raushan <raushan@huggingface.co>
@tonywu71
tonywu71 deleted the add-neomme branch September 18, 2026 07:35
@tonywu71
tonywu71 restored the add-neomme branch September 18, 2026 07:35
@tonywu71
tonywu71 deleted the add-neomme branch September 18, 2026 07:36
@tonywu71 tonywu71 mentioned this pull request Sep 18, 2026
4 of 5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants