Downloads · 30 days
0
kingjux/search-query-net
search-query-net is a text retrieval model from kingjux. Use it for the text retrieval task on the model card, and read the license before you ship it in a product. It is set up for pytorch. The card lists the license as mit.
A small neural network that reformulates a natural-language question into a keyword search query that retrieves the passage containing the answer. Trained end-to-end with REINFORCE against a BM25 retrieval reward — th…
Downloads · 30 days
0
Access
Public
Updated Jul 9, 2026
Repo size
1 GB
Likes
0
Public
Click a slice to open those files.
.pt990 MB · 98%
From the Hugging Face model README
A small neural network that reformulates a natural-language question into a keyword search query that retrieves the passage containing the answer. Trained end-to-end with REINFORCE against a BM25 retrieval reward — the reward is simply "did the generated query pull back the gold passage?", computed exactly from SQuAD labels (no human labels, no LLM judge).
This repo is a rigorous, from-scratch re-implementation of RL query reformulation in the spirit of Nogueira & Cho, Task-Oriented Query Reformulation with Reinforcement Learning (2017). It is a research / learning artifact, not a production web-search system.
| file | role |
|---|---|
model.py | QueryEmbeddingNet — non-autoregressive single-shot query generator (baseline arch) |
decoder_model.py | QueryDecoderNet — autoregressive pointer-generator decoder (gated copy/gen mixture) |
retrieval_env.py | vectorized BM25 passage index (scipy sparse) + retrieval reward + honest metrics |
prepare_data.py | builds a leak-free title-disjoint SQuAD split + one shared corpus + manifest |
runner.py | training + greedy held-out eval; all knobs are config-gated |
driver.py | multi-seed experiment runner (mean ± std, ledger) |
selector.py | learned head selector (best-of-N without gold labels) |
DECISIONS.md | the full experiment log — every hypothesis, result, and dead end |
Held-out test split (title-disjoint from train/val), one shared 20,958-passage BM25 index, selected on val / reported once on test, 3 seeds. Metric = recall@5 (gold passage in top-5).
| model | recall@5 |
|---|---|
| random 3-term query (floor) | 0.00 |
| honest baseline (single-shot, restrict-to-question) | 0.659 ± 0.012 |
| AR pointer-decoder + warm-start (this repo's best) | 0.834 ± 0.002 |
| best-of-4-heads (oracle over heads) | 0.845 |
| question-as-query (BM25 oracle) | 0.83 |
The autoregressive decoder with an IDF/question-order warm-start is the big lever: it produces a strong single query and closes almost the entire gap to the BM25 oracle on this corpus.
DECISIONS.md for the full story)pip install torch transformers datasets scipy numpy
python prepare_data.py # build leak-free SQuAD data + BM25 corpus
python check_data.py # assert no train/val/test leakage
# best config (AR pointer-decoder + warm-start), confirmed recall@5 = 0.834 ± 0.002
python runner.py '{"arch":"ar_pointer","allow_expansion":false,"warm_start_steps":200,"teacher":"idf","baseline":"rloo","reward_mode":"gold_rank","temp_consistent":true,"steps":600,"save_ckpt":true}'
Training is fast (~a few minutes on one GPU). allow_expansion:true enables the generation branch so
the model can emit tokens not in the question (query expansion) — the path to exceed the copy-only
BM25 oracle; still experimental.
The encoder reads the question; the decoder emits a short query. Reward = BM25 retrieval outcome (gold-passage rank), optimized with REINFORCE using a per-question leave-one-out (RLOO) baseline. A pointer-generator mixes a copy distribution over the question's own tokens with a generation distribution over the full vocabulary, gated by a learned scalar — copy is the safe default, generation enables expansion. A supervised warm-start (imitate a BM25-good query built from the question's informative terms) solves the sparse-reward cold-start before RL takes over.