Back to "C# में सरल ओसीआर और NER विशेषता निष्कर्षण 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

C# में सरल ओसीआर और NER विशेषता निष्कर्षण ONNX के साथ

Wednesday, 21 January 2026

के रूप में I' बना रहा है सुदृढ़आरएजी मैं सामाजिक मीडिया पढ़ रहा हूँ जहां लोग एक ही बात पूछते रहते हैं ओसीआर स्थान में गहराई मैंने सोचा कि मैं यह करने के लिए एक '\ beginer friendly\ '\ दृष्टिकोण लिखने के लिए गैर-\ LLM\ (\ या LLM वैकल्पिक

आपके पास पाठ के साथ छवियाँ हैं।

इस लेख में दिखाया गया है सरलतम संभव पाइपलाइन टेसरेक्टCity name (optional, probably does not need a translation) पाठ निष्कर्षण के लिए, फिर BERT NER इकाई पहचान के लिए ONNX के माध्यम से () सभी स्थानीय

यहाँ निर्धारक का अर्थ होता है, स्थिर संस्करणों, स्थिर भाषा डेटा

नूगेट अभी आ रहा है यह एक सरल में पैकेजिंग करता हूँ mostlylucid.ocrner लाइब्रेरी. अभी के लिए


पूर्ण पाइपलाइन

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

दो चरण-,-दो मॉडल-,-दो ही स्थानीय रूप से चल रहे हैं।


भाग 1: OCR Tesseract के साथ

टेसरेक्टCity name (optional, probably does not need a translation) मानक खुला है टेसरेक्ट, एक C

dotnet add package Tesseract

आपको प्रशिक्षित डेटा फ़ाइलों की भी जरूरत है. डाउनलोड eng.traineddata से टेसडाटा और इसे एक में रखें tessdata फ़ोल्डर.

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

कि-एमएसके0 यह-एम एसके1 कॉल ExtractText("invoice.png") और आप एक स्ट्रिंग मिलता है

महत्वपूर्ण: TesseractEngine वास्तविक अनुप्रयोगों में इसे एक बार बनाना और पुनः उपयोग करना

सीमाएं

टेसरेक्ट के लिए अच्छी तरह से काम करता है साफ, उच्च-मानक फ़ॉन्टों में व्यतिरेक पाठयह संघर्ष करता है

  • शैलीकृत या सजावटी फ़ॉन्ट
  • कम गुणात्मक स्कैन या फोटो
  • घुमाया या वक्रित पाठ
  • जटिल पृष्ठभूमि पर पाठ
  • उपशीर्षक के साथ एनिमित GIFs
  • हाइफेनटेड लाइन ब्रेक (inter-\nnationalएनईआर से पहले ) को पोस्ट - प्रसंस्करण की आवश्यकता हो सकती है

उन उत्पादन प्रणालियों के लिए जो विचित्र चीजों को संभालने की आवश्यकता है तीन--Tier OCR पाइपलाइन-जो फ्लोरेंस को जोड़ता है-2 एक मध्य स्तर के रूप में ONNX और हार्ड केसों के लिए विज़न एलएलएम escalation

इस अभ्यास के लिए हम'हम मानेंगे कि आप एक अन्य स्रोत से स्वच्छ छवि या पाठ है

व्यावहार में आप सामान्यतः ओसीआर आउटपुट को सामान्य बनाना चाहते हैं (trim whitespace


भाग 2: NER के साथ ONNX

यह दृष्टिकोण क्यों काम करता है

कोड में डुबोने से पहले-,, हम क्या कर रहे हैं समझें।

NER? क्या है

नामित इकाई पहचान (NER) एक सुलझाए गए समस्या है।

  • पीईआर व्यक्ति नाम
  • ORG संगठनों
  • LOC स्थान
  • MISC अन्य इकाइयां

नमूना नहीं समझता, पाठ नहीं समझती, यह सीखा है, लाखों चिह्नित उदाहरणों से सांख्यिकीय पैटर्न NER विशेषता निष्कर्षण है यह स्टेरॉयड पर पैटर्न मिल रहा है '

क्यों ONNX?

ओनिक्स ( Open Neural Network Exchange ML मॉडलों के लिए एक मानक ढाँचा है frozen inference DLL तंत्रिका नेटवर्क के लिए

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

प्रमुख अंतर्दृष्टि किसी और ने कठिनाई से काम किया (पॉइथोन में मॉडल को प्रशिक्षण

सिर्फ LLM का उपयोग क्यों नहीं करें

आप जीपीटी को पाठ भेज सकते हैं

दृष्टिकोण गति प्रति डोक लागत 1000 डोक 4 गोपनीयता МSK5 सुसंगतता
ओनिक्स एनआर उच्च
स्थानीय एलएलएम API Small Models can be flaky Variable
एलएलएम API ठ्̧रें

LLM जटिल तर्क के लिए महान हैं


पाइपलाइन

यहाँ हम क्या बना रहे हैं

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

हर कदम सरल है


चरण 1: मॉडल डाउनलोड करें

You need three files from HuggingFace. Download them manually to a folder ./models/ner/):

फ़ाइल

------ ------ -----
vocab.txt डाउनलोड करें
config.json डाउनलोड करें

मॉडल है बेर्ट द्वारा ऑनिक्स प्रारूप में निर्यात किया गया प्रतिरक्षण.

आपका फ़ोल्डर इस प्रकार दिखेगा

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

चरण 2: परियोजना सेटअप

नया कंसोल एप्लिकेशन बनाएँ और NuGet पैकेज जोड़ें

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

That's it. Two packages

  • OnnxRuntime - मॉडल चलाता है
  • ML. टोकनाइजर्स पाठ हस्तचालित करता है → टोकन रूपांतरण

चरण 3: टोकनीकरण को समझना

इससे पहले कि मॉडल पाठ को संसाधित कर सकता है, हम इसे संख्याओं में रूपांतरित करने की जरूरत है टोकनीकरण.

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

प्रमुख बिंदुएं

  • [CLS] और [SEP] ये विशेष टोकन हैं जो वाक्य सीमाओं को चिह्नित करते हैं
  • प्रत्येक शब्द से एक संख्या बन जाता है vocab.txt
  • नमूना सिर्फ संख्याओं को देखता है-, कभी भी वास्तविक पाठ नहीं

टोकनाइजर लोड किया जा रहा है

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

क्यों LowerCaseBeforeTokenization = false? यह मॉडल काष्ठित पाठ पर प्रशिक्षित किया गया है।

महत्वपूर्ण: टोकनाइजर होना चाहिए मॉडल ठीक से मिलते हैं. एक भिन्न vocab का उपयोग करते हुए, कैशिंग विकल्पM SK2 या विशेष टोकन IDs silently परिणामों को ह्रास करेगा vocab.txt जो मॉडल के साथ जहाज़ बनाता है

पाठ टोकनाइजिंग

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

इसके बाद हमारे पास है

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

चरण 4: मॉडल चलाना

अब हम इन संख्याओं को ONNX मॉडल में खिसकाते हैं

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

मॉडल लोड किया जा रहा है

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

महत्वपूर्णएक बार टोकनाइजर और निष्कर्ष सत्र बनाएँInferenceSession बनाने के लिए महंगा है

इनपुट तैयार कर रहा है

मॉडल अपेक्षा करता है

  • इनपुट: हमारे टोकन आईडी के रूप में long[]
  • ध्यानवास्तविक टोकन के लिए : 1
// 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;
    }
}

रनिंग इन्फेर्स

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

आउटपुट logits आकारवाला है [1, sequence_length, 9]प्रत्येक टोकन स्थिति के लिए संभव लेबल


चरण 5: आउटपुट डेकोडिंग

मॉडल कच्चे अंक आउटपुट करता है

  1. प्रत्येक टोकन के लिए उच्चतम -scoring लेबल ढूंढें
  2. इन लेबलों को वास्तविक इकाइयों में बदलें

BIO टैगों को समझना

मॉडल का उपयोग करता है जैव संकेतन:

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 = एक व्यक्ति अस्तित्व की शुरुआत
  • I-PER = एक व्यक्ति इकाई के अंदर
  • ओ = किसी भी इकाई के बाहर
  • B-ORG = एक संगठन की शुरुआत

यह मॉडल को कई - शब्द इकाइयों जैसे |" |John Smith | " | या " |United Kingdom

लेबल मैपिंग

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

नोट: यह विशिष्ट मॉडल मानक का उपयोग करता है 9-label CoNLL स्कीमा config.json (id2label क्षेत्र). यदि आप मॉडलों को स्क्वाप करते हैं तो config से लेबल पढ़ने के बजाय हार्डकोडिंग

सर्वोत्तम लेबल ढूंढना

प्रत्येक टोकन के लिए हम सबसे अधिक logit अंक वाले लेबल चुनते हैं

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

Softmax फ़ंक्शन कच्चे अंक को संभाव्यताओं में परिवर्तित करता है (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;
}

विश्वसनीयता पर नोट: सॉफ्टमैक्स प्राप्तियां हैं नातेदार, कैलिब्रेटेड संभाव्यताएं नहीं हैं। 0.92 सही के रूप में "92% ". इनको कम से कम confidence भविष्यवाणी फ़िल्टर करने के लिए इस्तेमाल करें


चरण 6: Entities निकाल रहा है

अब हमारे पास प्रति--token भविष्यवाणी हैं।

पहला, WordPiece विलयन सहायकों को मिलाएँ ## उपशब्द और punctuation अंतराल सही रूप से

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

अब इकाई निष्कर्षण. इकाई विश्वास है न्यूनतम टोकन विश्वास इस तरह एक एक ही कमजोर टोकन नहीं है

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

नोट: WordPiece पूरी तरह से MergeWordPieces-कुछ विशेष नियंत्रण प्रवाह की आवश्यकता नहीं है


पूर्ण उदाहरण

यहाँ एक न्यूनतम कार्य उदाहरण है जिसे आप नकल कर सकते हैं और चला सकते हैं

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

आउटपुट

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

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

शब्दपीस उपशब्दों को समझना

BERT का उपयोग करता है WordPiece टोकनीकरण, जो अज्ञात शब्दों को उपशब्दों में विभाजित करता है ## उपसर्ग का अर्थ है

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

MergeWordPieces सहायक इस को संभालता है: ## टोकन बिना जगह के जोड़े जाते हैं "Elasticsearch" के बजाय "Elastic search".


निष्पादन टिप

1. बैच बहुविध पाठ

यदि आपके पास कई पाठ हैं, तो उन्हें बैचों में संसाधित करें

// 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. स्मार्ट बकेटिंग का उपयोग करें

हमेशा पैड नहीं करें 512. सबसे छोटा बकेट का उपयोग करें जो फिट है

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

व्यवहार में,ONNX NER चलने के लिए पर्याप्त तेजी से है खुराक के दौरान इनलाइन, सिर्फ एक बैच काम के रूप में नहीं है

GPU त्वरण

उच्च throughput के लिए DirectML का उपयोग करें

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

यह LLM के विपरीत कब प्रयोग करें

| ONNX NER का उपयोग करें। |--------------|------------------------| उच्च मात्रा Docs की (1000s ) | एक -off विश्लेषण मानक इकाइयां |Privacy-sensitive data | जब आपको स्पष्टीकरण की जरूरत है निर्णायक पाइपलाइनें विशेषता निष्कर्षण

दोनों दृष्टिकोण काम करते हैं विभिन्न समस्याएं. एनईआर संरचना को निकालता है


द बिगर पिक्चर

यह पैटर्नशीतल मॉडलों के साथ deterministic निष्कर्षण, बाद में वैकल्पिक संश्लेषणएक LLM में कच्चे पाठ को धकेलने से और यह व्यवहार करता है आशा करने के बजाय बहुत बेहतर स्कैल्स

NER आप कुछ नहीं है।

अन्य विशेषता निष्कर्षण कार्यों के लिए समान दृष्टिकोण लागू होता है।

जहाँ यह ठीक है: यह ओसीआर | + | एनईआर पाइपलाइन एक भवन खंड है |. | संपूर्ण तस्वीर के लिए | कम RAG और LucidRAG दस्तावेज़ीकरण. जो इकाइयां आप यहाँ निकालते हैं नोड्स बन जाते है


संसाधन

लाइब्रेरी:

संबंधित लेख:

  • तीन--Tier OCR पाइपलाइन - जब सरल OCR नहीं है
  • कम RAG - जहाँ बाहर निकाले गए इकाइयां बड़ी तस्वीर में फिट हो
  • LucidRAG - इकाई डुप्लीकेट और ग्राफ निर्माण के साथ पूर्ण कार्यान्वयन
logo

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