Entity Linking and Knowledge Graph Construction

Michael BrenndoerferMay 12, 202570 min read

Link text mentions to knowledge-base entities, rank candidates with context and document coherence, and build a graph whose assertions retain source evidence.

Language AI HandbookAll chapters
Part 8 of 67Semantics and Information Extraction
Chapter 54Entity Linking and Knowledge Graph Construction
Reading the full chapter · 17 sections · 70 min17 sections · 70 min

All 17 sections · 70 min read

Entity Linking and Knowledge Graph ConstructionFull chapter · Sections in this chapter1 / 17 ↓
Reading tools Definitions, links & notes

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

Notes & comments

Entity Linking and Knowledge Graph Construction

A named entity recognizer might label Jordan as a person or a location. The tag alone does not identify the referent. To join the mention to a database record, you still need to decide whether it refers to Michael Jordan, the country, or another entity. Choosing the wrong record can attach otherwise correctly extracted facts to the wrong subject.

Recognition gives you a text span; linking assigns a knowledge-base identity. Strings can already be searched and joined, but equality of strings is an unreliable substitute for equality of referents. With correct links and distinct catalog IDs, different surface forms can contribute to the same entity record while identically spelled names for different entities remain separate.

Entity linking (EL) assigns text mentions to entries in a chosen knowledge base (KB). A linker can target Wikipedia articles, Wikidata items, biomedical concepts, or records in a private catalog. In this chapter, each catalog entry has an identifier and a description; aliases and graph relations provide additional evidence when available. These fields are useful design choices, rather than requirements that every knowledge base satisfies.

Knowledge graph construction (KGC) combines identity decisions with relation extraction and data management. Linking identifies the arguments. Relation extraction also needs a predicate, such as orbits in (mercury_planet, orbits, sun). A model can predict the arguments and predicate jointly or in separate stages. A construction workflow can validate candidates and resolve duplicate records under application rules while retaining supporting evidence. Provenance makes an assertion traceable; it does not establish that the source or the extractor was correct.

We study linking and construction together because their errors can propagate. A wrong linked argument can produce a wrong graph edge. In the other direction, graph compatibility can help disambiguate a mention, and an application constraint can flag an incompatible proposed edge. Such checks depend on the quality of the graph and the chosen validation rules.

Named entity recognition and BIO tagging from Named Entity Recognition and BIO Tagging provide a starting point for finding mention spans. Relation and event extraction from the preceding chapter provides candidate predicates and arguments. We reuse TF-IDF to explain context matching, then add entity identity and graph compatibility. The worked example compares a popularity baseline, local scoring, and joint scoring before building an inspectable assertion catalog.

The Entity Linking Task

Formally, let a document dd contain annotated entity mentions M={m1,…,mn}M = \{m_1, \dots, m_n\}. A mention has a half-open character span [starti,endi)[start_i,end_i) and a surface form surfisurf_i. Given a fixed KB entity inventory E\mathcal{E}, the target assignment is a total function Φ:M→E∪{NIL}\Phi: M \to \mathcal{E} \cup \{\text{NIL}\}. Each mention receives one entity identifier or the label NIL.

NIL means that the referent has no matching entry in the specified KB snapshot. It is distinct from uncertainty: a mention may have a KB referent even when the model cannot choose among candidates. An application may abstain and send such a case for review without claiming that the referent is absent. A nonentity span is a mention-detection error and should be rejected under the annotation policy, rather than automatically labeled NIL.

Three things make this hard.

Name variation. One entity can have several names or abbreviations. A catalog might record Apple Inc., Apple, and a translated name as aliases of the same entry. The exact accepted aliases depend on the catalog and the task. Storing only a canonical title misses any alternative spelling that exact lookup cannot normalize to that title.

Name collision. A string such as Mercury can name different entities. Normalization that helps retrieve spelling variants can also collapse distinctions between names. A string match can retrieve candidates. When the string is ambiguous, use context or other evidence before accepting a link, or abstain.

Unlinkable mentions. A document may refer to a newly created organization or another entity outside the KB. The proportion of these mentions depends on the corpus and KB snapshot. Evaluation must include them when the intended deployment permits out-of-KB referents.

Entity Linking

Given entity mentions and a fixed knowledge base, entity disambiguation selects the matching KB identifier or NIL. End-to-end entity linking also detects the mention spans. Candidate entities need not share the exact surface string: descriptions or other aliases may retrieve them. The task's annotation policy determines which named, nominal, or nested mentions count as targets.

For this chapter, group the decisions into four stages so their errors can be evaluated separately:

  1. Mention detection: find the spans that refer to entities.
  2. Candidate generation: propose a small set of KB entities that the mention could refer to.
  3. Disambiguation (ranking): score candidates and pick one.
  4. NIL prediction and acceptance policy: distinguish an out-of-KB referent from a link that the application will not accept.

The stages have different error sets. Detection determines which spans reach the linker, retrieval determines which IDs a restricted reranker can choose, and ranking orders those IDs. NIL prediction concerns KB absence; an acceptance policy may also abstain on an uncertain in-KB case. Measure the stages separately to identify which decisions an improved component could repair.

For a reranker restricted to a retrieved candidate list, a missing gold entity cannot be selected. On a fixed set of gold in-KB mentions, linking accuracy is therefore no greater than candidate recall@k. Every correct prediction requires the gold entity to have been retrieved, so the inequality follows by counting those cases. This bound does not directly cover mention-detection F1, NIL classification, or systems that generate additional candidates after retrieval. Measure these stages separately.

Inspecting Alias Ambiguity

An alias dictionary can reveal ambiguity before a model is trained: inspect how many entities each string retrieves and how its anchor counts are distributed. Frequency and ambiguity are different properties. A rare name may have few training examples and still collide with another entity; a frequent name may have one dominant referent. Analyze both the number of candidates and the amount of supervision for the target entity.

The most frequent sense (MFS) baseline chooses the entity with the largest training-corpus anchor count for an alias. It exposes how much accuracy a system obtains from the alias prior alone. Its strength depends on the distribution of senses in the test set. In the toy table below, the company receives more apple counts than the fruit; those numbers are invented, not a measurement of Wikipedia anchors.

MFS is wrong whenever the current referent is outside the set of highest-count candidates. Words such as metal can support the element reading of Mercury, while Chicago Bulls can support Michael Jordan. Domain labels alone do not determine either answer: a chemistry article can discuss a planet, and a sports article can discuss a country. This is the same tension between a corpus prior and local evidence discussed in Lexical Semantics and Word Senses.

The prior estimates alias usage in its reference corpus; context supplies evidence about this occurrence. Neither establishes truth by itself. Compare a prior-only baseline with contextual scoring on development data from the intended domain, and inspect errors on rare entities separately. Botha et al. (2020) report challenges for rare entities and low-resource languages in their multilingual setting.

Mention Detection for Linking

An NER model from Named Entity Recognition can supply candidate spans, but its annotation inventory may differ from the linking task's inventory. A linker that must identify nominal concepts, for example, needs spans that a proper-name tagger may never propose. Check the target annotation policy before reusing the detector unchanged.

The desired mention inventory depends on the linking task. It may include:

  • Nominal mentions: "the company", "the tech giant", "the city".
  • Nested and overlapping spans: in Bank of America Tower, the building and the bank are different entities.
  • Targets outside a particular detector's inventory, possibly including works of art, laws, proteins, chemical compounds, or events.

For example, resolving "the company" can require discourse information beyond a name dictionary. The string Bank of America Tower contains an organization name, but whether to annotate both spans is a dataset decision, not an automatic requirement. Biomedical concept linking can use a different inventory again; MedMentions annotates mentions of UMLS concepts in biomedical abstracts (Mohan and Li, 2019).

One design is to propose spans at high recall and use later scoring to reject unsupported proposals. Scoring more proposals adds work when cost per proposal is fixed and positive; high detection recall alone does not guarantee precision after filtering. Keep two rejection reasons separate: a span may not be an entity mention at all, or it may be a valid mention whose referent is outside the KB. Evaluate span rejection and NIL handling according to the task's annotation rules.

Joint training offers another design: use mention-entity compatibility to help decide which spans to retain. Kolitsas et al. (2018) learn scores useful to both detection and disambiguation. A separate detector can also use KB-aware features; the distinction is whether its decisions are trained jointly with the linker, not whether it can access such evidence at all.

Anchor Dictionary

An anchor dictionary maps a surface string to entity candidates and counts of observed links. A Wikipedia hyperlink supplies an anchor string and a target article; using a Wikidata ID such as Q308 additionally requires resolving the article target to the corresponding item. The counts estimate alias usage in the collected corpus, rather than an entity's probability in every future domain.

In a dictionary-based linker, the table provides both candidates and a prior. Record the corpus and KB versions used to build it, resolve redirects consistently, and choose normalization rules that match the task. A dictionary is one candidate-generation design; description-based retrieval can operate without an anchor table, as in the zero-shot setting of Logeswaran et al. (2019).

Candidate Generation

Candidate generation is a retrieval problem. Given a mention and its context, return up to kk plausible KB entities. Large inventories make the retrieval design consequential: the BLINK study evaluates retrieval over 5.9 million candidate entities (Wu et al., 2020). A cheap retrieval stage can reduce how many mention-description pairs an expensive reranker must evaluate. Those paper-specific counts are not current catalog-size claims.

If the reranker scores each returned candidate once, each additional candidate adds one pair evaluation. With fixed positive cost per pair, it also adds that scoring cost. For a fixed ranked retrieval result, enlarging kk cannot remove a retrieved gold entity, so recall@k is nondecreasing. It need not reach one: some referents may remain unretrieved at the tested depths. Select kk by measuring retrieval recall, downstream accuracy, and latency rather than assuming a particular list size is sufficient.

Dictionary-based generation

The classic approach builds an alias table from Wikipedia anchor text. For each surface string mm, you store the set of entities it has been used to link to, along with counts. At inference time, you look up the normalized surface form and get candidates plus the prior

P(e∣m)=count(m→e)∑e′∈C(m)count(m→e′)P(e \mid m) = \frac{\text{count}(m \to e)}{\sum_{e' \in \mathcal{C}(m)} \text{count}(m \to e')}

where:

  • P(e∣m)P(e \mid m): the fraction of recorded reference-corpus links with normalized alias mm whose target is ee
  • count(m→e)\text{count}(m \to e): how many times the alias table records the surface string mm being linked to entity ee
  • C(m)\mathcal{C}(m): the candidate set for mm, all entities mm has been linked to
  • ∑e′∈C(m)count(m→e′)\sum_{e' \in \mathcal{C}(m)} \text{count}(m \to e'): the total link count over all candidates, the normalizing constant

Here mm denotes a normalized alias string, rather than the entire occurrence with its context. The denominator must be positive. We retain only positive-count candidates in this example, so their priors sum to one and the logarithms used later are defined. An unseen alias needs another retrieval route or an explicit fallback; a zero denominator is not a probability distribution.

Exact dictionary lookup is limited to the stored keys and candidates. Potential gaps include:

  • Unseen aliases: a nickname, abbreviation, or translation absent from the collected dictionary.
  • Case and morphology: APPLE, apples, Apple's.
  • New entities: anything added to the KB after the dictionary was built.

If a shorthand has no dictionary key and normalization does not map it to one, exact lookup returns no candidates. Case and possessive handling likewise depend on the lookup rules: matching Apple's to apple needs an appropriate normalization or matching policy. A fixed table cannot return an entity missing from that table, even if a newer KB snapshot contains it.

The normalization rules from Text Normalization can reduce some spelling variation. Character n-gram retrieval is another option: overlapping character sequences can give similar spellings a nonzero retrieval score. This differs from the learned merge rules in Byte Pair Encoding. Measure whether approximate string matching retrieves useful variants and how many unrelated names it adds; spelling similarity alone does not establish identity.

Dense retrieval generation

A learned retriever can replace or augment the dictionary by mapping mention contexts and entity descriptions into a shared vector space. Similarity search then permits retrieval without an exact name match. This representation can connect different wording, although whether it retrieves the intended entity is an empirical question. The same retrieval pattern appears later in Retrieval-Augmented Generation.

BLINK (Wu et al., 2020) uses a bi-encoder to represent mention context and entity descriptions separately. Entity vectors can be precomputed, and an inner-product search retrieves candidates for a newly encoded mention. A cross-encoder then jointly encodes the mention and each retrieved description for reranking. The paper evaluates the resulting accuracy-speed tradeoff; a cross-encoder's advantage on another corpus must still be measured.

Description-based linking can represent an entity that has no labeled mention examples in linker training. In this bi-encoder design, encode its description and add the vector to the entity index; that can happen before queries arrive. This permits index updates without learning a new independent classifier weight for every entity. It does not guarantee retrieval: descriptions, language coverage, and domain shift still affect the match.

Generative generation

GENRE (De Cao et al., 2021) performs autoregressive entity retrieval: it generates an indexed entity name token by token, conditioned on the context. A trie can constrain continuations to allowed entity names. With correct termination constraints and a complete name-to-ID mapping, a completed output belongs to that index. It can still identify the wrong entity. The trie can cover the full name index or a supplied candidate subset, as in the paper's disambiguation experiments. Searching the full index avoids a separate retrieved shortlist, but remains limited by index coverage, the learned scores, and approximate decoding search.

Autoregressive decoding makes successive token decisions. Bi-encoder retrieval instead encodes the mention and searches a precomputed entity index. Their total latency also depends on batching, hardware, index search, name length, and decoding width. A valid constrained name is a structural guarantee, not a guarantee of better accuracy or of lower latency than another architecture.

Ranking: The Local Signal

The local scorer below combines the alias prior with context-description similarity. The prior summarizes the reference corpus's linked uses of that alias. Context can distinguish candidates when their descriptions overlap differently with the surrounding words, but it may provide little useful evidence. Neither feature guarantees the correct referent. Their contributions depend on the scoring rule and its weights.

The prior

P(e∣m)P(e \mid m) estimates how the reference corpus uses an alias. It favors whichever candidates receive more counts in that corpus, which need not be the intended referent in a new domain. A company-name prior estimated from technology reporting, for example, might differ from one estimated from a supplier catalog. Neither source establishes which reading fits a particular sentence.

Keep the prior-only argmax as a baseline. Adding context is useful only if it improves the decisions the application needs, so compare the combined ranker against that baseline on representative labeled examples.

Context similarity

Context can supply disambiguating evidence. Mercury orbits the Sun every 88 days shares orbits, 88, and days with the planet description below, whereas those tokens are absent from the element description. This overlap can favor the planet without the sentence saying "planet" or "element." Tokenization can change the similarity. Feature weighting can also change it by shifting a vector's direction. The prior contributes to the local score, and other candidates can change the ranking.

The classical implementation is a bag-of-words comparison. Represent the mention context (a window of surrounding tokens, or the whole sentence, or the whole document) as a sparse vector, represent each candidate entity by its KB description or by the text of its Wikipedia article, and take cosine similarity:

cos⁡(cm,de)=cm⋅de∥cm∥ ∥de∥\cos(\mathbf{c}_m, \mathbf{d}_e) = \frac{\mathbf{c}_m \cdot \mathbf{d}_e}{\lVert \mathbf{c}_m \rVert \, \lVert \mathbf{d}_e \rVert}

where:

  • cm\mathbf{c}_m: the context vector for mention mm (for example, term counts or TF-IDF weights over the surrounding window)
  • de\mathbf{d}_e: the description vector for candidate entity ee, built the same way from its KB description or article text
  • cm⋅de\mathbf{c}_m \cdot \mathbf{d}_e: the dot product, summing the weighted overlap of shared terms
  • ∥cm∥\lVert \mathbf{c}_m \rVert and ∥de∥\lVert \mathbf{d}_e \rVert: the Euclidean norms of the two vectors, normalizing away length differences

The vectors can use raw counts or TF-IDF weights. For nonzero real vectors, cosine lies in [−1,1][-1,1]; for the nonnegative count vectors here it lies in [0,1][0,1]. The displayed ratio is undefined for a zero vector, so the code adopts a similarity of zero when either set is empty. IDF gives greater weight to terms found in fewer documents, but a rare word is not automatically relevant evidence for this mention. A term occurring in every description can still differ in frequency or normalized weight; ubiquity alone does not establish that every scoring scheme ignores it.

Explicit Semantic Analysis (Gabrilovich and Markovitch, 2007) represents text using Wikipedia-derived concepts rather than only word dimensions. A shared concept dimension can connect texts without a shared surface word. Applying that representation to linking would still require constructing the concept associations and evaluating the resulting mention-entity score.

Yamada et al. (2016) jointly embed words and entities in one vector space. Description-based bi-encoders and cross-encoders provide other ways to score mention-entity compatibility. A cross-encoder processes each pair jointly, while a bi-encoder can reuse separately encoded entity vectors. Pair evaluations can be batched; the relevant cost is the amount of pair computation, not a claim that each pair must occupy its own unbatched model call.

Putting them together

We use the following illustrative local score:

s(e,m)=αlog⁡P(e∣m)+βcos⁡(cm,de)s(e, m) = \alpha \log P(e \mid m) + \beta \cos(\mathbf{c}_m, \mathbf{d}_e)

where:

  • s(e,m)s(e, m): the local score of candidate entity ee for mention mm
  • P(e∣m)P(e \mid m): the empirical probability of target ee among recorded links with normalized alias a(m)a(m) in the reference corpus. In this score, P(e∣m)P(e \mid m) abbreviates P(e∣a(m))P(e \mid a(m)) for mention occurrence mm
  • cm\mathbf{c}_m: a vector representation of the mention context (surrounding window, sentence, or document)
  • de\mathbf{d}_e: a vector representation of the candidate entity's description or article text
  • cos⁡(cm,de)\cos(\mathbf{c}_m, \mathbf{d}_e): the cosine similarity between context and entity description
  • α\alpha: weight on the log prior probability of the candidate entity given the alias in the reference corpus
  • β\beta: weight on the context-similarity (local evidence) term

We restrict α\alpha and β\beta to finite nonnegative values. For an evaluated system, select their values on development data; the toy uses hand-chosen weights to expose the mechanics. The weights depend on feature scale as well as usefulness. For positive priors, the difference between their contributions is αlog⁡(P(e1∣m)/P(e2∣m))\alpha\log(P(e_1\mid m)/P(e_2\mid m)). The logarithm log⁡p\log p tends to negative infinity as pp approaches zero from above. The weighted contribution αlog⁡p\alpha\log p has that limit when α>0\alpha>0; with α=0\alpha=0 it is zero for every positive pp. Logarithms do not by themselves prevent popularity from dominating context. The toy candidates all have positive priors.

Other features can include type compatibility, name similarity, and graph proximity to already linked entities. If a mention is known to denote a person under the annotation policy, a country candidate is incompatible. A predicted NER type can itself be wrong, however, and metonymic uses depend on the annotation convention. Use type predictions as fallible evidence, or justify any hard constraint with the task's rules.

The weighted local score is a ranking quantity. Its value of 0.9 would not imply a 90 percent chance of correctness. Interpreting a score probabilistically requires a suitable calibration procedure and validation on the relevant distribution. Margins can support an acceptance policy, but need their own evaluation.

The Coherence Signal

Independent local decisions can miss evidence available elsewhere in a document. Once Chicago Bulls is linked to the basketball team, its connection to Michael Jordan can help rank an ambiguous Jordan mention. This is a useful signal, not a universal topical constraint: documents can compare unrelated people, countries, products, or events.

Collective entity linking scores combinations of assignments rather than choosing every entity independently. Ratinov et al. (2011) study local and global disambiguation, and Hoffart et al. (2011) combine mention-entity compatibility with coherence among entities. We use a small pairwise objective to inspect that idea. Whether coherence improves accuracy depends on the data, the compatibility measure, and its weight.

The Milne-Witten relatedness measure

Milne and Witten (2008) use Wikipedia link structure to compare entities. The same shared-inlink count can represent very different degrees of overlap for small and large inlink sets. The following normalized link-overlap score accounts for those sizes; we explicitly distinguish its distance form from the compatibility reward used in our maximization problem.

Let WW be the article universe, with more than one article, and let AA and BB be the sets of articles linking to entities aa and bb. For nonempty overlap and min⁡(∣A∣,∣B∣)<∣W∣\min(|A|,|B|)<|W|, first compute a normalized link distance and then turn it into a bounded compatibility reward:

D(a,b)=log⁡max⁡(∣A∣,∣B∣)−log⁡∣A∩B∣log⁡∣W∣−log⁡min⁡(∣A∣,∣B∣),r(a,b)=max⁡{0,1−D(a,b)}.\begin{aligned} D(a,b) &= \frac{\log \max(|A|,|B|)-\log |A\cap B|}{\log |W|-\log \min(|A|,|B|)}, \\ r(a,b) &= \max\{0,1-D(a,b)\}. \end{aligned}

where:

  • WW: the article universe used by the link graph
  • AA and BB: articles linking to aa and bb, respectively
  • ∣A∩B∣|A\cap B|: their number of shared inlink articles
  • D(a,b)D(a,b): a distance, with smaller values indicating greater overlap at fixed marginal counts
  • r(a,b)r(a,b): the clipped compatibility reward used below

We use natural logarithms; any common valid logarithm base gives the same ratio. Identical nonempty proper subsets give D=0D=0 and r=1r=1. At fixed ∣A∣|A|, ∣B∣|B|, and ∣W∣|W|, more shared articles decrease the distance. The clipped reward cannot decrease, but can remain zero while D≥1D\geq1. This normalization is not a probability or a statistical test of independence.

We explicitly return r=0r=0 when either inlink set is empty or the sets are disjoint. If both sets equal WW, the ratio has a zero denominator; the toy implementation returns r=1r=1 for that identical-set case. Clamping 1−D1-D below at zero is an implementation convention because the unbounded distance can exceed one. These boundary policies make the score defined on every pair in our small graph.

Collective disambiguation

Let C(mi)\mathcal{C}(m_i) be the finite, nonempty candidate set for each mention mim_i included in the joint assignment. Apply a retrieval fallback or abstain on empty candidate lists before forming the product. With finite scores, the linking objective is a global optimization over those mentions.

e^∈arg⁡max⁡e∈C(m1)×⋯×C(mn)[∑i=1ns(ei,mi)+γ∑i<jr(ei,ej)]\hat{\mathbf{e}} \in \arg\max_{\mathbf{e} \in \mathcal{C}(m_1) \times \cdots \times \mathcal{C}(m_n)} \left[ \sum_{i=1}^{n} s(e_i, m_i) + \gamma \sum_{i < j} r(e_i, e_j) \right]

where:

  • e^\hat{\mathbf{e}}: the joint assignment of one entity to each mention, i.e. a tuple (e1,…,en)∈C(m1)×⋯×C(mn)(e_1, \dots, e_n) \in \mathcal{C}(m_1) \times \cdots \times \mathcal{C}(m_n)
  • C(mi)\mathcal{C}(m_i): the candidate entity set for mention mim_i
  • s(ei,mi)s(e_i, m_i): the local score of entity eie_i for mention mim_i (prior plus context similarity)
  • r(ei,ej)r(e_i, e_j): the pairwise relatedness between entities eie_i and eje_j (Milne-Witten, defined above)
  • γ≥0\gamma\geq0: a finite weight on the pairwise coherence term, trading off document coherence against local evidence

This is a structured prediction score with unary terms ss and pairwise terms γr\gamma r. When every candidate set is nonempty and all scores are finite, exponentiating the total score and normalizing over the finite Cartesian product defines a conditional distribution. Its maximum-probability assignments are exactly the highest-scoring assignments. In that interpretation the displayed terms are log-potentials. The factorization includes every mention pair, unlike the linear-chain model in Conditional Random Fields.

For arbitrary fully connected pairwise terms, the linear-chain decomposition used by the Viterbi Algorithm does not directly solve this objective. Exhaustive search considers the full Cartesian product of candidate sets. Special parameter choices can be simpler, including γ=0\gamma=0 below. Options for the general case include:

  • Exhaustive search when the candidate-set product is small enough that you can score every assignment.
  • Integer linear programming with one binary choice variable per (mention, candidate), one-of-kik_i constraints for each mention mim_i with ki=∣C(mi)∣k_i = |\mathcal{C}(m_i)|, and auxiliary variables or equivalent constraints for pairwise products. These are assignment constraints, not transitivity constraints between mentions. AIDA's graph algorithm instead searches for a dense subgraph (Hoffart et al., 2011).
  • Approximate search with loopy max-product belief propagation, or with Gibbs sampling from the score-induced distribution and selection of the highest-scoring complete assignment visited.
  • Greedy sequential refinement: visit each mention, test its candidates while holding the other assignments fixed, and accept the best change only if it strictly improves the score. Repeat until a complete pass makes no change. On a finite candidate space, strict improvements cannot revisit an assignment. The resulting assignment has no improving single-mention move, but need not be the global optimum.

At γ=0\gamma=0, the objective decomposes into independent local choices, provided each candidate set is nonempty. Increasing γ\gamma gives compatibility more influence and can overturn local evidence, including a correct local decision. There is no guarantee that a news article benefits from a larger value than another document. Select the weight on held-out development data and inspect mixed-topic documents for erroneous pressure toward one topic.

Collective Entity Linking

In the pairwise design here, collective entity linking adds compatibility rewards to the local scores and chooses assignments jointly. The compatibility bonus can favor an assignment that differs from the independent local choices. Whether it outweighs a local-score gap depends on γ\gamma and the compatibility values; topical consistency alone does not establish the correct referents.

Our objective permits coherence to override local evidence; it does not enforce a tie-breaker-only policy. A different design could gate that contribution by a confidence criterion, but such a gate would need its own definition and validation. Keep this distinction visible when interpreting a successful toy example: a useful compatibility bonus can also propagate a wrong neighboring assignment.

Neural and End-to-End Approaches

The stage decomposition helps diagnose errors, but it does not require separate learned representations. Components may share an encoder or be trained jointly. Joint systems can use linking evidence when deciding whether a span is a mention, while a modular design permits separate replacement or evaluation of its components.

Joint mention detection and linking

Kolitsas et al. (2018) jointly learn mention detection and disambiguation scores for candidate span-entity pairs. ELQ (Li et al., 2020) uses a bi-encoder and predicts mention starts, ends, and token membership from question representations. Its mention representations are compared with entity vectors. The paper's one-pass description concerns the input question's BERT encoding; entity encoding and index search are separate parts of the computation. Sharing that question representation permits detection and linking to use the same contextual evidence. Evaluate total latency rather than inferring it from the number of question-encoder passes alone.

Joint mention detection does not by itself require a fixed entity inventory. The update procedure depends on the entity representation. A model with entity-specific learned parameters may need training changes, whereas a description-based model may encode new descriptions and update its index. Distinguish model retraining from index rebuilding when comparing maintenance costs.

Zero-shot and description-based linking

Zero-shot entity linking evaluates entities without labeled training mentions in the specified split. Description-based matching gives the model information about a new entity without requiring a learned class weight for its ID. State what was held out: entities, domains, languages, and mention strings are different generalization tests. This use of "zero-shot" also differs from prompting an LLM without task demonstrations.

ReFinED (Ayoola et al., 2022) combines mention detection, fine-grained entity typing, and disambiguation in one document forward pass, using entity types and descriptions. Its reported efficiency and accuracy apply to the paper's benchmarks and configurations. This is an example of sharing computation across predicted mentions, not a guarantee that every target mention in a new document will be detected correctly.

ZESHEL (Logeswaran et al., 2019) evaluates linking to unseen entities in held-out domains using entity descriptions, without in-domain labeled training data or alias metadata in its task formulation. It tests a relevant kind of transfer, but does not establish performance on an arbitrary private or continually changing KB. A deployment evaluation should reproduce the intended languages, entity descriptions, mention inventory, and unknown-entity rate.

Multilingual linking

Botha et al. (2020) use a dual encoder for mentions in more than 100 languages, linking to a language-agnostic KB. This is distinct from mGENRE, the autoregressive multilingual system of De Cao et al. (2022). mGENRE predicts entity names and considers names in multiple languages, rather than fixing every target name to one canonical language. Both approaches separate the entity's identity from the language of a particular mention or name. The outline for Multilingual Language Models and Cross-Lingual Transfer lists shared representations under Cross-Lingual Transfer and Alignment.

Multilingual linking adds possible gaps in aliases, descriptions, and training supervision across languages. A cross-lingual representation may match a mention to a description in another language, but that ability must be evaluated. A missing description alone does not establish that the entity is absent from the KB: another retrieval route may find it, or the system may abstain.

LLM-based linking

An LLM can also be placed after retrieval: supply the mention, its context, and candidate IDs with descriptions, then request one ID or an abstention action. This is a system design option, not an accuracy claim. Evaluate it against a smaller reranker and the prior-only baseline on the same candidate lists, with the same mention spans and KB snapshot.

Evaluate the following potential failure modes:

  • Identifier hallucination. An unconstrained output can contain an invented QID or title. Restrict the response to an allowed candidate ID or the designated abstention action. Check any returned ID against the chosen KB snapshot.
  • Popularity effects. Test whether the reranker improves rare-entity cases rather than relying on the familiar candidate name.
  • Prompt sensitivity. Evaluate several fixed candidate permutations and record whether the decisions change. Testing one fixed order does not test invariance to other orders.
  • Cost and latency. Measure total cost at the intended mention volume, including retrieval, batching, output validation, and any fallback calls.

Validate every returned ID against the allowed list and the chosen KB snapshot. This can reject an invented identifier; it cannot detect a semantically wrong choice that is nevertheless a valid candidate. Likewise, a fixed candidate order does not establish the absence of popularity or order bias. Keep identifier validity, referential correctness, and run-to-run variability as separate checks.

Unlinkable Mentions and Entity Typing

A closed-KB disambiguation benchmark may assume every gold mention has a KB referent. An open-KB task relaxes that assumption and needs NIL evaluation. Independently of the gold label, an application may abstain when evidence is insufficient. Report which setting the system and its metrics implement.

Possible abstention features include:

  • The top-ranked candidate’s score s(e∗,m)s(e^*, m).
  • The margin s(e∗,m)−s(e(2),m)s(e^*, m) - s(e^{(2)}, m) between the top two candidates.
  • The similarity assigned to the top-ranked candidate by the retriever, when the retriever supplies one.

These features indicate uncertainty under the scorer, not proof of KB absence. A low margin may mean two valid KB candidates are difficult to distinguish. An empty candidate list may mean an alias lookup failed for a known entity. To predict NIL itself, validate the decision rule against both in-KB and out-of-KB gold mentions. A threshold can shift when the corpus, candidate generator, or KB changes.

An acceptance policy should reflect error costs. If false accepted links are more costly than delayed assertions, abstention may be preferable to forcing a candidate. Rejecting a link that would otherwise be accepted lowers accepted coverage for a fixed mention set; it does not guarantee a correct remaining graph. Mislabeling an in-KB referent as NIL can also create duplicate local nodes, so record abstention separately when the evidence establishes uncertainty rather than absence.

Fine-grained entity typing predicts categories such as person/artist/musician, with the hierarchy determined by the chosen type inventory. ReFinED uses fine-grained typing jointly with linking. Such predictions can contribute compatibility evidence, but a predicted type should not be silently treated as a verified ontology constraint. Application validation rules must also specify how they handle missing or uncertain types.

Evaluating Entity Linking

Entity-linking scores are comparable only under the stated evaluation protocol. A change in target spans, KB inventory, or matching rule can change what an F1 score measures. Check these choices before comparing systems:

Mention-level versus document-level. Mention-level micro metrics pool the counted decisions across documents, so documents with more gold or predicted mentions can contribute more to the aggregate. A document-macro metric instead averages a stated per-document measure. An all-mentions-correct document score is another, stricter criterion; it is not the definition of macro averaging. Report the aggregation rule and the handling of documents with no target mentions.

Strong versus weak matching. In GERBIL's annotation-matching terminology, strong matching requires the same entity and exact mention boundaries. Weak matching permits overlapping spans while still requiring the same entity (Röder et al., 2018). This is separate from resolving equivalent IDs or redirects to one referent. A subclass or topically related entity is not the same entity merely because it is nearby in a taxonomy.

NIL conventions. State whether NIL mentions are evaluated, whether distinct NIL mentions must also be clustered by referent, and how false accepted links and abstentions enter each denominator. An in-KB linking score alone does not measure NIL detection. Include both kinds of gold mentions when evaluating an open-KB system, and report the accepted-link precision and coverage when an application allows abstention.

Mention boundary errors. If the gold span is Apple Inc. and the detector returns only Inc., strict annotation matching rejects the prediction even if the ID is correct. Weak matching may accept the overlap. Record the span rule, tokenization, and offset convention so that a boundary disagreement is not mistaken for a referent disagreement.

Benchmark examples. AIDA-CoNLL adds entity disambiguation annotations to CoNLL newswire material (Hoffart et al., 2011). In the convention used by Kolitsas et al. (2018), AIDA-A is development data and AIDA-B is test data; do not tune on the latter. MedMentions provides a different setting, with manually annotated UMLS concept mentions in biomedical abstracts (Mohan and Li, 2019). Record the exact release, target ID space, mention inventory, and split files instead of assuming a benchmark name uniquely identifies the experiment.

Contamination and overlap. A target entity's presence in the inference KB is normally required for an in-KB linking task and is not, by itself, train-test leakage. Distinguish labeled training mentions of that entity, exposure to its description, reuse of test text, and development choices made after seeing test labels. These are different forms of overlap. An entity-disjoint evaluation such as ZESHEL addresses one transfer setting; a fixed-KB benchmark addresses another. State the intended setting before interpreting either score.

KB versioning also affects reproduction. Between KB snapshots, identifiers may be redirected or deprecated, records may be merged, and a conflated record may be split into separately identified records. Retain the mapping and snapshot used for evaluation rather than silently resolving historical annotations against a different live catalog. We discuss evaluation design further in Part LIII: Evaluation Fundamentals.

Decompose errors into mention detection failures, candidate generation failures (gold entity absent from the list), and ranking failures (gold entity retrieved but a different ID ranks first before acceptance). Evaluate NIL predictions against KB membership and assess the acceptance policy separately. An application can abstain even when the gold ID ranks first. Changing only a candidate-constrained reranker cannot recover an excluded gold entity. A low aggregate F1 alone does not identify which stage needs changing.

Linked arguments give a relation extractor candidate endpoints; they do not turn a sentence into a verified fact. Graph construction adds choices about identity, relations, validation, and storage. Consistent IDs let assertions from different documents join, while source records let a consumer inspect how each assertion arose.

A graph construction project should decide which of the following it needs:

  • Nodes with stable identity. The entity mercury_planet must be one node, referenced consistently across documents, not a new node per mention.
  • Edges with a schema. This project can use a controlled relation vocabulary: orbits, is_capital_of, member_of. Free-text relation labels alone do not specify validation constraints or equivalence rules.
  • Types or a class hierarchy when the application needs type-aware retrieval or inference. A hierarchy alone does not infer geographic containment without the relevant relations and rules.
  • Provenance fields chosen for the application, such as a source document, supporting span, extractor version, and extraction time.
  • Temporal scope for relations that change, such as (X, ceo_of, Y), including a representation for unknown or open-ended bounds.
  • Confidence or evidence counts when they support review or fusion. Repeated copies of one source are not independent corroboration. A count alone does not establish which conflicting assertion is correct.

These choices support different operations. Stable identifiers support joins, but do not automatically prevent duplicates. A controlled relation vocabulary reduces predicate variation; application validation rules can detect incompatible records. Provenance supports tracing assertions, temporal scope supports date-aware queries, and scores can prioritize review. None of these is a guarantee of correctness, and a class hierarchy or a confidence field is not a defining requirement for every knowledge graph.

RDF and the property-graph alternative

Two data models worth distinguishing are RDF and property graphs.

An RDF 1.1 graph is a set of subject-predicate-object triples. Subjects can be IRIs or blank nodes; predicates are IRIs; objects can be IRIs, blank nodes, or literals. In the following illustrative triples all three positions use prefixed IRIs. Here ex: abbreviates the example namespace https://example.org/.

turtle
CollapseExpand
@prefix ex: <https://example.org/> .

ex:mercury_planet ex:orbits ex:sun .
ex:amman ex:is_capital_of ex:jordan_country .

An RDF dataset contains a default graph and zero or more named graphs (W3C RDF 1.1 Concepts, 2014). An application can group assertions by source or context using graph names, but must define what that grouping means. Statement-level metadata can instead use a statement resource, including RDF 1.1 reification. Reifying a triple describes it; it does not by itself assert the described triple. SPARQL provides standardized graph queries, including a federation extension, but we do not implement that query language in the toy store below.

Property graphs attach properties to vertices and edges, so an application can store source and score fields directly on an edge. They are not an area without standards: ISO/IEC 39075:2024 defines GQL data structures and operations for property graphs. An application still needs to choose its entity identifiers, source policy, and domain ontology. Interoperability in either model still requires agreement about those meanings.

Choose a representation according to the required interchange formats, query behavior, inference rules, and update workflow. RDF and a property graph can both carry provenance when it is modeled explicitly. A data-model label alone does not establish faster queries, better identity resolution, or more trustworthy assertions.

The construction pipeline

A realistic construction pipeline looks like this:

  1. Ingest documents and record source metadata (URL, retrieval time, license).
  2. Segment and parse into sentences and structured blocks.
  3. Detect mentions, retrieve and rank KB candidates, and record separate NIL predictions or abstentions.
  4. Extract relations with patterns, classifiers, or an LLM, conditioned on the linked entity types.
  5. Canonicalize arguments to nodes under an explicit merge policy, including local nodes for justified out-of-KB referents.
  6. Validate against schema constraints: type checking, cardinality, domain/range restrictions.
  7. Compare candidates with existing content, retaining conflicts that the fusion policy cannot justify resolving.
  8. Store surviving assertions with the provenance captured during ingestion and extraction.

Capture provenance when candidates are created and preserve it through validation and fusion; do not wait until the last step to reconstruct missing evidence. Linking, canonicalization, and fusion errors require different repairs. Evaluate each with appropriate labeled cases as well as testing the complete pipeline.

Entity resolution and canonicalization

Entity linking maps text mentions to KB entries. Entity resolution identifies records or nodes that refer to the same entity; a later merge policy decides how to combine their fields and identifiers. Resolution can use names, attributes, graph neighborhoods, and source documents when those are retained. Its available context depends on the record schema and retained evidence; record matching need not discard the source text.

The mechanics are similar to linking but operate on records rather than mentions:

  • Blocking. All-pairs comparison of nn records requires n(n−1)/2n(n-1)/2 unordered pairs. At one million records, that is 499,999,500,000 pairs. When that cost exceeds the available budget, blocking can retrieve a smaller candidate set using keys such as normalized names, identifiers, or neighbors.
  • Pairwise matching. Score each candidate pair with string similarity (Jaro-Winkler, token-set ratio), attribute agreement, and neighborhood overlap. Then classify match/non-match.
  • Clustering. Decide which candidate records belong to one entity. Connected components enforce a transitive grouping; a clustering objective can instead penalize inconsistent pair decisions. Choose a representative only after deciding membership.

Blocking trades candidate coverage against comparison cost. A pair excluded by every retrieval or blocking route will not reach the pairwise matcher. Candidate keys can use name tokens, identifiers, or neighbors, but their usefulness depends on missingness, name collisions, and graph errors. Measure candidate-pair recall on labeled duplicates rather than assuming one kind of key is best.

A pairwise classifier can say that A matches B and B matches C while rejecting A-C. Connected components merge all three because a path connects them. One alternative is a clustering objective that penalizes both joining dissimilar records and separating similar ones; this still depends on the quality of the pairwise evidence and its optimization. Choosing a canonical representative happens after deciding cluster membership. It does not repair a mistaken cluster.

owl:sameAs Is a Trap

OWL's owl:sameAs asserts identity of the denoted individuals. Identity is symmetric and transitive, with substitution consequences under OWL reasoning. Whether a store materializes those consequences depends on its inference configuration. Halpin et al. (2010) analyze uses of owl:sameAs that are weaker than its identity semantics. If the evidence establishes only a context-dependent match, record that weaker claim and its scope rather than asserting global identity.

owl:sameAs is appropriate when identity is the intended, justified claim. A custom property does not become weaker merely because it has another name: specify its semantics and the reasoning rules that use it. Likewise, recording a canonical ID can cause an application to merge records, so that decision also needs evidence and a reversible history.

Provenance and Temporal Scope

A consumer may need to ask: where did this assertion come from, and when does it apply? Source provenance and temporal scope support different answers. Their required detail depends on the application; not every assertion needs a finite validity interval.

An old but accurately extracted source can describe a relation that no longer holds. Conversely, a current assertion can have weak source support. One timestamp cannot in general represent both when the relation applied and when the system recorded it. Keep those roles explicit when the query requirements distinguish them.

Provenance

PROV-O provides classes and properties for representing entities, activities, agents, and their provenance relationships (W3C, 2013). For the extraction workflow here, useful record fields include:

  • The source document identifier and, ideally, a content hash.
  • The character span in the source that supports the assertion.
  • The extractor name and version.
  • The timestamp of extraction.
  • A confidence score.

Retain the source version together with the span. Offsets into a mutable page can point to different text after an edit. A content hash identifies a version but does not recover its bytes, so keep a snapshot or another reliable retrieval route when reproduction matters. Record the offset convention, such as half-open Unicode character positions. The source text and locator let a reviewer inspect the asserted evidence; they do not prove that the assertion follows from it.

Temporal scope

Some relations change over time, including employment and office holding. Wikidata statements can use qualifiers such as start and end time to record context. Its preferred, normal, and deprecated ranks are a separate mechanism. A normal rank is neutral about accuracy and currency, and several statements can have preferred rank. Rank alone does not determine whether a statement is valid on a requested date.

Wikidata's model is a good template even if you do not use RDF:

  • Main claim: the item, property, and value; the statement record can also carry qualifiers, references, and rank.
  • Qualifiers: additional context, such as time bounds, scope, or role.
  • References point to sources that support a statement or document where its value came from.
  • Rank: a preference or deprecation annotation, which can apply to several values.

Separating values, qualifiers, references, and ranks permits different assertions to be represented without collapsing their contexts. A consumer must still define how to handle disagreement, missing references, and temporal qualifiers. Neither references nor ranks are calibrated confidence probabilities.

Bitemporal modeling separates valid time, when an assertion applies in the modeled world, from transaction time, when it is recorded in the database (Jensen and Snodgrass, 2018). Suppose a fictional contract takes effect in 2020 but the database first records it in 2023. Its valid-time start is 2020 and its transaction-time start is 2023. The effective date must come from the evidence; an announcement date is not automatically an effective date. With the relevant retained history, the two axes distinguish a query about 2021 from a query about what was recorded as of 2022.

Confidence propagation

Before composing assertions, check that the relation-composition rule is sound for their meanings and that their temporal and other scopes align. Calibrated marginal probabilities alone do not determine a conjunction probability. Independence is sufficient for multiplying the marginals under the relevant probability model. With premise probabilities 0.9 and 0.8 but no dependence information, their conjunction can range from 0.7 to 0.8. A product score of 0.72 or a minimum score of 0.8 may instead be chosen as a heuristic, but must be labeled as such. A valid conjunction also does not establish an unsupported inference rule.

Worked Example

We will compare three linking strategies using a small KB, an alias table, eight documents, and a coherence graph. The data and weights are chosen to expose the scoring mechanics. These results are a worked calculation, not a benchmark or an estimate of deployment accuracy.

The toy KB has eleven entities. Each has a label and a short description, which is what a description-based linker would index:

PythonIn [3]
CollapseExpand
import math
import re
from collections import defaultdict
from itertools import product

import numpy as np
import pandas as pd
PythonIn [4]
CollapseExpand
ENTITIES = {
    "mercury_planet": {
        "label": "Mercury (planet)",
        "desc": (
            "Mercury is the smallest planet in the Solar System and closest to the Sun. "
            "Mercury orbits the Sun every 88 days and has a heavily cratered surface."
        ),
    },
    "mercury_element": {
        "label": "Mercury (element)",
        "desc": (
            "Mercury is a chemical element with the symbol Hg and atomic number 80. "
            "It is a heavy silvery metal that is liquid at room temperature and was "
            "once common in thermometers."
        ),
    },
    "mercury_god": {
        "label": "Mercury (mythology)",
        "desc": (
            "Mercury is a god in Roman religion and mythology, the patron god of "
            "financial gain, commerce, and eloquence. He is a messenger of the gods "
            "and the Roman counterpart of the Greek god Hermes."
        ),
    },
    "apple_inc": {
        "label": "Apple Inc.",
        "desc": (
            "Apple Inc. is an American multinational technology company headquartered in "
            "Cupertino, California. Apple designs the iPhone, the Mac, and the iPad, and "
            "runs the App Store and iCloud."
        ),
    },
    "apple_fruit": {
        "label": "Apple (fruit)",
        "desc": (
            "An apple is an edible fruit produced by an apple tree and grown in orchards. "
            "Ripe apples contain fiber and vitamin C. Harvest timing depends on cultivar and climate."
        ),
    },
    "jordan_country": {
        "label": "Jordan",
        "desc": (
            "Jordan is a country in Western Asia, bordered by Saudi Arabia, Iraq, Syria, "
            "and Israel. The capital city is Amman."
        ),
    },
    "jordan_person": {
        "label": "Michael Jordan",
        "desc": (
            "Michael Jordan is an American former professional basketball player and "
            "businessman. He played fifteen seasons in the National Basketball Association "
            "and won six championships with the Chicago Bulls."
        ),
    },
    "chicago_bulls": {
        "label": "Chicago Bulls",
        "desc": (
            "The Chicago Bulls are an American professional basketball team based in "
            "Chicago. The Bulls compete in the National Basketball Association."
        ),
    },
    "amman": {
        "label": "Amman",
        "desc": (
            "Amman is the capital and largest city of Jordan. It is a commercial "
            "and cultural center of Jordan."
        ),
    },
    "iphone": {
        "label": "iPhone",
        "desc": (
            "The iPhone is a line of smartphones designed and marketed by Apple. It runs "
            "the iOS operating system."
        ),
    },
    "hubble": {
        "label": "Hubble Space Telescope",
        "desc": (
            "The Hubble Space Telescope is a space telescope launched into low Earth "
            "orbit in 1990. It observes planets, stars, and distant galaxies."
        ),
    },
}

Notice that the descriptions are written in natural language and contain the kind of distinguishing terms a context-similarity scorer would look for. The planet description mentions planet, Sun, 88 days, and cratered. The element description mentions chemical element, Hg, metal, liquid, and thermometers. The god description mentions Roman, mythology, commerce, and Hermes. After tokenization and masking, shared terms contribute to the overlap. All retained description terms contribute to the norm.

Next, the alias table. These invented counts stand in for anchor statistics. Their proportions let us inspect cases where context or coherence changes the prior's preferred candidate; they do not estimate Wikipedia usage.

PythonIn [5]
CollapseExpand
ALIAS_COUNTS = {
    "mercury": {
        "mercury_element": 450,
        "mercury_planet": 400,
        "mercury_god": 150,
    },
    "apple": {"apple_inc": 650, "apple_fruit": 350},
    "jordan": {"jordan_country": 700, "jordan_person": 300},
    "chicago bulls": {"chicago_bulls": 1000},
    "amman": {"amman": 1000},
    "iphone": {"iphone": 1000},
    "hubble": {"hubble": 1000},
}

ALIAS_TO_CANDIDATES = {
    alias: list(counts) for alias, counts in ALIAS_COUNTS.items()
}

The alias table supplies both the priors and the candidate sets. mercury has three candidates; apple and jordan have two each; the other listed aliases have one each. A production dictionary also needs a policy for normalization, unseen aliases, redirects, and updates.

The skew in the alias table is easier to see as a distribution than as a dictionary. The following computation turns the counts into per-alias priors, then the figure compares the prior mass of the three ambiguous aliases.

PythonIn [6]
CollapseExpand
alias_prior_rows = []
for alias, counts in ALIAS_COUNTS.items():
    total = sum(counts.values())
    for entity, count in counts.items():
        alias_prior_rows.append(
            {
                "alias": alias,
                "entity": entity,
                "prior": count / total,
            }
        )

alias_prior_df = pd.DataFrame(alias_prior_rows)
ambiguous_aliases = ["mercury", "apple", "jordan"]
ambiguous_prior_df = alias_prior_df[
    alias_prior_df["alias"].isin(ambiguous_aliases)
].copy()
ambiguous_prior_df["alias"] = pd.Categorical(
    ambiguous_prior_df["alias"], categories=ambiguous_aliases, ordered=True
)
ambiguous_prior_df = ambiguous_prior_df.sort_values(
    ["alias", "prior"], ascending=[True, False]
).reset_index(drop=True)
ambiguous_prior_df["x"] = np.arange(len(ambiguous_prior_df))
VisualizationOut [7]
CollapseExpand
Grouped bar chart of alias priors for Mercury, Apple, and Jordan candidates.
Prior mass from the invented alias counts for three ambiguous aliases. The Mercury element and planet priors are 0.45 and 0.40, with 0.15 for the god. The Apple and Jordan priors lean toward the technology company and the country respectively. These hand-chosen priors do not measure general popularity.

Now the test corpus and the gold labels. Each document has one or two sentences. Several contain distinguishing words. In d5, the toy annotation maps the individual apple to the fruit category in the KB. The wording in d8 can refer to the word Mercury itself. For this score comparison, we assign the planet as the gold label. After masking the matched alias, d8 leaves Mercury without description-word overlap, so we can isolate the graph contribution. We register gold labels as (doc_id, start, end, entity) so we can score predictions later.

PythonIn [8]
CollapseExpand
DOCS = [
    {
        "doc_id": "d1",
        "text": "Mercury is the closest planet to the Sun and completes an orbit every 88 days.",
    },
    {
        "doc_id": "d2",
        "text": "Mercury is a heavy silvery metal that is liquid at room temperature.",
    },
    {
        "doc_id": "d3",
        "text": "In Roman mythology Mercury was the messenger of the gods and the patron of commerce.",
    },
    {
        "doc_id": "d4",
        "text": "Apple reported record iPhone sales in its latest quarterly earnings.",
    },
    {
        "doc_id": "d5",
        "text": "She picked a ripe apple from the tree in the orchard.",
    },
    {
        "doc_id": "d6",
        "text": "Jordan signed with the Chicago Bulls in 1984 and won six championships.",
    },
    {
        "doc_id": "d7",
        "text": "Jordan is a country in Western Asia whose capital is Amman.",
    },
    {
        "doc_id": "d8",
        "text": "The Hubble article appears first. Mercury appears in the next paragraph.",
    },
]

GOLD = [
    ("d1", 0, 7, "mercury_planet"),
    ("d2", 0, 7, "mercury_element"),
    ("d3", 19, 26, "mercury_god"),
    ("d4", 0, 5, "apple_inc"),
    ("d4", 22, 28, "iphone"),
    ("d5", 18, 23, "apple_fruit"),
    ("d6", 0, 6, "jordan_person"),
    ("d6", 23, 36, "chicago_bulls"),
    ("d7", 0, 6, "jordan_country"),
    ("d7", 53, 58, "amman"),
    ("d8", 4, 10, "hubble"),
    ("d8", 34, 41, "mercury_planet"),
]

The gold records retain each mention's exact character span together with its entity ID. That detail matters because the evaluation will match on exact spans. A linker that picks the right entity but the wrong span will count as an error. This follows the strict span-and-entity convention described above. The toy corpus is also constructed so that each ambiguous alias appears in contexts with different evidence. That is what lets us compare the prior-only, local-only, and collective strategies.

Mention detection here is a gazetteer: match any alias as a whole word, case-insensitively. This detector has no learned span filter. A false detection on ordinary text would need mention rejection, separately from deciding whether an entity referent is absent from the KB.

PythonIn [9]
CollapseExpand
ALIASES_SORTED = sorted(ALIAS_COUNTS, key=len, reverse=True)
ALIAS_BY_GROUP = {f"alias_{i}": alias for i, alias in enumerate(ALIASES_SORTED)}
ALIAS_PATTERN = re.compile(
    r"\b(?:"
    + "|".join(
        f"(?P<{group}>{re.escape(alias)})"
        for group, alias in ALIAS_BY_GROUP.items()
    )
    + r")\b",
    re.IGNORECASE,
)


def detect_mentions(doc):
    mentions = []
    for match in ALIAS_PATTERN.finditer(doc["text"]):
        alias = ALIAS_BY_GROUP[match.lastgroup]
        mentions.append(
            {
                "doc_id": doc["doc_id"],
                "surface": match.group(0),
                "alias": alias,
                "start": match.start(),
                "end": match.end(),
                "candidates": ALIAS_TO_CANDIDATES[alias],
            }
        )
    return mentions

The regular expression tries longer aliases first at a given position. If new york and new york city were both aliases, this ordering would choose the longer alternative when both are valid whole-word matches starting at the same character. This non-overlapping matcher does not enumerate nested mentions.

ConsoleOut [10]
CollapseExpand
Documents: 8   Mentions detected: 12   Gold mentions: 12
  d1  'Mercury'  span=(0, 7)  candidates=['mercury_element', 'mercury_planet', 'mercury_god']
  d2  'Mercury'  span=(0, 7)  candidates=['mercury_element', 'mercury_planet', 'mercury_god']
  d3  'Mercury'  span=(19, 26)  candidates=['mercury_element', 'mercury_planet', 'mercury_god']
  d4  'Apple'  span=(0, 5)  candidates=['apple_inc', 'apple_fruit']

The matcher finds the twelve gold spans and no additional spans in this constructed corpus. That result depends on the chosen text and alias inventory. It does not establish how often the same matcher would miss entities or match nonentities elsewhere.

Next, the two scoring functions. Context similarity uses a binary-cosine over content words, which is a transparent stand-in for a TF-IDF or dense encoder. Stopwords come from scikit-learn's English list.

PythonIn [11]
CollapseExpand
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS


def content_words(text):
    tokens = re.findall(r"[a-z0-9]+", text.lower())
    return {t for t in tokens if t not in ENGLISH_STOP_WORDS and len(t) > 1}


def cosine(a, b):
    if not a or not b:
        return 0.0
    return len(a & b) / math.sqrt(len(a) * len(b))


ENTITY_CONTENT = {
    eid: content_words(meta["desc"]) for eid, meta in ENTITIES.items()
}


def prior(entity, alias):
    counts = ALIAS_COUNTS[alias]
    return counts.get(entity, 0) / sum(counts.values())


def local_scores(mention, context, alpha=1.0, beta=10.0):
    scores = {}
    alias_words = content_words(mention["surface"])
    context_evidence = context - alias_words
    for eid in mention["candidates"]:
        p = prior(eid, mention["alias"])
        sim = cosine(context_evidence, ENTITY_CONTENT[eid] - alias_words)
        scores[eid] = alpha * math.log(p) + beta * sim
    return scores

The filter removes listed stopwords and one-character tokens, then represents the remaining words as a set. cosine computes the cosine of binary word-presence vectors, returning zero when either set is empty. local_scores removes the surface words from both sides so the alias itself does not count as disambiguating evidence. The regex retains only ASCII letters and digits and does not lemmatize. Its English stopword list is not a multilingual tokenization policy, and scikit-learn documents limitations of that list. A changed similarity function needs its weights re-evaluated rather than assumed interchangeable.

ConsoleOut [12]
CollapseExpand
mercury_planet     local score = +4.782
mercury_element    local score = -0.799
mercury_god        local score = -1.897

In document d1, the context words planet, sun, and days overlap heavily with the planet description, and the context term with β=10\beta = 10 overwhelms the small prior advantage that the element has. With α=1\alpha = 1 and β=10\beta = 10, we multiply cosine similarity by ten before adding the log-prior term; this does not mean ten times as much evidential trust because the features have different scales. The result is a ranking score that puts the planet first for this mention. If all three candidate similarities were zero, these positive weights would leave the prior to select the element.

The same prior-plus-context score can be decomposed into its two contributions. The next computation isolates the log-prior term and the context-similarity term for the Mercury candidates in document d1.

PythonIn [13]
CollapseExpand
mercury_demo = next(
    m for m in detect_mentions(DOCS[0]) if m["alias"] == "mercury"
)
mercury_demo_context = content_words(DOCS[0]["text"])
mercury_demo_rows = []
for entity in mercury_demo["candidates"]:
    p = prior(entity, "mercury")
    sim = cosine(
        mercury_demo_context - content_words(mercury_demo["surface"]),
        ENTITY_CONTENT[entity] - content_words(mercury_demo["surface"]),
    )
    mercury_demo_rows.append(
        {
            "entity": entity,
            "log_prior": math.log(p),
            "context_term": 10.0 * sim,
            "local_score": local_scores(mercury_demo, mercury_demo_context)[
                entity
            ],
        }
    )
mercury_demo_df = (
    pd.DataFrame(mercury_demo_rows)
    .sort_values("local_score", ascending=False)
    .reset_index(drop=True)
)
mercury_demo_df["x"] = np.arange(len(mercury_demo_df))
VisualizationOut [14]
CollapseExpand
Grouped bar chart of log-prior and context terms for Mercury candidates.
Decomposition of the local score for the Mercury candidates in document d1. The context term is large for the planet, while the log-prior term is slightly higher for the element. The figure shows why context similarity dominates the decision even though the prior alone would favor a different sense.

Now the coherence graph. We model Wikipedia's inlink structure as a set of articles, each containing a set of entities. Milne-Witten relatedness is computed from these article sets, and we clamp the empty-intersection case to zero.

PythonIn [15]
CollapseExpand
ARTICLES = {
    "Solar_System": {"mercury_planet", "venus", "earth", "sun"},
    "Mercury_(planet)": {"mercury_planet", "sun", "solar_system"},
    "Mercury_(element)": {"mercury_element", "thermometer", "chemistry"},
    "Roman_mythology": {"mercury_god", "jupiter_god", "hermes"},
    "Hermes": {"hermes", "mercury_god", "greek_mythology"},
    "Apple_Inc.": {"apple_inc", "iphone", "steve_jobs", "cupertino"},
    "Apple": {"apple_fruit", "orchard", "vitamin_c"},
    "Michael_Jordan": {"jordan_person", "chicago_bulls", "basketball", "nba"},
    "Chicago_Bulls": {"chicago_bulls", "basketball", "nba", "jordan_person"},
    "Jordan": {"jordan_country", "amman", "middle_east"},
    "Amman": {"amman", "jordan_country"},
    "iPhone": {"iphone", "apple_inc", "steve_jobs"},
    "Hubble_Space_Telescope": {"hubble", "nasa", "telescope"},
    "Great_Observatories": {"hubble", "mercury_planet", "telescope"},
}

ENTITY_ARTICLES = defaultdict(set)
for article, members in ARTICLES.items():
    for member in members:
        ENTITY_ARTICLES[member].add(article)

N_ARTICLES = len(ARTICLES)


def relatedness(a, b):
    A, B = ENTITY_ARTICLES[a], ENTITY_ARTICLES[b]
    if not A or not B:
        return 0.0
    shared = len(A & B)
    if shared == 0:
        return 0.0
    numerator = math.log(max(len(A), len(B))) - math.log(shared)
    denominator = math.log(N_ARTICLES) - math.log(min(len(A), len(B)))
    return max(0.0, 1.0 - numerator / denominator) if denominator > 0 else 1.0

ARTICLES is a hand-built incidence table, not a scraped Wikipedia graph. Its keys identify toy source articles; each value lists entity IDs linked from that article. Inverting it gives each entity's inlink set. In particular, the planet and Hubble share Great_Observatories, and Michael Jordan and the Bulls share two source articles. These chosen overlaps create the rewards we want to inspect.

ConsoleOut [16]
CollapseExpand
relatedness(mercury_planet  , hubble        ) = 0.435
relatedness(mercury_element , hubble        ) = 0.000
relatedness(jordan_person   , chicago_bulls ) = 1.000
relatedness(jordan_country  , chicago_bulls ) = 0.000

jordan_person shares two articles with chicago_bulls, so their relatedness is positive, while the country shares nothing and scores zero. mercury_planet shares the Great_Observatories article with hubble, providing a bonus we will inspect in d8. These values are compatibility scores for this graph, not calibrated probabilities of a semantic relation. A value of 0.6 does not mean a 60 percent chance that the entities are related.

Now the collective objective. The tiny candidate products in these documents let us enumerate every assignment and compare their computed floating-point scores.

PythonIn [17]
CollapseExpand
GAMMA = 1.0


def collective_assignment(tables, candidates_per_mention, gamma=GAMMA):
    best_combo, best_total = None, -np.inf
    for combo in product(*candidates_per_mention):
        total = sum(tables[i][combo[i]] for i in range(len(combo)))
        for i in range(len(combo)):
            for j in range(i + 1, len(combo)):
                total += gamma * relatedness(combo[i], combo[j])
        if total > best_total:
            best_total, best_combo = total, combo
    return best_combo, best_total

Enumeration cost grows with the product of the candidate-set sizes. With three candidates per mention and five mentions, the Cartesian product has 243 combinations. With ten mentions, it has 59,049. With twenty, it has over 3 billion. When enumeration exceeds the budget, use a different solver or an approximation with stated stopping rules. The toy enumerates all assignments, so it has neither sampling error nor an iterative convergence criterion.

PythonIn [18]
CollapseExpand
rows = []
for doc in DOCS:
    mentions = detect_mentions(doc)
    context = content_words(doc["text"])
    tables = [local_scores(m, context) for m in mentions]

    mfs = [
        max(m["candidates"], key=lambda e: prior(e, m["alias"]))
        for m in mentions
    ]
    loc = [max(t, key=t.get) for t in tables]
    coll, _ = collective_assignment(tables, [m["candidates"] for m in mentions])

    for i, m in enumerate(mentions):
        rows.append(
            {
                "doc": m["doc_id"],
                "surface": m["surface"],
                "start": m["start"],
                "end": m["end"],
                "most_frequent": mfs[i],
                "local_only": loc[i],
                "collective": coll[i],
                "local_score": round(tables[i][loc[i]], 3),
            }
        )

results = pd.DataFrame(rows)
Out[19]:
doc surface start end most_frequent local_only collective local_score
0 d1 Mercury 0 7 mercury_element mercury_planet mercury_planet 4.782
1 d2 Mercury 0 7 mercury_element mercury_element mercury_element 5.526
2 d3 Mercury 19 26 mercury_element mercury_god mercury_god 4.649
3 d4 Apple 0 5 apple_inc apple_inc apple_inc 0.545
4 d4 iPhone 22 28 iphone iphone iphone 1.336
5 d5 apple 18 23 apple_inc apple_fruit apple_fruit 1.450
6 d6 Jordan 0 6 jordan_country jordan_person jordan_person 3.160
7 d6 Chicago Bulls 23 36 chicago_bulls chicago_bulls chicago_bulls 0.000
8 d7 Jordan 0 6 jordan_country jordan_country jordan_country 6.098
9 d7 Amman 53 58 amman amman amman 3.381
10 d8 Hubble 4 10 hubble hubble hubble 0.000
11 d8 Mercury 34 41 mercury_element mercury_element mercury_planet -0.799

The table compares three strategies on every mention. The most_frequent column ignores context and always picks the prior argmax. The local_only column uses the prior plus context similarity for each mention independently. The collective column adds the coherence term and optimizes the whole document jointly. The local_score column reports the score of the local_only choice. The table shows where the strategies diverge. The component breakdowns quantify the individual score terms.

Now score all three strategies against the gold labels. A prediction earns credit only when its span and entity ID match the gold annotation.

PythonIn [20]
CollapseExpand
gold_by_span = {(doc_id, start, end): eid for doc_id, start, end, eid in GOLD}


def accuracy(column):
    correct = 0
    for _, row in results.iterrows():
        span = (row["doc"], row["start"], row["end"])
        if gold_by_span.get(span) == row[column]:
            correct += 1
    return correct / len(results)


scores = {
    col: accuracy(col) for col in ["most_frequent", "local_only", "collective"]
}
print(f"gold mentions: {len(GOLD)}   predicted mentions: {len(results)}")
for name, value in scores.items():
    print(f"  {name:14s} accuracy = {value:.3f}")

For each strategy, the function divides the number of correct span-and-entity predictions by the number of prediction rows. An empty results frame would make it divide by zero. Here each of the twelve unique gold spans has exactly one prediction row, with no extra rows, and each strategy predicts a KB ID. Under those conditions the fraction is accuracy over the fixed detected mentions. It is not a general end-to-end recall or F1 calculation: with missed gold spans, its denominator would not count those misses. Repeated correct rows would each get credit, even if another gold span was missed. For another corpus, compute detection recall and candidate recall separately, and use one-to-one matches between predictions and gold annotations for the specified linking precision and recall.

The most-frequent-sense baseline is the weakest by a wide margin: it only wins where the dominant sense happens to be correct. Local scoring fixes most of the errors by reading the context. Collective scoring fixes the remaining one.

On these twelve spans, local scoring changes four prior-only decisions. Adding coherence changes one more, so the collective and prior-only strategies differ on five spans. The next computation turns the accuracy scores into a plotting table, and the figure shows the gap.

PythonIn [21]
CollapseExpand
accuracy_plot_df = pd.DataFrame(
    {
        "strategy": ["most_frequent", "local_only", "collective"],
        "accuracy": [
            scores["most_frequent"],
            scores["local_only"],
            scores["collective"],
        ],
    }
)
VisualizationOut [22]
CollapseExpand
Bar chart comparing linking accuracy for three strategies on the toy corpus.
Accuracy over the twelve detected gold spans for three strategies on the constructed corpus. Prior-only scoring gets seven correct, local scoring eleven, and collective scoring twelve. These are illustrative in-sample calculations, not performance estimates on held-out data.

That remaining case is d8. Each toy document has exactly one mention with multiple candidates. With every other choice fixed, the pair rewards can only rescore that mention’s candidates. In d8, Hubble has one candidate, so the compatibility reward changes Mercury’s scores while Hubble’s assignment stays fixed.

PythonIn [23]
CollapseExpand
d8 = next(d for d in DOCS if d["doc_id"] == "d8")
d8_mentions = detect_mentions(d8)
d8_context = content_words(d8["text"])
mercury_m = next(m for m in d8_mentions if m["alias"] == "mercury")
d8_local = local_scores(mercury_m, d8_context)
d8_combined = {
    e: s + GAMMA * relatedness(e, "hubble") for e, s in d8_local.items()
}

d8_table = pd.DataFrame(
    [
        {
            "entity": e,
            "local": round(d8_local[e], 3),
            "coherence_bonus": round(GAMMA * relatedness(e, "hubble"), 3),
            "combined": round(d8_combined[e], 3),
        }
        for e in sorted(d8_local, key=lambda k: -d8_combined[k])
    ]
)
Out[24]:
entity local coherence_bonus combined
0 mercury_planet -0.916 0.435 -0.481
1 mercury_element -0.799 0.000 -0.799
2 mercury_god -1.897 0.000 -1.897

In d8, removing mercury leaves no content-word overlap with any of its three descriptions. The local score therefore follows the prior and prefers the element. The planet's shared toy article with Hubble adds enough coherence reward to reverse that ranking. There was no local-score tie. We supplied the graph connection as additional evidence, and it can introduce errors of its own. Here the combined score matches the assigned planet label. The sentence alone leaves open whether Mercury refers to an entity.

Let's visualize the relatedness submatrix for nine selected entities from the collective objective.

PythonIn [25]
CollapseExpand
HEAT_ENTITIES = [
    "mercury_planet",
    "mercury_element",
    "mercury_god",
    "hubble",
    "apple_inc",
    "apple_fruit",
    "jordan_country",
    "jordan_person",
    "chicago_bulls",
]
REL_MATRIX = np.array(
    [[relatedness(a, b) for b in HEAT_ENTITIES] for a in HEAT_ENTITIES]
)
VisualizationOut [27]
CollapseExpand
Symmetric compatibility heatmap with a unit diagonal. Off-diagonal rewards connect Mercury the planet with Hubble and Michael Jordan with the Chicago Bulls.
Milne-Witten relatedness between candidate entities, derived from a toy article-link graph. Darker cells in the light theme and brighter cells in the dark theme show larger compatibility rewards. Off-diagonal rewards connect Mercury the planet to Hubble and Michael Jordan to the Chicago Bulls. The diagonal is one because each entity has the same inlink set as itself; disjoint sets receive zero.

The off-diagonal cells connect the planet with Hubble and Michael Jordan with the Chicago Bulls. The Apple entities have no off-diagonal connection among the entities shown here; the company's iPhone neighbor is omitted from this matrix. The matrix is symmetric because swapping the two inlink sets leaves the score unchanged. These graph connections can supplement weak local evidence, but a document need not belong to a single topic. Most off-diagonal cells in this submatrix are zero. Coherence can change the selected assignment only when its weighted bonus changes the ordering of feasible assignments, including ties.

For each Mercury candidate in d8, the bars compare its local score with its score after adding compatibility with Hubble.

VisualizationOut [28]
CollapseExpand
Grouped bar chart comparing local and combined disambiguation scores for three Mercury senses.
Local score versus local score plus the coherence bonus for the three Mercury candidates in document d8. Without the coherence term the element wins on the prior alone; with it, the planet wins because of its shared toy article link with Hubble. The figure isolates the single mention that the coherence signal rescues.

The coherence bonus changes the local ranking here by overcoming the prior gap. With a larger γ\gamma, a compatibility reward can also outweigh correct local evidence. The bar chart makes the tradeoff visible. The local score alone favors the element. The combined score favors the planet. The planet gains the pair reward derived from sharing the toy Great_Observatories inlink with Hubble.

Before accepting an ID, we can require the top two local scores to be separated by a margin. The following policy abstains on an empty candidate table or a margin below 0.2. A single candidate passes this margin check without an ambiguity comparison; it still needs separate checks for mention validity and KB coverage. The threshold is chosen for demonstration, not calibrated on validation data.

PythonIn [29]
CollapseExpand
def choose_or_abstain(table, minimum_margin=0.2):
    ranked = sorted(table, key=table.get, reverse=True)
    if not ranked:
        return "ABSTAIN"
    if len(ranked) > 1 and table[ranked[0]] - table[ranked[1]] < minimum_margin:
        return "ABSTAIN"
    return ranked[0]


print("d1 local:", choose_or_abstain(demo_scores))
print("d8 local:", choose_or_abstain(d8_local))
print("empty candidates:", choose_or_abstain({}))
ConsoleOut [29]
CollapseExpand
d1 local: mercury_planet
d8 local: ABSTAIN
empty candidates: ABSTAIN

The policy accepts the planet for d1 and abstains on d8 and the empty table. Mercury's authored gold label in d8 is the planet, which is in the toy KB. The margin policy abstains on this in-KB gold target. An empty lookup also cannot establish KB absence. We have not implemented an open-world NIL detector or changed the three-strategy comparison above.

Building the Graph

We now start from hand-written relation-extraction candidates with linked arguments. The toy KB omits two objects, so we explicitly label those arguments NIL and assign document-scoped local nodes. This is a construction policy for this fixture; a failed link in general would first require separating uncertainty from confirmed KB absence.

PythonIn [30]
CollapseExpand
from hashlib import sha256

SOURCE_TEXTS = {
    "kg1": "Mercury orbits the Sun.",
    "kg2": "Hubble and Mercury appear together in this passage.",
    "kg3": "Amman is the capital of Jordan.",
    "kg4": "Apple designs the iPhone.",
    "kg5": "Michael Jordan played for the Chicago Bulls.",
    "kg6": "Mercury was used in thermometers.",
}

FACTS = [
    {
        "sid": "s1",
        "doc": "kg1",
        "subject": "mercury_planet",
        "relation": "orbits",
        "object": "sun",
        "object_nil": True,
        "confidence": 0.92,
    },
    {
        "sid": "s2",
        "doc": "kg2",
        "subject": "hubble",
        "relation": "co_mentioned_with",
        "object": "mercury_planet",
        "object_nil": False,
        "confidence": 0.88,
    },
    {
        "sid": "s3",
        "doc": "kg3",
        "subject": "amman",
        "relation": "is_capital_of",
        "object": "jordan_country",
        "object_nil": False,
        "confidence": 0.95,
    },
    {
        "sid": "s4",
        "doc": "kg4",
        "subject": "apple_inc",
        "relation": "designs",
        "object": "iphone",
        "object_nil": False,
        "confidence": 0.90,
    },
    {
        "sid": "s5",
        "doc": "kg5",
        "subject": "jordan_person",
        "relation": "member_of",
        "object": "chicago_bulls",
        "object_nil": False,
        "confidence": 0.71,
    },
    {
        "sid": "s6",
        "doc": "kg6",
        "subject": "mercury_element",
        "relation": "used_in",
        "object": "thermometer",
        "object_nil": True,
        "confidence": 0.64,
    },
]

for fact in FACTS:
    source_text = SOURCE_TEXTS[fact["doc"]]
    fact["span"] = (0, len(source_text))
    fact["source_sha256"] = sha256(source_text.encode("utf-8")).hexdigest()
    fact["extractor_version"] = "hand-written-fixture-v1"

The confidence values are illustrative scores. The second source sentence puts the names Hubble and Mercury together. The candidate supplies the planet ID as a linking hypothesis. The sentence leaves Mercury’s identity open and makes no claim that Hubble observed it. Each half-open span covers its entire retained source sentence using Python string indices, which count Unicode code points. The hash identifies the UTF-8 encoding of that sentence, and the extractor field records that these are hand-written fixtures. An extraction timestamp would be a separate field in a production record. Retaining SOURCE_TEXTS makes the offsets auditable; a hash alone would not recover the text.

The following store uses ordinary Python collections. It implements a small assertion catalog and source grouping; it does not implement RDF, SPARQL, OWL inference, or a production graph database. The earlier RDF example describes an interoperable representation to which a real system could export these records.

PythonIn [31]
CollapseExpand
statement_store = []
nodes = set(ENTITIES)
by_source = defaultdict(list)

Each assertion has its own statement ID. Repeated triples can therefore retain separate source records and confidence values instead of overwriting one another. For reproducible inspection, interpret offsets against the retained source version.

PythonIn [32]
CollapseExpand
for fact in FACTS:
    object_id = (
        f"local:{fact['doc']}:{fact['object']}"
        if fact["object_nil"]
        else fact["object"]
    )
    nodes.add(object_id)
    statement = {**fact, "object": object_id, "source": fact["doc"]}
    statement_store.append(statement)
    by_source[fact["doc"]].append(fact["sid"])
ConsoleOut [33]
CollapseExpand
statements: 6
source groups: 6
nodes: 13

The statement store contains one record per candidate assertion, plus a separate index of statement IDs by source. The object_nil flag triggers local node creation. For records with that flag, the same raw label in the same document reuses the same node ID. That keying rule leaves cross-document resolution open and does not verify that repeated labels refer to the same entity.

Now filter the records by their illustrative confidence scores. This is a review policy, not a truth test. We keep the lower-scoring records in the store and select a subset for display.

PythonIn [34]
CollapseExpand
provenance_rows = [
    {
        key: statement[key]
        for key in ["subject", "relation", "object", "confidence", "source"]
    }
    for statement in statement_store
    if statement["confidence"] >= 0.85
]
provenance_rows.sort(key=lambda row: row["confidence"], reverse=True)
provenance_df = pd.DataFrame(provenance_rows)
Out[35]:
subject relation object confidence source
0 amman is_capital_of jordan_country 0.95 kg3
1 mercury_planet orbits local:kg1:sun 0.92 kg1
2 apple_inc designs iphone 0.90 kg4
3 hubble co_mentioned_with mercury_planet 0.88 kg2

The filter selects four of the six records. The membership candidate at 0.71 and the thermometer candidate at 0.64 remain available for review. Their source sentences describe past relationships. These two records carry no validity dates, so the catalog leaves their present validity open. A deployment would choose its threshold from labeled validation data and the costs of false accepted facts and missed facts; the numerical value 0.85 has no calibrated meaning here.

The next example filters a separate set of records by their recorded year bounds. The championship records mark event years; the membership records use coarse inclusive year windows. Event time and the validity of an ongoing relation are different meanings, even when both are encoded using lower and upper bounds. A stored championship win does not become historically false after its event year. The time_role field keeps that distinction visible in this fixture.

PythonIn [36]
CollapseExpand
TEMPORAL_FACTS = [
    {
        "subject": "chicago_bulls",
        "relation": "has_championship",
        "object": "1991_title",
        "from_year": 1991,
        "through_year": 1991,
        "time_role": "event_year",
        "rank": "normal",
    },
    {
        "subject": "chicago_bulls",
        "relation": "has_championship",
        "object": "1996_title",
        "from_year": 1996,
        "through_year": 1996,
        "time_role": "event_year",
        "rank": "normal",
    },
    {
        "subject": "jordan_person",
        "relation": "member_of",
        "object": "chicago_bulls",
        "from_year": 1984,
        "through_year": 1993,
        "time_role": "membership_years",
        "rank": "preferred",
    },
    {
        "subject": "jordan_person",
        "relation": "member_of",
        "object": "chicago_bulls",
        "from_year": 1995,
        "through_year": 1998,
        "time_role": "membership_years",
        "rank": "preferred",
    },
]


def records_for_year(year):
    return [
        f for f in TEMPORAL_FACTS if f["from_year"] <= year <= f["through_year"]
    ]


for year in [1990, 1991, 1994, 1996, 1997]:
    active = records_for_year(year)
    print(
        f"{year}: "
        + (
            ", ".join(
                f"{f['subject']} {f['relation']} {f['object']}" for f in active
            )
            or "no matching records"
        )
    )

records_for_year selects records whose inclusive bounds contain the requested year. It returns the 1991 championship event for 1991 and the 1996 event for 1996, together with the membership records whose windows cover those years. It returns no records for 1994. This illustrates record selection, not a complete historical roster or a truth test. Membership-year windows do not specify day-level membership dates. Applications needing that precision must retain the applicable dates and an explicit policy for unknown bounds.

This assertion store does not resolve new nodes across sources: sun and thermometer each occur as an unknown object in one candidate. A larger store could retrieve candidate duplicate records, compare their evidence, and decide whether to merge them or map them to an updated KB. Record the evidence and version of a merge decision so that it can be inspected or reversed later.

Limitations and Impact

A linked identifier lets an application join a text mention to records elsewhere. A wrong identifier can make the same join carry the wrong attributes into search, question answering, or analytics. Aggregate accuracy alone does not show which entities or contexts suffer those errors. Inspect errors by entity frequency, alias ambiguity, domain, and KB coverage before choosing an acceptance policy.

Coverage can be uneven. Few recorded aliases or a sparse description can leave a linker with less useful evidence, depending on the mention and the other available records. The source corpus's most common reading can also compete with a less common domain-specific reading. For a catalog of small suppliers, for example, evaluate those suppliers and their aliases directly rather than extrapolating a newswire score. Description-based models offer one approach to rare and unseen entities, as in Botha et al. (2020); their availability does not guarantee accurate transfer.

Prior shift is a related risk. An empirical P(e∣m)P(e \mid m) summarizes the linked examples used to estimate it. A different deployment distribution can change which reading is most common. If representative linked data are available, compare re-estimated priors and retune the ranking weights on a separate development set. If they are unavailable, measure uncertainty about transfer instead of assuming the old prior describes the new corpus.

Evaluation scope can be mistaken for generality. Belonging to the target KB is expected for an in-KB test entity and is not itself contamination. Report separately whether test documents, supervised entity labels, aliases, or domains overlap training. State what is unseen in a zero-shot experiment. For candidate-constrained ranking on fixed gold in-KB mentions, candidate recall bounds attainable accuracy; ranking and detection need their own measurements.

Missing provenance makes repair harder. Without the supporting source version and span, an investigator may be unable to distinguish a linking error from a relation-extraction error. Other retained logs might still permit repair, so rebuilding is not inevitable. Decide what text, model versions, decisions, and access controls to retain, and account for their storage and privacy costs.

Changing assertions require an update policy. An employment relation may cease to hold even though its old source remains unchanged. Retain applicable validity bounds and recording history when queries need them; mark unknown bounds explicitly. A newer source is not automatically a more accurate one. Re-extraction, correction, and retention policies should preserve the distinction between an assertion being superseded and a source having been wrong.

Entity-aware retrieval can use linked IDs to retrieve records about a referent, while retaining the text that justifies a result. This connects to Retrieval-Augmented Generation. The outline for Knowledge, Memory, Editing, and Unlearning also lists external knowledge and temporal updates. The next chapter, Semantic Parsing and Meaning Representations, considers how to compose a structured interpretation of a request. A structured query can then use linked entities as its arguments, provided the system checks both the interpretation and the links.

Summary

Entity linking grounds mentions to identifiers in a chosen KB snapshot. Separate mention detection, candidate retrieval, ranking, and handling of uncertainty or KB absence. For fixed gold in-KB mentions, candidate-constrained ranking cannot recover an entity excluded by retrieval. A nonentity span, an uncertain link, and an out-of-KB referent need different treatment.

The example combines an alias prior P(e∣m)P(e \mid m) with context-description similarity. Each signal can mislead. For an evaluated system, select the weights on development data and check the feature scales; the toy weights are illustrative. Their weighted score is not a calibrated probability.

Pairwise coherence can couple mention assignments when its weighted rewards cannot be reduced to separate per-mention scores. The toy uses a nonnegative reward derived from normalized inlink overlap. Its exhaustive search finds the highest-scoring assignment for each tiny document and changes the d8 prediction from the local winner to the assigned planet label. Strong coherence can also favor the wrong topic, so this constructed improvement does not establish a general accuracy gain.

Neural approaches include joint span-and-entity models, bi-encoder retrieval with cross-encoder reranking, and autoregressive entity-name retrieval. Description-based models can represent entities absent from supervised linker training. Whether an index update also needs retraining depends on the architecture. A generative output restricted to valid names can still identify the wrong referent.

Graph construction combines linked arguments with extracted candidate relations. Consistent identifiers support joins; provenance, temporal scope, and merge history support inspection and repair. RDF and property graphs offer different representations. The executable statement store illustrates assertion identity and provenance using Python collections; it does not implement RDF or SPARQL semantics.

Evaluation needs an explicit span-matching rule, NIL and abstention conventions, averaging scheme, KB version, and split policy. Measure the stages separately and include representative target-domain data. The worked example's seven, eleven, and twelve correct links explain the mechanics of three scores; they do not replace that evaluation.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about entity linking and knowledge graph construction.

Entity Linking and Knowledge Graph Construction Quiz

Question 1 of 80 of 8 completed
In the entity linking formulation used in the chapter, what does the label NIL mean for a mention?
Join the discussionAsk a question or share what you learned.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

A different angle
All writing
Language AI Handbook · Vol. 3

Semantic Parsing and Meaning Representations

Learn how semantic parsing turns language into logical forms, AMR graphs, and executable SQL, with schema linking, constrained generation, and evaluation.

61 min read
Language AI Handbook · Vol. 3

Relation, Event, and Temporal Extraction

Extract typed relations, structured events, and normalized times from documents. Covers distant supervision, anchoring, interval algebra, and evaluation.

105 min read
Language AI Handbook · Vol. 3

Semantic Roles and Argument Structure

Explore semantic roles, argument structure, and semantic role labeling. Learn predicates, thematic roles, FrameNet, PropBank, and SRL evaluation with examples.

78 min read

About the author

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2025entitylinking, author = {Michael Brenndoerfer}, title = {Entity Linking and Knowledge Graph Construction}, year = {2025}, url = {https://mbrenndoerfer.com/writing/entity-linking-knowledge-graph-construction-nlp}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-11-13} }
APAAcademic
Michael Brenndoerfer (2025). Entity Linking and Knowledge Graph Construction. Retrieved from https://mbrenndoerfer.com/writing/entity-linking-knowledge-graph-construction-nlp
MLAAcademic
Michael Brenndoerfer. "Entity Linking and Knowledge Graph Construction." 2026. Web. November 13, 2026. <https://mbrenndoerfer.com/writing/entity-linking-knowledge-graph-construction-nlp>.
CHICAGOAcademic
Michael Brenndoerfer. "Entity Linking and Knowledge Graph Construction." Accessed November 13, 2026. https://mbrenndoerfer.com/writing/entity-linking-knowledge-graph-construction-nlp.
HARVARDAcademic
Michael Brenndoerfer (2025) 'Entity Linking and Knowledge Graph Construction'. Available at: https://mbrenndoerfer.com/writing/entity-linking-knowledge-graph-construction-nlp (Accessed: November 13, 2026).
SimpleBasic
Michael Brenndoerfer (2025). Entity Linking and Knowledge Graph Construction. https://mbrenndoerfer.com/writing/entity-linking-knowledge-graph-construction-nlp
Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy