Views
No views yet
submission_v23_judgecol.csv).| Metric | Score |
|---|---|
| Private LB (final ranking) | 0.694836 |
| Public LB | 0.697241 |
| Retrieval baseline (start of project) | ~0.485 |
0.37·ROUGE-1 F1 + 0.37·ROUGE-L F1 + 0.26·LLM-judge(1–5, normalised).Aka_Gha Akan/Twi, Amh_Eth Amharic,
Lug_Uga Luganda, Swa_Ken Kiswahili) and 4 English-by-country splits
(Eng_Eth, Eng_Gha, Eng_Ken, Eng_Uga) — are routed by a per-subset
winner map into two regimes: Train.csv · Val.csv · Test.csv
│
ETL: trainval retrieval pool · RAG-format SFT · cached embeddings
│
┌──────────┴──────────┐
│ Per-subset router │
└────┬────────────┬────┘
GENERATION │ │ SELECTION
(Aka_Gha,Eng_Gha, │ │ (Eng_Ken,Eng_Uga,Swa_Ken,
Eng_Eth) │ │ Lug_Uga,Amh_Eth)
┌──────────────┴───┐ ┌───┴──────────────────────┐
│ 4 QLoRA bases │ │ retrieve top-C candidates │
│ (THIS REPO): │ │ e5 + bge-m3 + TF-IDF │
│ Qwen2.5-14B n32 │ └───┬──────────────────────┘
│ Qwen3-14B n16 │ │ per-subset selector:
│ Gemma-3-12B n16 │ │ LGBMRanker / afro-xlmr CE /
│ Mistral-Nemo n16│ │ linear blend
└──────────────┬───┘ └───┬──────────────────────┘
MBR sample ~80/Q │ (selector models NOT in this repo)
→ self-consensus pick │
└────────┬────────┘
Per-column assembly (3 columns scored independently):
TargetR1F1/RLF1 = consensus (gen) / selector (sel)
TargetLLM = 7B-judge pick over the pool
│
submission_v23_judgecol.csv (private LB 0.694836)Aka_Gha, Eng_Gha, Eng_Eth): answered by a
4-way cross-base ensemble of the QLoRA adapters in this repo. Each adapter
is MBR-sampled (temperature 0.7, top-p 0.9), the ~80 pooled samples per
question are scored by self-consensus (the sample with the highest mean
unigram-F1 against the others), and the consensus sample is emitted. The gain
comes from cross-base diversity (different pretraining lineages), not from any
single stronger base.Eng_Ken, Eng_Uga, Swa_Ken, Lug_Uga,
Amh_Eth): near-identical questions exist in the training pool, so a real human
answer is retrieved and selected rather than generated (LambdaRank /
afro-xlmr cross-encoder / linear-blend selectors). Those selector models are
not in this repo — see the full pipeline (link below); this repo is the
generative model.| Folder | Base model | License | SFT data | Decode |
|---|---|---|---|---|
adapters/qwen2.5-14b-instruct | Qwen/Qwen2.5-14B-Instruct | Apache-2.0 | train + val | MBR n=32 |
adapters/qwen3-14b | Qwen/Qwen3-14B | Apache-2.0 | train-only | MBR n=16 |
adapters/gemma-3-12b-it | unsloth/gemma-3-12b-it | Gemma Terms | train-only | MBR n=16 |
adapters/mistral-nemo-12b | unsloth/Mistral-Nemo-Instruct-2407 | Apache-2.0 | train-only | MBR n=16 |
adapter_model.safetensors, ~450–540 MB) — load it on
top of its base model; do not expect a standalone model.⚠️ Not medical advice. These models generate information-style text tuned to a ROUGE/LLM-judge benchmark. They are not a diagnostic tool and must not be used for clinical decisions or deployed to patients without expert review. They can hallucinate, omit warnings, or produce outdated guidance. Always route real health questions to qualified professionals.
reproduce_eval.py.Train/Val;
no medical KB or guideline grounding.1# Install CUDA-12.8 torch first, then the rest:
2pip install torch==2.9.0 --index-url https://download.pytorch.org/whl/cu128
3pip install transformers==4.57.3 peft==0.18.0 bitsandbytes==0.48.2 \
4 accelerate==1.12.0 rouge-score==0.1.2 scikit-learn==1.6.1 \
5 pandas==2.2.3 numpy==2.1.3
6# For fast batched/MBR inference the pipeline uses vllm==0.13.0 (optional here).reproduce_eval.py is CPU-only (no GPU, no model download).MKL_THREADING_LAYER=GNU (vLLM/MKL under
conda), PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True.1python demo_inference.py \
2 --adapter qwen2.5-14b-instruct \
3 --subset Eng_Gha \
4 --question "What are the danger signs during pregnancy that need urgent care?" \
5 --train-csv /path/to/Train.csv # optional: k=3 few-shot retrievaldemo_inference.py does):1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
3from peft import PeftModel
4
5base = "Qwen/Qwen2.5-14B-Instruct"
6bnb = BitsAndBytesConfig(load_in_4bit=True, bnb_4bit_quant_type="nf4",
7 bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True)
8tok = AutoTokenizer.from_pretrained(base)
9model = AutoModelForCausalLM.from_pretrained(base, quantization_config=bnb, device_map="auto")
10model = PeftModel.from_pretrained(model, "adapters/qwen2.5-14b-instruct")
11
12# Build the RAG prompt exactly as in training (see demo_inference.build_prompt):
13messages = [{"role": "system", "content": system_prompt},
14 {"role": "user", "content": rag_user_prompt}]
15prompt = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
16out = model.generate(**tok(prompt, return_tensors="pt").to(model.device),
17 max_new_tokens=512, do_sample=False)
18print(tok.decode(out[0], skip_special_tokens=True))The prompt format matters: the adapters were trained with a specific system instruction + a k=3 retrieved-example RAG user turn + a word-budget hint.demo_inference.build_prompt()reproduces it byte-for-byte. Mistral-Nemo's chat template drops the system role, so the demo merges system→user for that adapter.
reproduce_eval.py for the exact pooling + selection logic.reproduce_eval.py recomputes the validation proxy for the three generation
subsets directly from the saved decode samples (eval_data/) — the same
4-way pooling + self-consensus + UnicodeTokenizer ROUGE used in the submission:python reproduce_eval.pysubset n ROUGE-1 ROUGE-L proxy
Aka_Gha 1114 0.4353 0.2729 0.3541
Eng_Gha 1104 0.4997 0.3564 0.4281
Eng_Eth 564 0.7352 0.7052 0.7202(R1+RL)/2 tracks the 74% ROUGE portion. A UnicodeTokenizer
is required — the stock ROUGE tokenizer regexes on [a-z0-9] and silently
collapses Amharic/Akan/Luganda scripts to ~0.seed=42.<language>, match example style/length, no disclaimers) +
user (k=3 leave-one-out retrieved same-subset Q&A few-shot + "answer in about N
words" budget) + assistant (gold answer). Retrieval for few-shot uses LaBSE dense
embeddings (English/Kiswahili) or TF-IDF char n-grams (Akan/Luganda).
Qwen2.5-14B is trained on train+val; the other three on train-only (so
they can be honestly scored on val). Gemma-3-12B is multimodal — LoRA is
restricted to .*language_model.* (the SigLIP vision tower is skipped).Train.csv / Val.csv / Test.csv.
No external datasets; the retrieval pool, RAG few-shot examples, and all
fine-tuning targets are built solely from challenge data. Challenge data is not
redistributed in this repo.adapters/gemma-3-12b-it/ is a derivative of google/gemma-3-12b-it and is
additionally subject to the Gemma Terms of Use
(commercial use permitted). Each adapter is only usable together with its
respective base model, whose own license applies..
├── README.md # this model card
├── DOCUMENTATION.md / .pdf # full method write-up (ETL/modeling/inference/metrics)
├── demo_inference.py # single-question inference with one adapter
├── reproduce_eval.py # CPU-only reproduction of the reported val proxy
├── requirements-inference.txt # minimal env for the demos
├── notebooks/demo.ipynb # notebook walkthrough (eval + inference)
├── adapters/
│ ├── qwen2.5-14b-instruct/ # LoRA adapter (base: Qwen2.5-14B-Instruct)
│ ├── qwen3-14b/ # (base: Qwen3-14B)
│ ├── gemma-3-12b-it/ # (base: unsloth/gemma-3-12b-it)
│ └── mistral-nemo-12b/ # (base: Mistral-Nemo-Instruct-2407)
├── eval_data/ # saved val decode samples + gold, for reproduce_eval.py
├── submission/
│ └── submission_v23_judgecol.csv # the exact winning submission (private LB 0.694836)
└── full_pipeline/ # end-to-end reproduction of the WHOLE 8-subset submission
├── reproduce.sh # staged driver: raw CSVs -> submission_v23_judgecol.csv
├── requirements.txt # full pinned environment
└── scripts/ # ETL, QLoRA training, retrieval/selection arm, MBR decode, buildsreproduce_eval.py (top level) verifies the generation model's reported
validation ROUGE offline, no GPU — the fast check for these weights.full_pipeline/reproduce.sh regenerates the entire submission_v23_judgecol.csv
(all 8 subsets, both arms) from the raw challenge CSVs; see DOCUMENTATION.md
§12. This requires the challenge data and a GPU (~38 h serial on one RTX 5090).Zindi — Multilingual Health Question-Answering Challenge.
Winning submission: cross-base 4-way QLoRA generation ensemble + per-subset
retrieval selection. Private LB 0.694836.