This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Einfache OCR- und NER-Feature-Extraktion mit ONNX
Wednesday, 21 January 2026
Wie I''ve gebaut wurde klarRAG IM'-m Lesen von sozialen Medien, wo die Leute halten fragen das gleiche Ding .-'-wie Sie Funktionen aus gescannten Text erhalten .?'-m die Kategorie Fehler ist immer . . '-m benutzen Sie einfach ein LLM-'...-what WORKS aber ist sehr teuer . tief im OCR-Raum Ich dachte mir, dass I'-D schreiben Sie eine kostenlose '-Beginner freundlich '-Annäherung an die NON---LLM oder LLM optional )-Wege zu tun
Sie haben Bilder mit Texten, die Sie extrahieren möchten, dass texten, ,, dann finden Sie die nützliche Struktur in ihr Namen, die ,, Unternehmen, Orte, ohne einen LLM-,, Versand von Daten in die Cloud oder zahlen per Token, .
Dieser Artikel zeigt die einfachste Möglichkeit Pipeline-Ausrüstung Tesseract für Textextraktion, dann BERT NER Alle lokalen, alle deterministischen, alle in C.#.
Deterministisch bedeutet hier feste Versionen.,.Festsprachdaten.,. und kein adaptives Lernen zur Laufzeit.
Sie wollen die verpackte Version. Diese Pipeline ist jetzt ein NuGet-Paket mit automatischem Download von Modellen, , und einem CLI-Tool. Teil Das NuGet-Paket für die fertige Version von --to---use
Die vollständige Pipeline
flowchart LR
subgraph OCR["Part 1: OCR"]
IMG[Image]
TESS[Tesseract]
TXT[Raw Text]
end
subgraph NER["Part 2: NER"]
TOK[Tokenize]
BERT[BERT NER<br/>ONNX]
ENT[Entities]
end
IMG --> TESS
TESS --> TXT
TXT --> TOK
TOK --> BERT
BERT --> ENT
style TESS stroke:#f60,stroke-width:3px
style BERT stroke:#f60,stroke-width:3px
style ENT stroke:#090,stroke-width:3px
Zwei Schritte: zwei Modelle: beide, die lokal laufen, ., Let.', bauen jedes Teil:
OCR mit Tesseract
Tesseract ist die Standard-Open---Source-OCR-Engine, die wir verwenden Tesseract-.-NETEin Wrapper mit einem Gewicht von mehr als 1 kg
dotnet add package Tesseract
Sie benötigen auch die trainierten Daten-Dateien. eng.traineddata von tessdata und legen Sie es in einem tessdata Ordner„ ."
using Tesseract;
public static string ExtractText(string imagePath)
{
using var engine = new TesseractEngine("./tessdata", "eng", EngineMode.Default);
using var img = Pix.LoadFromFile(imagePath);
using var page = engine.Process(img);
return page.GetText();
}
Dass ,''s es ruft ExtractText("invoice.png") und du erhältst eine Zeichenkette
Wichtig:
TesseractEngineist teuer, um zu erstellen. In realen Anwendungen...... erstellen Sie es einmal und verwenden Sie es wieder..
Einschränkungen
Tesseract funktioniert gut für clean-,-High---Kontrasttext in Standard FontsEs kämpft mit...
- Stylisierte oder dekorative Schriftarten
- Scans oder Fotos in niedriger Qualität
- rotierter oder gekrümmter Text
- Text auf komplexen Hintergründen
- Animierte GIF mit Untertiteln
- Hyphenierte Zeilenumbrüche
inter-\nnationalVor der NER-Verarbeitung kann es erforderlich sein, dass -
Für Produktionssysteme, die mit dem seltsamen Zeug umgehen müssen, siehe Die Pipeline von Three--.Tier OCR-- , die Florence .-2 ONNX als mittlere Ebene und Vision LLM Eskalation für harte Fälle hinzufügt .
Für dieses Tutorial Nehmen wir an, dass Sie saubere Bilder oder Text aus einer anderen Quelle haben.
In der Praxis werden Sie normalerweise OCR-Ausgang normalisieren möchten, bevor Sie es an NER-. übergeben.
Teil 2:-NER mit ONNX
Warum dieser Ansatz funktioniert
Bevor du in Code eintauchst, lass 's verstehen, was wir tatsächlich tun, wenn du einen Entwickler von C.# bist, der 's nie berührt hat.
Was ist NER-??
Genannte Entitätserkennung ist ein gelöstes Problem, das Forscher neuronale Netzwerke trainiert haben, die Text lesen können und die interessanten Bits hervorheben.
- GESAMTBETRAG Personennamen: John Smith, Mr. Smith, Dr. ., Jane Doe, Mrs.
- ORG Organisationen von ("Microsoft, ",, ", NHS, MSC4, MCSK5, ACME Corp,"),
- LOC Standort von ("London",
- MISC Sonstige Einrichtungen
Das Modell doesn't .'-t ?"-versteht den Text ,--it-'s lernte statistische Muster aus Millionen von gekennzeichneten Beispielen NER ist Feature-Extraktion. ,, nicht argumentieren. Es ist ein Muster, das auf Steroiden passt
Warum ONNX-??
ONNX Öffnen Sie Neural Network Exchange.) ist ein Standardformat für ML-Modelle. Gefrorene Inferenz DLL für neuronale Netzwerke, feste Gewichte in den Tensoren von ,, ohne Trainingslogik, ohne Zufälligkeit
flowchart LR
subgraph Training["Training (Python)"]
PT[PyTorch Model]
TF[TensorFlow Model]
end
subgraph Export["Export Once"]
ONNX[model.onnx]
end
subgraph Runtime["Run Anywhere"]
CS[C# App]
CPP[C++ App]
JS[JavaScript App]
end
PT --> ONNX
TF --> ONNX
ONNX --> CS
ONNX --> CPP
ONNX --> JS
style ONNX stroke:#f60,stroke-width:4px
style CS stroke:#090,stroke-width:3px
Die wichtigsten Erkenntnisse jemand anderes hat die harte Arbeit gemacht Schulung des Modells in Python, )., Du führst die Inferenz einfach in C.#.,
Warum nicht einfach ein LLM-??
Sie konnten Text an GPT--4 schicken und bitten, die Leute und Firmen in diesem Text zu finden.
Anfluggeschwindigkeit Geschwindigkeit Kosten pro Stunde Datenschutz Datenkonsistenz |-------------------|-------|--------------|-----------------------|-------------| | ONNX NER |-~50-|-M-$0-M|-M -|-Hoch-|- | Lokale LLM-API Kleine Modelle können flockig sein. | LLM API Daten, die extern gesendet werden
LLMs sind ideal für komplexe Argumentationen..-Für Musterextraktion im Maßstab.,-Ein dediziertes Modell ist schneller und frei.
Die Pipeline
Hier ist das, was wir bauen
flowchart LR
subgraph Input
TEXT[Raw Text]
end
subgraph Tokenization["Step 1: Tokenization"]
TOK[Split into tokens]
IDS[Convert to IDs]
end
subgraph Model["Step 2: ONNX Inference"]
BERT[BERT Model]
LOGITS[Logits Output]
end
subgraph Output["Step 3: Decode"]
LABELS[BIO Labels]
ENT[Entities]
end
TEXT --> TOK
TOK --> IDS
IDS --> BERT
BERT --> LOGITS
LOGITS --> LABELS
LABELS --> ENT
style BERT stroke:#f60,stroke-width:4px
style ENT stroke:#090,stroke-width:3px
Jeder Schritt ist einfach.
Schritt Download des Modells
Sie benötigen drei Dateien von HuggingFace-. . Laden Sie sie manuell in einen Ordner herunter ./models/ner/):
Datei Datei Größe Adresse Adresse Adresse
|------|------|-----|
| model.onnx |-MB-|-Mb Herunterladen |
| vocab.txt |" , , ~230, KB , |" Herunterladen |
| config.json |" , , ~1, KB , |" Herunterladen |
Das Modell ist BERT---base---NER in das ONNX-Format exportiert durch Schutz.
Dein Ordner sollte so aussehen
models/
ner/
model.onnx (the neural network)
vocab.txt (word → ID mapping)
config.json (label definitions)
Schritt Projekt-Einrichtung
Erstellen Sie eine neue Konsolen-App und fügen Sie die NuGet Pakete.
dotnet new console -n NerDemo
cd NerDemo
dotnet add package Microsoft.ML.OnnxRuntime
dotnet add package Microsoft.ML.Tokenizers
Dass-''s es -.' Zwei Pakete
- OnnxRuntime Führt das Modell
- ML-.-Tokenisatoren Behandelt Text-Token-Konvertierung
Schritt zum Verständnis der Tokenisierung
Bevor das Modell Text verarbeiten kann, müssen wir es in Zahlen umwandeln. Tokenisierung.
flowchart TD
subgraph Input
TEXT["John works at Microsoft"]
end
subgraph Tokenize["Tokenization"]
T1["[CLS]"]
T2["John"]
T3["works"]
T4["at"]
T5["Microsoft"]
T6["[SEP]"]
end
subgraph IDs["Token IDs"]
I1["101"]
I2["1287"]
I3["2573"]
I4["1120"]
I5["7513"]
I6["102"]
end
TEXT --> T1 & T2 & T3 & T4 & T5 & T6
T1 --> I1
T2 --> I2
T3 --> I3
T4 --> I4
T5 --> I5
T6 --> I6
style T1 stroke:#c00,stroke-width:3px
style T6 stroke:#c00,stroke-width:3px
style I2 stroke:#090,stroke-width:3px
style I5 stroke:#090,stroke-width:3px
Wesentliche Punkte
[CLS]und[SEP]sind spezielle Token, die Satzgrenzen markieren- Jedes Wort wird zu einer Zahl aus
vocab.txt - Das Modell sieht nur Zahlen nie den tatsächlichen Text.
Laden des Tokenizers
using Microsoft.ML.Tokenizers;
// Load the vocabulary file
var vocabPath = "./models/ner/vocab.txt";
var options = new BertOptions
{
LowerCaseBeforeTokenization = false, // BERT-NER is case-sensitive!
UnknownToken = "[UNK]",
ClassificationToken = "[CLS]",
SeparatorToken = "[SEP]",
PaddingToken = "[PAD]"
};
using var stream = File.OpenRead(vocabPath);
var tokenizer = BertTokenizer.Create(stream, options);
Warum? LowerCaseBeforeTokenization = falseDieses Modell wurde auf kassierten Text trainiert ,. , , " , John , und ," , John , " , haben unterschiedliche Bedeutungen , - , ein Name wahrscheinlich nicht
WichtigDer Tokenizer muss Passen Sie das Modell genau mit einem anderen Vokabeln , , , Gehäuse-Option oder spezielle Token-IDs wird lautlos degradieren Ergebnisse ,. , immer verwenden die
vocab.txtdass Schiffe mit dem Modell
Text markieren
var text = "John Smith works at Microsoft in London.";
// Tokenize (splits into subwords)
var encoded = tokenizer.EncodeToTokens(text, out _);
// Get special token IDs
var clsId = 101; // [CLS] token
var sepId = 102; // [SEP] token
// Build the full sequence: [CLS] + tokens + [SEP]
var tokenIds = new List<int> { clsId };
tokenIds.AddRange(encoded.Select(t => t.Id));
tokenIds.Add(sepId);
// Also keep the text tokens for later
var tokens = new List<string> { "[CLS]" };
tokens.AddRange(encoded.Select(t => t.Value));
tokens.Add("[SEP]");
Nach diesem haben wir
tokenIds:[101, 1287, 3455, 2573, 1120, 7513, 1999, 2414, 119, 102]tokens:["[CLS]", "John", "Smith", "works", "at", "Microsoft", "in", "London", ".", "[SEP]"]
Schritt Das Modell laufen lassen
Jetzt geben wir diese Zahlen in das ONNX-Modell ein Das Modell gibt an jeder Position die Punktzahlen für jedes mögliche Label zurück.
flowchart LR
subgraph Inputs
IDS["Token IDs<br/>[101, 1287, 3455, ...]"]
MASK["Attention Mask<br/>[1, 1, 1, ...]"]
end
subgraph Model
ONNX["BERT NER<br/>model.onnx"]
end
subgraph Outputs
LOG["Logits<br/>[batch, seq_len, 9]"]
end
IDS --> ONNX
MASK --> ONNX
ONNX --> LOG
style ONNX stroke:#f60,stroke-width:4px
Laden des Modells
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
// Configure for best performance
var sessionOptions = new SessionOptions
{
GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL,
IntraOpNumThreads = Math.Min(4, Environment.ProcessorCount)
};
// Load the model (takes ~2 seconds first time)
var session = new InferenceSession("./models/ner/model.onnx", sessionOptions);
WichtigErstellen Sie die Tokenizer- und Inferenzsession einmal.(.Singleton, /.service, ). und verwenden Sie sie wieder.MS K4. Don.'.t reload pro document.
InferenceSessionist teuer zu erstellen.
Eingaben vorbereiten
Das Modell erwartet
- Eingabe-_-IDsUnsere Token-IDs als
long[] - Achtung_ Mask1 für echte Tokens, ,, MSC3 für Polsterung
// Pad to a fixed length (BERT requires fixed shapes; powers of 2 are cache-friendly)
int sequenceLength = 64; // or 128, 256, 512
var inputIds = new long[sequenceLength];
var attentionMask = new long[sequenceLength];
for (int i = 0; i < sequenceLength; i++)
{
if (i < tokenIds.Count)
{
inputIds[i] = tokenIds[i];
attentionMask[i] = 1;
}
else
{
inputIds[i] = 0; // PAD token
attentionMask[i] = 0;
}
}
Ausführen von Schlussfolgerungen
// Create tensors (shape: [batch_size=1, sequence_length])
var inputIdsTensor = new DenseTensor<long>(inputIds, [1, sequenceLength]);
var attentionMaskTensor = new DenseTensor<long>(attentionMask, [1, sequenceLength]);
// Build inputs
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input_ids", inputIdsTensor),
NamedOnnxValue.CreateFromTensor("attention_mask", attentionMaskTensor)
};
// Run the model
using var results = session.Run(inputs);
// Get output logits
var output = results.First(r => r.Name == "logits");
var logits = output.AsTensor<float>();
Die Ausgabe logits hat Form [1, sequence_length, 9]Mögliche Beschriftungen für jede Tokenposition
Schritt - Dekodierung der Ausgabe
Die Modellausgänge rohe Punktzahlen... Wir brauchen zu...
- Finden Sie für jeden Token das höchste ,-'scoring Label
- Diese Etiketten in tatsächliche Entities umrechnen
BIO-Tags verstehen
Das Modell verwendet BIO-Notation:
flowchart LR
subgraph Tokens
T1["John"]
T2["Smith"]
T3["works"]
T4["at"]
T5["Microsoft"]
end
subgraph Labels
L1["B-PER"]
L2["I-PER"]
L3["O"]
L4["O"]
L5["B-ORG"]
end
T1 --> L1
T2 --> L2
T3 --> L3
T4 --> L4
T5 --> L5
style L1 stroke:#090,stroke-width:3px
style L2 stroke:#090,stroke-width:3px
style L5 stroke:#00f,stroke-width:3px
- BESCHÄFTIGUNG Beginn einer Person Entität
- EINLEITUNG Innerhalb einer Person
- O Die Ausserhalb irgendeiner Entität nicht interessant
- B.--ORG Beginn einer Organisation
Dies lässt das Modell mit Multi---Wort-Entitäten wie z.B. ", John Smith, ", oder ", United Kingdom, MKK4, umgehen
Das Mapping des Etiketts
// These are the 9 labels from the protectai/bert-base-NER-onnx config.json (id2label field)
string[] labels =
{
"O", // 0: Outside any entity
"B-MISC", // 1: Beginning of Miscellaneous
"I-MISC", // 2: Inside Miscellaneous
"B-PER", // 3: Beginning of Person
"I-PER", // 4: Inside Person
"B-ORG", // 5: Beginning of Organization
"I-ORG", // 6: Inside Organization
"B-LOC", // 7: Beginning of Location
"I-LOC" // 8: Inside Location
};
AnmerkungDieses spezifische Modell verwendet das Standard-Zeichen CoNLL schema... Einige ONNX-Exporte enthalten Etikettennamen in
config.json(id2labelWenn Sie Modelle tauschen, lesen Sie die Etiketten von config statt hardcoding.
Das beste Label finden
Für jedes Zeichen wählen wir das Etikett mit dem höchsten Logit-Score.
var predictions = new List<(string Token, string Label, float Confidence)>();
int numLabels = 9;
for (int i = 0; i < tokens.Count; i++)
{
// Skip special tokens
if (tokens[i] is "[CLS]" or "[SEP]" or "[PAD]")
continue;
// Find highest scoring label
float maxScore = float.MinValue;
int maxIndex = 0;
for (int j = 0; j < numLabels; j++)
{
float score = logits[0, i, j];
if (score > maxScore)
{
maxScore = score;
maxIndex = j;
}
}
// Convert logit to probability (softmax)
float confidence = Softmax(logits, i, numLabels, maxIndex);
predictions.Add((tokens[i], labels[maxIndex], confidence));
}
Das Softmax Funktion wandelt Rohpunkte in Wahrscheinlichkeiten um
static float Softmax(Tensor<float> logits, int position, int numLabels, int targetIndex)
{
// Find max for numerical stability
float maxLogit = float.MinValue;
for (int j = 0; j < numLabels; j++)
maxLogit = Math.Max(maxLogit, logits[0, position, j]);
// Compute softmax
float sumExp = 0f;
for (int j = 0; j < numLabels; j++)
sumExp += MathF.Exp(logits[0, position, j] - maxLogit);
return MathF.Exp(logits[0, position, targetIndex] - maxLogit) / sumExp;
}
Hinweis zum VertrauenDie Softmax-Werte sind relativNicht kalibrierte Wahrscheinlichkeiten von .... Sie sind nützlich für Ranking und Schwellenbildung, aber nicht behandeln
0.92Verwenden Sie sie, um niedrig zu filtern......
Schritt zur Extraktion von Unternehmen
Jetzt haben wir per-- ,token Vorhersagen ,. , Wir müssen sie in Entities zusammenführen , . ,
Erstes, das WordPiece zusammenführen Helfer... diese Handle ## Subwords und Interpunktionsabstand korrekt
static void AppendWordPiece(StringBuilder sb, string token)
{
if (string.IsNullOrEmpty(token)) return;
// WordPiece continuation: "##soft" → append without space
if (token.StartsWith("##", StringComparison.Ordinal))
{
sb.Append(token.AsSpan(2));
return;
}
// No leading space if first token or if punctuation
if (sb.Length > 0 && !IsPunctuationToken(token))
sb.Append(' ');
sb.Append(token);
}
static bool IsPunctuationToken(string token) =>
token.Length == 1 && char.IsPunctuation(token[0]);
static string MergeWordPieces(IEnumerable<string> tokens)
{
var sb = new StringBuilder();
foreach (var t in tokens)
AppendWordPiece(sb, t);
return sb.ToString();
}
Nun ist das Entity-Konfidenzgefühl der Entity Mindestsicherheit über die Spanne ,- ,konservativ , , , so dass ein einzelner schwacher Token wird versteckt durch eine Durchschnittung , : ,
public sealed class Entity
{
public required string Text { get; init; }
public required string Type { get; init; } // PER, ORG, LOC, MISC
public required float Confidence { get; init; }
}
static List<Entity> ExtractEntities(
IReadOnlyList<(string Token, string Label, float Confidence)> predictions)
{
var entities = new List<Entity>();
string? currentType = null;
var currentTokens = new List<string>();
float currentConfidence = 1.0f;
void Flush()
{
if (currentType == null || currentTokens.Count == 0) return;
entities.Add(new Entity
{
Type = currentType,
Text = MergeWordPieces(currentTokens),
Confidence = currentConfidence
});
currentType = null;
currentTokens.Clear();
currentConfidence = 1.0f;
}
foreach (var (token, label, conf) in predictions)
{
// Continue current entity if model says I-<same type>
if (currentType != null && label == $"I-{currentType}")
{
currentTokens.Add(token); // keep ## form, merge handles it
currentConfidence = Math.Min(currentConfidence, conf);
continue;
}
// New entity begins
if (label.StartsWith("B-", StringComparison.Ordinal))
{
Flush();
currentType = label[2..];
currentTokens.Add(token);
currentConfidence = conf;
continue;
}
// Anything else (O, or I- without matching current type) ends the entity
Flush();
}
Flush();
return entities;
}
Anmerkung: WordPiece wird vollständig von MergeWordPiecesKeine spezielle Steuerungsströmung erforderlich
Vollständiges Beispiel
Hier ist ein minimales Arbeitsbeispiel, das Sie kopieren und ausführen können.
using System.Text;
using Microsoft.ML.OnnxRuntime;
using Microsoft.ML.OnnxRuntime.Tensors;
using Microsoft.ML.Tokenizers;
// === Configuration ===
var modelPath = "./models/ner/model.onnx";
var vocabPath = "./models/ner/vocab.txt";
// === Load tokenizer ===
var bertOptions = new BertOptions
{
LowerCaseBeforeTokenization = false,
UnknownToken = "[UNK]",
ClassificationToken = "[CLS]",
SeparatorToken = "[SEP]"
};
using var vocabStream = File.OpenRead(vocabPath);
var tokenizer = BertTokenizer.Create(vocabStream, bertOptions);
// === Load model ===
var sessionOptions = new SessionOptions
{
GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL
};
using var session = new InferenceSession(modelPath, sessionOptions);
// === Process text ===
var text = "John Smith, CEO of Microsoft, announced the acquisition in London yesterday.";
// Tokenize
var encoded = tokenizer.EncodeToTokens(text, out _);
// Build sequence with special tokens
var tokens = new List<string> { "[CLS]" };
tokens.AddRange(encoded.Select(t => t.Value));
tokens.Add("[SEP]");
var rawIds = new List<int> { 101 }; // [CLS]
rawIds.AddRange(encoded.Select(t => t.Id));
rawIds.Add(102); // [SEP]
// Pad to fixed length
int seqLen = 64;
var inputIds = new long[seqLen];
var attentionMask = new long[seqLen];
for (int i = 0; i < seqLen; i++)
{
inputIds[i] = i < rawIds.Count ? rawIds[i] : 0;
attentionMask[i] = i < rawIds.Count ? 1 : 0;
}
// === Run inference ===
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input_ids",
new DenseTensor<long>(inputIds, [1, seqLen])),
NamedOnnxValue.CreateFromTensor("attention_mask",
new DenseTensor<long>(attentionMask, [1, seqLen]))
};
using var results = session.Run(inputs);
var logits = results.First().AsTensor<float>();
// === Decode predictions ===
string[] labels = ["O", "B-MISC", "I-MISC", "B-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC"];
// WordPiece merge helper
static string MergeWordPieces(List<string> tokens)
{
var sb = new StringBuilder();
foreach (var t in tokens)
{
if (t.StartsWith("##", StringComparison.Ordinal))
sb.Append(t.AsSpan(2));
else if (sb.Length > 0 && t.Length > 0 && !char.IsPunctuation(t[0]))
sb.Append(' ').Append(t);
else
sb.Append(t);
}
return sb.ToString();
}
Console.WriteLine($"Input: {text}\n");
Console.WriteLine("Entities found:");
string? currentType = null;
var currentTokens = new List<string>();
for (int i = 1; i < tokens.Count - 1; i++) // Skip [CLS] and [SEP]
{
var token = tokens[i];
// Find best label
int bestIdx = 0;
float bestScore = float.MinValue;
for (int j = 0; j < 9; j++)
{
if (logits[0, i, j] > bestScore)
{
bestScore = logits[0, i, j];
bestIdx = j;
}
}
var label = labels[bestIdx];
// Continue current entity if model says I-<same type>
if (currentType != null && label == $"I-{currentType}")
{
currentTokens.Add(token);
continue;
}
// New entity begins
if (label.StartsWith("B-"))
{
if (currentType != null)
Console.WriteLine($" [{currentType}] {MergeWordPieces(currentTokens)}");
currentType = label[2..];
currentTokens = [token];
continue;
}
// Anything else ends the current entity
if (currentType != null)
Console.WriteLine($" [{currentType}] {MergeWordPieces(currentTokens)}");
currentType = null;
currentTokens.Clear();
}
// Output last entity
if (currentType != null)
Console.WriteLine($" [{currentType}] {MergeWordPieces(currentTokens)}");
Ausgabe„ :"
Input: John Smith, CEO of Microsoft, announced the acquisition in London yesterday.
Entities found:
[PER] John Smith
[ORG] Microsoft
[LOC] London
WordPiece-Unterwörter verstehen
BERT-Verwendung WordPiece Tokenisierung, die unbekannte Wörter in Subworte aufspaltet , , ## Präfix bedeutet " Fortbestehen des vorherigen Wortes "MSC1"
flowchart LR
subgraph Original
W1["Elasticsearch"]
end
subgraph Tokenized
T1["Elastic"]
T2["##search"]
end
subgraph Merged
M1["Elasticsearch"]
end
W1 --> T1 & T2
T1 --> M1
T2 --> M1
style T2 stroke:#c00,stroke-width:3px
Das MergeWordPieces Helfer handhabt diese ## Token werden ohne einen Raum angehängt. "Elasticsearch" statt "Elastic search".
Leistungstipps
Mehrsprachige Texte für die Batch
Wenn du viele Texte hast, dann verarbeite sie in Chargen.
// Instead of: 1 text × 1 inference = 50ms
// Do: 16 texts × 1 inference = 100ms (6ms per text)
int batchSize = 16;
var batchInputIds = new long[batchSize, seqLen];
// ... fill batch ...
var tensor = new DenseTensor<long>(batchInputIds, [batchSize, seqLen]);
Verwenden Sie Smart Bucketing
Verwenden Sie immer den kleinsten Eimer, der passt.
int[] buckets = [32, 64, 128, 256, 512];
int targetLength = buckets.FirstOrDefault(b => b >= tokenIds.Count);
if (targetLength == 0) targetLength = 512;
In der Praxis ist ONNX NER schnell genug zum Laufen Inline während der EinnahmeSie können Entitäten als Dokumente extrahieren, anstatt sie für später anzusprechen.
GPU-Beschleunigung
Für einen hohen Durchsatz verwenden Sie DirectML (.Windows.). oder CUDAM:.
dotnet add package Microsoft.ML.OnnxRuntime.DirectML # Windows GPU
# or
dotnet add package Microsoft.ML.OnnxRuntime.Gpu # NVIDIA CUDA
var options = new SessionOptions();
options.AppendExecutionProvider_DML(); // Use GPU
Wann dies gegen LLM zu verwenden ist
Verwendung von ONNX-NER-LLM-L(-GPT-S-4/-Claude-)-D| |--------------|------------------------| Hohes Volumen der Doktrinen der ) Aus einer Analyse der - Aus Standard-Entitäten - (Peoples -,Porgs - ,Pords - )Pendants-|Pongs - Benutzerdefinierte Entitätstypen Datenschutz-Sensitive Daten Wenn Sie Erklärungen benötigen Deterministische Pipelines - Sondierungsanalyse - | Beschreibung des Merkmals Extraktion der | Interpretation der Synthese der MSC3
Beide Ansätze arbeiten – .-Sie lösen unterschiedliche ProblemeNER extrahiert Struktur, LLMs Grund für Bedeutung
Das größere Bild
Dieses MusterDeterministische Extraktion mit gefrorenen Modellen, gefolgt von optionaler Synthese später- ist viel besser als Rohtext in einen LLM zu schieben und zu hoffen, dass er sich verhält.
NER ist nicht etwas, das du "-agentify -".- It-'-s-Infrastruktur -.- Sie laufen es auf Ingestion -,- speichern die Entitäten und verwenden sie stromabwärts für die Filterung - ,-Verknüpfung von , oder Fütterung in anspruchsvollere Pipelines -MSC8-
Der gleiche Ansatz gilt für andere Feature-Extraktionsaufgaben.:.Embeddings.,.Klassifikation.MSC2.Sentiment... Train einmal, oder verwenden Sie vortrainiert ),. Export nach ONNX.,. Laufen überall. ,. deterministisch.....
Wo das passtDiese OCR + NER-Pipeline ist ein Baustein für das gesamte Bild.- how extrahierte Entitäten fließen in den Graphenbau ein. Reduzierte RAG und der klarRAG DokumentationDie Entitäten, die Sie hier extrahieren, werden zu Knoten. Die Dokumente werden zu Kanten. Die LLM sieht nur, was sie braucht.
Aufkommen
Bibliotheken Modelle:
- Tesseract-.-NET Wrapper für Tesseract OCR
- tessdata Ausgebildete Datendateien für Tesseract
- BERT---Basis ONNX --NER Das NER-Modell, das wir verwenden
- ONNX Laufzeit Offizielle Dokumentation
- ML-.-Tokenisatoren Microsoft®'s tokenizer Bibliothek
Nächster Schritt:
- Teil Das NuGet-Paket Diese Pipeline als One---line NuGet installieren mit OpenCV Preprocessing-,. Florence.-2. Vision,. Microsoft, ..Recognisers, ,. und einem CLI-Tool
Verwandte Artikel:
- Die Pipeline von Three--.Tier OCR Wenn die einfache OCR ausreichend ist
- Reduzierte RAG Wo extrahierte Entitäten in das größere Bild passen
- klarRAG Vollständige Implementierung mit Entity-Deduplizierung und Graphenkonstruktion