Aller au contenu principal

Correspondance sémantique

Le Glossaire ESFC utilise une correspondance sémantique avancée assistée par intelligence artificielle pour connecter les termes à travers 10 vocabulaires alimentaires et d'Analyse du Cycle de Vie différents, permettant une mise en correspondance intelligente entre sources et la découverte de relations.

Vue d'ensemble​

La correspondance sémantique crée des relations entre les termes de différentes sources (FoodEx2, Hestia, ecoinvent, etc.) en utilisant une cascade de correspondance sophistiquée à 4 étapes qui combine la correspondance exacte, la détection de synonymes et les représentations vectorielles par intelligence artificielle.

Fonctionnalités clés :

  • Correspondance assistée par intelligence artificielle - Représentations vectorielles OpenAI et Google AI
  • Cascade à 4 étapes - Multiples stratégies de correspondance avec solutions de repli
  • Validation de la qualité - Score de confiance et analyse de la qualité des correspondances
  • Débogage interactif - Visualisation des correspondances en temps réel
  • Sans configuration - Repli sur le mode simulé sans clés d'interface de programmation applicative

Stratégie de correspondance​

Cascade à 4 étapes​

Le système de correspondance sémantique utilise une approche en cascade, essayant des méthodes de plus en plus sophistiquées :

Étape 1: Correspondance contextuelle (confiance la plus élevée)
↓ (si pas de correspondance)
Étape 2: Correspondance exacte du nom
↓ (si pas de correspondance)
Étape 3: Correspondance par synonymes
↓ (si pas de correspondance)
Étape 4: Recherche par représentation vectorielle sémantique

Chaque étape à différents niveaux de confiance et cas d'utilisation.

Étape 1 : Correspondance contextuelle​

Méthode : Combine nom + catégorie pour la correspondance Confiance : 0,95 - 1,0 (très élevée) Cas d'utilisation : Lorsque le nom et le contexte de catégorie correspondent

Algorithme :

function contextualMatch(term: Term, targetTerms: Term[]): Match | null {
const searchKey = `${term.name} ${term.category}`.toLowerCase()

for (const target of targetTerms) {
const targetKey = `${target.name} ${target.category}`.toLowerCase()

if (searchKey === targetKey) {
return {
sourceId: term.id,
targetId: target.id,
confidence: 1.0,
method: 'contextual'
}
}
}
return null
}

Exemple :

Source: FoodEx2 "Apple" (catégorie: "Fruits")
Cible: Hestia "Apple" (catégorie: "Inputs & Products")
Correspondance: ✅ Contextuelle (confiance: 1,0)

Étape 2 : Correspondance exacte du nom​

Méthode : Correspondance exacte de chaîne sur les noms normalisés Confiance : 0,85 - 0,95 (élevée) Cas d'utilisation : Noms identiques entre différentes sources

Normalisation :

  • Conversion en minuscules
  • Suppression des espaces de début et fin
  • Suppression des caractères spéciaux
  • Gestion des pluriels (optionnel)

Algorithme :

function exactMatch(term: Term, targetTerms: Term[]): Match | null {
const normalized = normalizeName(term.name)

for (const target of targetTerms) {
if (normalized === normalizeName(target.name)) {
return {
sourceId: term.id,
targetId: target.id,
confidence: 0.9,
method: 'exact'
}
}
}
return null
}

function normalizeName(name: string): string {
return name.toLowerCase().trim().replace(/[^a-z0-9\s]/g, '')
}

Exemple :

Source: "Wheat grain"
Cible: "wheat grain"
Correspondance: ✅ Exacte (confiance: 0,9)

Étape 3 : Correspondance par synonymes​

Méthode : Dictionnaire de synonymes intégré Confiance : 0,70 - 0,85 (moyenne-élevée) Cas d'utilisation : Noms alternatifs connus et variations courantes

Dictionnaire de synonymes :

const SYNONYMS = {
'beef': ['cattle meat', 'bovine meat'],
'pork': ['pig meat', 'swine meat'],
'milk': ['dairy milk', 'cow milk'],
'wheat': ['common wheat', 'bread wheat'],
'rice': ['paddy rice', 'rice grain'],
'CO₂': ['carbon dioxide', 'co2 emission'],
'CH4': ['methane', 'methane emission'],
'N2O': ['nitrous oxide', 'n2o emission']
}

Algorithme :

function synonymMatch(term: Term, targetTerms: Term[]): Match | null {
const termSynonyms = getSynonyms(term.name)

for (const target of targetTerms) {
const targetNormalized = normalizeName(target.name)

for (const synonym of termSynonyms) {
if (normalizeName(synonym) === targetNormalized) {
return {
sourceId: term.id,
targetId: target.id,
confidence: 0.8,
method: 'synonym',
matchedSynonym: synonym
}
}
}
}
return null
}

Exemple :

Source: "Beef"
Cible: "Cattle meat"
Correspondance: ✅ Synonyme (confiance: 0,8, via "cattle meat")

Étape 4 : Recherche par représentation vectorielle sémantique​

Méthode : Similarité vectorielle assistée par intelligence artificielle utilisant des représentations vectorielles Confiance : 0,50 - 0,70 (moyenne) Cas d'utilisation : Similarité sémantique lorsque les correspondances exactes échouent

Fournisseurs d'intelligence artificielle :

  1. OpenAI (recommandé)

    • Modèle : text-embedding-3-small
    • Dimensions : 1536
    • Économique et précis
  2. Google Generative AI (alternative)

    • Modèle : text-embedding-004
    • Dimensions : 768
    • Bonne alternative à OpenAI
  3. Mode simulé (repli)

    • Représentations vectorielles déterministes basées sur les chaînes
    • Aucune clé d'interface de programmation applicative requise
    • Adapté aux tests

Algorithme :

async function semanticMatch(
term: Term,
targetTerms: Term[],
provider: 'openai' | 'google'
): Promise<Match | null> {
// Générer la représentation vectorielle pour le terme source
const sourceEmbedding = await generateEmbedding(
`${term.name} ${term.category}`,
provider
)

let bestMatch: Match | null = null
let highestSimilarity = 0

for (const target of targetTerms) {
// Générer la représentation vectorielle pour le terme cible
const targetEmbedding = await generateEmbedding(
`${target.name} ${target.category}`,
provider
)

// Calculer la similarité cosinus
const similarity = cosineSimilarity(sourceEmbedding, targetEmbedding)

if (similarity > highestSimilarity && similarity > 0.5) {
highestSimilarity = similarity
bestMatch = {
sourceId: term.id,
targetId: target.id,
confidence: similarity,
method: 'semantic',
similarity: similarity
}
}
}

return bestMatch
}

function cosineSimilarity(a: number[], b: number[]): number {
const dotProduct = a.reduce((sum, val, i) => sum + val * b[i], 0)
const magnitudeA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0))
const magnitudeB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0))
return dotProduct / (magnitudeA * magnitudeB)
}

Exemple :

Source: "Grass-fed beef cattle"
Cible: "Extensive pasture cattle production"
Similarité de représentation vectorielle: 0,72
Correspondance: ✅ Sémantique (confiance: 0,72)

Intégration des fournisseurs d'intelligence artificielle​

Configuration OpenAI​

# Définir la clé d'interface de programmation applicative
export OPENAI_API_KEY="sk-..."

# Exécuter la correspondance sémantique
npm run match-glossaries

Fonctionnalités OpenAI :

  • Derniers modèles de représentation vectorielle
  • Haute précision pour la terminologie alimentaire/Analyse du Cycle de Vie
  • Coûts d'interface de programmation applicative raisonnables
  • Temps de réponse rapides

Exemple d'appel d'interface de programmation applicative :

import OpenAI from 'openai'

const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})

async function getEmbedding(text: string): Promise<number[]> {
const response = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text,
dimensions: 1536
})
return response.data[0].embedding
}

Configuration Google AI​

# Définir la clé d'interface de programmation applicative
export GOOGLE_API_KEY="AIza..."

# Exécuter la correspondance sémantique avec Google
npm run match-glossaries

Fonctionnalités Google AI :

  • Alternative à OpenAI
  • Bon support multilingue
  • Tarification compétitive
  • Représentations vectorielles fiables

Exemple d'appel d'interface de programmation applicative :

import { GoogleGenerativeAI } from '@google/generative-ai'

const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY!)

async function getEmbedding(text: string): Promise<number[]> {
const model = genAI.getGenerativeModel({ model: 'text-embedding-004' })
const result = await model.embedContent(text)
return result.embedding.values
}

Mode simulé (sans clés d'interface de programmation applicative)​

# Exécuter sans clés d'interface de programmation applicative (représentations vectorielles simulées)
npm run match-glossaries:mock

Fonctionnalités du mode simulé :

  • Représentations vectorielles déterministes basées sur les chaînes
  • Pas de coûts d'interface de programmation applicative
  • Adapté aux tests
  • Résultats reproductibles

Algorithme simulé :

function mockEmbedding(text: string, dimensions: number = 1536): number[] {
const embedding = new Array(dimensions).fill(0)

for (let i = 0; i < text.length; i++) {
const charCode = text.charCodeAt(i)
embedding[i % dimensions] += charCode / 1000
}

// Normaliser
const magnitude = Math.sqrt(
embedding.reduce((sum, val) => sum + val * val, 0)
)
return embedding.map(val => val / magnitude)
}

Intégration de base de données​

Les correspondances sémantiques peuvent être stockées dans PostgreSQL avec pgvector pour une recherche de similarité efficace :

Configuration pgvector​

-- Activer l'extension pgvector
CREATE EXTENSION IF NOT EXISTS vector;

-- Créer la table des représentations vectorielles
CREATE TABLE term_embeddings (
id SERIAL PRIMARY KEY,
term_id VARCHAR(255) NOT NULL,
term_name TEXT NOT NULL,
term_source VARCHAR(50) NOT NULL,
embedding vector(1536),
created_at TIMESTAMP DEFAULT NOW()
);

-- Créer un index pour la recherche de similarité
CREATE INDEX ON term_embeddings
USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);

-- Trouver des termes similaires en utilisant la distance cosinus
SELECT
term_id,
term_name,
term_source,
1 - (embedding <=> query_embedding) as similarity
FROM term_embeddings
WHERE term_source != 'source_to_exclude'
ORDER BY embedding <=> query_embedding
LIMIT 10;

Stockage des représentations vectorielles​

import { Pool } from 'pg'

async function storeEmbedding(
termId: string,
termName: string,
source: string,
embedding: number[]
): Promise<void> {
const pool = new Pool({
connectionString: process.env.DATABASE_URL
})

await pool.query(
`INSERT INTO term_embeddings (term_id, term_name, term_source, embedding)
VALUES ($1, $2, $3, $4)`,
[termId, termName, source, `[${embedding.join(',')}]`]
)
}

Validation de la qualité​

Score de confiance​

Chaque correspondance se voit attribuer un score de confiance basé sur la méthode de correspondance :

MéthodePlage de confianceQualité
Contextuelle0,95 - 1,0Excellente
Exacte0,85 - 0,95Très bonne
Synonyme0,70 - 0,85Bonne
Sémantique0,50 - 0,70Acceptable

Seuils de confiance :

  • Confiance élevée (≥ 0,85) : Acceptation automatique
  • Confiance moyenne (0,70 - 0,84) : Révision recommandée
  • Confiance faible (< 0,70) : Révision manuelle requise

Indicateurs de qualité des correspondances​

Bonnes correspondances :

  • ✅ Score de confiance élevé (≥ 0,85)
  • ✅ Catégories similaires
  • ✅ Même domaine (alimentaire, Analyse du Cycle de Vie, emballage)
  • ✅ Descriptions cohérentes

Correspondances douteuses :

  • ⚠️ Confiance moyenne (0,70 - 0,84)
  • ⚠️ Catégories différentes
  • ⚠️ Correspondance inter-domaines
  • ⚠️ Chevauchement partiel des noms

Correspondances médiocres :

  • Confiance faible (< 0,70)
  • ❌ Catégories non liées
  • ❌ Domaines différents
  • ❌ Non-concordance sémantique

Méthodes de validation​

Validation automatisée :

function validateMatch(match: Match): ValidationResult {
const issues: string[] = []

// Vérifier le seuil de confiance
if (match.confidence < 0.5) {
issues.push('Confidence below threshold')
}

// Vérifier la cohérence des catégories
if (match.sourceCategory !== match.targetCategory) {
issues.push('Category mismatch')
}

// Vérifier l'alignement des domaines
if (!domainsAlign(match.sourceDomain, match.targetDomain)) {
issues.push('Domain mismatch')
}

return {
valid: issues.length === 0,
confidence: match.confidence,
issues: issues,
recommendation: issues.length === 0 ? 'accept' : 'review'
}
}

Révision manuelle :

  • Exporter les correspondances en CSV/Excel
  • Réviser les correspondances à faible confiance
  • Vérifier les correspondances inter-domaines
  • Documenter les décisions de validation

Optimisation des performances​

Traitement par lots​

Traiter plusieurs termes par lots pour réduire les appels d'interface de programmation applicative :

async function batchEmbeddings(
texts: string[],
batchSize: number = 100
): Promise<number[][]> {
const batches: number[][][] = []

for (let i = 0; i < texts.length; i += batchSize) {
const batch = texts.slice(i, i + batchSize)
const embeddings = await Promise.all(
batch.map(text => generateEmbedding(text))
)
batches.push(embeddings)
}

return batches.flat()
}

Stratégie de mise en cache​

Mettre en cache les représentations vectorielles pour éviter les appels d'interface de programmation applicative redondants :

import fs from 'fs'

class EmbeddingCache {
private cache: Map<string, number[]>
private cacheFile: string

constructor(cacheFile: string) {
this.cacheFile = cacheFile
this.cache = this.loadCache()
}

async getEmbedding(text: string, provider: string): Promise<number[]> {
const cacheKey = `${provider}:${text}`

if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)!
}

const embedding = await generateEmbedding(text, provider)
this.cache.set(cacheKey, embedding)
this.saveCache()

return embedding
}

private loadCache(): Map<string, number[]> {
if (fs.existsSync(this.cacheFile)) {
const data = JSON.parse(fs.readFileSync(this.cacheFile, 'utf8'))
return new Map(Object.entries(data))
}
return new Map()
}

private saveCache(): void {
const data = Object.fromEntries(this.cache)
fs.writeFileSync(this.cacheFile, JSON.stringify(data, null, 2))
}
}

Limitation de débit​

Respecter les limites de débit des interfaces de programmation applicatives :

import pLimit from 'p-limit'

const limit = pLimit(10) // Max 10 requêtes simultanées

async function matchWithRateLimit(
sourceTerms: Term[],
targetTerms: Term[]
): Promise<Match[]> {
const matches = await Promise.all(
sourceTerms.map(term =>
limit(() => findBestMatch(term, targetTerms))
)
)

return matches.filter(m => m !== null) as Match[]
}

Cas d'utilisation​

Correspondance inter-vocabulaires​

FoodEx2 vers Hestia :

FoodEx2: A010101 (Blé tendre)
↓ correspondance sémantique
Hestia: term/crop-wheat
↓ fournit
Données d'incidences environnementales

ecoinvent vers Eaternity :

ecoinvent: market for wheat grain | GLO
↓ correspondance sémantique
Eaternity: FlowNode.product_name = "Wheat grain"
↓ permet
Calcul de l'empreinte carbone EOS

Import de données utilisateur​

Correspondance d'en-têtes de colonnes CSV :

En-tête utilisateur: "Produktname" (allemand)
↓ correspondance sémantique
Propriété Eaternity: eaternity-property-productname
↓ correspond à
Champ de l'interface de programmation applicative EOS: FlowNode.product_name

Applications de recherche​

Analyse du Cycle de Vie multi-sources :

Question de recherche: "Empreinte carbone du bœuf biologique"
↓ correspondance sémantique
FoodEx2: F010101 (Beef) + facette Biologique
Hestia: term/livestock-cattle-organic
ecoinvent: cattle for slaughtering, organic | CH
↓ combine
Analyse du Cycle de Vie complète avec multiples sources de données

Débogage interactif​

Fonctionnalités de l'interface web​

Le site web du Glossaire ESFC fournit des outils de débogage interactifs :

Visualisation des correspondances :

  • Affichage de la qualité des correspondances en temps réel
  • Visualisation du score de confiance
  • Indicateur de méthode (contextuelle, exacte, synonyme, sémantique)
  • Comparaison des termes source/cible

Outils de débogage :

  • Explication de la correspondance (pourquoi cette correspondance a été sélectionnée)
  • Correspondances alternatives (autres correspondances potentielles)
  • Scores de similarité pour les correspondances sémantiques
  • Comparaison de catégorie et de domaine

Options d'export :

  • Exporter les correspondances en CSV
  • Télécharger le graphe de relations
  • Générer un rapport de correspondance
  • Sauvegarder pour révision manuelle

Exécution de la correspondance sémantique​

Ligne de commande​

# Correspondance sémantique complète (production)
npm run match-glossaries

# Mode test (données échantillons)
npm run match-glossaries:test

# Mode simulé (sans clés d'interface de programmation applicative)
npm run match-glossaries:mock

# Paires de sources spécifiques
node scripts/glossary-matcher.js \
--source foodex2 \
--target hestia \
--output mappings.json

Configuration​

// Configuration de scripts/glossary-matcher.js
const config = {
provider: 'openai', // ou 'google'
verbose: true,
mockMode: false,
confidenceThreshold: 0.5,
maxResults: 10,
batchSize: 100,
cacheFile: './cache/embeddings.json'
}

Formats de sortie​

JSON :

{
"matches": [
{
"sourceId": "foodex2-A010101",
"sourceName": "Common wheat",
"targetId": "hestia-term-crop-wheat",
"targetName": "Wheat crop",
"confidence": 0.95,
"method": "contextual",
"validated": true
}
],
"statistics": {
"totalSourceTerms": 31601,
"totalTargetTerms": 36044,
"matchesFound": 15823,
"averageConfidence": 0.82,
"methodBreakdown": {
"contextual": 8934,
"exact": 4521,
"synonym": 1876,
"semantic": 492
}
}
}

Graphe de relations (JSON-LD) :

{
"@context": "http://www.w3.org/2004/02/skos/core#",
"@graph": [
{
"@id": "foodex2:A010101",
"@type": "Concept",
"prefLabel": "Common wheat",
"exactMatch": "hestia:term-crop-wheat",
"relatedMatch": "ecoinvent:market-wheat-grain"
}
]
}

Bonnes pratiques​

Stratégie de correspondance​

  1. Commencer par les méthodes à haute confiance

    • S'appuyer sur les correspondances contextuelles et exactes lorsque possible
    • Utiliser la recherche sémantique comme repli
    • Valider manuellement les correspondances à faible confiance
  2. Correspondance spécifique au domaine

    • Faire correspondre les termes alimentaires aux sources alimentaires (FoodEx2, Hestia)
    • Faire correspondre les processus d'Analyse du Cycle de Vie à ecoinvent
    • Faire correspondre les propriétés au schéma Eaternity
  3. Qualité plutôt que quantité

    • Préférer moins de correspondances de haute qualité
    • Réviser et valider les correspondances douteuses
    • Documenter les décisions de correspondance

Optimisation des performances​

  1. Traitement par lots

    • Traiter les termes par lots
    • Utiliser des requêtes simultanées (avec limites)
    • Mettre en cache les représentations vectorielles
  2. Mises à jour incrémentales

    • Ne re-correspondre que les termes modifiés
    • Maintenir l'historique des correspondances
    • Suivre le statut de validation
  3. Gestion des ressources

    • Surveiller l'utilisation et les coûts de l'interface de programmation applicative
    • Utiliser le mode simulé pour le développement
    • Mettre en cache les représentations vectorielles localement

Documentation associée​