BIO Tagging › The BIO Scheme

Michael BrenndoerferUpdated January 3, 202610 min read
Language AI HandbookAll chapters
Part 6 of 67Sequence Labeling
Chapter 38BIO Tagging › The BIO Scheme
Reading section 2 of 14 · The BIO Scheme · 10 minSection 2 of 14 · 10 min

Book contents

Section 2 of 14 · 10 min read

BIO Tagging › The BIO SchemeOne section · Sections in this chapter2 / 14 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

BIO tagging encodes entity boundaries through prefix annotations. Each token receives a label combining a position indicator (B, I, or O) with an entity type. The three positions work together to delimit entity spans without ambiguity.

BIO Tagging Scheme

BIO (Beginning-Inside-Outside) is a tagging scheme for sequence labeling where each token receives a label indicating its position relative to entity spans. B marks the first token of an entity, I marks subsequent tokens within the same entity, and O marks tokens outside any entity.

The design is minimal but complete. You need exactly three pieces of information to reconstruct any flat (non-overlapping) entity annotation: where does the entity start, where does it continue, and where is there no entity. B, I, and O capture precisely these three cases. Any more prefixes would be redundant; any fewer would lose boundary information.

The scheme originated in work on text chunking and part-of-speech tagging before NER, where researchers needed a way to identify multi-word phrases using per-word labels. When NER became a major shared task at conferences like CoNLL in the early 2000s, BIO tagging was adopted as the standard label format because annotators, training pipelines, and evaluation scripts could all work with the same consistent representation. The CoNLL-2003 dataset, which remains a standard English NER benchmark, uses BIO tags over four entity types (PER, LOC, ORG, MISC) and set the template that most NER datasets have followed since.

Let's see how BIO tagging works on a concrete example:

In[3]:
Code
# A sentence with named entities
sentence = ["Barack", "Obama", "visited", "New", "York", "City", "yesterday"]

# Entity spans as (start_idx, end_idx, entity_type)
# Note: end_idx is exclusive (Python convention)
entities = [
    (0, 2, "PER"),  # Barack Obama
    (3, 6, "LOC"),  # New York City
]

# BIO tags for each token
bio_tags = ["B-PER", "I-PER", "O", "B-LOC", "I-LOC", "I-LOC", "O"]

meanings = {
    "B-PER": "Begin person entity",
    "I-PER": "Inside person entity",
    "B-LOC": "Begin location entity",
    "I-LOC": "Inside location entity",
    "O": "Outside any entity",
}
Out[4]:
Console
Token-level BIO Annotation:
----------------------------------------
Token        BIO Tag    Meaning
----------------------------------------
Barack       B-PER      Begin person entity
Obama        I-PER      Inside person entity
visited      O          Outside any entity
New          B-LOC      Begin location entity
York         I-LOC      Inside location entity
City         I-LOC      Inside location entity
yesterday    O          Outside any entity

The output shows how each token receives exactly one label. "Barack" gets B-PER because it is the first token of the person entity. "Obama" gets I-PER because it continues the same entity. "visited" gets O because it is not part of any entity. "New" starts a fresh entity with B-LOC, and "York" and "City" continue it with I-LOC.

Out[5]:
Visualization
Horizontal sequence of token boxes and colored BIO tag boxes with entity span brackets below.
BIO tagging applied to a seven-token sentence containing two named entities. Token boxes (top row) appear above their BIO tag labels (bottom row, colored). Blue boxes mark person entity tokens (Barack Obama), green boxes mark location entity tokens (New York City), and gray boxes mark non-entity tokens. Brackets below show the reconstructed entity spans.

The BIO scheme achieves two critical goals. First, it marks entity boundaries explicitly. When you see a B tag, you know a new entity starts at that position. When you see an I tag following a B tag of the same type, you know the entity continues. Second, it handles adjacent entities correctly. If "Barack Obama" and "Michelle Obama" appeared consecutively without a gap, the B prefix on "Michelle" would clearly mark the second entity's start, producing B-PER I-PER B-PER I-PER.

It is worth pausing to appreciate why this property matters so much in practice. Annotators frequently tag lists of proper nouns: "John Smith, Jane Doe, and Robert Johnson attended the summit." Without the B marker, a reading of consecutive PER tokens would be ambiguous about how many people are named. With BIO, each B-PER resets the entity counter, so even a list of ten names is perfectly unambiguous. The same logic applies to location lists in news text ("Paris, Berlin, and London"), organization lists in financial documents, and product names in e-commerce text.

Why Not Just Use Entity Types?

A simpler approach might label each token with just its entity type: PER, LOC, or O. Let's see why this fails.

In[6]:
Code
# Two consecutive person entities
sentence_adjacent = ["Barack", "Obama", "Michelle", "Obama", "attended"]

# With simple entity-type labels (no B/I distinction)
simple_labels = ["PER", "PER", "PER", "PER", "O"]

# With BIO labels
bio_labels = ["B-PER", "I-PER", "B-PER", "I-PER", "O"]
Out[7]:
Console
Adjacent Entities Problem:
-------------------------------------------------------
Token        Simple     BIO       
-------------------------------------------------------
Barack       PER        B-PER     
Obama        PER        I-PER     
Michelle     PER        B-PER     
Obama        PER        I-PER     
attended     O          O         

Simple labels: 1 entity 'Barack Obama Michelle Obama'
BIO labels:    2 entities 'Barack Obama' and 'Michelle Obama'

Without the B prefix, there is no way to determine where one entity ends and another begins. The simple scheme makes adjacent same-type entities indistinguishable from single multi-token entities. Real text contains many such cases: lists of names, multiple locations in a sentence, consecutive organization mentions. BIO tagging handles all of them correctly.

This failure mode is not theoretical. Consider sports reporting: "Federer Djokovic Nadal will all compete." Or financial news: "Microsoft Google Apple reported earnings." Labeling these with simple type tags produces one enormous entity rather than three distinct ones. Any downstream application that counts entities, links them to databases, or resolves coreferences would produce completely wrong results from the simpler scheme.

The deeper reason BIO works is that it encodes both the type of each token and its structural role within an entity. The structural information, whether a token is the start, the interior, or entirely outside, is independent of the type information and adds a structural dimension that flat type labels cannot provide.

The O Tag

The O tag marks tokens that don't belong to any entity. It carries no suffix because "outside" is the only interpretation, there is no entity type to specify. In typical NER datasets, O tokens vastly outnumber entity tokens since most words in a sentence are not named entities:

In[8]:
Code
from collections import Counter

example_text = [
    "The",
    "president",
    "of",
    "the",
    "United",
    "States",
    "met",
    "with",
    "Angela",
    "Merkel",
    "in",
    "Berlin",
    ".",
]

example_bio = [
    "O",
    "O",
    "O",
    "O",
    "B-LOC",
    "I-LOC",
    "O",
    "O",
    "B-PER",
    "I-PER",
    "O",
    "B-LOC",
    "O",
]

tag_counts = Counter(example_bio)
total = len(example_bio)
Out[9]:
Console
Tag Distribution in Sample Sentence:
----------------------------------------
O       :  8 tokens ( 61.5%)
B-LOC   :  2 tokens ( 15.4%)
I-LOC   :  1 tokens (  7.7%)
B-PER   :  1 tokens (  7.7%)
I-PER   :  1 tokens (  7.7%)

Entity tokens: 5/13 (38.5%)
Outside tokens: 8/13 (61.5%)
Out[10]:
Visualization
Bar chart showing tag frequency counts with O tag having the highest count and entity tags having 1-2 tokens each.
Tag frequency distribution for a sample sentence, illustrating the class imbalance typical in NER datasets. The O (outside) tag accounts for the majority of tokens, while B and I entity tags are relatively rare. This imbalance challenges training algorithms and motivates strategies like weighted loss functions or focal loss.

This class imbalance, where O tokens dominate, is characteristic of sequence labeling tasks. Training algorithms must account for it, often through weighted loss functions or sampling strategies. A baseline model that always predicts O achieves deceptively high accuracy but zero utility, since it never identifies any entity.

The imbalance is especially severe for fine-grained entity types. In a dataset with 18 entity types, each individual type might account for only one or two percent of all tokens. If you train a model that treats every token prediction independently with equal loss weights, the model learns that the safest policy is to predict O for everything, since the loss from missing an entity is small compared to the aggregate loss from misclassifying the overwhelming majority of non-entity tokens. Weighted cross-entropy, focal loss, and oversampling entity-bearing sentences all address this, but the fundamental imbalance is a structural property of language: most words in any document are connective tissue rather than named referents.