A clean, reproducible supervised fine-tuning (SFT) baseline for scientific question answering (QA) with a focus on clarity, evaluation rigor, and latency/accuracy tradeoffs.
The task is based on Consensus-style scientific QA: given a user question and a paper abstract, generate a short answer grounded in the paper context.
We build a supervised fine-tuning (SFT) pipeline for scientific question answering. Early runs failed due to noisy targets and misalignment, producing repetitive outputs. We introduce an oracle extractive target construction and filtering strategy that aligns supervision with the input context. With early stopping on ROUGE‑L and MPS‑friendly training settings, the final model reaches ROUGE‑1 > 0.70 and ROUGE‑L ~0.664 while remaining feasible on a Mac mini.
Given a question and a paper abstract, generate a short, faithful answer. This is a small‑data regime with noisy labels, so target design and evaluation alignment dominate performance.
Raw inputs include query, context, and a noisy label. The clean pipeline:
- Builds an oracle target by selecting the most relevant sentence(s) from the abstract.
- Drops rows with low overlap between input and target.
- Appends
[END]to targets and uses it as EOS at decode time.
See data/process_data_sft_clean.py for implementation.
- Model: FLAN‑T5 small (encoder‑decoder, text‑to‑text)
- Input:
Question: ... Context: ... - Objective: token‑level cross‑entropy on oracle targets
- Decoding: beam search + repetition control +
[END]as EOS
Best‑run settings (see configs/consensus/clean_fit_end_es_rouge_long.yaml):
- Batch size 4, LR 1.5e‑5, weight decay 0.01
- Early stopping on
eval_rougeL(patience 25) - Gradient checkpointing + MPS cache controls
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Build processed dataset
python data/process_data_sft.py \
--input_csv data/input_datasets/training_data.csv \
--output_csv data/processed/training_data_sft.csv \
--target_style label
# Train a small MPS-friendly model
python scripts/train.py --config configs/small_high_accuracy_mps.yaml- Primary: ROUGE‑L (summary quality, sequence coherence)
- Secondary: ROUGE‑1/2, BLEU
- Selection: early stopping on ROUGE‑L to align with generation quality
- Metric formulas:
docs/metrics_definitions.md
Best run (oracle + early stopping on ROUGE‑L):
- BLEU 0.6237
- ROUGE‑1 0.7143
- ROUGE‑2 0.6352
- ROUGE‑L 0.6637
Iteration trajectory:
baseline (low latency): BLEU 0.0587, ROUGE‑L 0.2094
clean (oracle) run: BLEU 0.5564, ROUGE‑L 0.5865
long clean run: BLEU 0.6032, ROUGE‑L 0.6538
best concensus-sft run: BLEU 0.6237, ROUGE‑L 0.6637
src/consensus_sft/: core data + metrics utilitiesscripts/: training/eval/inference/benchmark helpersconfigs/: reproducible configsdocs/: learning materials and evaluation guidancereports/: model card + templatesdata/: raw + processed datasetsdata/source_materials/: source brief, data dictionary, and supplied CSVs
configs/default.yaml: baseline trainingconfigs/low_latency.yaml: faster decoding + shorter lengthsconfigs/small_high_accuracy_mps.yaml: best small-model quality runconfigs/high_accuracy.yaml: larger model (CPU)configs/consensus/clean_fit.yaml: cleaned QA-style pipeline (oracle targets)configs/consensus/clean_fit_end.yaml: cleaned QA-style +[END]stopping token
python scripts/eval_model.py --config configs/small_high_accuracy_mps.yaml
python scripts/benchmark_inference.py --model_path outputs/flan_t5_small_high_accuracy_mps \
--text "Question: ... Context: ..." --runs 20PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 python inference.py \
--model_path outputs/consensus_clean/flan_t5_small_clean_end_es_rouge_long/best_checkpoint \
--input_csv data/input_datasets/inference_data.csv- Config:
configs/consensus/clean_fit_end_es_rouge_long.yaml - Output:
outputs/consensus_clean/flan_t5_small_clean_end_es_rouge_long/ - Best checkpoint:
outputs/consensus_clean/flan_t5_small_clean_end_es_rouge_long/best_checkpoint/ - Metrics: BLEU 0.6237, ROUGE-1 0.7143, ROUGE-2 0.6352, ROUGE-L 0.6637
- Notes: targets are oracle sentence extracts;
[END]token used as EOS for complete outputs; early stopping oneval_rougeL(patience 25) - Callout: ROUGE-1 > 70% on the scientific QA task.
- Baseline (low-latency) was weak: BLEU 0.0587, ROUGE-L 0.2094.
- First stable clean run (oracle targets) jumped to BLEU 0.5564, ROUGE-L 0.5865.
- Long clean run improved further: BLEU 0.6032, ROUGE-L 0.6538.
- Best concensus-sft run: BLEU 0.6237, ROUGE-L 0.6637.
- Net gain vs earliest baseline: ~10× BLEU and ~3× ROUGE-L, with coherent outputs.
- MPS stability: gradient checkpointing + cache clears + low watermark.
- Reproducibility: configs are versioned; outputs are intentionally not tracked.
- Failure modes: see
docs/failure_modes.md.
- Small dataset; metrics can be noisy across seeds.
- ROUGE/BLEU are imperfect for semantic correctness.
- Next steps: semantic metrics (BERTScore/BLEURT), NLI-based faithfulness checks, stronger data curation.
Run the 3‑seed sweep (no timeouts in your terminal):
cd concensus-sft
source .venv_scitldr/bin/activate
PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 python scripts/seed_sweep.py \
--base_config configs/consensus/seed_sweep_base.yaml \
--seeds 13,21,1337Resume a seed if interrupted:
PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 python scripts/consensus/train_clean.py \
--config /tmp/seed_sweep_13.yaml \
--resume_from_checkpoint outputs/consensus_seed_sweep/flan_t5_small_clean_end_es_rouge_long_seed13/checkpoint-6000See:
docs/learning_path.mddocs/learning_summary.mddocs/sft_review_notes.mddocs/evaluation_guidance.mddocs/metrics_definitions.mddocs/architecture.mddocs/diagrams.md
- Raw data is never mutated. All transformations go through
data/process_data_sft.py. - Outputs are intentionally not tracked in git.