Instructions to use MagicCard/msrh-zindi-magic with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use MagicCard/msrh-zindi-magic with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
File size: 7,656 Bytes
028081c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | #!/usr/bin/env python3
"""Ensemble builder β reproduces go.csv from 19 per-cand prediction CSVs.
Reads `candidate_csvs/<descriptive_name>.csv` and computes V2 medoid_ngram per row.
No GPU needed. Pure CPU + ROUGE scoring (rouge-score==0.1.2).
Run from /mnt/msrh/Magic_submission/ folder:
python /mnt/msrh/Magic_submission/scripts/build_ensemble.py
Output:
- A regenerated submission CSV written under /mnt/msrh/Magic_submission/.
- The script also prints its md5 alongside the shipped go.csv (a2ecca4a8e1aa01acf9a8b9a1d56ebf2)
so you can compare. md5 byte-equality is NOT guaranteed across machines (see README Β§3
"Note on byte-identity vs functional reproducibility"); functional LB equivalence is.
"""
import csv, pathlib, json, sys
from rouge_score import rouge_scorer
# Resolve paths relative to this script
ROOT = pathlib.Path("/mnt/msrh/Magic_submission")
CAND_DIR = ROOT / "candidate_csvs"
OUT_DIR = ROOT
DATA_DIR = ROOT / "data"
if not (DATA_DIR / "Test.csv").exists():
print(f"ERROR: Test.csv not found at {DATA_DIR/'Test.csv'}")
print("Place Test.csv + SampleSubmission.csv in /mnt/msrh/Magic_submission/data/ then re-run.")
sys.exit(1)
TEST_CSV = DATA_DIR / "Test.csv"
SAMPLE_CSV = DATA_DIR / "SampleSubmission.csv"
# 19 candidates in EXACT ORDER β order matters because medoid ties resolve to
# the first-encountered cand. Reordering changes ties β different output bytes.
CAND_ORDER = [
# βββ 12 fewshot LoRA cands + 3 no-fewshot mediators βββ
# 3 Q3.5-27B K=3 variants (3ep ck-1200/1100, 5ep ck-1200)
"Qwen3.5-27B-3fewshots-bs64-3eps-ckpt-1200.csv",
"Qwen3.5-27B-3fewshots-bs64-3eps-ckpt-1100.csv",
"Qwen3.5-27B-3fewshots-bs64-5eps-ckpt-1200.csv",
# Q3.5-27B K=4, K=7 peak ckpts (v1 prompt)
"Qwen3.5-27B-4fewshots-bs64-3eps-ckpt-1600.csv",
"Qwen3.5-27B-7fewshots-bs64-3eps-ckpt-1600.csv",
# Q3.6-27B family (K=3 RecA, K=4, K=5 peak, K=7)
"Qwen3.6-27B-3fewshots-bs64-3eps-ckpt-1600.csv",
"Qwen3.6-27B-4fewshots-bs64-3eps-ckpt-1400.csv",
"Qwen3.6-27B-5fewshots-bs64-3eps-ckpt-1200.csv",
"Qwen3.6-27B-7fewshots-bs64-3eps-ckpt-1600.csv",
# Q3-32B family (K=3, K=5, K=7)
"Qwen3-32B-3fewshots-bs64-3eps-ckpt-1400.csv",
"Qwen3-32B-5fewshots-bs64-3eps-ckpt-1700.csv",
"Qwen3-32B-7fewshots-bs64-3eps-ckpt-1600.csv",
# 3 no-fewshot baselines (mediators) β long-trained without demos for decorrelation
"Qwen3.5-27B-NoFewshots-bs64-5eps-ckpt-2800.csv",
"Qwen3.6-27B-NoFewshots-bs64-5eps-ckpt-2600.csv",
"Qwen3-32B-NoFewshots-bs64-4eps-ckpt-6500.csv",
# βββ v8 anchored-extraction prompt (Q3.5 K=5) βββ
"Qwen3.5-27B-5fewshots-bs64-3eps-v8prompt-ckpt-1500.csv", # β best standalone 0.72325 LB
# βββ 3 cross-arch EARLY ckpts (anti-overfit) βββ
"Qwen3.5-27B-7fewshots-bs64-3eps-ckpt-1200.csv", # β early-tap (vs peak ck-1600)
"Qwen3-32B-7fewshots-bs64-3eps-ckpt-1200.csv", # β early-tap
"Qwen3.6-27B-5fewshots-bs64-3eps-ckpt-1000.csv", # β very early-tap (vs peak ck-1200)
]
# Standalone LB for each cand (verified on Zindi public LB, for documentation only)
CAND_LB = {
"Qwen3.5-27B-3fewshots-bs64-3eps-ckpt-1200.csv": 0.7148,
"Qwen3.5-27B-3fewshots-bs64-3eps-ckpt-1100.csv": 0.7124,
"Qwen3.5-27B-3fewshots-bs64-5eps-ckpt-1200.csv": 0.7102,
"Qwen3.5-27B-4fewshots-bs64-3eps-ckpt-1600.csv": 0.7162,
"Qwen3.5-27B-5fewshots-bs64-3eps-v8prompt-ckpt-1500.csv": 0.72325,
"Qwen3.5-27B-7fewshots-bs64-3eps-ckpt-1600.csv": 0.7150,
"Qwen3.5-27B-7fewshots-bs64-3eps-ckpt-1200.csv": 0.7084,
"Qwen3.6-27B-3fewshots-bs64-3eps-ckpt-1600.csv": 0.7060,
"Qwen3.6-27B-4fewshots-bs64-3eps-ckpt-1400.csv": 0.7091,
"Qwen3.6-27B-5fewshots-bs64-3eps-ckpt-1200.csv": 0.7136,
"Qwen3.6-27B-5fewshots-bs64-3eps-ckpt-1000.csv": 0.7086,
"Qwen3.6-27B-7fewshots-bs64-3eps-ckpt-1600.csv": 0.7194,
"Qwen3-32B-3fewshots-bs64-3eps-ckpt-1400.csv": 0.7011,
"Qwen3-32B-5fewshots-bs64-3eps-ckpt-1700.csv": 0.7081,
"Qwen3-32B-7fewshots-bs64-3eps-ckpt-1600.csv": 0.71383,
"Qwen3-32B-7fewshots-bs64-3eps-ckpt-1200.csv": 0.7111,
"Qwen3.5-27B-NoFewshots-bs64-5eps-ckpt-2800.csv": 0.6948,
"Qwen3.6-27B-NoFewshots-bs64-5eps-ckpt-2600.csv": 0.6933,
"Qwen3-32B-NoFewshots-bs64-4eps-ckpt-6500.csv": 0.6884,
}
scorer = rouge_scorer.RougeScorer(["rouge1", "rouge2"], use_stemmer=False)
def load(path):
with open(path, newline="") as f:
r = csv.DictReader(f)
col = "TargetRLF1" if "TargetRLF1" in r.fieldnames else r.fieldnames[1]
return {row["ID"]: str(row[col]) for row in r}
def medoid(texts):
"""V2 medoid_ngram: pick text with HIGHEST sum of pairwise (R1.F + R2.F) to all others."""
best, best_s = 0, -1.0
for i in range(len(texts)):
s = 0.0
for j in range(len(texts)):
if i == j:
continue
rr = scorer.score(texts[j], texts[i])
s += rr["rouge1"].fmeasure + rr["rouge2"].fmeasure
if s > best_s:
best_s, best = s, i
return best
def main():
print(f"Loading {len(CAND_ORDER)} cand CSVs from {CAND_DIR}...")
all_preds = {}
for name in CAND_ORDER:
path = CAND_DIR / name
if not path.exists():
print(f" MISSING: {name}")
continue
all_preds[name] = load(path)
print(f" OK: {name} ({len(all_preds[name])} rows)")
if len(all_preds) != len(CAND_ORDER):
print(f"ERROR: only {len(all_preds)}/{len(CAND_ORDER)} cand CSVs loaded.")
sys.exit(1)
# Load Test IDs in order
with open(TEST_CSV, newline="") as f:
ids = [r["ID"] for r in csv.DictReader(f)]
with open(SAMPLE_CSV, newline="") as f:
sample_cols = next(csv.reader(f))
target_cols = [c for c in sample_cols if c.startswith("Target")]
print(f"\nProcessing {len(ids)} Test rows Γ {len(CAND_ORDER)} cands "
f"({len(CAND_ORDER)*(len(CAND_ORDER)-1)//2} pairwise per row)...")
members = list(CAND_ORDER) # ORDER critical for medoid tiebreak
picks, chosen = [], []
for i, id_ in enumerate(ids):
if i % 500 == 0:
print(f" row {i}/{len(ids)}")
texts = [all_preds[m].get(id_, "") for m in members]
p = medoid(texts)
picks.append(p)
chosen.append(texts[p])
out_path = OUT_DIR / "go_reproduced.csv"
with open(out_path, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=sample_cols, lineterminator="\n")
w.writeheader()
for id_, ans in zip(ids, chosen):
w.writerow({"ID": id_, **{tc: ans for tc in target_cols}})
print(f"\n=== DONE ===")
print(f"Output: {out_path}")
# Pick distribution
print("\nPick distribution (per cand %):")
for i, m in enumerate(members):
n = picks.count(i)
pct = n / len(picks) * 100
marker = " β" if "v8prompt" in m or "ckpt-1200" in m or "ckpt-1000" in m else ""
print(f" {m:<60} {n:>4} ({pct:>5.1f}%){marker}")
# Verify match
import hashlib
h = hashlib.md5(open(out_path, "rb").read()).hexdigest()
EXPECTED = "a2ecca4a8e1aa01acf9a8b9a1d56ebf2"
if h == EXPECTED:
print(f"\nβ
MD5 MATCH: {h} (byte-identical to go.csv)")
else:
print(f"\nβ MD5 MISMATCH:")
print(f" Reproduced: {h}")
print(f" Expected : {EXPECTED}")
if __name__ == "__main__":
main()
|