Skip to main content

IMRNNs

IMRNNs

Interpretable Modular Retrieval Neural Networks
Efficient, interpretable dense retrieval through dynamic embedding modulation.

University of Maryland, Baltimore County    KAI² Lab    Hugging Face    EACL 2026

PyPI version Supported Python versions Build status Hugging Face downloads CC BY 4.0 license EACL 2026 paper

Paper · PyPI · Model checkpoint · Project website · Author portfolio


Given a query and candidate documents, IMRNNs dynamically modulates both sides of their dense embeddings before ranking the documents by cosine similarity. A Query Adapter conditions each document representation on the query, while a Document Adapter uses feedback from the candidate set to adapt the query representation. The base encoder stays frozen.

IMRNNs operates inside the dense-retrieval stage. It does not add a downstream cross-encoder stage.

Choose your path

I want to… Start here
Rank text with the released model Five-minute quick start
Understand why a document received its score Explain a retrieval decision
Use vectors from an existing retrieval system Rank existing embeddings
Download or load a checkpoint explicitly Checkpoint loading
Train and evaluate an adapter Training and evaluation
Use my own BEIR-format data Custom datasets
Look up a command or Python API Reference

Installation

For text and embedding ranking with the released checkpoint:

python -m pip install imrnns

For training and BEIR evaluation:

python -m pip install "imrnns[train,eval]"

IMRNNs supports Python 3.10–3.12 and CPU inference. Set device="cuda" when a CUDA-enabled PyTorch installation and compatible GPU are available. The first text-ranking call downloads the pinned MiniLM encoder and the IMRNN checkpoint; later calls use the local Hugging Face cache.

Five-minute quick start

This walkthrough uses a genuine example from the BEIR SciFact corpus: claim 130 and three official document titles. SciFact marks document 27768226 as the relevant document in its test relevance judgments.

from imrnns import IMRNNAdapter

CLAIM = (
    "Articles published in open access format are more likely to be cited "
    "than traditional journals."
)

DOCUMENTS = {
    "27768226": "Open Access Increases Citation Rate",
    "38180456": "Short-term medical service trips: a systematic review of the evidence.",
    "16979690": (
        "Effect on the quality of peer review of blinding reviewers and asking "
        "them to sign their reports: a randomized controlled trial."
    ),
}

adapter = IMRNNAdapter.from_pretrained(
    encoder="minilm",
    dataset="scifact",
    device="cpu",
)

results = adapter.rank(
    query=CLAIM,
    documents=list(DOCUMENTS.values()),
    document_ids=list(DOCUMENTS),
    top_k=3,
)

for result in results:
    print(
        result.rank,
        result.document_id,
        f"base={result.base_score:.4f}",
        f"adapted={result.adapted_score:.4f}",
        f"delta={result.score_delta:+.4f}",
        sep=" | ",
    )

Observed with the released checkpoint on CPU:

1 | 27768226 | base=0.6616 | adapted=0.6380 | delta=-0.0236
2 | 38180456 | base=0.1311 | adapted=0.1115 | delta=-0.0196
3 | 16979690 | base=0.1331 | adapted=0.1096 | delta=-0.0235

The gold SciFact document is ranked first. Each RetrievalResult contains:

Field Meaning
rank Position after IMRNN modulation
index Original position in the supplied candidate list
document_id Optional identifier supplied by your application
text Original document text when using rank()
base_score Frozen encoder's cosine score
adapted_score Cosine score in the modulated embedding space
score_delta adapted_score - base_score for that query-document pair

A negative score_delta does not mean the ranking became worse. It means that one pair's cosine score moved downward. Ranking quality depends on the relative ordering across all candidates; the corpus-level metrics later in this README measure that ordering directly.

Explain a retrieval decision

Use explain() with the same SciFact claim and its top document:

from pathlib import Path

explanation = adapter.explain(
    query=CLAIM,
    document=DOCUMENTS["27768226"],
    top_tokens=5,
)

print("query concepts:", explanation.query_tokens)
print("document concepts:", explanation.document_tokens)
print(
    f"base={explanation.base_score:.4f}",
    f"adapted={explanation.adapted_score:.4f}",
    f"delta={explanation.score_delta:+.4f}",
)

Path("imrnns-explanation.html").write_text(
    explanation.to_html(),
    encoding="utf-8",
)

Observed concept labels include bibliography, access, author, and citation for the query, and access and citation for the document. RetrievalExplanation also exposes the complete query and document modulation vectors through query_modulation and document_modulation.

The concepts are nearest encoder-vocabulary directions obtained through a Moore–Penrose back-projection. WordPiece fragments such as ##wall can appear, and these labels should be interpreted as inspection aids rather than causal natural-language rationales. explain() scores a single pair, whereas rank() can use feedback from several candidates, so their adapted scores can differ slightly.

Rank existing embeddings

rank_embeddings() accepts NumPy arrays, PyTorch tensors, or numeric Python sequences. The following example derives real embeddings from the same SciFact inputs instead of using random vectors:

from sentence_transformers import SentenceTransformer

BASE_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
BASE_REVISION = "c9745ed1d9f207416be6d2e6f8de32d1f16199bf"

encoder = SentenceTransformer(
    BASE_MODEL,
    revision=BASE_REVISION,
    device="cpu",
)
query_embedding = encoder.encode(CLAIM, convert_to_numpy=True)
document_embeddings = encoder.encode(
    list(DOCUMENTS.values()),
    convert_to_numpy=True,
)

embedding_adapter = IMRNNAdapter.from_pretrained(
    encoder="minilm",
    dataset="scifact",
    device="cpu",
    load_encoder=False,
)

results = embedding_adapter.rank_embeddings(
    query_embedding=query_embedding,
    document_embeddings=document_embeddings,
    document_ids=list(DOCUMENTS),
    top_k=3,
)

This returns the same document order and scores as the text example because it uses the same pinned encoder and input strings. In a production retrieval system, replace these arrays with the vectors returned by your vector store. The query and document dimensions must match the checkpoint's input dimension.

Use a custom retrieval checkpoint

If you trained an adapter for another compatible encoder, load it without a text encoder and pass vectors directly:

custom_adapter = IMRNNAdapter.from_checkpoint(
    "./my-adapter.pt",
    encoder_model_name="my-organization/my-sentence-transformer",
    embedding_dim=768,
    load_encoder=False,
    device="cpu",
)

results = custom_adapter.rank_embeddings(
    query_embedding=query_vector,
    document_embeddings=document_vectors,
    document_ids=document_ids,
    top_k=10,
)

Checkpoint loading

The public release currently contains one ready-to-use adapter:

Encoder Dataset Dimension Download
MiniLM (all-MiniLM-L6-v2) SciFact 384 Hugging Face files

from_pretrained() is the simplest loader. To download the file explicitly:

imrnns download --encoder minilm --dataset scifact

The equivalent Python helper is:

from imrnns import download_checkpoint

downloaded = download_checkpoint(
    encoder="minilm",
    dataset="scifact",
)
print(downloaded.checkpoint_path)

Load a local copy directly:

adapter = IMRNNAdapter.from_checkpoint(
    "./imrnns-minilm-scifact.pt",
    encoder="minilm",
    device="cpu",
)

For reproducible deployments, pass a Hugging Face commit hash through revision. For offline execution after the files have been cached, pass local_files_only=True and, if needed, the same cache_dir used during the initial download.

Training and evaluation

Install the optional dependencies before running this workflow:

python -m pip install "imrnns[train,eval]"

One-command workflow

imrnns run downloads the BEIR dataset when necessary, builds the embedding cache, mines dense hard negatives, trains the adapter, and evaluates the saved checkpoint:

imrnns run \
  --encoder minilm \
  --dataset scifact \
  --datasets-dir ./datasets \
  --cache-dir ./cache/minilm-scifact \
  --output-dir ./checkpoints \
  --device cpu

The trained checkpoint is written to ./checkpoints/imrnns-minilm-scifact.pt, and the command prints a JSON report containing the training history, base metrics, adapted metrics, metric deltas, and the all-metrics pass indicator.

Run each stage separately

Build a reusable embedding and hard-negative cache:

imrnns cache \
  --encoder minilm \
  --dataset scifact \
  --datasets-dir ./datasets \
  --cache-dir ./cache/minilm-scifact \
  --device cpu

Train from that cache:

imrnns train \
  --encoder minilm \
  --dataset scifact \
  --datasets-dir ./datasets \
  --cache-dir ./cache/minilm-scifact \
  --output-dir ./checkpoints \
  --device cpu

Evaluate the saved adapter against the frozen base retriever:

imrnns evaluate \
  --encoder minilm \
  --dataset scifact \
  --datasets-dir ./datasets \
  --cache-dir ./cache/minilm-scifact \
  --checkpoint ./checkpoints/imrnns-minilm-scifact.pt \
  --device cpu

The evaluation output includes base_metrics, adapted metrics, metric_delta, and beats_base_all_metrics. The final field is true only when every adapted metric is strictly greater than its corresponding base metric.

Default training configuration

Setting Default
Projector Same-dimensional, identity initialized
Hypernetwork hidden dimension 128
Dropout 0
Positive document Highest-relevance document per query
Negatives 63 dense hard negatives from the base top 100
Objective Improvement-margin loss, margin 0.05
Optimizer Adam
Learning rate / weight decay 1e-4 / 1e-5
Batch size 32
Maximum epochs / patience 30 / 7
Seed 42

Use imrnns train --help or imrnns run --help to inspect and override the available hyperparameters.

Custom datasets

IMRNNs accepts a local dataset in the standard BEIR layout:

my_dataset/
├── corpus.jsonl
├── queries.jsonl
└── qrels/
    ├── train.tsv
    └── test.tsv

Minimal records using the same SciFact example look like this:

{"_id":"27768226","title":"Open Access Increases Citation Rate","text":"Scientific abstract text goes here."}
{"_id":"130","text":"Articles published in open access format are more likely to be cited than traditional journals."}
query-id	corpus-id	score
130	27768226	1

Load and inspect a split in Python:

from imrnns import IMRNNDataset

dataset = IMRNNDataset.from_beir_directory(
    "./my_dataset",
    split="test",
)
print(len(dataset.corpus), len(dataset.queries), len(dataset.qrels))

Use --dataset-path ./my_dataset instead of --dataset scifact with the training commands. When official train and test qrels exist, IMRNNs reserves 15% of the training queries for validation and keeps the test set untouched. A dataset with one qrels split uses a deterministic 70/15/15 partition.

Encoders

The built-in encoder aliases can be used when caching and training:

Alias SentenceTransformers model Dimension
minilm sentence-transformers/all-MiniLM-L6-v2 384
e5 intfloat/e5-large-v2 1024
mpnet sentence-transformers/all-mpnet-base-v2 768

Only the MiniLM–SciFact adapter is distributed as a ready-to-use checkpoint. The other aliases are available for creating your own caches and checkpoints.

For another SentenceTransformers encoder, provide its model name and dimension:

imrnns run \
  --encoder-model-name my-organization/my-model \
  --embedding-dim 768 \
  --encoder-revision COMMIT_HASH \
  --dataset-path ./my_dataset \
  --cache-dir ./cache/custom-model \
  --output-dir ./checkpoints

Use --query-prefix and --passage-prefix when required by the selected encoder. Reuse the identical model, revision, prefixes, and embedding dimension when loading its checkpoint.

Published checkpoint evaluation

The released MiniLM–SciFact checkpoint was evaluated on the complete official 300-query SciFact test set. Base and adapted retrieval use the same top-100 candidate sets.

Metric What it measures Base MiniLM IMRNN
nDCG@10 Quality and ordering of the first ten results 0.64508 0.69166
Recall@10 Relevant documents found in the first ten results 0.78333 0.84333
MRR@10 How early the first relevant document appears 0.60472 0.64800

The training study contains dataset splits, configuration, additional datasets, and metric deltas.

Reference

Command-line interface

Command Purpose
imrnns info Show the package version, release checkpoint, recipe, and supported encoders
imrnns download Download the released MiniLM–SciFact checkpoint
imrnns list-assets List cached embeddings and local or repository checkpoints
imrnns cache Download/load data, encode it, and mine dense hard negatives
imrnns train Train an adapter from a compatible cache
imrnns evaluate Compare a checkpoint with its frozen base retriever
imrnns run Execute cache, train, and evaluate stages end to end

Run imrnns <command> --help for every available option.

Public Python API

API Purpose
IMRNNAdapter.from_pretrained() Load a released adapter from Hugging Face
IMRNNAdapter.from_checkpoint() Load a local adapter checkpoint
IMRNNAdapter.rank() Encode and rank text documents
IMRNNAdapter.rank_embeddings() Rank existing document vectors
IMRNNAdapter.explain() Inspect score changes, modulation vectors, and vocabulary concepts
RetrievalExplanation.to_html() Render a dependency-free HTML explanation fragment
download_checkpoint() Download a release checkpoint without constructing an adapter
get_download_count() Read the public Hugging Face download count
IMRNNDataset.from_beir_directory() Load and validate a local BEIR split
cache_embeddings() Build a training cache programmatically
train() Train, save, and evaluate an adapter programmatically
evaluate() Evaluate a saved adapter programmatically
run() Execute the complete programmatic workflow

The package also exports IMRNN, ModelConfig, EncoderSpec, RetrievalResult, RetrievalExplanation, and TokenAttribution for advanced integration.

Troubleshooting

Problem Resolution
BEIR download support requires... Install imrnns[train,eval]
No released checkpoint for an encoder/dataset Use minilm with scifact, or train a compatible checkpoint
Embedding-dimension error Use vectors from the checkpoint's exact base encoder and dimension
Offline loading fails Download once, then reuse the same cache_dir with local_files_only=True
A pair has a negative score_delta Compare the full adapted ordering; a pairwise score change is not a ranking metric
Explanation tokens look fragmented They are encoder-vocabulary directions and can include WordPiece fragments
Cache incompatibility error Rebuild the cache with the same dataset, encoder revision, seed, and negative count

Development

git clone https://github.com/YashSaxena21/IMRNNs.git
cd IMRNNs
python -m pip install -e ".[dev]"
pytest
ruff check src tests scripts examples
python -m build
twine check dist/*

Bug reports, feature proposals, and focused pull requests are welcome through GitHub Issues and GitHub Pull Requests.

Citation

If IMRNNs supports your research, please cite the EACL 2026 paper:

@inproceedings{saxena-etal-2026-imrnns,
  title = {{IMRNN}s: An Efficient Method for Interpretable Dense Retrieval via Embedding Modulation},
  author = {Saxena, Yash and Padia, Ankur and Gunaratna, Kalpa and Gaur, Manas},
  booktitle = {Findings of the Association for Computational Linguistics: EACL 2026},
  year = {2026},
  pages = {6324--6337},
  doi = {10.18653/v1/2026.findings-eacl.333},
  url = {https://aclanthology.org/2026.findings-eacl.333/}
}

Authors and acknowledgments

IMRNNs is authored by Yash Saxena, Ankur Padia, Kalpa Gunaratna, and Manas Gaur. Visit Yash Saxena's portfolio for additional projects and publications.

This work is associated with the University of Maryland, Baltimore County and the KAI² Lab, and was published in the Findings of EACL 2026.

License

The code, scripts, documentation, and checkpoints are available under the Creative Commons Attribution 4.0 International License. Attribution requirements are documented in ATTRIBUTION.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

imrnns-0.2.3.tar.gz (1.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

imrnns-0.2.3-py3-none-any.whl (45.8 kB view details)

Uploaded Python 3

File details

Details for the file imrnns-0.2.3.tar.gz.

File metadata

  • Download URL: imrnns-0.2.3.tar.gz
  • Upload date:
  • Size: 1.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for imrnns-0.2.3.tar.gz
Algorithm Hash digest
SHA256 04f3a3031751abb2bf1811e02cf1995b6901a5977539b3213bfca1c89b7ac2de
MD5 912795a85001f2b4343cea12f8eba9a7
BLAKE2b-256 17da4edc9ab9d70049a0177a9cdbeb83dfcf71e40351e32d7d476ce1079efe71

See more details on using hashes here.

Provenance

The following attestation bundles were made for imrnns-0.2.3.tar.gz:

Publisher: publish.yml on YashSaxena21/IMRNNs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file imrnns-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: imrnns-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 45.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for imrnns-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 4325c6bedb512171d1fb91207ad22fbc70c95e8de9449bc916ad984421a5151c
MD5 17759015c2fab1c98e0806b7c2c320a2
BLAKE2b-256 65c3abcd9bcbb0929cae16d644d5e6b4a9ddfb718a6470638aabe0c64b9c83db

See more details on using hashes here.

Provenance

The following attestation bundles were made for imrnns-0.2.3-py3-none-any.whl:

Publisher: publish.yml on YashSaxena21/IMRNNs

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page