--- language: en license: mit library_name: pytorch base_model: FacebookAI/roberta-base pipeline_tag: question-answering tags: - extractive-qa - grounded-qa - hallucination-free - selective-prediction - abstention - rag datasets: - rajpurkar/squad_v2 metrics: - exact_match model-index: - name: grounded-pointer-qa results: - task: type: question-answering dataset: name: SQuAD v2 (held-out test half, retrieval setting) type: rajpurkar/squad_v2 metrics: - type: exact_match value: 74.6 name: EM (EM-optimal gate) - type: precision value: 91.7 name: Answered precision at 90%-precision gate (9% coverage) --- # Grounded Pointer QA An extractive question-answering model that **cannot hallucinate by construction**: its output layer can only point at spans inside retrieved passages of your documents. It has no vocabulary to generate from. A trained abstention head refuses when the loaded knowledge does not contain the answer, decoding is deterministic (argmax, so the same question over the same documents gives the same answer every time), and knowledge is **hot-swappable**: point it at a new folder of text files and it answers from those, with no retraining. Built on `roberta-base` (125M params) with pointer and abstention heads, finetuned on SQuAD v2 against real TF-IDF retrieval. The model never saw gold passages during training, only what the retriever actually returned, so it learned to abstain on retrieval misses too. ![Exact match by build stage](ablation.png) ## Operating modes The checkpoint ships with a calibrated confidence gate (`P(answerable) x P(span)`), selected on a calibration split and verified on a disjoint held-out test split: | Mode | Gate | Coverage | Answered precision | EM | |---|---|---|---|---| | "Right or silent" (shipped default) | 0.965 | 9% | 91.7% | 57.1 | | EM-optimal | 0.295 | 45% | 72.3% | 74.6 | Pass a lower `gate` to `ask()` for more coverage at lower precision. ## Usage ```python # files needed: proqa.pt, modeling_proqa.py (both in this repo) # pip install torch transformers scikit-learn numpy (plus pypdf for PDFs) from modeling_proqa import GroundedQA qa = GroundedQA("proqa.pt") qa.load_folder("path/to/your/notes") # .txt / .md / .pdf qa.ask("when does the vendor contract expire?") # {'answer': '30 November 2026', 'source': '...expires on 30 November 2026...', # 'confidence': 0.98} qa.ask("what is the capital of France?") # not in your docs # {'answer': None, 'source': None, 'confidence': 0.0} ``` ## Roadmap Today the model answers one self-contained question at a time. A conversational grounding layer (multi-turn context and follow-up questions, with every answer still a verified quote) is coming. ## Training One NVIDIA RTX 5060 Ti (16 GB): about 2.5 h finetune (batch 8 x 4 passages x 384 tokens, bf16, lr 2e-5, 2 epochs) plus a calibration pass.