Skip to content
ml-recall

Topic notes

NLP Tasks

Framing NLP tasks as sequence classification, token classification (BIO tags), extractive QA or seq2seq, and scoring them with entity F1, exact match, BLEU, ROUGE and chrF.

3-min read AI-checked

Practise 40 questions Take a test

Key ideas

How a task is framed decides the model's output head:

framing output examples
sequence classification one label per text (or text pair) sentiment, topic, natural language inference
token classification one label per token POS tagging, NER, chunking
extractive QA start and end position of a span SQuAD-style reading comprehension
sequence-to-sequence a new text of its own length translation, summarization, generative QA

Formulas

BLEU5 clips each candidate n-gram count at its maximum count in a single reference, giving modified precisions pnp_n, and combines them with a brevity penalty (cc = candidate length, rr = reference length):

BLEU=BP⋅exp⁡(∑n=1Nwnlog⁡pn),BP={1c>re1−r/cc≤r\mathrm{BLEU} = \mathrm{BP} \cdot \exp\Big(\sum_{n=1}^{N} w_n \log p_n\Big), \qquad \mathrm{BP} = \begin{cases} 1 & c > r \\ e^{1 - r/c} & c \le r \end{cases}

Worked example

BLEU with N=2N = 2, w1=w2=12w_1 = w_2 = \tfrac12. Reference: "the quick brown fox jumps" (r=5r = 5); candidate: "the the fox jumps" (c=4c = 4).

In code

def spans(tags):                                        # BIO tags -> set of (start, end, type)
    out, start, label = set(), None, None
    for i, t in enumerate(tags + ["O"]):
        if start is not None and t != "I-" + label:     # the current span ends before i
            out.add((start, i, label)); start = None
        if t.startswith("B-") or (t.startswith("I-") and start is None):
            start, label = i, t[2:]
    return out

gold = ["B-PER", "I-PER", "O", "B-LOC", "O", "B-ORG", "I-ORG"]
pred = ["B-PER", "O",     "O", "B-LOC", "O", "B-PER", "I-PER"]
g, p = spans(gold), spans(pred)
P, R = len(g & p) / len(p), len(g & p) / len(g)
print(sorted(p))                    # [(0, 1, 'PER'), (3, 4, 'LOC'), (5, 7, 'PER')]
print(round(P, 3), round(R, 3), round(2 * P * R / (P + R), 3))  # 0.333 0.333 0.333

Token accuracy of the same prediction is 4/7≈0.574/7 \approx 0.57, but only one of three entities is right (the PER has a wrong boundary, the ORG a wrong type).

Common pitfalls

References

  1. Hugging Face LLM Course — Ch. 7, Token classification (Processing the data, Metrics)
  2. Hugging Face LLM Course — Ch. 7, Question answering (Processing the training data, Post-processing)
  3. Jurafsky & Martin, Speech and Language Processing, 3rd ed. draft (Aug 2026), Ch. 18 Sequence Labeling for Parts of Speech and Named Entities — Sec. 18.2 Part-of-Speech Tagging, 18.3 Named Entities and Named Entity Tagging, 18.6 Evaluation of Named Entity Recognition
  4. Jurafsky & Martin, Speech and Language Processing, 3rd ed. draft (Aug 2026), Ch. 13 Machine Translation — Sec. 13.1 Language Divergences and Typology, 13.6.2 Automatic Evaluation (chrF, BLEU)
  5. Papineni et al. (2002), BLEU: a Method for Automatic Evaluation of Machine Translation — Sec. 2.1 Modified n-gram precision, 2.2.2 Sentence brevity penalty, 2.3 BLEU details
  6. Hugging Face LLM Course — Ch. 7, Summarization (Metrics for text summarization)
  7. Wikipedia: Named-entity recognition — Problem, Definition (names as contiguous spans with no nesting, "Bank of America")