Vai al contenuto principale
Lo scopo della classificazione dei documenti è assegnare i documenti a diverse categorie predefinite. Questo è molto utile quando si gestisce un flusso di documenti che include documenti di vario tipo e si deve identificare il tipo di ciascun documento. Ad esempio, si possono ordinare contratti, fatture e ricevute in cartelle diverse oppure rinominarli in base al loro tipo. Questo può essere fatto automaticamente con un sistema preaddestrato. Una delle caratteristiche principali della classificazione dei documenti è che i tipi di documenti da distinguere sono noti in anticipo. ABBYY FineReader Engine può classificare i documenti in base al loro contenuto, alle caratteristiche dell’immagine oppure tenendo conto sia delle caratteristiche del testo riconosciuto sia di quelle dell’immagine. Vediamo il processo nel dettaglio. Si compone di due passaggi principali:
  1. Creazione di un database di classificazione
Per ogni categoria, scegliere alcuni documenti o pagine rappresentativi. Verranno utilizzati per creare il database di classificazione.
  1. Classificazione dei documenti
Il database creato nel passaggio precedente può essere utilizzato per classificare i documenti. I documenti in arrivo vengono inviati a un sistema di classificazione preaddestrato, che utilizza il database di classificazione per determinare la categoria. Potrebbe anche essere necessario classificare i documenti in base ad alcuni dei loro attributi, come l’autore o il valore del barcode. Questo articolo non tratta questo tipo di classificazione. Se si desidera classificare i documenti in base ai loro attributi, è necessario implementare algoritmi personalizzati che possono utilizzare gli scenari di estrazione del testo, riconoscimento a livello di field o riconoscimento di barcode per l’estrazione dei dati. La procedura descritta di seguito è illustrata anche dallo strumento dimostrativo Classification per Windows e dall’esempio di codice Classification per Linux e macOS.

Implementazione dello scenario

Gli esempi di codice forniti in questo argomento sono specifici per Windows.
Di seguito è riportata una descrizione dettagliata del metodo consigliato per usare ABBYY FineReader Engine per classificare i documenti.
Per iniziare a usare ABBYY FineReader Engine, è necessario creare l’oggetto Engine. L’oggetto Engine è l’oggetto principale nella gerarchia degli oggetti di ABBYY FineReader Engine e fornisce diverse impostazioni globali, alcuni metodi di elaborazione e i metodi per creare gli altri oggetti.Per creare l’oggetto Engine, è possibile usare la funzione InitializeEngine. Vedi anche altri modi per caricare l’oggetto Engine (Win).

C#

public class EngineLoader : IDisposable
{
    public EngineLoader()
    {
        // Inizializza queste variabili con il percorso completo di FREngine.dll, il tuo Customer Project ID
        // e, se applicabile, il percorso del file token della licenza online e la password della licenza online
        string enginePath = "";
        string customerProjectId = "";
        string licensePath = "";
        string licensePassword = "";
        // Carica la libreria FREngine.dll
        dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
           
        try
        {
            if (dllHandle == IntPtr.Zero)
            {
                throw new Exception("Can't load " + enginePath);
            }
            IntPtr initializeEnginePtr = GetProcAddress(dllHandle, "InitializeEngine");
            if (initializeEnginePtr == IntPtr.Zero)
            {
                throw new Exception("Can't find InitializeEngine function");
            }
            IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine");
            if (deinitializeEnginePtr == IntPtr.Zero)
            {
                throw new Exception("Can't find DeinitializeEngine function");
            }
            IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow");
            if (dllCanUnloadNowPtr == IntPtr.Zero)
            {
                throw new Exception("Can't find DllCanUnloadNow function");
            }
            // Converte i puntatori in delegati
            initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer(
                initializeEnginePtr, typeof(InitializeEngine));
            deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(
                deinitializeEnginePtr, typeof(DeinitializeEngine));
            dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(
                dllCanUnloadNowPtr, typeof(DllCanUnloadNow));
            // Chiama la funzione InitializeEngine
            // passando il percorso del file della licenza online e la password della licenza online
            int hresult = initializeEngine(customerProjectId, licensePath, licensePassword, 
                "", "", false, ref engine);
            Marshal.ThrowExceptionForHR(hresult);
        }
        catch (Exception)
        {
            // Libera la libreria FREngine.dll
            engine = null;
            // Elimina tutti gli oggetti prima della chiamata a FreeLibrary
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            FreeLibrary(dllHandle);
            dllHandle = IntPtr.Zero;
            initializeEngine = null;
            deinitializeEngine = null;
            dllCanUnloadNow = null;
            throw;
        }
    }
    // Funzioni di Kernel32.dll
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
    private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    private static extern bool FreeLibrary(IntPtr hModule);
    // Funzioni di FREngine.dll
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private delegate int InitializeEngine(string customerProjectId, string licensePath, 
        string licensePassword, string tempFolder, string dataFolder, bool isSharedCPUCoresMode, 
        ref FREngine.IEngine engine);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DeinitializeEngine();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DllCanUnloadNow();
    // Variabili private
    private FREngine.IEngine engine = null;
    // Handle della libreria FREngine.dll
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}
Crea un oggetto ClassificationEngine, che funge da factory per gli altri oggetti della Classification API. Usa il metodo CreateClassificationEngine dell’oggetto Engine.

C#

FREngine.IEngine engine;
FREngine.IClassificationEngine classEngine = engine.CreateClassificationEngine();
I metodi di addestramento e classificazione utilizzano uno speciale tipo di oggetto creato a partire da un documento o da una pagina: ClassificationObject, che contiene tutte le informazioni rilevanti per la classificazione.Per preparare un documento da utilizzare in uno scenario di classificazione, procedi come segue:
  1. Carica le immagini da elaborare. Esistono diversi modi per farlo: ad esempio, puoi creare l’oggetto FRDocument tramite il metodo CreateFRDocument dell’oggetto Engine, quindi aggiungere le immagini all’oggetto FRDocument creato da file usando il metodo AddImageFile.
  2. Se intendi addestrare o usare un classificatore del tipo che tiene conto delle caratteristiche del testo (CT_Combined, CT_Text), esegui prima il riconoscimento del documento usando un metodo appropriato. Useremo i metodi Analyze e Recognize dell’oggetto FRDocument. La sintesi del documento non è necessaria per la classificazione.
Sebbene l’elaborazione parallela non sia supportata per la classificazione in sé, potrebbe essere necessaria per il riconoscimento preliminare dei documenti in Windows e Linux. Se il numero di documenti da classificare è elevato, consigliamo di usare Batch Processor o altri metodi di elaborazione parallela descritti in Parallel Processing with ABBYY FineReader Engine.
  1. Usa il metodo CreateObjectFromDocument dell’oggetto ClassificationEngine per creare un ClassificationObject contenente le informazioni della prima pagina del documento. Se devi usare un’altra pagina del documento, chiama il metodo CreateObjectFromPage.
  2. La proprietà Description di ClassificationObject è vuota per impostazione predefinita. Specifica questa proprietà se hai bisogno di una descrizione appropriata.
In alcuni casi, il documento o la pagina riconosciuti possono comunque non contenere alcun testo riconosciuto (ad esempio, se per errore è stata usata una pagina vuota). In questo caso, il ClassificationObject non può essere usato per i classificatori che richiedono caratteristiche del testo. Per un’ulteriore verifica, puoi usare la relativa proprietà SuitableClassifiers.

C#

// Crea l'oggetto FRDocument
FREngine.IFRDocument frDocument = engine.CreateFRDocument();
// Aggiungi le immagini
frDocument.AddImageFile( "C:\\MyImage.tif", null, null );
// Facoltativo: analizza e riconosci il documento
frDocument.Analyze( null, null, null );
frDocument.Recognize( null, null );
// Crea l'oggetto di classificazione
FREngine.IClassificationObject clObject = classEngine.CreateObjectFromDocument( frDocument );
// Inseriamo nella descrizione la categoria a cui appartiene l'oggetto
clObject.Description = "CategoryA_Object1";
Per addestrare un classificatore in grado di distinguere tra diversi tipi di documenti, è necessario un set di dati categorizzato che contenga esempi di ciascun tipo. Usa l’oggetto TrainingData per popolare e gestire questo set di dati:
  1. Crea un oggetto vuoto con il metodo CreateTrainingData dell’oggetto ClassificationEngine.
  2. Accedi alla raccolta di categorie tramite la proprietà Categories.
  3. Usa più volte il metodo AddNew dell’oggetto Categories per aggiungere una categoria per ciascun tipo di documento che intendi classificare. Il metodo richiede come parametro di input una string con l’etichetta della categoria. L’etichetta verrà restituita dai metodi di classificazione, quindi deve essere univoca all’interno dell’insieme di categorie.
  4. Per ogni oggetto Category appena aggiunto, apri la raccolta di oggetti di classificazione tramite la proprietà Objects. Con il metodo IClassificationObjects::Add, aggiungi gli oggetti di classificazione che corrispondono a questa categoria.
    Nessuna categoria può rimanere vuota. Per ovvie ragioni, per l’addestramento sono necessarie almeno due categorie.
  5. Una volta configurato il set di dati di addestramento, puoi salvarlo in un file su disco per utilizzarlo in seguito, ad esempio se l’accuratezza del modello addestrato si rivela insoddisfacente e desideri aggiungere o correggere alcuni dati per migliorarne la qualità. L’oggetto TrainingData mette a disposizione il metodo SaveToFile.

C#

FREngine.ITrainingData trainingData = classEngine.CreateTrainingData();
FREngine.ICategories categories = trainingData.Categories;
// Aggiungi la prima categoria
FREngine.ICategory category = categories.AddNew( "CategoryA" );
// Aggiungi gli oggetti di classificazione preparati al passaggio 3
category.Objects.Add( clObject ); // ripeti per tutti gli oggetti di questa categoria
...
// Ripeti per tutte le categorie
...
// Dopo aver aggiunto tutte le categorie, salva il set di dati di addestramento
trainingData.SaveToFile( "C:\\trainingData.dat" );
La funzionalità per l’addestramento del modello è fornita dall’oggetto Trainer. Per crearlo, utilizzare il metodo CreateTrainer dell’oggetto ClassificationEngine.Contiene tutte le impostazioni per il tipo di classificatore e la procedura di addestramento, nei due sotto-oggetti TrainingParams e ValidationParams. Determinare le impostazioni necessarie e modificare le proprietà corrispondenti:
  • Il tipo di classificatore (ITrainingParams::ClassifierType). Questa impostazione determina quali caratteristiche del documento vengono prese in considerazione per l’assegnazione di una categoria: le caratteristiche dell’immagine, il contenuto del testo riconosciuto o entrambi. Per selezionare un tipo che utilizza il contenuto del testo, è necessario assicurarsi che tutti gli oggetti di classificazione nel set di dati di training siano stati creati a partire da documenti precedentemente riconosciuti.
  • La modalità di addestramento (ITrainingParams::TrainingMode). Questa impostazione determina se il processo di addestramento debba privilegiare un’elevata precisione (quanti degli elementi selezionati sono corretti), un recall elevato (quanti degli elementi corretti vengono selezionati) oppure un equilibrio tra i due.
  • Se utilizzare la convalida incrociata k-fold (IValidationParams::ShouldPerformValidation). Consigliamo di utilizzare la convalida incrociata quando il campione di addestramento non è grande, poiché consente di addestrare più modelli su partizioni diverse dello stesso campione e selezionare il migliore. Se si dispone di una grande quantità di dati categorizzati, può essere preferibile disattivare la convalida, addestrare il modello sull’intero campione di addestramento e quindi utilizzare i metodi di classificazione (Passaggio 6) per testare il modello su un altro campione, calcolando dal proprio lato gli indicatori di prestazione.
  • I parametri della convalida incrociata k-fold: il numero di parti in cui viene suddiviso il campione di addestramento (IValidationParams::FoldsCount) e il numero di iterazioni (IValidationParams::RepeatCount). Si noti che il numero di oggetti richiesto nel set di addestramento a ogni iterazione deve essere almeno 4 per il classificatore di testo e almeno 8 per il classificatore combinato. Assicurarsi che il campione di addestramento contenga un numero sufficiente di oggetti.
A questo punto è possibile addestrare un modello. Passare l’oggetto TrainingData configurato nel Passaggio 4 al metodo TrainModel dell’oggetto Trainer. Il metodo restituisce una raccolta TrainingResults che, con le funzionalità attualmente disponibili, contiene un solo oggetto TrainingResult. Se si è scelto di eseguire la convalida incrociata, esaminare i punteggi di prestazione nel relativo sotto-oggetto ValidationResult.
L’addestramento del modello e la classificazione verranno eseguiti in modalità sequenziale su Linux e Windows, indipendentemente dal valore di IMultiProcessingParams::MultiProcessingMode.
La proprietà ITrainingResult::Model consente di accedere al modello di classificazione addestrato. È possibile salvarlo in un file tramite il metodo SaveToFile oppure utilizzarlo direttamente per classificare alcuni documenti (procedere al Passaggio 6).

C#

// Crea l'oggetto trainer e imposta i parametri
FREngine.ITrainer trainer = classEngine.CreateTrainer();
trainer.TrainingParams.ClassifierType = (int)FREngine.ClassifierTypeEnum.CT_Image; // il classificatore utilizzerà solo le caratteristiche dell'immagine
// Lasciare le altre impostazioni ai valori predefiniti e addestrare il modello direttamente
FREngine.ITrainingResults results = trainer.TrainModel ( trainingData );
// Verifica il punteggio F1 del modello
double F1 = results[0].ValidationResult.FMeasure;
// Recupera il modello di classificazione
FREngine.IModel model = results[0].Model;
// Salva il modello per uso futuro
model.SaveToFile( "C:\\model.dat" );
Per utilizzare il modello addestrato per la classificazione:
  1. Se il modello non è attualmente caricato, chiama il metodo CreateModelFromFile dell’oggetto ClassificationEngine per caricarlo da un file su disco.
  2. Prepara gli oggetti di classificazione a partire dai documenti che devi classificare, come descritto nel Passaggio 3.
  3. Per ogni oggetto di classificazione, chiama il metodo Classify dell’oggetto Model, passando ClassificationObject come parametro di input. Il metodo restituisce una raccolta di oggetti ClassificationResult, ciascuno dei quali contiene l’etichetta della categoria e la relativa probabilità. I risultati sono ordinati per probabilità dal più alto al più basso. Recupera il risultato e verifica che il livello di probabilità sia per te accettabile.
    Se il classificatore non è in grado di assegnare una categoria, viene restituito null invece della raccolta di risultati.
L’addestramento del modello e la classificazione vengono eseguiti in modalità sequenziale in Linux e Windows, indipendentemente dal valore di IMultiProcessingParams::MultiProcessingMode.

C#

// Apri il modello addestrato
FREngine.IModel model = classEngine.CreateModelFromFile( "C:\\model.dat" );
// Classifica l'oggetto
FREngine.IClassificationResults classResults = model.Classify( clObject );
// Accedi al miglior risultato e alla relativa probabilità
string label = classResults[0].CategoryLabel;
double probability = classResults[0].Probability;
Dopo aver terminato di lavorare con ABBYY FineReader Engine, devi scaricare l’oggetto Engine. Per farlo, utilizza la funzione esportata DeinitializeEngine.

C#

public class EngineLoader : IDisposable
{
    // Scarica FineReader Engine
    public void Dispose()
    {
        if (engine == null)
        {
            // Engine non è stato caricato
            return;
        }
        engine = null;
        // Elimina tutti gli oggetti prima della chiamata a FreeLibrary
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        int hresult = deinitializeEngine();
 
        hresult = dllCanUnloadNow();
        if (hresult == 0)
        {
            FreeLibrary(dllHandle);
        }
        dllHandle = IntPtr.Zero;
        initializeEngine = null;
        deinitializeEngine = null;
        dllCanUnloadNow = null;
        // Genera l'eccezione dopo la pulizia
        Marshal.ThrowExceptionForHR(hresult);
    }
    // Funzioni di Kernel32.dll
    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
    private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
    [DllImport("kernel32.dll")]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
    [DllImport("kernel32.dll")]
    private static extern bool FreeLibrary(IntPtr hModule);
    // Funzioni di FREngine.dll
    [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
    private delegate int InitializeEngine( string customerProjectId, string LicensePath, string LicensePassword, , , , ref FREngine.IEngine engine);
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DeinitializeEngine();
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int DllCanUnloadNow();
    // Variabili private
    private FREngine.IEngine engine = null;
    // Handle di FREngine.dll
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}

Risorse necessarie

Puoi utilizzare il file FREngineDistribution.csv per creare automaticamente un elenco dei file necessari per il funzionamento della tua applicazione. Per l’elaborazione in questo scenario, seleziona nella colonna 5 (RequiredByModule) i seguenti valori: Core Core.Resources Opening Opening, Processing Processing Processing.Classification Processing.Classification.NaturalLanguages Processing.OCR Processing.OCR, Processing.ICR Processing.OCR.NaturalLanguages Processing.OCR.NaturalLanguages, Processing.ICR.NaturalLanguages Se modifichi lo scenario standard, adegua di conseguenza i moduli richiesti. Devi inoltre specificare le lingue dell’interfaccia, le lingue di riconoscimento ed eventuali funzionalità aggiuntive utilizzate dalla tua applicazione (ad esempio, Opening.PDF se devi aprire file PDF oppure Processing.OCR.CJK se devi riconoscere testi in lingue CJK). Per ulteriori dettagli, consulta Working with the FREngineDistribution.csv File.

Ottimizzazione aggiuntiva

Puoi trovare ulteriori informazioni sulla configurazione delle diverse fasi di elaborazione nei seguenti articoli:

Vedi anche

Implementazione degli scenari di utilizzo di base