Ga naar inhoud

Automatische vertaling

Dit artikel is automatisch vertaald vanuit de oorspronkelijke Engelse versie.

De definitieve gids voor NER in 2026: encoders, LLM's en de 3-laagse productiearchitectuur

Twee jaar geleden betekende het kiezen van een NER-aanpak dat je moest kiezen tussen snelheid (encoder-modellen) en nauwkeurigheid (LLM's). Die trade-off is verdwenen. Een GLiNER-model met 300M parameters evenaart de zero-shot nauwkeurigheid van een 13B UniNER en draait daarbij 100x sneller. Een nieuwere bi-encoder-variant schaalt naar miljoenen entiteitstypen met een 130x throughput-voordeel ten opzichte van de oorspronkelijke cross-encoder. Het productiepatroon dat hieruit ontstond: gebruik LLM's om data te labelen, fine-tune compacte encoders en deploy met ONNX of Rust.

Ik bouwde de companion repo en benchmarkte elke grote aanpak. Encoders wonnen de productiemarkt voor NER. LLM's blijven essentieel, maar dan als generatoren van trainingsdata in plaats van inference-engines. Deze gids behandelt de papers, benchmarks en deploymentpatronen achter die verschuiving.

Companion repo: ner-field-guide — uitvoerbare demo's voor GLiNER, ONNX-export, een LLM-as-teacher-pijplijn en gestructureerde extractie met Instructor.

TL;DR: Je hoeft NER-modellen niet meer vanaf nul te bouwen. Voor zero-shot extractie verslaat GLiNER op CPU via ONNX zelfs de nieuwste LLM's tegen vrijwel nul kosten. Voor domeinspecifieke taken levert de LLM-as-teacher-pijplijn (LLM labels → review → fine-tune) encoders op die 90-93%+ F1 halen. Voor gevallen die redenering vereisen gebruik je Instructor of Outlines met een LLM API — maar reken op \$2+/1K docs. De 3-laagse architectuur combineert alle drie.


Waarom NER nu belangrijker is: agents, RAG en document intelligence

NER betekent nog steeds hetzelfde: benoemde spans in tekst vinden en classificeren. Wat veranderde, is waar het opduikt. Vroeger was het de laatste stap in een NLP-pijplijn, het stille utility-onderdeel waar niemand blogposts over schreef. Nu is het een bouwsteen binnen RAG-systemen, agent loops en documentverwerkingsplatformen. Daarom is de NER-onderzoeksgolf van 2024-2026 relevant buiten academische benchmarks.

RAG: betere retrieval via entiteitsextractie

Standaard RAG — tekst chunken, embeddings maken, ophalen op basis van similariteit en hopen op het beste — valt uit elkaar wanneer gebruikers vragen naar specifieke entiteiten. Als iemand vraagt "wat zei Anthropic over model safety in Q4 2024?", moet het systeem "Anthropic" en "Q4 2024" herkennen als filters, en niet alleen vertrouwen op embedding-similariteit.

Tijdens het indexeren extraheer je entiteiten uit elke chunk en sla je die op als metadata: {"organizations": ["Anthropic"], "dates": ["Q4 2024"], ...}. Zo kun je eerst filteren op entiteit voordat je vector search uitvoert. Knowledge graph RAG (GraphRAG, LlamaIndex property graphs) gaat verder: NER plus relation extraction bouwt een graaf die multi-hop-vragen kan beantwoorden waar vlakke embeddings dat niet kunnen.

Tijdens query time sturen entiteiten die uit de vraag van de gebruiker zijn gehaald de routing aan. Een vraag met een bedrijfsnaam gaat naar een finance-index; een vraag met medicijnnamen naar een klinische knowledge base. GLiNER werkt hier goed omdat query-entiteiten onvoorspelbaar zijn — je kunt niet voor elk nieuw entiteitstype waar gebruikers naar vragen opnieuw retrainen.

AI-agents: tekst omzetten naar gestructureerde feiten

Agents ontvangen ongestructureerde tekst — webpagina's, API-responses, gebruikersberichten — en moeten daarop handelen. NER zet die tekst om in gestructureerde feiten waar de agent over kan redeneren, die hij kan opslaan of doorgeven aan tools.

Er zijn twee plekken waar dit het belangrijkst is.

De eerste is tool routing. Wanneer een gebruiker zegt "plan een meeting met Sarah Chen van Accenture op donderdag om 14:00", moet de agent PERSON: Sarah Chen, ORGANIZATION: Accenture en DATETIME: Thursday 2pm extraheren voordat de calendar API wordt aangeroepen. Een encoder-NER-model doet dit in minder dan 10 ms. Een LLM voegt 1-2 seconden per call toe, en dat stapelt zich op over multi-step workflows totdat je "instant" agent aanvoelt alsof hij draait op een filosofiediploma.

De tweede is entiteitstracking over gesprekken heen. Agentgeheugensystemen moeten weten dat "Sarah" in beurt 3 en "Ms. Chen" in beurt 12 dezelfde persoon zijn. NER identificeert de spans; entity linking koppelt ze aan dezelfde ID.

De beperking in beide gevallen is latency. Een NER-call van 200 ms binnen een agent chain van 10 stappen voegt 2 seconden waarneembare vertraging toe. Daarom zijn encoder-modellen, en niet LLM-gebaseerde extractie, de juiste keuze voor entiteitswerk binnen agent loops.

Document intelligence: van afbeeldingen naar gestructureerde data

OCR zet afbeeldingen om in tekst. NER zet tekst om in gestructureerde velden. Samen drijven ze documentdigitalisering op schaal aan.

De standaardpijplijn: voer OCR uit (Tesseract, Azure Document Intelligence, AWS Textract) om tekst en bounding boxes te krijgen, en voer daarna NER uit om gestructureerde velden te extraheren — invoice_number, vendor_name, line_items, total, due_date — uit wat eerder een JPEG van een papieren factuur was. Dezelfde aanpak werkt voor contracten, medische dossiers en regulatoire filings.

Moderne platformen combineren drie stappen: layout understanding (is dit een header of een tabelcel?), entiteitsextractie (welk type is deze tekst?) en relation extraction (welke waarden horen bij elkaar?). GLiNER 2 verwerkt alle drie in één forward pass; één modelcall kan {vendor: "Acme Corp", amount: "\$4,200", due_date: "2026-04-15"} uit een factuur teruggeven.

Hier tellen kosten. Een middelgroot accountantskantoor verwerkt maandelijks tienduizenden facturen. Elke factuur via een LLM routeren, zelfs een kostenefficiënt model als GPT-5.4 Nano, kost honderden dollars per dag. Een fine-getunede GLiNER op CPU kost centen. Je CFO merkt het verschil. Het pad: annoteer 500 voorbeeldfacturen met een LLM, fine-tune GLiNER op de resultaten en deploy op CPU voor \$0.10/uur.

PII-detectie en LLM-guardrails

Privacyregelgeving (GDPR, HIPAA, CCPA) vereist dat persoonlijke data wordt gevonden voordat die downstream systemen bereikt. Voor LLM-deployments betekent dat inputs scannen voordat ze het model raken en outputs scannen voordat ze de gebruiker bereiken.

NER handelt dit direct af. De-identificatiemodellen vinden spans van PERSON, SSN, PHONE, EMAIL en ADDRESS en redigeren die of vervangen ze door fictieve equivalenten. John Snow Labs' klinische modellen halen 96% F1 op PHI-detectie (vs. Azure 91%, AWS 83%, GPT-4o 79%) terwijl ze 100K+ klinische notities per dag verwerken.

Voor LLM-guardrails werkt NER als een pre-screeninglaag: scan gebruikersinput op PII voordat die naar een externe API gaat, en blokkeer of anonimiseer daarna. Dit is sneller en eenvoudiger dan het LLM vragen zichzelf te modereren. GLiNER is hier vooral nuttig omdat PII-categorieën per jurisdictie verschillen. Je kunt nieuwe entiteitstypen zoals "genetische informatie" onder nieuwe regelgeving toevoegen zonder retraining.


GLiNER herschrijft de NER-economie met een model van 300M parameters

GLiNER (NAACL 2024, Zaratiana et al.) maakte encoder-gebaseerde NER concurrerend met LLM's tegen een fractie van de kosten. In plaats van NER te behandelen als sequence labeling of text generation, behandelt GLiNER het als een matchingprobleem: score elke kandidaat-tekstspan (elke aaneengesloten reeks woorden zoals "Bill Gates" of "Microsoft") tegen elk entiteitstype-label, en behoud daarna de paren met hoge score.

Het model neemt entiteitstype-labels en inputtekst als één enkele sequentie: [ENT] person [ENT] organization [ENT] date [SEP] Bill Gates founded Microsoft.... Een bidirectionele transformer (DeBERTa-v3) encodeert alles gezamenlijk.

Uit de output bouwt het model twee sets representaties op: één voor entiteitstypen (uit [ENT] tokenposities) en één voor tekstspans (door start- en eindtokenvectoren te combineren via een kleine FFN). Een dot product tussen een spanrepresentatie en een entiteitstype-representatie geeft een score.

Pas sigmoid toe en je krijgt de waarschijnlijkheid dat de span van token \(i\) tot token \(j\) behoort tot entiteitstype \(t\): \(\\phi(i, j, t) = \\sigma(S_{ij}^T \\cdot q_t)\), waarbij \(S_{ij}\) de spanvector is die door de FFN wordt geproduceerd en \(q_t\) de entiteitstype-embedding uit de corresponderende [ENT] token is (Zaratiana et al., 2024, Eq. 1). Spans zijn begrensd op 12 tokens om het snel te houden.

GLiNER-architectuur: tokens voor entiteitstypen en teksttokens worden gezamenlijk geëncodeerd door DeBERTa, waarna spanrepresentaties worden gescoord tegen entiteitstype-embeddings via dot product

In de praktijk betekent dit dat elke natuurlijke-taalbeschrijving als label werkt tijdens inference. Geen retraining. Je geeft gewoon de entiteitstypen door die je wilt ("persoon", "bijwerking van geneesmiddel", "financieel instrument"), en het model scoort spans ertegen. Er zijn drie formaten beschikbaar: GLiNER-S (50M params), GLiNER-M (90M) en GLiNER-L (300M). Trainingsdata komt uit de Pile-NER-dataset: 44.889 passages met 240K entiteitsspans over 13K entiteitstypen, allemaal gelabeld door ChatGPT. Het trainen van GLiNER-L duurt ongeveer 4 uur op één enkele A100 (Zaratiana et al., 2024).

Benchmarkresultaten

Zero-shot-resultaten uit Zaratiana et al. (2024), Tabellen 1 en 2:

Model Params CrossNER F1 Gem. (20 datasets)
GLiNER-L 300M 60.9% 47.8%
GoLLIE 7B 58.0% --
UniNER-13B 13B 55.6% --
GLiNER-M 90M 55.4% --
UniNER-7B 7B 53.7% 45.7%
GLiNER-S 50M 52.7% --
ChatGPT (GPT-3.5) -- 47.5% 36.5%

GLiNER-M met 90M parameters evenaart UniNER-13B bijna (55.4% vs 55.6% F1) terwijl het 140x kleiner is. Zelfs de kleine GLiNER-S van 50M verslaat ChatGPT (GPT-3.5) met 5 F1-punten. De meertalige variant — alleen getraind op Engelse data — verslaat ChatGPT in 8 van de 10 niet-Engelse talen (Zaratiana et al., 2024). Let op: nieuwere LLM's (GPT-5.4, Claude 4.6) scoren waarschijnlijk hoger op deze benchmarks, maar de kostenkloof is alleen maar groter geworden — deze modellen zijn nog steeds ordes van grootte duurder dan een 90M encoder op CPU.

Het ecosysteem is groot: 280+ GLiNER-compatibele modellen op HuggingFace, ~350.000 PyPI-downloads per maand, ~2.800 GitHub-sterren. Varianten dekken biomedische tekst, PII-detectie, nieuws en meertalige ondersteuning.

Uit quickstart.py:

from gliner import GLiNER

model = GLiNER.from_pretrained("urchade/gliner_medium-v2.1")
text = "Bill Gates founded Microsoft on April 4, 1975."
labels = ["person", "organization", "date"]
entities = model.predict_entities(text, labels, threshold=0.5)

for entity in entities:
    print(f"  {entity['text']} => {entity['label']} ({entity['score']:.3f})")
# Bill Gates => person (0.987)
# Microsoft => organization (0.991)
# April 4, 1975 => date (0.974)

Hoe GLiNER zich verhoudt tot spaCy

Geen enkele NER-gids is compleet zonder spaCy~21M downloads per maand, de kakkerlak onder de NLP-libraries (liefdevol bedoeld — het overleeft alles). Maar het lost een ander probleem op dan GLiNER.

De pijplijnen van spaCy (en_core_web_sm, en_core_web_trf) doen closed-vocabulary NER: een vaste set entiteitstypen (PERSON, ORG, GPE, DATE, enz.) die tijdens training is gedefinieerd. Wil je een nieuw entiteitstype? Verzamel gelabelde data en retrain. De transformer-backed en_core_web_trf haalt 89.8% F1 op OntoNotes 5.0, maar alleen voor zijn 18 vooraf gedefinieerde typen.

GLiNER doet open-vocabulary NER: elk label werkt tijdens inference, zonder retraining. Dat maakt het de betere keuze wanneer entiteitstypen vooraf onbekend zijn, vaak veranderen of domeinspecifiek zijn ("bijwerking van geneesmiddel", "financieel instrument", "threat indicator").

Mijn aanbeveling: gebruik spaCy voor standaard entiteitstypen waarvoor pretrained pijplijnen goed gevalideerd zijn. Gebruik GLiNER wanneer je flexibele, zero-shot-typen nodig hebt of wanneer je pijplijn zich zonder retraining moet aanpassen. Ze werken goed samen — spaCy verzorgt tokenization en sentence splitting, GLiNER verzorgt entiteitsextractie.


UniNER en NuNER: hoe klein kun je gaan?

UniNER (ICLR 2024, Zhou et al.) en NuNER (EMNLP 2024, Bogdanov et al.) distilleren allebei LLM-annotaties naar kleinere NER-modellen — maar ze verschillen van mening over hoe klein je kunt gaan.

UniNER: het maximalistische pad

UniNER fine-tunet LLaMA-7B/13B op 44.889 NER-paren (240K entiteiten, 13K typen) gegenereerd door ChatGPT. Voor elk entiteitstype beantwoordt het model "What describes [type] in the text?" en geeft JSON-lijsten terug. Een belangrijke trainingstruc: frequency-based negative sampling verhoogt F1 van 31.5% naar 53.4% (Zhou et al., 2024).

UniNER-7B haalt 41.7% zero-shot F1 over 43 datasets — 7 punten beter dan de 34.9% van ChatGPT. De 13B-variant komt uit op 43.4%, slechts 1.7 punt meer voor bijna het dubbele aan compute (Zhou et al., 2024).

Het productieprobleem: als 7B autoregressief model heeft UniNER N forward passes nodig voor N entiteitstypen, vereist het 14GB+ VRAM (dus daarmee is je GPU-budget voor de lunch al op), en het heeft een restrictieve CC BY-NC 4.0-licentie.

NuNER: het minimalistische pad

NuNER start vanuit RoBERTa-base (125M parameters) en gebruikt contrastive training met 4,38 miljoen GPT-3.5-annotaties over 200K concepten — totale annotatiekosten onder \$500. Na training wordt de conceptencoder weggegooid; de tekstencoder past vervolgens in elke standaard NER-pijplijn als vervanging voor RoBERTa (Bogdanov et al., 2024).

De resultaten: NuNER verslaat gewone RoBERTa met 6-15 F1-punten over alle few-shot-groottes. Met slechts een dozijn voorbeelden per entiteitstype evenaart NuNER UniNER-7B terwijl het 56x kleiner is (Bogdanov et al., 2024).

Beide papers laten hetzelfde zien: het distilleren van LLM-annotaties naar kleinere modellen levert NER op die de teacher verslaat. En je hebt geen 7B parameters nodig — 125M is genoeg wanneer fine-tuningdata beschikbaar is, met MIT-licentie en CPU-vriendelijke inference.


GLiNER 2: één model, vier taken

Het oorspronkelijke GLiNER-ecosysteem kreeg een groeiend probleem: aparte modellen voor NER (GLiNER), relation extraction (GLiREL), classificatie (GLiClass) en document-level RE (GLiDRE) — elk met zijn eigen deployment, zijn eigen Docker-container en zijn eigen regel in je spreadsheet "services waarvan we bidden dat ze niet stukgaan". GLiNER 2 (EMNLP 2025, Zaratiana et al.) voegt alle vier samen in één 205M-parameter model met een schema-gedreven interface.

De architectuur behoudt het cross-encoder-ontwerp maar vergroot de context naar 2.048 tokens (4x het origineel) en voegt declaratieve schema's toe om extractietaken te definiëren. De training gebruikt 135.698 echte documenten geannoteerd met GPT-4o plus 118.636 synthetische voorbeelden (Zaratiana et al., 2025).

Op zero-shot CrossNER scoort GLiNER 2 0.590 F1 — dicht bij de 0.599 van GPT-4o zoals gebenchmarkt in de paper (medio 2025). Nieuwere modellen zoals GPT-5.4 scoren waarschijnlijk hoger, maar tegen een fractie van de snelheid en kosten van GLiNER 2. Voor classificatie haalt het 0.72 gemiddeld over 7 benchmarks, beter dan DeBERTa-v3-large (0.69). Op CPU draait GLiNER 2 classificatie in 130-208 ms, onafhankelijk van het aantal labels. DeBERTa schaalt lineair: 1.714 ms voor 5 labels, 16.897 ms voor 50 (Zaratiana et al., 2025).

from gliner2 import GLiNER2
extractor = GLiNER2.from_pretrained("fastino/gliner2-base-v1")

# Multi-task composition in ONE forward pass
schema = (extractor.create_schema()
    .entities({"person": "Names of people", "company": "Organization names"})
    .classification("sentiment", ["positive", "negative", "neutral"])
    .relations(["works_for", "founded", "located_in"])
    .structure("product_info")
        .field("name", dtype="str")
        .field("price", dtype="str"))
results = extractor.extract(text, schema)

Eén modeldeployment vervangt er vier. Eenvoudigere infrastructuur, concurrerende nauwkeurigheid.


De bi-encoder: schalen naar NER met een miljoen labels

De oorspronkelijke GLiNER encodeert labels en tekst samen — en dat creëert een bottleneck. Meer entiteitstypen betekent een langere inputsequentie, en de performance zakt snel voorbij ~30 typen. De GLiNER bi-encoder (februari 2026, Stepanov et al.; arXiv 2602.18487) lost dit op door tekst- en labelencoding op te splitsen in twee aparte transformers.

Cross-encoder vs bi-encoder: de cross-encoder encodeert labels en tekst gezamenlijk, terwijl de bi-encoder aparte encoders gebruikt met vooraf berekende label-embeddings

De tekstencoder gebruikt ModernBERT (Ettin-familie), de labelencoder gebruikt sentence transformers (BGE of MiniLM). Spans en labels worden gescoord via dot product. De truc: embeddings van entiteitstypen kunnen één keer vooraf worden berekend en gecachet. Tijdens inference hoeft alleen de tekst nog geëncodeerd te worden — label lookup is direct.

Er zijn vier modelgroottes beschikbaar, allemaal gebenchmarkt op CrossNER (Stepanov et al., 2026, Tabel 1):

Model Parameters CrossNER F1 Throughput (H100) Met vooraf berekende labels
bi-edge-v2.0 60M 54.0% 13.64 ex/s 24.62 ex/s
bi-small-v2.0 108M 57.2% 7.99 ex/s 15.22 ex/s
bi-base-v2.0 194M 60.3% 5.91 ex/s 9.51 ex/s
bi-large-v2.0 530M 61.5% 2.68 ex/s 3.60 ex/s

Bij 1.024 entiteitstypen verliest de bi-encoder (edge, vooraf berekend) slechts 5.2% throughput ten opzichte van één enkel label. De cross-encoder verliest 98.7% (10.7 → 0.14 ex/s). Dat is een 130x throughput-voordeel op schaal. Met 100 entiteitstypen op één enkele H100 verwerkt de bi-encoder 1,96 miljoen predicties per dag versus 368K voor de cross-encoder (Stepanov et al., 2026).

De nauwkeurigheid blijft ook overeind. Bi-encoder-large haalt 61.5% CrossNER F1, iets boven de 60.9% van de cross-encoder. De auteurs bevelen bi-base-v2.0 (194M) aan als sweet spot: 98% van de nauwkeurigheid van het grote model bij 2,6x de snelheid (Stepanov et al., 2026).

from gliner import GLiNER

model = GLiNER.from_pretrained("knowledgator/gliner-bi-base-v2.0")

# Pre-compute embeddings for massive label sets — encode once, use forever
entity_types = ["person", "organization", "date"]  # Can be thousands or millions
entity_embeddings = model.encode_labels(entity_types, batch_size=8)

# Inference only encodes text — labels are a cached lookup
outputs = model.batch_predict_with_embeds(texts, entity_embeddings, entity_types)

Toepassingen zijn onder andere biomedische NER tegen de UMLS-ontologie (4M+ concepten), enterprise-taxonomieën die evolueren zonder modelretraining, en entity linking via het companion-framework GLiNKER.


LLM's als teachers: de \\(70-pijplijn die het \\\)8/uur-model verslaat

Het LLM-as-teacher-patroon is een standaardproductiepijplijn geworden. Drie studies laten zien wat het kost en wat het oplevert.

De LLM-as-teacher-pijplijn: een LLM labelt ruwe data, mensen reviewen een subset, de encoder wordt fine-getuned en gedeployed tegen 80x lagere kosten

De CFM-case study

Capital Fund Management extraheerde bedrijfsnamen uit ~900K headlines van financieel nieuws. Zero-shot GLiNER scoorde 87.0% F1. Ze gebruikten Llama 3.1-70b (destijds het sterkste open model) om de volledige dataset in ~8 uur te annoteren voor ~\$70, waarna mensen nog 2.714 samples reviewden via Argilla in nog eens 8 uur.

Fine-tuning van GLiNER op deze data duwde de performance naar 93.4% F1 — zelfs beter dan de 92.7% van de Llama 70b-teacher. Het fine-getunede model draait op \\(0.10/uur op CPU** versus \\\)8/uur voor de teacher — 80x goedkoper** met betere nauwkeurigheid (CFM Case Study). Vandaag zou je nog betere teacher-annotaties krijgen met Llama 4 Maverick of GPT-5.4 Mini tegen vergelijkbare of lagere kosten.

De Refuel AI-studie

Refuel AI benchmarkte LLM-labeling op 8 NLP-datasets, waaronder CoNLL-2003. GPT-4 (maart 2023) haalde 88.4% overeenkomst met ground truth — hoger dan 86.2% voor ervaren menselijke annotators. LLM's waren 20x sneller en 7x goedkoper. Hun ensembling-aanpak — goedkope modellen voor makkelijke voorbeelden, GPT-4 voor moeilijke — bereikte 95%+ overeenkomst terwijl de kosten laag bleven (Refuel AI Technical Report). Met GPT-5.4 en Claude 4.6 nu beschikbaar is de kwaliteit van teachers alleen maar verbeterd.

Tonic.ai Clinical NER

Tonic.ai liet zien dat het patroon ook werkt voor klinische NER. Alleen LLM-annotaties trainden een RoBERTa LoRA-model naar 0.70 F1 op het NCBI Disease Corpus (vs. 0.81 met volledig menselijke labels). Op extractie van healthcare ID's haalde het 0.947 F1 — boven de productiedrempel van 0.914 — met nul menselijk gelabelde data.

De standaardpijplijn

Het productierecept is uitgekristalliseerd in zes stappen:

  1. Schrijf annotatierichtlijnen in natuurlijke taal
  2. Maak een kleine menselijk gelabelde validatieset (50-200 documenten)
  3. Gebruik een LLM (GPT-5.4 Mini, Llama 4 Maverick of Qwen3.5) om bulk-trainingsdata te labelen
  4. Review een subset via Argilla of Label Studio
  5. Fine-tune een compacte encoder (GLiNER, SpanMarker, RoBERTa)
  6. Deploy tegen 16-80x lagere inferencekosten

De kosten van NER zijn niet langer "annoteer al je trainingsdata door een leger undergrads in te huren." Het is "bouw een goede validatieset, schrijf duidelijke richtlijnen en laat de LLM de rest doen."


Waar GLiNER faalt en LLM's essentieel blijven

De Sease-benchmark (oktober 2025) testte GLiNER tegen GPT-4.1-mini op 30 query-parsingtaken. GPT-4.1-mini behaalde 100% volledig correct. GLiNER kwam uit op 53% (16 van 30). Maar GLiNER reageerde in 0.08 seconden versus de 1.21 seconden van het LLM — 15x sneller.

GLiNER faalt in drie specifieke patronen:

  1. Impliciete entiteiten: "event" extraheren uit "Elton John performed at Madison Square Garden" — nergens staat letterlijk "event", maar het LLM leidt "concert" af
  2. Gevoeligheid voor labelformulering: "2022" scoort 0.388 tegen "date" maar 0.958 tegen "year" — kleine labelwijzigingen veroorzaken grote scoreverschillen
  3. Value mapping: GLiNER geeft de exacte oppervlaktetekst terug ("family houses") in plaats van de canonieke waarde ("Single family house") — LLM's doen dit vanzelf

Geneste en overlappende entiteiten

GLiNER worstelt ook met geneste entiteiten. In "New York University" kan een mens zowel "New York" (LOCATION) als "New York University" (ORGANIZATION) labelen. GLiNER kiest alleen de span met de hoogste score. Dit is relevant in biomedische tekst ("acute myeloid leukemia" bevat zowel een ziekte als een modifier) en juridische tekst (geneste organisatorische hiërarchieën). Gespecialiseerde modellen kunnen nesting aan, maar het flat-span-ontwerp van GLiNER niet.

In de praktijk: GLiNER verwerkt expliciete entiteitsextractie — de kern van productie-NER. LLM's nemen het over wanneer extractie inferentie, redenering of mapping naar vooraf gedefinieerde ontologieën vereist. Gebruik beide.


NER evalueren: metrics, valkuilen en testsets bouwen

Ik heb teams modellen zien shippen die 95% F1 scoorden op hun testset en in productie faalden — omdat de testset de echte documentverdeling niet weerspiegelde. Je testset is niet je productiedata. Ik weet dat dit obvious klinkt. Ik heb het toch fout zien gaan.

De kernmetrics

  • Entity-level F1: de standaardmetric. Een predictie is alleen correct als zowel de span-grenzen als het type exact overeenkomen met ground truth. Dit rapporteren de meeste papers.
  • Token-level F1: scoort elk token onafhankelijk. Dit blaast resultaten op omdat het grotendeels correct hebben van een lange entiteit partial credit oplevert. Geef de voorkeur aan entity-level F1.
  • Precision vs Recall: deze hebben vaak asymmetrische kosten. Voor de-identificatie is recall belangrijker — een naam missen is erger dan te veel redigeren. Voor database-extractie is precision belangrijker — foutieve entries vervuilen downstream analyse.

Veelvoorkomende evaluatievalkuilen

  1. Opblazing door partial match: "Bill" geëxtraheerd terwijl het gold-label "Bill Gates" is — sommige scripts tellen dit als partial match. Gebruik exact span matching tenzij je een reden hebt om dat niet te doen.
  2. Typeverwarring: "Microsoft" correct geïdentificeerd als span maar gelabeld als PERSON in plaats van ORG moet nul scoren. Controleer of je evaluatiecode dit goed afhandelt.
  3. Leakage van de testset: als testentiteiten overlappen met trainingsentiteiten, worden scores opgeblazen. Zero-shot-benchmarks (CrossNER, Few-NERD) bestaan om generalisatie te testen.

Een domeintestset bouwen

Voor productie-evaluatie raad ik aan:

  1. Sample uit productiedata, niet uit gecureerde voorbeelden. Neem de rommelige documenten mee die je model echt gaat zien.
  2. 200-500 geannoteerde documenten geven stabiele F1-schattingen. Onder 100 zijn betrouwbaarheidsintervallen te breed.
  3. Minimaal twee annotators, met inter-annotator agreement (Cohen's kappa > 0.8). Als mensen het oneens zijn, kan je model het niet beter doen.
  4. Stratificeer op moeilijkheid — makkelijke gevallen (schone tekst, standaardtypen) en moeilijke gevallen (ambigue entiteiten, jargon, ruisende tekst).

Productie-NER in vier sectoren

Hier zijn de meest volwassen NER-deployments die ik heb gevonden, met concrete cijfers.

Gezondheidszorg

De gezondheidszorg heeft de meest volwassen NER-tooling. John Snow Labs heeft 2.500+ pretrained modellen, waaronder 1.200+ voor healthcare, die 400+ klinische entiteitstypen dekken en mappen op ICD-10, SNOMED CT, LOINC en RxNorm. Hun de-identificatiemodellen halen 96% F1 (vs. Azure 91%, AWS 83%, GPT-4o 79%), met Providence St. Joseph Health dat dagelijks 100K-500K klinische notities verwerkt.

Het open-source-OpenMed-project biedt 380+ biomedische NER-modellen met 29.7M HuggingFace-downloads en zet nieuwe state-of-the-art op 10 van de 12 publieke biomedische benchmarks.

Financiële NER

De belangrijkste use case: extractie uit SEC-filings. Finance NLP van John Snow Labs extraheert 11+ entiteitstypen uit 10-K/10-Q-filings (adressen, tickers, fiscale jaren, beurzen). Varianten van FinBERT-MRC halen 0.87-0.93 F1 op financiële entiteitstaken. De grootste uitdaging: lange documenten en geneste entiteiten in complexe financiële instrumenten.

E-commerce

NER op enorme schaal. Walmart's EAMT-systeem (KDD 2023) traint op 965 miljoen queries met ~60 entiteitslabels en zorgt voor een 0.51% GMV-stijging in A/B-tests — miljoenen aan incrementele omzet. Het framework TripleLearn van Home Depot (AAAI 2021) bracht NER F1 via iteratieve training van 69.5 naar 93.3.

Cybersecurity

Het iACE-systeem (CCS 2016) verwerkte 71.000 artikelen uit 45 securityblogs en extraheerde 900K IOC-items met 98% precision en 93% recall. Moderne systemen zoals CyNER combineren DeBERTa (F1 >91%) met regex-gebaseerde IOC-heuristieken. De uniforme dataset CyberNER (2025) harmoniseert vier datasets tot 21 STIX 2.1-gealigneerde entiteitstypen, met RoBERTa op 0.736 F1.


Deployment-optimalisatie: van Python naar sub-millisecond inference

Ik testte drie manieren om GLiNER voor productie te versnellen in de companion repo.

ONNX-export

GLiNER heeft native ONNX-conversie en er bestaan al vooraf geconverteerde modellen op HuggingFace (onnx-community/gliner_small-v2.1). ONNX Runtime levert op CPU 1.5-3x versnelling op ten opzichte van PyTorch, met vier optimalisatieniveaus van basic tot mixed-precision.

Uit onnx_export.py:

# Export with quantization
# python convert_to_onnx.py --model_path model/ --save_path onnx/ --quantize True

# Load ONNX model — same API, faster inference
from gliner import GLiNER
model = GLiNER.from_pretrained("path/to/model", load_onnx_model=True)

# Same predict_entities call, 1.5-3x faster on CPU
entities = model.predict_entities(text, labels, threshold=0.5)

INT8-kwantisatie

Dynamische kwantisatie verkleint modellen met 2.4x (438MB → 181MB) met minder dan 0.6% F1-verlies. De snelheid verbetert 1.8x op CPU. Op Intel VNNI-CPU's met ONNX Runtime bereikt INT8 tot 6x versnelling ten opzichte van PyTorch FP32.

from onnxruntime.quantization import quantize_dynamic, QuantType

# One-line quantization — 2.4x smaller, <1% F1 loss
quantize_dynamic("gliner.onnx", "gliner_int8.onnx", weight_type=QuantType.QInt8)

gline-rs: Rust-herimplementatie

gline-rs (Apache 2.0) elimineert Python-overhead. Op CPU: 6.67 seq/s versus 1.61 in Python — een 4.1x versnelling. Op een RTX 4080: 248.75 seq/s (gline-rs benchmarks). Het ondersteunt span- en tokenmodellen, GPU/NPU via ONNX Runtime, en wordt geleverd als crate op crates.io.

use gliner::{GLiNER, TokenMode, Parameters, RuntimeParameters, TextInput};

let model = GLiNER::<TokenMode>::new(
    Parameters::default(), RuntimeParameters::default(),
    "tokenizer.json", "model.onnx")?;

let input = TextInput::from_str(
    &["My name is James Bond."], &["person", "vehicle"])?;
let output = model.inference(input)?;
// => "James Bond" : "person" (99.7%)

Het pakket fast-gliner biedt Python-bindings via PyO3 — Rust-snelheid met Python-ergonomie.

Samenvatting van de optimalisatiestack

Optimalisatie Versnelling vs PyTorch Modelgrootte F1-impact Beste voor
ONNX Runtime 1.5-3x Gelijk Geen Snelle winst, elke hardware
INT8 Quantization 3-6x 2.4x kleiner <0.6% loss CPU-deployment, memory-constrained
gline-rs (Rust) 4.1x (CPU) ONNX-formaat Geen Hoge throughput, latency-kritisch
gline-rs + INT8 4-8x 2.4x kleiner <1% loss Productie op schaal

Gestructureerde extractie: Instructor vs Outlines

Wanneer je meer flexibiliteit nodig hebt dan encoder-modellen bieden — impliciete entiteiten, redenering, ontology mapping — zijn er twee libraries voor gestructureerde extractie uit LLM's.

Instructor (~12.600 GitHub-sterren, ~8.8M downloads/maand in maart 2026) van Jason Liu patcht LLM SDK's zodat ze Pydantic-responsmodellen accepteren, met automatische retry bij validation failure. Het ondersteunt 15+ providers en inspireerde OpenAI's native feature voor structured output.

Uit structured_extraction.py:

import instructor
from pydantic import BaseModel
from typing import List, Literal
from openai import OpenAI

class Entity(BaseModel):
    name: str
    label: Literal["PERSON", "ORGANIZATION", "LOCATION"]

class ExtractEntities(BaseModel):
    entities: List[Entity]

client = instructor.from_openai(OpenAI())
result = client.chat.completions.create(
    model="gpt-5.4-mini", temperature=0.0,
    response_model=ExtractEntities,
    messages=[{"role": "user", "content": "BioNTech SE acquired InstaDeep in the U.K."}])
# entities=[Entity(name='BioNTech SE', label='ORGANIZATION'), ...]

Outlines (~13.600 GitHub-sterren) van dottxt kiest een andere aanpak: constrained token generation via Finite State Machines. In plaats van output te genereren en daarna opnieuw te proberen na een validation failure, voorkomt Outlines dat ongeldige tokens überhaupt worden gegenereerd — 100% schema compliance, nul retries. AWS-benchmarks tonen 98% schema adherence versus 76% voor post-generation validation, met 5x snellere generatie vergeleken met unconstrained generation met retries na post-validatie.

import outlines

model = outlines.models.transformers("microsoft/Phi-3-mini-128k-instruct")
generator = outlines.generate.json(model, ExtractEntities)
result = generator("Extract entities from: BioNTech SE acquired InstaDeep in the U.K.")

De keuze hangt af van waar je je modellen draait. Instructor is het best voor cloud-LLM-API's — snel prototypen, ondersteuning voor meerdere providers, vertrouwde Pydantic-patronen. Outlines wint voor lokale modellen waar je formaatgaranties nodig hebt zonder externe dependencies. Beide werken goed voor NER-achtige extractie, maar geen van beide evenaart de throughput van encoders: Instructor voegt 200 ms-2 s API-latency per call toe, en Outlines hangt af van de snelheid van het lokale model. Voor high-throughput NER zijn encoders nog steeds 10-100x sneller.


De 3-laagse productiearchitectuur

Zo zou ik dit alles combineren voor productie-NER.

De 3-laagse architectuur: encoder-modellen verwerken 90%+ van het volume tegen lage kosten, GLiNER 2 verwerkt multi-task extractie en LLM's behandelen de moeilijkste 10% terwijl ze ook Tier 1-modellen trainen

Tier 1 — Encoder-modellen voor bekende entiteitstypen. GLiNER (cross-encoder voor <30 typen, bi-encoder voor 30+), fine-getuned via de LLM-as-teacher-pijplijn. Deploy met ONNX + INT8 of gline-rs. Verwerkt >90% van het volume met sub-50ms latency en vrijwel nul kosten. De bi-encoder schaalt naar miljoenen entiteitstypen met vooraf berekende embeddings.

Tier 2 — GLiNER 2 voor multi-task extractie. Wanneer één request NER + classificatie + relation extraction + gestructureerde data nodig heeft, vervangt het 205M-parameter model van GLiNER 2 vier deployments. Draait op CPU in 130-208 ms ongeacht het aantal labels — goed voor documentpijplijnen die meerdere extractietaken in één pass nodig hebben.

Tier 3 — LLM's voor extractie met veel redenering. Wanneer entiteiten impliciet zijn, contextuele inferentie nodig hebben of ontology mapping vereisen, routeer dan naar een LLM (GPT-5.4 Mini, Claude Sonnet 4.6, Llama 4 Maverick) via Instructor (cloud) of Outlines (lokaal). Dit behandelt de ~10% van de queries die encoders missen. Dezelfde LLM's genereren ook trainingsdata voor Tier 1.

Wat kost dit? Een fine-getunede GLiNER haalt 93.4% F1 tegen \\(0.10/uur op CPU**, en evenaart daarmee de **92.7% F1** van zijn Llama-70b-teacher op **\\\)8/uur — 80x goedkoper.


Trade-offs en beperkingen

Niets hiervan is gratis. In ML is er altijd een addertje onder het gras — de enige vraag is wanneer je het ontdekt.

LLM-as-teacher-fouten propageren. Als het LLM consequent een specifiek entiteitstype fout labelt (bijvoorbeeld dochterondernemingen verwarren met moederbedrijven), erft de fine-getunede encoder die bias. De oplossing is gerichte menselijke review — focus inspanning op entiteitstypen waar de confidence van het LLM laag of inconsistent is, niet op willekeurige sampling.

Kwantisatieverliezen zijn niet uniform. Het gemiddelde F1-verlies van ~0.6% door INT8 kan groter zijn op zeldzame entiteitstypen met subtiele grenspatronen (chemische verbindingen, afkortingen van meerdere woorden). Benchmark gekwantiseerde modellen altijd op je specifieke entiteitstypen, niet alleen op geaggregeerde F1.

Wanneer de 3-laagse architectuur overkill is. Eén domein, goed gedefinieerde entiteitstypen, 500+ gelabelde voorbeelden? Een fine-getunede RoBERTa- of spaCy-pijplijn is eenvoudiger en voldoende. Het 3-tier-patroon betaalt zich uit bij (a) meerdere domeinen, (b) evoluerende entiteitstypen of (c) een mix van makkelijke en moeilijke extractietaken. Als je alleen namen en datums uit facturen extraheert, werkt Tier 1 alleen al.

Kwaliteitsplafond van de bi-encoder. De bi-encoder ruilt joint attention in voor throughput. Wanneer labelsemantiek met tekstcontext interageert ("date" vs "year" vs "period" voor dezelfde span), wint de cross-encoder nog steeds. Gebruik de cross-encoder voor high-stakes taken met weinig labels; gebruik de bi-encoder voor breedte.


Belangrijkste conclusies

  1. Encoders hebben gewonnen. GLiNER en bi-encoder-varianten verslaan LLM's op standaard NER-benchmarks tegen 80-130x lagere kosten. Zelfs met GPT-5.4 Nano en Llama 4 die prijzen omlaag duwen, is een LLM draaien voor elke NER-query niet langer te rechtvaardigen.
  2. LLM's zijn essentieel — als teachers. Ze labelen trainingsdata voor \$70 die duizenden zou kosten aan menselijke annotatie, en de fine-getunede encoder eindigt er meestal mee het LLM te verslaan dat hem heeft getraind.
  3. De bi-encoder ontsluit schaal met miljoenen labels. Vooraf berekende embeddings lossen het probleem van kwadratische complexiteit op, met slechts 5.2% throughput-verlies bij 1.024 labels.
  4. GLiNER 2 consolideert multi-task extractie. Eén 205M-model voor NER + classificatie + RE + gestructureerde extractie.
  5. Evalueer op je eigen data. Gebruik entity-level F1, bouw testsets uit productiedocumenten en benchmark gekwantiseerde modellen op je specifieke entiteitstypen.
  6. Gebruik het hybride patroon. Tier 1/2 voor snelle extractie, Tier 3 voor redenering. Dezelfde LLM's die de moeilijkste 10% afhandelen genereren de trainingsdata voor de 90%.

Referenties

Papers

Industry Papers

Case studies

Tools and Frameworks