Smart MCQ Solver

IITM BS — DA2001P Deep Learning & GenAI · Shubham Gupta · roll 22f3001874
Task 5-way MCQ Metric MAP@3 Kaggle public LB 0.75436 Ensemble 0.7·DeBERTa + 0.3·RoBERTa Plus a from-scratch transformer
Given a question and five candidate answers A–E, this app predicts the correct option. The Kaggle task is scored by MAP@3, so it returns a ranked top-3 with per-option confidences rather than a single letter. Each question is flattened to {question}\nA. {A}\nB. {B}\nC. {C}\nD. {D}\nE. {E} and read by a 5-way sequence classifier whose classes are the option letters — exactly the format used at training time. Everything runs in your browser; no server, no queue, nothing sent anywhere.

Question

Worked examples

Prediction

Load an example and press Solve.

Models

The from-scratch model runs immediately. The two fine-tuned readers are ~285 MB of quantized weights, so they are loaded on demand rather than blocking page load.
What is actually running here
ModelParamsRoleRuns as
Scratch-base transformer
written from scratch in pure PyTorch — no transformers, no pretrained weights
651,909comparison hand-written JavaScript port, 2.6 MB float32 weights
DeBERTa-v3-small (fine-tuned)141,898,757 ensemble, weight 0.70ONNX int8 via Transformers.js
RoBERTa-base (fine-tuned)124,649,477 ensemble, weight 0.30ONNX int8 via Transformers.js

Fine-tuning recipe (both readers): all 2,000 labelled rows, 3 epochs, AdamW lr 2e-5, weight decay 0.01, batch size 8, max_len 160 fixed padding, float32, linear schedule with 50 warmup steps of 750, non-finite-gradient guard. Final train loss 0.0028 (DeBERTa) and 0.0022 (RoBERTa).

From-scratch architecture: token embedding (3,016-token word-level vocabulary built from the training corpus) + fixed sinusoidal positional encoding → 2 × pre-LN block {multi-head self-attention with 4 heads, hand-written scaled dot-product; position-wise FFN Linear→GELU→Linear} → masked mean-pool over non-PAD tokens → 5-way linear head. d_model=128, trained 12 epochs with OneCycleLR(max_lr=3e-4, pct_start=0.1). Its JavaScript port here reproduces the PyTorch logits to a maximum absolute difference of 9.5e-7 over 200 real questions, with 200/200 identical top-1 predictions.

Honest reading of the results — please expand
  • The best from-scratch validation accuracy was 0.9867 (0.65 M params), and a 1.77 M variant reached 1.0000 — while a 109 M bert-base-uncased fine-tuned on the same split reached only 0.6233. That inversion is the finding, not a win: the benchmark rewards memorisation capacity.
  • The dataset leaks. The 2,000 training rows contain only 588 unique questions (the same question re-wrapped in interchangeable instruction templates, mean 3.40 copies each), and 458 of 500 test rows (91.6%) have all five options byte-identical to a training row. A plain TF-IDF nearest-neighbour lookup — no neural network at all — scores 100.00% under a random 5-fold split but 41.00% under a GroupKFold grouped on the option set. Roughly 59 accuracy points of any "near-perfect validation" number is leakage.
  • The honest external number is the Kaggle public leaderboard: MAP@3 = 0.75436. That came from a larger 5-architecture rank-fusion (RRF) ensemble, not the two-model ensemble in this app, which was built for Milestone 5 and was never itself scored on the leaderboard. Chance = 0.20; "always pick the longest option" = 0.4201; competition leader = 0.768.
  • Quantization disclosure. The two readers run here as int8 ONNX so they fit in a browser. Measured against the fp32 PyTorch checkpoints on 300 labelled rows: top-1 agreement 99.67%, MAP@3 1.000000 → 0.998333. Top-1 is essentially preserved; the rank 2–3 tail can reorder because those probabilities (~0.001) sit below int8 resolution. The from-scratch model is not quantized — it is exact.
  • Try the out-of-domain example. On that probe the ensemble picks the wrong option (D, 47%): RoBERTa is 99.7% confident in a wrong answer and outvotes DeBERTa, which does rank the correct option first — as does the from-scratch model. So the ~99% confidences you see on benchmark rows do not transfer off-distribution, and the ensemble is not uniformly better than its members. One example proves nothing on its own; it is there to make that visible rather than hide it.