Grounded Table Reasoning Traces — Methodology

A technical reference for the ML approach: teaching a small model to answer constrained questions over real tables and emit reasoning traces grounded in specific cells, by routing its comprehension through a deterministic engine.

Qwen3-4B · QLoRA TAT-QA · CC BY 4.0 Held-out test, scored once

The reader-facing narrative with a live demo is in the write-up; the consolidated, honestly-bounded results are in RESULTS.md. This page is the lean ML reference.

1. Problem & approach

Small language models comprehend table questions well but cannot reliably execute the arithmetic. The goal is grounded reasoning traces: every step tied to specific, verifiable cells, with every numeric claim provably true.

The design is neuro-symbolic. The model's job is comprehension: it emits a structured operation (which op, which columns, which thresholds and directions, read from the question). A small deterministic engine computes the answer and returns the exact cells it read. So the model contributes understanding and explanation; proven code contributes the arithmetic and the grounding. A safety gate falls back to the model when the emitted operation is not supported by the question, keeping the system never worse than the model alone.

2. Data & task

Tables come from TAT-QA (Zhu et al., ACL 2021; CC BY 4.0): real financial tables from company filings. TAT-QA's native questions are ~64% free-form arithmetic and only ~8% match clean selection/threshold predicates, so we keep the real tables and generate three families of constrained, programmatically-verifiable questions over them (both row- and column-orientations, via transpose; explicit-direction phrasing so the gold is a mechanical computation):

Taskquestion_typeGold
Winner selectionbest_under_constraintargmax of a target column over rows passing a threshold constraint
Constraint filteringthreshold_filterthe set of rows satisfying all conditions
Trade-offtradeoff_summarythe Pareto frontier across two metrics

Independent validator. A rule-based validator recomputes the answer from a stored spec and verifies each cited cell (existence, value match, numeric-comparison and threshold correctness, relevance). It is deliberately kept independent of the answer engine (its own predicate re-implementations, no shared helpers) so that "engine output passes validator" is not circular. Total/subtotal rows are excluded from the entity universe by a single shared rule, so the inference-time universe matches the gold spec.

Splits & sizes. 1,266 validated examples (100% pass the validator); 177 silver-rendered training records. The eval split is leakage-free at the table level: re-split by source table (orientation suffixes stripped; 166/224 tables had straddled a per-example split), asserted disjoint at both id and content-hash level — dev 190 / locked test 254, plus a separate 12-example out-of-distribution anchor (an extremum task type not in training).

3. Output schema

The model emits one JSON object per example: trace_steps[] (each a short natural-language description + a structured cites list), a structured final_answer, and the operation the engine consumes. Citations carry both coordinates and semantics for exact, reorder-robust validation:

TypeShape
CellRefrow:int, col:int, col_name:str, value — the unit of evidence
TraceStepkind:"filter"|"compare"|"aggregate"|"select"|"conclude", description, cites:[CellRef]
operationop type + column names + thresholds + directions (e.g. {type, target, target_dir, constraint, op, threshold})

4. Model & training

QLoRA SFT, fully local on a single RTX 3090 (~11 min/run). Base Qwen3-1.7B first (to rehearse the converter → SFT → eval loop), then promoted to the Qwen3-4B target on identical data.

Silver training labels. Each question is rendered N=3 times (headless Claude); only answers that agree and pass the Tier-1 groundedness + numeric-consistency checks are kept (high-precision, ~56% yield). The held-out eval set keeps the silver set honest.

5. The executor — answer by construction

SFT alone left answers at ~59%; error analysis showed the residual was arithmetic-execution errors (flipped comparisons, fumbled dominance), not misreadings. So instead of trusting the model's arithmetic, the model emits the structured operation and a deterministic engine (src/executor.py) computes the answer: row filtering, argmax/min, multi-condition thresholds, and Pareto dominance, with explicit epsilon tolerance, tie handling, and total-row exclusion. Operations reference column names, so they are robust to reordering and are exactly what a model can read off the question.

By design the engine is independent of the validator (separate predicate re-implementations). The validator remains a genuine external check on the engine's output; sharing code would make "executor output passes validator" circular — the same independence the validator keeps from the generator.

6. Grounding & the safety gate

Grounding by construction

The model's hand-typed citations have the same disease as its arithmetic (wrong/derived numbers), so they get the same cure: the trace cites the cells the engine actually read (executor.evidence_for) — grounded by construction (read straight from the table, so they always exist and match). The trace prose stays model-authored (the explanation); the cited evidence becomes system-constructed (the proof).

Out-of-vocabulary safety gate

The engine only helps inside its trained operation vocabulary. The gate checks the question carries the signal the emitted op type requires — threshold language for best/threshold ops, trade-off/Pareto language for tradeoff. If the signal is absent, the operation is fabricated, so the system falls back to the model's own answer. Deployable from question text + op type alone (no gold); a "do no harm" check, not merely an OOD patch.

7. Evaluation methodology

Metrics (over all examples — an unparseable output scores 0 on every metric): engine answer accuracy vs. the model's own answer, operation present / whole-op exact-match, trace groundedness, full valid-trace, and a per-type breakdown.

Protocol. A dev slice (190) for iteration; the locked test (254) scored exactly once under a pre-registered frozen config (Qwen3-4B + the SFT-exec adapter, greedy/deterministic, strict validator).

Circularity and the anchors. The in-distribution gold is spec-derived and the engine re-implements the same semantics, so an in-distribution score measures operation-comprehension consistency on unseen tables, not correctness against an outside oracle. Two blind anchors break the circularity: independent LLM annotators answering from the raw table + question alone agreed with stored gold 36/36; a blind human oracle agreed 24/24 on the two natural types (0 gold errors). The OOD anchor (12 extremum examples, a task type outside the operation vocabulary) is the only fully non-circular slice — and it is what exposed the executor's specialization limit (below).

8. Results & key findings

Progression (trained-model stages on dev 190; the two system layers re-scored on the locked test 254):

StageValid traceGroundedAnswer
Base Qwen3-1.7B (zero-shot)0%1%18%
+ QLoRA SFT (1.7B)17%49%20%
Base Qwen3-4B (zero-shot)pending (measured separately)
+ QLoRA SFT (4B, same data)44%70%52%
+ Executor (answer by construction)69%71%95.7%
+ Grounding (citations by construction)94.9%96.9%95.7%

On the locked test (254): engine answer 95.7% [92.4–97.6] vs. the model's own arithmetic 59.4% — a paired +93 / −1 (exact-McNemar p ≈ 10⁻²⁶); by type 97.6 / 96.6 / 92.9%. Grounding lifts the trace-grounded rate 71.3% → 96.9% (valid 68.9% → 94.9%). The gate takes OOD 0% → 100% and in-distribution 95.7% → 96.1% (fires on 9/254, never on a case the engine got right). Full numbers and CIs: RESULTS.md.

Key findings

9. Limitations