§7.5Evaluating the Reader & Domain Adaptation

Part II NLPT pp. 62–69 · ~3 min read

  • exact match
  • f1-score
  • domain adaptation
  • squad

With the retriever measured, we turn to the reader . How do we score an extracted answer against the ground truth?

Exact Match and F1

Extractive QA uses two metrics:

  • Exact Match (EM) — a strict binary: 1 only if the prediction equals the gold answer exactly (after normalization), else 0.
  • F1 — the harmonic mean of token-level precision and recall, so partial overlaps still get partial credit.

Both first normalize (lowercase, strip punctuation, fix whitespace) and compare as a bag of words. Edit the prediction below to feel the difference — one extra token zeroes EM, and F1 can be generous with wrong-but-overlapping answers:

Predicted tokens: about6000hours

EM = 0precision = 0.67recall = 1.00F1 = 0.80

EM needs an exact match (one extra token → 0). F1 = harmonic mean of token precision and recall (green = overlapping tokens).

Exact Match & F1 — edit the prediction. Try "6000 hours" (EM=1), or "about 6000 dollars" (F1 drops to 0.4 even though EM is already 0).

For pred = "about 6000 hours" vs gold = "6000 hours", EM = 0 (the extra “about”) but F1 = 0.8. Swap in "about 6000 dollars" and F1 falls to 0.4 — so tracking both metrics is important: EM underestimates, F1 overestimates.

from farm.evaluation.squad_evaluation import compute_f1, compute_exact
compute_exact("6000 hours", "about 6000 hours")   # 0
compute_f1("6000 hours", "about 6000 hours")      # 0.8

Since a question can have several valid answers, the score is the best over all gold answers, averaged across all question-answer pairs. Haystack’s EvalAnswers node reports top_1_em and top_1_f1.

The domain gap

Running our SQuAD-fine-tuned MiniLM on SubjQA, the scores are much worse than its SQuAD 2.0 numbers (EM 76.1, F1 79.5). Two reasons: customer reviews are informal and unlike Wikipedia, and SubjQA’s questions and answers are subjective. Models are also known to overfit to SQuAD specifically.

Domain adaptation

The fix is domain adaptation : fine-tune the reader further on SubjQA. FARMReader.train() expects the SQuAD JSON format, which groups question-answer pairs under each product’s reviews:

# create_paragraphs() builds, per product, an array of {context, qas}:
[{"qas": [{"question": "How is the bass?", "id": "...",
           "is_impossible": False,
           "answers": [{"text": "The only fault in the sound is the bass",
                        "answer_start": 650}]}],
  "context": "I have had many sub-$100 headphones ..."}]

reader.train(data_dir=".", use_gpu=True, n_epochs=1, batch_size=16,
             train_filename="electronics-train.json",
             dev_filename="electronics-validation.json")

The payoff is large — domain adaptation increased EM by ~6× and more than doubled F1:

0.000.250.500.060.22SQuAD0.360.47SQuAD + SubjQA0.230.35SubjQA onlyEMF1score
Reader EM/F1 across fine-tuning strategies (illustrative of the book’s reported gains). SQuAD + SubjQA wins; SQuAD alone is far worse; SubjQA alone underperforms the combination.

Why not fine-tune on SubjQA alone? With only 1,295 examples the model overfits; the SQuAD pretraining supplies broad reading comprehension that SubjQA then specializes.

Note — cross-validation for small datasets

With small datasets like SubjQA, transformers overfit easily. Using cross-validation when evaluating gives a more reliable estimate than a single train/dev split.

Both components are now tuned. Next we measure the whole pipeline together and look beyond extractive QA.

Check yourself: reader evaluation & domain adaptation

1.How does Exact Match (EM) differ from F1 for scoring an answer?

2.For pred='about 6000 hours' and gold='6000 hours', why is EM=0 but F1=0.8?

3.Why track BOTH EM and F1 rather than just one?

4.Why does the SQuAD-fine-tuned reader do much worse on SubjQA than on SQuAD?

5.What is domain adaptation here, and what was its effect?

6.Why fine-tune on SQuAD *then* SubjQA, rather than on SubjQA alone?

6 questions