Skip to content

model : support classifier_activation for rerankers - #29692

Merged
ggerganov merged 5 commits into
ggml-org:masterfrom
boshjerns:modernbert-classifier-activation
Oct 8, 2026
Merged

ggerganov merged 5 commits into
ggml-org:masterfrom
boshjerns:modernbert-classifier-activation

Conversation

@boshjerns

@boshjerns boshjerns commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Rank head applies GELU for ModernBERT and tanh for other architectures. ModernBertForSequenceClassification reads classifier_activation from config.json, and ibm-granite/granite-embedding-reranker-english-r2 sets it to "silu", so its scores are still off after #29627.

This PR:

  • adds a {arch}.classifier.activation key (gguf-py and llama-arch)
  • writes it from _try_set_pooling_type when the config has classifier_activation (gelu, silu and tanh are accepted)
  • reads it in llama_model_base::load_hparams and maps gelu to the exact (erf) GELU like transformers. A key in the GGUF always takes precedence. Without the key the head uses tanh, and ModernBERT uses the exact GELU (the transformers default)
  • applies the activation in the rank head instead of checking the architecture

Additional information

Scores for 6 query/passage pairs (f16, llama-server --reranking, CPU) compared with transformers in fp32:

Model classifier_activation Max difference, master Max difference, this PR
Alibaba-NLP/gte-reranker-modernbert-base gelu 0.005 0.005
ibm-granite/granite-embedding-reranker-english-r2 silu 0.23 0.001

ModernBERT reranker GGUFs converted before this PR have no key and now get the exact GELU instead of the tanh approximation. In my tests their scores moved by up to 0.0006 and match a GGUF with the key set to gelu. A GGUF of a model that uses another activation (granite, silu) has to be reconverted to pick it up. Other architectures still use tanh. GGUFs converted with this branch still load on master (which ignores the new key).

Checks: CPU build with LLAMA_FATAL_WARNINGS=ON (no warnings), ctest -L 'main|python', flake8, editorconfig-checker, the gguf-py tests, test-llama-archs and the server rerank/embedding tests.

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: YES. The code, the verification and this description were done with an AI agent; I reviewed them and I'm responsible for the changes.

Comment thread conversion/base.py Outdated
Comment thread src/llama-graph.cpp Outdated
Comment thread src/llama-hparams.h Outdated
Comment thread src/models/modern-bert.cpp Outdated
@boshjerns

Copy link
Copy Markdown
Contributor Author

I removed GGML_UNARY_OP_COUNT. act_cls defaults to GGML_UNARY_OP_TANH now, and ModernBERT sets GGML_UNARY_OP_GELU_ERF only when the GGUF has no classifier activation key, so a key in the file always wins. The enum has no unset value, so load_arch_hparams checks for the key itself. Let me know if you'd rather have it done another way.

And yes, checked the numbers, it should have been GELU_ERF. I put llama.cpp's own pooled vector through the transformers head so both heads get the same input. Max diff in the score (24 pairs, CPU, f32 GGUF, transformers 4.57.6 fp32):

weights GELU GELU_ERF
gte-reranker-modernbert-base 0.0003443 0.0000010
granite-embedding-reranker-english-r2 weights with classifier_activation set to gelu (test only) 0.0005627 0.0000005

It doesn't show end to end because the encoder FFN is on the tanh gelu too and that error is bigger. ModernBERT GGUFs without the key score slightly differently now (up to 0.0006 in these tests) and match a GGUF with the key set to gelu.

With the erf head in both runs, switching the encoder to ggml_geglu_erf takes gte from a mean diff of 0.002201 to 0.000602. One pair is still 0.004065 off and I haven't worked out why. I left the encoder alone here, do you want that as a separate PR?

@CISC CISC added the merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. label Oct 7, 2026
@ggerganov
ggerganov merged commit 37ac634 into ggml-org:master Oct 8, 2026
21 of 22 checks passed
auRose94 pushed a commit to auRose94/llama.cpp that referenced this pull request Oct 28, 2026
* model : support classifier_activation for rerankers

Assisted-by: Claude Opus 5.5

* model : map classifier gelu to gelu_erf and accept tanh

Assisted-by: Claude Opus 5.5

* model : default act_cls to tanh, ModernBERT falls back to gelu_erf

Assisted-by: Claude Opus 5.5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conversion merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. model Model specific

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants