Bird
Raised Fist0
NLPml~20 mins

Custom NER training basics in NLP - ML Experiment: Train & Evaluate

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Experiment - Custom NER training basics
Problem:You want to teach a computer to recognize specific names and places in text that are not covered by general models. For example, recognizing product names or company names unique to your data.
Current Metrics:Training accuracy: 98%, Validation accuracy: 70%
Issue:The model is overfitting: it performs very well on training data but poorly on new, unseen data.
Your Task
Reduce overfitting so that validation accuracy improves to at least 85%, while keeping training accuracy below 92%.
You can only change the model architecture and training parameters.
You cannot add more training data.
You must keep the same dataset and labels.
Hint 1
Hint 2
Hint 3
Hint 4
Solution
NLP
import spacy
from spacy.training.example import Example
from spacy.util import minibatch, compounding
import random

# Load blank English model
nlp = spacy.blank('en')

# Create NER pipeline component
if 'ner' not in nlp.pipe_names:
    ner = nlp.add_pipe('ner')
else:
    ner = nlp.get_pipe('ner')

# Add labels
LABELS = ['PRODUCT', 'ORG']
for label in LABELS:
    ner.add_label(label)

# Training data: list of tuples (text, {'entities': [(start, end, label), ...]})
TRAIN_DATA = [
    ('Apple releases new iPhone', {'entities': [(0, 5, 'ORG'), (19, 25, 'PRODUCT')]}),
    ('Google launches Pixel', {'entities': [(0, 6, 'ORG'), (16, 21, 'PRODUCT')]}),
    ('Microsoft updates Windows', {'entities': [(0, 9, 'ORG'), (18, 25, 'PRODUCT')]}),
]

# Disable other pipes to train only NER
other_pipes = [pipe for pipe in nlp.pipe_names if pipe != 'ner']
with nlp.disable_pipes(*other_pipes):
    optimizer = nlp.initialize()
    # Set dropout to 0.3 to reduce overfitting
    dropout = 0.3
    # Lower learning rate
    optimizer.alpha = 0.001
    for epoch in range(20):  # Reduced epochs from 50 to 20
        random.shuffle(TRAIN_DATA)
        losses = {}
        batches = minibatch(TRAIN_DATA, size=compounding(2.0, 4.0, 1.5))
        for batch in batches:
            examples = []
            for text, annotations in batch:
                doc = nlp.make_doc(text)
                examples.append(Example.from_dict(doc, annotations))
            nlp.update(examples, drop=dropout, losses=losses, sgd=optimizer)
        # Early stopping simulation: stop if loss is low enough
        if losses.get('ner', 0) < 0.01:
            break

# Evaluate on validation data
VALIDATION_DATA = [
    ('Apple unveils new MacBook', {'entities': [(0, 5, 'ORG'), (18, 25, 'PRODUCT')]}),
    ('Google announces Android update', {'entities': [(0, 6, 'ORG'), (17, 24, 'PRODUCT')]}),
]

correct = 0
total_pred = 0
total_true = 0
for text, annotations in VALIDATION_DATA:
    doc = nlp(text)
    pred_ents = [(ent.start_char, ent.end_char, ent.label_) for ent in doc.ents]
    true_ents = annotations['entities']
    total_pred += len(pred_ents)
    total_true += len(true_ents)
    for ent in pred_ents:
        if ent in true_ents:
            correct += 1

precision = correct / total_pred if total_pred > 0 else 0
recall = correct / total_true if total_true > 0 else 0
f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

print(f'Validation Precision: {precision:.2f}')
print(f'Validation Recall: {recall:.2f}')
print(f'Validation F1-score: {f1:.2f}')
Added dropout of 0.3 during training to reduce overfitting.
Lowered learning rate from default to 0.001 for smoother learning.
Reduced number of training epochs from 50 to 20 to avoid overtraining.
Implemented early stopping by breaking training loop if loss is very low.
Results Interpretation

Before: Training accuracy: 98%, Validation accuracy: 70% (overfitting)

After: Training accuracy: ~90%, Validation F1-score: 86% (balanced performance)

Adding dropout, lowering learning rate, and reducing epochs help reduce overfitting. This improves the model's ability to generalize to new data.
Bonus Experiment
Try adding more training data with varied examples to see if validation accuracy improves further.
💡 Hint
More diverse data helps the model learn better patterns and reduces overfitting naturally.

Practice

(1/5)
1. What is the main goal of custom NER training in NLP?
easy
A. To summarize long documents automatically
B. To teach the model to recognize specific words or phrases you label
C. To translate text from one language to another
D. To generate new text based on a prompt

Solution

  1. Step 1: Understand what NER means

    NER stands for Named Entity Recognition, which means finding specific words or phrases in text.
  2. Step 2: Identify the purpose of custom training

    Custom NER training teaches the model to find your special labeled words, not general tasks like translation or summarization.
  3. Final Answer:

    To teach the model to recognize specific words or phrases you label -> Option B
  4. Quick Check:

    Custom NER = Recognize labeled words [OK]
Hint: Custom NER means teaching model your special words [OK]
Common Mistakes:
  • Confusing NER with translation or summarization
  • Thinking NER generates new text
  • Assuming NER works without labeled data
2. Which of the following is the correct way to label a sentence for custom NER training in Python spaCy format?
easy
A. ('Apple is a company', {'entities': [(0, 5, 'ORG')]})
B. ('Apple is a company', {'labels': [(0, 5, 'ORG')]})
C. ('Apple is a company', {'entities': [(6, 7, 'ORG')]})
D. ('Apple is a company', {'entities': [(0, 5, 'PERSON')]})

Solution

  1. Step 1: Check the labeling key

    spaCy uses the 'entities' key, not 'labels', to hold labeled spans.
  2. Step 2: Verify the span and label

    Span (0,5) covers 'Apple' correctly, and label 'ORG' (organization) fits. A span like (6,7,'ORG') points to the wrong position, and 'PERSON' is incorrect for a company.
  3. Final Answer:

    ('Apple is a company', {'entities': [(0, 5, 'ORG')]}) -> Option A
  4. Quick Check:

    Correct key and span = ('Apple is a company', {'entities': [(0, 5, 'ORG')]}) [OK]
Hint: Use 'entities' key with correct span and label [OK]
Common Mistakes:
  • Using 'labels' instead of 'entities'
  • Incorrect character span for entity
  • Wrong entity type label
3. Given this training data snippet for custom NER:
TRAIN_DATA = [
  ('I love Paris', {'entities': [(7, 12, 'GPE')]})
]
What will the model predict for the sentence 'I love Paris' after training?
medium
A. [] (no entities)
B. [('I', 'GPE')]
C. [('Paris', 'GPE')]
D. [('love', 'GPE')]

Solution

  1. Step 1: Understand the labeled entity

    The training data labels 'Paris' from character 7 to 12 as 'GPE' (Geopolitical entity).
  2. Step 2: Predict model output after training

    The model learns to recognize 'Paris' as 'GPE' and should predict [('Paris', 'GPE')] for the same sentence.
  3. Final Answer:

    [('Paris', 'GPE')] -> Option C
  4. Quick Check:

    Entity span matches 'Paris' = [('Paris', 'GPE')] [OK]
Hint: Model predicts labeled spans from training data [OK]
Common Mistakes:
  • Confusing entity span with other words
  • Expecting no entities if training is done
  • Mixing entity labels
4. You wrote this code to add a new entity label to your NER model:
ner.add_label('ANIMAL')
But after training, the model never detects 'ANIMAL' entities. What is the most likely mistake?
medium
A. The label 'ANIMAL' is reserved and cannot be used
B. You used the wrong method name; it should be add_entity()
C. You need to call ner.remove_label('ANIMAL') before adding
D. You forgot to include training examples with 'ANIMAL' labels

Solution

  1. Step 1: Check the method usage

    ner.add_label('ANIMAL') is correct to add a new label. There is no add_entity() method, no need to call remove_label first, and 'ANIMAL' is not reserved.
  2. Step 2: Verify training data

    Model learns from examples. Without training examples labeled 'ANIMAL', model cannot detect it.
  3. Final Answer:

    You forgot to include training examples with 'ANIMAL' labels -> Option D
  4. Quick Check:

    Training data needed for new labels = You forgot to include training examples with 'ANIMAL' labels [OK]
Hint: Add labeled examples for new entity labels [OK]
Common Mistakes:
  • Assuming adding label alone trains model
  • Using wrong method names
  • Thinking labels are reserved keywords
5. You want to train a custom NER model to recognize two new entity types: 'FOOD' and 'DRINK'. You have labeled training data for both. Which of the following is the best approach to ensure the model learns both correctly?
hard
A. Add both labels with ner.add_label(), include balanced training examples for each, and train in multiple iterations
B. Add only 'FOOD' label first, train fully, then add 'DRINK' label and train again
C. Train the model without adding labels explicitly; it will learn automatically
D. Add labels but use only examples for 'FOOD' to avoid confusion

Solution

  1. Step 1: Add all new labels before training

    Adding both 'FOOD' and 'DRINK' labels upfront ensures model knows what to learn.
  2. Step 2: Provide balanced training data and train iteratively

    Balanced examples for both labels and multiple training loops help model learn both well.
  3. Final Answer:

    Add both labels with ner.add_label(), include balanced training examples for each, and train in multiple iterations -> Option A
  4. Quick Check:

    All labels + balanced data + training = Add both labels with ner.add_label(), include balanced training examples for each, and train in multiple iterations [OK]
Hint: Add all labels and balanced data before training [OK]
Common Mistakes:
  • Adding labels one by one with separate training
  • Skipping label addition
  • Training with unbalanced or missing examples