How do you normalize service names before semantic matching?

Hi everyone,

I’m working on a project that involves matching businesses based on the services they provide. One challenge I’ve noticed is that different companies often describe the same service using different terms.

For example:

  • Junk removal vs. debris hauling
  • Tree trimming vs. tree pruning
  • Pressure washing vs. power washing
  • House cleanout vs. estate cleanout

A simple keyword match misses many of these relationships, while pure semantic search sometimes returns services that are only loosely related.

I’m curious how others approach this problem.

  • Do you maintain a synonym dictionary?
  • Do you normalize service names before generating embeddings?
  • Have you had better results with taxonomy-based matching or embedding-based retrieval?
  • How do you handle services that partially overlap?

I’d appreciate hearing about real-world approaches that have worked well.

Hmm. Looking at existing systems, it seems this is often handled by combining several components rather than relying on a single method:


I have not implemented this exact service-matching use case, but the adjacent systems I found seem to converge on roughly the same separation of responsibilities.

My direct answers would be:

  1. A synonym dictionary can help, but I would make it a concept-centered alias registry rather than a collection of pairwise rewrite rules.
  2. Light normalization before embedding is useful, but I would preserve the original label and avoid collapsing meaningful modifiers too early.
  3. Taxonomy and embedding retrieval are complementary, not competing alternatives: the taxonomy defines the possible concepts, while lexical and embedding methods retrieve candidates from it.
  4. Partial overlap should not automatically become synonymy. It is usually safer to retain a relation such as exact, broader, narrower, related, rejected, or unknown.

A practical default pipeline might look like this:

raw service label
+ business category / description / other available context
        ↓
light deterministic cleanup
        ↓
exact-alias, lexical, and dense candidate retrieval
        ↓
task-specific disambiguation or relation decision
        ↓
exact / related / reject / unknown
        ↓
downstream business-matching policy

The most important distinction may be what “match” means downstream:

Desired result Reasonable treatment
Same canonical service Use conservative exact or close mappings and reject unresolved ambiguity
Useful search expansion Broader, narrower, and related services can remain candidates, but should retain their relation labels
Evidence that a provider can perform the requested work A service name alone may be insufficient; category, description, scope, location, audience, equipment, or other capability fields may matter

That distinction determines the labels, evaluation set, and threshold policy more than the embedding model does.

For a first implementation, I would probably start with:

  • a small, versioned service registry;
  • stable service IDs;
  • preferred labels and aliases;
  • a short definition or scope note;
  • optional parent and related-service links;
  • an explicit unknown or needs_review result;
  • a small evaluation set containing both clear synonyms and deliberately confusing near-neighbors.

Even a modest hand-reviewed set can answer more useful questions than comparing models on undifferentiated examples:

  • Does the correct concept appear in the top K candidates?
  • Can the final stage distinguish the same service from a related service?
  • How often is a taxonomy-external input forced into a plausible but wrong concept?
  • What fraction can be accepted automatically, reviewed, or rejected?
Why I would separate the registry, retrieval, and final decision

The original question combines several problems that look similar at the surface but have different contracts.

1. Concept inventory

First, there needs to be some target concept space:

service_id
preferred_label
aliases
definition
scope_note
parent_ids
related_ids
status
version
provenance

The important part is that the ID represents the concept, not whichever text label happens to be preferred today.

This is close to the general model used by controlled vocabularies. The W3C SKOS reference separates concepts from labels and provides vocabulary for preferred labels, alternate labels, definitions, scope notes, broader/narrower relationships, and related concepts.

You do not need to implement RDF or adopt SKOS literally. Its useful contribution here is the separation:

concept identity
≠ preferred display name
≠ alternate label
≠ relationship to another concept

The Open Referral HSDS guidance provides a service-oriented example of the same general structure. It models services separately from taxonomy terms; taxonomy terms can have codes, names, descriptions, and hierarchical parents, and a service can be associated with one or more classifications.

2. Candidate generation

Exact aliases, lexical search, fuzzy matching, sparse retrieval, and dense retrieval can all be useful here.

Their job is not necessarily to decide that two services are equivalent. Their lower-risk job is:

Given this input, which canonical concepts deserve closer inspection?

Different retrieval methods cover different failure modes:

Candidate method Often useful for Typical failure
Exact alias Known, curated variants Ambiguous aliases and unseen variants
Character or token matching Spelling, inflection, shared terminology Semantically equivalent labels with little lexical overlap
Dense embedding Paraphrases and lexically distant candidates Related concepts appearing as if they were equivalent
Hybrid retrieval Combining complementary candidate sets Fusion method and weighting still require evaluation

The usual information-retrieval pattern is to retrieve generously and then evaluate a smaller candidate set more strictly. The Sentence Transformers retrieve-and-rerank example illustrates this general architecture.

However, its standard CrossEncoder examples estimate query-document relevance. A service-normalization decision may instead require labels such as:

same concept
broader concept
narrower concept
related but not substitutable
unrelated
insufficient context

A generic search reranker does not automatically learn those distinctions. A second stage could therefore be:

  • deterministic rules;
  • a pair classifier trained on service relations;
  • a reranker fine-tuned on domain examples;
  • taxonomy constraints;
  • selective human review;
  • or a mixture of these.

3. Final matching decision

Candidate score and final decision should remain separate outputs.

The current Reconciliation Service API draft makes a similar separation between:

  • candidate identity;
  • an optional score;
  • individual matching features;
  • and a Boolean match decision.

It also allows contextual properties and types to be supplied with the name. That is useful here because a short service label may be ambiguous by itself, while category or description can make it resolvable.

A candidate record might therefore look more like:

{
  "service_id": "example:123",
  "preferred_label": "Example service",
  "relation": "related",
  "retrieval_score": 0.81,
  "decision": "review",
  "features": {
    "alias_match": false,
    "category_match": true,
    "description_similarity": 0.76,
    "parent_match": true
  }
}

This makes debugging and later policy changes much easier than storing only a normalized string and one cosine value.

Alias ambiguity and partial overlap

A synonym dictionary is still useful, but two edge cases seem important.

One alias may identify multiple concepts

Short occupational titles, product attributes, medical terms, and service names can all be context-dependent.

For example, a generic label such as installation, cleanup, repair, or consulting may legitimately appear under several concepts. Even an exact alias match does not prove uniqueness unless the registry guarantees that alias is unambiguous in the relevant category.

A safer registry permits something like:

alias
  → candidate concept A
  → candidate concept B

and then uses context to disambiguate.

Possible contextual fields include:

  • business category;
  • service description;
  • target object;
  • customer or audience;
  • residential versus commercial;
  • location or service area;
  • emergency versus scheduled work;
  • equipment, certification, or constraints.

This also means an evaluation dataset should not force every ambiguous alias into one arbitrary “correct” ID. It can instead use:

  • a set of acceptable concepts;
  • a relation label;
  • or needs_context.

Similarity chains should not become automatic synonym chains

Suppose:

A is close to B
B is close to C

That does not necessarily imply that A and C are interchangeable.

The distinction is explicit in SKOS. It provides relations such as:

  • exactMatch;
  • closeMatch;
  • broadMatch;
  • narrowMatch;
  • relatedMatch.

In particular, “close” and “related” relationships are not intended to behave like unrestricted transitive identity.

You may not need all of those labels initially. A useful MVP could be:

exact
related
reject
unknown

and later expand related into:

close
broader
narrower
overlapping

only if the downstream application actually treats those cases differently.

Do not decide the example pairs without domain policy

Pairs such as:

  • tree trimming / tree pruning;
  • junk removal / debris hauling;
  • house cleanout / estate cleanout;

may be synonyms in one marketplace, related specializations in another, or operationally non-substitutable under certain provider constraints.

That is not merely a language-model question. It is partly a domain-policy question:

Under what conditions should the system treat two services as interchangeable for this product?

Encoding that policy explicitly is likely to be more stable than expecting one similarity threshold to infer it.

How much normalization to perform before embedding

I would separate surface cleanup from semantic canonicalization.

Usually low-risk before retrieval

  • Unicode normalization;
  • case normalization;
  • whitespace normalization;
  • punctuation normalization;
  • obvious spelling corrections;
  • carefully curated abbreviation expansion;
  • singular/plural handling where appropriate.

Potentially destructive before the relation decision

  • deleting domain modifiers;
  • replacing one service with a broader parent;
  • replacing a specialization with a generic service;
  • automatically expanding all related terms as synonyms;
  • discarding the original provider wording.

For example, modifiers such as these may carry the distinction that the matcher eventually needs:

estate
commercial
residential
emergency
mobile
industrial
licensed
hazardous
interior
exterior

I would therefore retain at least:

raw_label
cleaned_label
candidate_concepts
chosen_relation
normalization_version

It can also be useful to embed multiple concept representations separately:

preferred label
preferred label + definition
known aliases
preferred label + category + scope

Then evaluate which representation improves candidate recall without turning related concepts into false equivalents.

The O*NET Alternate Titles automation report is a useful adjacent example. O*NET separates relatively deterministic processing, such as acronym and abbreviation standardization, from harder decisions involving vague titles, occupation mismatches, context differences, and level mismatches. Its process combines dictionary logic, search ranking, semantic similarity, rules, and analyst validation rather than treating all normalization as a single embedding operation.

This is an occupational taxonomy rather than a service taxonomy, so its thresholds and performance should not be transferred directly. The architectural separation is still informative.

Unknown services and the free-form tail

A taxonomy is unlikely to contain every service that providers will enter.

If the system always chooses the nearest canonical concept, a taxonomy-external input can still receive a very plausible score. That produces a particularly difficult failure mode: the result looks confident, is semantically related, and is nevertheless the wrong normalization.

For that reason, I would make these first-class outcomes:

matched
related candidate
unknown / no suitable concept
needs review

A single global cosine threshold may be a useful baseline, but I would not assume that it cleanly separates:

  • correct known concepts;
  • close but incorrect concepts;
  • ambiguous aliases;
  • taxonomy-external inputs.

Useful additional signals can include:

  • top-1 score;
  • top-1 minus top-2 margin;
  • exact or lexical evidence;
  • category compatibility;
  • relation classifier output;
  • agreement among retrieval methods;
  • whether contextual fields support the same candidate;
  • whether the candidate is a parent, sibling, or related concept.

A real production data model can also retain standardized concepts and free-form services side by side.

For example, the Google Business Profile ServiceList API distinguishes Google-defined structured services, identified by serviceTypeId, from merchant-entered free-form services that are not exposed in the structured service data.

That does not reveal Google’s internal matching algorithm, but it is a useful design precedent: not every provider label has to be forced immediately into the standardized inventory.

Unknown labels can also become taxonomy-maintenance input:

frequent unknown
        ↓
cluster and review
        ↓
new alias, new concept, or explicit rejection rule
        ↓
versioned registry update
A small evaluation plan before choosing the model

I would build a small stratified set before spending much time selecting embedding models or tuning one threshold.

Suggested groups

Group What it tests
Clear aliases Basic known normalization
Lexically distant aliases Semantic candidate retrieval
Ambiguous aliases Need for context or multiple candidates
Close siblings Relatedness versus equivalence
Parent-child pairs Hierarchical relation handling
Partial-overlap pairs Whether binary synonymy is too coarse
Same-category hard negatives Difficult false positives
Unknown services Rejection and taxonomy coverage
Modifier-sensitive pairs Whether preprocessing removes important meaning

The nearby wrong candidates are especially valuable. Sentence Transformers calls these hard negatives: examples that appear similar but are not correct for the task.

Measure the stages separately

Candidate retrieval

Recall@1
Recall@5
Recall@10

Question:

Was an acceptable concept present among the retrieved candidates?

Final relation decision

Use a confusion matrix over whichever labels you adopt:

exact
broader
narrower
related
reject
unknown

Question:

Once the right candidate was available, did the system assign the correct relation?

Unknown handling

Measure:

unknown false-accept rate
known false-reject rate
review coverage

Question:

When the correct concept was absent, did the system abstain or select a plausible wrong concept?

Operational coverage

automatically accepted
sent to review
rejected

This makes it possible to compare policies, not just models.

For example:

Policy Likely trade-off
Conservative automatic matching Higher precision, more review
Search-oriented expansion Higher recall, more related results
Provider recommendation Requires more context and downstream constraints

A useful threshold is therefore not necessarily the one with the best overall accuracy. It is the one that fits the cost of the downstream error.

Avoid one misleading aggregate score

A system can achieve apparently good overall accuracy if the dataset contains mostly easy aliases, while still failing on the exact cases motivating the post.

I would report the groups separately, especially:

  • unambiguous aliases;
  • ambiguous aliases;
  • close-but-distinct concepts;
  • unknown inputs.

Also, if an alias legitimately maps to multiple concepts, evaluate against the acceptable set or mark it as requiring context. Otherwise the benchmark will classify some reasonable candidates as model errors merely because it imposed a false single-answer assumption.

A small adjacent-domain sanity check

As a quick proxy, I tried a small held-out-title experiment using the current O*NET occupational registry and its alternate job titles.

This is not a service-domain benchmark, and it cannot determine whether any of the pairs in the original post are equivalent. It was only meant to test whether the expected failure modes appear in a mature adjacent taxonomy.

A few observations were consistent with the layered design above:

  • adding definitions or alias information mainly helped put the correct concept somewhere in the candidate list;
  • high-scoring incorrect dense matches were frequently officially related occupations rather than random occupations;
  • some exact alternate-title strings were attached to more than one occupational concept;
  • after deliberately removing the true concept from the candidate inventory, the nearest remaining concept could still receive a convincing similarity score;
  • a simple lexical/dense fusion was not automatically better than each component, even though the two methods recovered different correct cases.

I would interpret that only as a sanity check:

candidate retrieval, ambiguity resolution, relation classification, and unknown rejection are distinct problems.

It does not establish a service-domain model choice or production threshold.

Conditional implementation paths

A possible decision flow is:

If you already have a suitable taxonomy

  1. Keep stable IDs, preferred labels, aliases, definitions, scope notes, and hierarchy.
  2. Preserve the raw provider label.
  3. Retrieve candidates using exact, lexical, and/or dense methods.
  4. Decide the relation in a separate stage.
  5. Retain unknown and review.
  6. Evaluate candidate recall and final decisions separately.

If you have a taxonomy, but it was built for a different purpose

Check whether its granularity matches the downstream decision.

A statistical, procurement, or directory taxonomy may be authoritative for its own purpose while still being too broad, too narrow, or differently organized for provider matching.

In that case, possible options are:

  • use it as a high-level backbone;
  • add an application-specific layer beneath it;
  • map an internal service registry to it;
  • or use it only as one contextual feature.

If you do not have a taxonomy

Start smaller than a full ontology:

frequent canonical concepts
+ curated aliases
+ short scope notes
+ explicit unknown/free-form storage

Grow it from observed provider labels and reviewed failures.

This keeps the initial work proportional to actual usage and avoids designing an enormous hierarchy before knowing which distinctions matter.

If false positives are expensive

Prefer:

exact or strongly supported match
otherwise review / unknown

Use conservative automatic acceptance and preserve related candidates for inspection.

If recall is more important

Return a wider candidate set with explicit relation labels rather than silently declaring every result equivalent.

If you have labeled relation pairs

A task-specific pair classifier or reranker becomes worth testing.

Train and evaluate it on difficult cases:

  • same concept;
  • close sibling;
  • broader or narrower;
  • related but not substitutable;
  • unknown.

If you only have positive synonym pairs

Mine or manually add hard negatives before training. Otherwise the model may learn general topical relatedness rather than the boundary between equivalent and merely related services.

So my default answer would be:

Use a taxonomy or small concept registry as the destination.
Use an alias dictionary for high-confidence known variants.
Use lexical and embedding retrieval to generate candidates.
Use a separate task-specific step to determine the relation.
Preserve unknowns and ambiguous cases instead of forcing a match.
Evaluate retrieval, relation decisions, and rejection separately.

That seems closer to how existing reconciliation and taxonomy systems are structured than searching for one universal service-name normalizer.

The two implementation details that would change the route most are:

  1. whether “match” means the same canonical service, useful search expansion, or actual provider capability; and
  2. which contextual fields are available beyond the short service label.

Everything else—model choice, thresholds, relation granularity, and review policy—can be selected downstream of those decisions.