Come l'IMSC ha costruito. **lucido.RAG Sto leggendo i social media dove le persone continuano a chiedere la stessa cosa. 'Come si ottengono caratteristiche dal testo scansionatoM SK3 l'errore nella categoria è sempre ' usate solo un LLMMST5 che funziona ma è molto costosoMst6 Quindi come io in profondità nello spazio OCR. Ho pensato che avrei scritto un 'applicabile per i principianti 'approccio al non-non--LLM (o LLM opzionale) modo di fare questo
Avete immagini con un testo. volete estrarre quel testo , poi trovate la struttura utile all'interno (i nomiM SK3 aziendeMSC4 postiMST5 senza chiamare un LLMMSSK6 spedire i dati alla nuvolaMSM7 o pagare per un tokenMSV8
Questo articolo mostra il La più semplice possibile. Pipeline: Tesseract per l'estrazione del testo, allora BERT NER (via ONNXM SK1 per il riconoscimento delle entità. Tutti localiMST3 Tutti deterministiciMSS4 Tutti in CMS5
Determinista qui significa versioni fixe, dati linguistici fissati, e nessuna apprendimento adattativo al runtimeM SK2
NuGet sta per arrivare. - IM SK1m imballare questo in un semplice
mostlylucid.ocrnerbiblioteca. Per ora, il codice sotto è copiareM SK2fare la pastaMSC3
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
Due fasi , due modelli, entrambi funzionano localmente. LasciateM SK3 costruire ogni parteMSC4
Tesseract è il motore OCR open standard. Tesseract.NET, a CM SK1 wrapper.
dotnet add package Tesseract
Servono anche i file di dati addestrati. scaricare eng.traineddata a partire da tessdata. E lo mettiamo in una 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();
}
Quella è la chiamata. ExtractText("invoice.png") E ottenete una stringa.
Importante:
TesseractEngineÈ costoso creare. Nelle applicazioni reali, lo crea una volta e lo riutilizzaM SK2
Tesseract funziona bene per pulito, alto- testo contrastante nelle font standard. Si batte con :
inter-\nnational) potrebbe avere bisogno di un processo post- prima del NER.Per i sistemi di produzione che devono gestire le cose strane, vedete Il Pipeline Three-Tier OCR- che aggiunge Florence-2 ONNX come escalazione intermedia e Vision LLM per i casi rigidi .
Per questo tutorial, noi' supponiamo che abbiate immagini pulite o un testo da un'altra fonte M SK2Parsing PDF , copyMSC4pasteMST5 eccMSSK6
In pratica, di solito vorrete normalizzare il risultato OCR M SK1 trim whitespace, collapse repetite newlinesMSC3 correggere l'ifenazione ovvia ) prima di trasferirlo a NERMNK5
Prima di approfondire il codice, lasciate che ' capisca cosa stiamo facendo.
Riconoscere le entità chiamate (NER) è un problema risolto. I ricercatori hanno addestrato reti neurali che possono leggere il testo e evidenziare i "bit interessantiM SK2
Il modello non capisce il testo. NER è l'estrazione delle caratteristiche, non il ragionamento. E' il modello che corrisponde ai steroidi.
NX (Open Neural Network Exchange) è un formato standard per i modelli MLM SK2 Pensate a questo come ad una Frozen inference DLL per le reti neurali: peso fisso in , tensioni fuori , nessuna logica per l'addestramento, nessuna casualità:
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
Il punto di vista chiave: qualcun altro ha fatto il duro lavoro. (struire il modello in Python). Si fa solo l'inferenza in CM SK2
Potreste mandare un messaggio al GPT-4 e chiedere " trovare le persone e le aziende in questo messaggioM SK2 Funziona ! MaMSC4
| Approco | Velocità | Costo per S1000 dossiers M | Privacy R | Consistenza D | |||||
|---|---|---|---|---|---|---|---|---|---|
| NER ONNX | ~50ms | ||||||||
| LLM API locale | 4-30s | $0 | I modelli piccoli possono essere impermeabili | Variabile | |||||
| LLM API |
I LLM sono fantastici per un ragionamento complesso. Per l'estrazione di schemi su larga scalaM SK1 un modello dedicato è 40x più veloce e libero .
Questo è quello che stiamo costruendo.
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
Ogni passo è semplice.
Avete bisogno di tre file da HuggingFace. Fatela scaricare manualmente in un folder M SK1e .g., ./models/ner/):
| File | Size | URL | ||
|---|---|---|---|---|
model.onnx |
~430MB | Lavorare | ||
vocab.txt |
~230KB | Lavorare | ||
config.json |
~1KB | Lavorare |
Il modello è Bert-base-NER L'export è stato fatto nel formato ONNX. Proteggerai..
La vostra cartella dovrebbe sembrare:
models/
ner/
model.onnx (the neural network)
vocab.txt (word → ID mapping)
config.json (label definitions)
Creare una nuova app per la console e aggiungere i pacchetti NuGet:
dotnet new console -n NerDemo
cd NerDemo
dotnet add package Microsoft.ML.OnnxRuntime
dotnet add package Microsoft.ML.Tokenizers
Che's it. Due pacchettiM SK2
Prima che il modello possa elaborare il testo, dobbiamo convertirlo in numeri. Tokenizzazione.
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
punti chiave:
[CLS] e [SEP] Sono dei simboli speciali che marcano i confini delle frasi.vocab.txtusing 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);
Perché? LowerCaseBeforeTokenization = false? Questo modello è stato addestrato su un testo con casetta. "JohnM SK3 e MSC4johnMSC5 hanno significati diversiMST6unoMSS7 è probabile che sia un nomeMTS8 unoMSSS9 probabilmente non sia
Importante: Il tokenizzatore deve Corresponde esattamente al modello. Utilizzando un altro vocab, opzione di cabinaM SK2 o degli identificatori di tocco speciali diminuisce silentmente i risultati . Usate sempre
vocab.txtche nave con il modello.
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]");
Dopo questo , abbiamo :.
tokenIds: [101, 1287, 3455, 2573, 1120, 7513, 1999, 2414, 119, 102]tokens: ["[CLS]", "John", "Smith", "works", "at", "Microsoft", "in", "London", ".", "[SEP]"]Ora inseriamo quei numeri nel modello ONNX. Il modello restituisce "logitiM SK2 punteggi reali per ogni possibile etichetta in ogni posizione .
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);
Importante: Creare una volta il tokenizzatore e la sessione di deduzione (singletonM SK2service) e riutilizzarliMST4 DonMSSK5 non riscarciare per ogni documentoMSC6
InferenceSessionÈ costoso creare.
Il modello si aspetta:
long[]// 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>();
Il risultato. logits Ha una forma. [1, sequence_length, 9]-9 possibili etichette per ogni posizione di toccoM SK1
Il modello produce punteggi grezzi. Dobbiamo:
Il modello usa Notazione 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
Questo permette al modello di gestire molte entità di parole come "John SmithM SK2 o "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
};
Nota.: Questo modello specifico usa il schema CoNLL standard 9-label. Alcuni esportazioni ONNX includono i nomi delle etichette in
config.json(id2labelcampo). Se scambiate modelli, leggete le etichette dalla configurazione piuttosto che dal hardcoding
Per ogni tocco, scegliamo l'etichetta con il punteggio logit più altoM 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));
}
L'informazione figura nella parte dispositiva. Softmax Funzione converte i punteggi grezzi alle probabilità (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;
}
Nota sulla fiducia: I punteggi della Softmax sono relative., probabilità non calibrateM SK1 Sono utili per la classificazione e il tasso di soglia , ma non sono usati
0.92come "92% corretto". Usarli per filtrare le previsioni di fiducia bassaM SK2 non come verità di fondo .
Ora abbiamo proiezioni per -, "token predictions" e .. Dobbiamo fusionarle in entità.
Primo, gli aiuti per la fusione di WordPiece. Queste manipolazioni ## parole sottostantive e spazi per la punteggiatura corretti:
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();
}
Ora l'estrazione dell'entità. La fiducia nell'entity è la La fiducia minima nel tocco In questo modo, un singolo tocco debole non è nascosto dall'evidenza media.
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;
}
Note: WordPiece è gestito interamente da MergeWordPieces-non c'è bisogno di un flusso di controllo specialeM SK1
Qui' è un esempio di lavoro minimo che potete copiare e far funzionare:
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)}");
Output:
Input: John Smith, CEO of Microsoft, announced the acquisition in London yesterday.
Entities found:
[PER] John Smith
[ORG] Microsoft
[LOC] London
BERT usa Tokenizzazione di WordPiece, che divide parole sconosciute in sotto parole. ## Prefixo significa "continuazione della parola precedente":
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
L'informazione figura nella parte dispositiva. MergeWordPieces l'aiuto gestisce questo: ## i token sono attaccati senza uno spazio, che produca "Elasticsearch" invece di "Elastic search".
Se avete molti messaggi, processarli in lotti:
// 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]);
Don' non immergersi sempre a 512. Usare il contenitore più piccolo che si adatti.
int[] buckets = [32, 64, 128, 256, 512];
int targetLength = buckets.FirstOrDefault(b => b >= tokenIds.Count);
if (targetLength == 0) targetLength = 512;
In pratica, ONNX NER è abbastanza veloce da funzionare inline durante l'ingestione., non solo come un lavoro di lotto. Si possono estrarre le entità quando arrivano i documenti piuttosto che metterli in fila per un ulteriore periodo.
Per un alto throughput, usa DirectML (Windows) o CUDAM SK3
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
| Utilizza ONNX NER | Utilizzi LLM (GPTM SK3Claude) |
|---|---|
| High volume (1000s of docs) | OneM SK4off analysis SSK5 |
| Entità standard (persone, organiM SK3 luoghiMSC4 | Tipi di entità personalizzati SSK6 |
| PrivatezzaM SK1dati sensibili | Quando avete bisogno di spiegazioni |
| Pipeline deterministici | Analisi esplorative PSK2 |
| Extrazione delle caratteristiche | Interprezione PES2 sintesi PES3 |
Entrambi approcci funzionano. Risolvono diversi problemi.. NER extrae la strutturaM SK1 LLMs ragione sul significato.
Questo schema-Extrazione deterministica con modelli congelati, seguito da una sintesi opzionale dopo.-scalle molto meglio di spingere il testo grezzo in un LLM e sperare che si comporte.
Il NER non è qualcosa che voi "agentificiate". Ma l'infrastruttura di 'You run it on ingestionM SK4 store the entitiesM Sk5 and use them downstream for filteringM sk6 linking M Sk7 or feeding into more sophisticated pipelinesMtk8
Lo stesso approccio si applica ad altre attività di extrazione delle caratteristiche: embeddings, classificazioneM SK2 sentimenti . Treno una volta MSC4 o usare pre-preservatoMSL5 esportazione a ONNXMST6 andare ovunqueMst7 deterministamenteM st8
Dove questo va bene?: Questo OCR + Il tubo del NER è un blocco di costruzione. Per il quadro completoM SK3 come le entità extraite entrano nella costruzione dei graficiMSC4 la deduplicazioneMNK5 e la ripresaMZK6 vediamo RAG ridotto e la Documentazione LucidRAG. Le entità che si estraggono qui diventano nodi; i documenti diventano bordi ; il LLM vede solo quello di cui ha bisognoM SK3
Le biblioteche & Modelli:
Article connessi:
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.