Back to "Enkel OCR och NER Feature Extraction i C# med ONNX"

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

AI CSharp Docker NER OCR ONNX Tutorial

Enkel OCR och NER Feature Extraction i C# med ONNX

Wednesday, 21 January 2026

I ” ' ” har byggt klarRAG Jag läser sociala medier där folk frågar samma sak. djupt i OCR-rummet Jag tänkte att jag skulle skriva ett 'beginner friendly ' sätt att göra detta på icke-

Du har bilder med text.

Detta artikel visar så enkel som möjligt Pipeline: Tesseract för textuttvingning, så BERT NER ( via ONNX) för enhetens utmärkelse

Deterministisk betyder här fixera versioner , fixera språkdata, och inget adaptivt inlärning på starttid

NuGet kommer snart - I'm förpackar detta till en enkel mostlylucid.ocrner bibliotek. För tillfället


Den fulla Pipelinen

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

Två steg: ,, två modeller, ,, båda igång lokalt. ., Låt ' bygga varje del.


Del 1: OCR med Tesseract

Tesseract är standardopen, -, källkods OCR-motor. Tesseract.NET, en C

dotnet add package Tesseract

Du behöver också de tränade datafilerna. ladda ner eng.traineddata från tessdata och placera den i en tessdata folder.

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();
}

Det var "'", det var . ExtractText("invoice.png") och du får en string.

Nyckel: TesseractEngine är dyrt att skapa

Begränsningar

Tesseract fungerar bra för ren, hög- kontrasttext i standardfonter. Den kämpar med

  • Stiliserade eller dekorativa fonter
  • Low-kvalitélla skanningar eller foton
  • Roterade eller böjda text
  • Text på komplexa bakgrunder
  • Animaerade GIFs med undertitel
  • Hyfenerade linjer bryter (inter-\nnational) kan behöva processing efter - innan NER

För produktionssystem som behöver hantera konstiga saker Det tre---Tier-OCR-röret- som lägger till Florence -2 ONNX som en medelklass och Vision LLM uppgradering för hårda fall

För detta inlärning, antar vi att du har ren bild eller text från en annan källa.

I praktiken, vill du vanligtvis normalisera OCR-uttrycket ( trim whitespace , kollapsa upprepade nya linjer , fixa uppenbart hyphenation M SK4 innan du skickar ut det till NERM SK5


Del 2: NER med ONNX

Varför den här metoden fungerar

Innan vi ska dyka in i kod, låt oss förstå vad vi gör. Om du är en C-utvecklare som aldrig rört ML, så är det här för dig.

Vad är NER?

Namerade entitetserkänning (NER) är ett löst problem.

  • PER - Personen namn "John Smith", "DrM SK4 Jane DoeMST5
  • ORG - Organiseringar
  • LOC - Lokalisering | " | London | ", | МSK3 | Mount Everest
  • MISC - Andra entityder "COVID-19",

Modellet förstår inte texten, utan lär sig statistiska mönster från miljontals märkta exempel. NER är dragning av dragningar Det matchar mönster på steroider

Varför ONNX?

NX (, Open Neural Network Exchange, ) är ett standardformat för ML-modeller. frozen inference DLL för nervnätverk

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

Den viktigaste insikten någon annan gjorde hårt arbete (trening av modellen i Python ). Du kör bara slutsatsen i C

Varför inte bara använda en LLM

Du skulle kunna skicka ett sms till GPT, och fråga: "" Hitta människorna och företagen i det här smset.

tillvägagångspunkt Rymden kostnaderna per 1000 dokument integritet konsistens
Northeastern ~50ms ♫ ♫ $0 ♫ ♫ Lokalt ♫ Högt ♫
Lokal LLM API 4-30s $0 ♫ ♫ ♫ Små modeller kan vara fläckiga ♫
LLM API

LLMs är bra för komplext resonemang. För mönster extraktion på skalaM SK1 en dedikerad modell är 40x snabbare och gratis .


Pipelinet

Här är vad vi bygger

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

Varje steg är enkelt.


steg 1: ladda ner modellen

Du behöver tre filer från HuggingFace. ladda ner dem manuell till en ordning (e .gM SK3 ./models/ner/):

Att filera Storleken URL
model.onnx ~430MB ladda ner
vocab.txt ~230KB ladda ner
config.json ~1KB ladda ner

Modellet är bert-bas-NER exporterade till ONNX format av: skyddai.

Din folder borde se ut som:

models/
  ner/
    model.onnx      (the neural network)
    vocab.txt       (word → ID mapping)
    config.json     (label definitions)

steg 2: Projektuppsättning

Att skapa en ny konsolapp och lägga till NuGet paket

dotnet new console -n NerDemo
cd NerDemo
dotnet add package Microsoft.ML.OnnxRuntime
dotnet add package Microsoft.ML.Tokenizers

Det är det. Två paket.

  • OnnxRuntime - kör modellen
  • ML.Tokenizer - Handler text → token omvandling

steg 3: förståndet av tokenisering

Innan modellen kan bearbeta texten, måste vi konvertera den till siffror. symbolisering.

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

Nyckelpunkter:

  • [CLS] och [SEP] är speciella token som markerar meningsgränser
  • Varje ord blir ett tal från vocab.txt
  • Modellet ser bara siffror , aldrig den faktiska texten

Att ladda ut tokenisatorn

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);

Varför? LowerCaseBeforeTokenization = false? Den här modellen undervisades på kasserade texter . | | " | John · " | och " | john · M SK5 | har olika betydelser - | en | ' | är troligtvis ett namn

Nyckel: Markifieren måste matchar modellen exakt. Med hjälp av en annan vocab vocab.txt som skepper med modellen.

Att tokenisera text

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]");

Efter detta har vi

  • tokenIds: [101, 1287, 3455, 2573, 1120, 7513, 1999, 2414, 119, 102]
  • tokens: ["[CLS]", "John", "Smith", "works", "at", "Microsoft", "in", "London", ".", "[SEP]"]

steg 4: Att köra modellen

Nu lägger vi in dessa siffror i den ONNX-modellen.

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

Lade upp modellen

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);

Nyckel: Att skapa tokenisatorn och inferenssessionen en gång ♫ (singleton ♫InferenceSession är dyrt att skapa

Att förbereda inmatningar

modellen förväntar sig:

  • input_ids: Våra tokensidentifikationer som long[]
  • uppmärksamhet_ masker: 1 för riktiga tokens
// 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;
    }
}

Running Inference

// 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>();

Utgången logits har form [1, sequence_length, 9]-9 möjliga etiketter för varje tokenposition


steg 5: Dekodering av utgången

Modellet ger ut råresultat. Vi behöver

  1. Hitta den högsta tärningsetikern för varje token
  2. Omvandla dessa etiketter till faktiska enheter

Understanding BIO Tags

Modellet använder 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
  • B-PER = Början av en Person
  • I-PER = Inuti ( Kontinuation | ) | en Person
  • O = Utanför någon enhet ( inte intressant
  • B-ORG = Början av en organisation

Detta låter modellen hantera flera ordbeståndsdelar som "John Smith

Etikettens kartläggning

// These are the 9 labels the model was trained on (CoNLL-2003 dataset)
string[] labels =
{
    "O",       // 0: Outside any entity
    "B-PER",   // 1: Beginning of Person
    "I-PER",   // 2: Inside Person
    "B-ORG",   // 3: Beginning of Organization
    "I-ORG",   // 4: Inside Organization
    "B-LOC",   // 5: Beginning of Location
    "I-LOC",   // 6: Inside Location
    "B-MISC",  // 7: Beginning of Miscellaneous
    "I-MISC"   // 8: Inside Miscellaneous
};

Beteckning: Den här specifika modellen använder standarden 9-label CoNLL-schemat config.json (id2label fält). Om du byter modeller, läsa etiketter från konfiguration snarare än att koda

Att hitta den bästa etiketten

För varje token, väljer vi etiketten med den högsta logit poängenM SK1

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));
}

Den Softmax funktionen konverterar rå poäng till sannolikheter (0-1):

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;
}

Tillåtelse: Softmax poäng är relaterande, inte kalibrerade sannolikheter 0.92 som "92% korrekt ". Använd dem för att filtrera låga ufdomsprognoser


steg 6: Extrahera enheter

Nu har vi per- -token förutsägelser.

Först, WordPiece sammanfogade hjälper. De här vingarna ## underord och punktspacing 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();
}

Nu är enhetens extraktion minimalt märkeförtroende över hela skalan.

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;
}

Nota: WordPiece hanteras helt av MergeWordPieces- inga särskilda kontrollen behövs


Ett komplett exempel

Här är ett minimalt fungerande exempel som du kan kopiera och köra.

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-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC", "B-MISC", "I-MISC"];

// 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)}");

utgång:

Input: John Smith, CEO of Microsoft, announced the acquisition in London yesterday.

Entities found:
  [PER] John Smith
  [ORG] Microsoft
  [LOC] London

Understanding WordPiece Subwords

BERT använder WordPiece-tokenisering, som delar ut okända ord i underord ## prefix betyder " Fortsättning av det tidigare ordet

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

Den MergeWordPieces hjälparbetare hanterar detta: ## symboler är tillskrivna utan ett utrymme, som producerar "Elasticsearch" istället för "Elastic search".


Performance-tips

1. Batch flera texter

Om du har många texter, bearbeta dem i lager.

// 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]);

2. Använd Smart Bucketing

Använd den minsta burken som passar

int[] buckets = [32, 64, 128, 256, 512];
int targetLength = buckets.FirstOrDefault(b => b >= tokenIds.Count);
if (targetLength == 0) targetLength = 512;

I praktiken, ONNX NER är tillräckligt snabb för att fungera iline under intag, inte bara som ett slagjobb. Du kan extrahera enheter när dokument kommer in snarare än att ställa upp dem i queue för senare .

3. GPU: s accelerering

För hög hastighet använder DirectML (Windows ) eller CUDA

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

När man ska använda detta mot LLM

Använd ONNX NER
Hög volymen 1000 Doser 2 3 1 4 analys 5
Standardbeståndsdelar ( människor , organisationer МSK3 ställen )
Privatsphäre
Deterministiska ledningar Eksploratorisk analys
Extraktion av egenskaper Interpretering / Synthes

Båda tillvägagångssätten fungerar olika problem. NER extraherar struktur


Den större bilden

Detta mönster-deterministisk extraktion med frysta modeller, följt av optionell syntektion senare- räknar mycket bättre än att trycka in råtext i en LLM och hoppas att den beter sig

NER är inte något du gör.

Samma tillvägagångssätt gäller även för andra dragningar av egenskaper.

Där det passar: Den här OCR: n | | +| NER: s ledning är en byggsten | Reduzerad RAG och LucidRAG-dokumentation. De varelser du extraherar här blir noder ; dokumenten blir rander ; LLM ser bara vad den behöver


resurser

Bibliotek & Modeller:

Related Articles:

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.