Saltar al contenido principal
El objetivo de la clasificación de documentos es asignar los documentos a distintas categorías predefinidas. Esto resulta muy útil cuando se trabaja con un flujo documental que incluye documentos de varios tipos y es necesario identificar el tipo de cada documento. Por ejemplo, puede que desee ordenar contratos, facturas y recibos en distintas carpetas, o cambiarles el nombre según su tipo. Esto puede hacerse automáticamente con un sistema preentrenado. Una de las principales características de la clasificación de documentos es que los tipos de documentos que se deben distinguir se conocen de antemano. ABBYY FineReader Engine puede clasificar documentos en función de su contenido, de las características de la imagen, o teniendo en cuenta tanto las características del texto reconocido como las de la imagen. Veamos el proceso en detalle. Consta de dos pasos principales:
  1. Crear una base de datos de clasificación
Para cada categoría, elija varios documentos o páginas representativos. Se utilizarán para crear la base de datos de clasificación.
  1. Clasificar documentos
La base de datos creada en el paso anterior puede utilizarse para clasificar documentos. Los documentos entrantes se envían a un sistema de clasificación preentrenado que utiliza la base de datos de clasificación para determinar la categoría. También puede que necesite clasificar documentos según algunos de sus atributos, como el autor o el valor del código de barras. Este artículo no trata ese tipo de clasificación. Si desea clasificar documentos según sus atributos, deberá implementar sus propios algoritmos, que pueden utilizar los escenarios de extracción de texto, reconocimiento a nivel de campo o reconocimiento de códigos de barras para la extracción de datos. El procedimiento descrito a continuación también se ilustra con la herramienta de demostración Classification para Windows y el ejemplo de código Classification para Linux y macOS.

Implementación del escenario

Los ejemplos de código de este tema son específicos de Windows.
A continuación, se ofrece una descripción detallada del método recomendado para usar ABBYY FineReader Engine para clasificar documentos.
Para empezar a trabajar con ABBYY FineReader Engine, debe crear el objeto Engine. El objeto Engine es el objeto principal de la jerarquía de objetos de ABBYY FineReader Engine y proporciona varias configuraciones globales, algunos métodos de procesamiento y métodos para crear los demás objetos.Para crear el objeto Engine, puede usar la función InitializeEngine. Consulte también otras formas de cargar el objeto Engine (Win).

C#

public class EngineLoader : IDisposable
{
    public EngineLoader()
    {
        // Inicialice estas variables con la ruta completa a FREngine.dll, su Customer Project ID
        // y, si corresponde, la ruta a su archivo de token de Online License y la contraseña de Online License
        string enginePath = "";
        string customerProjectId = "";
        string licensePath = "";
        string licensePassword = "";
        // Cargue la biblioteca 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");
            }
            // Convierta los punteros en delegados
            initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer(
                initializeEnginePtr, typeof(InitializeEngine));
            deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(
                deinitializeEnginePtr, typeof(DeinitializeEngine));
            dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(
                dllCanUnloadNowPtr, typeof(DllCanUnloadNow));
            // Llame a la función InitializeEngine 
            // y pase la ruta al archivo de Online License y la contraseña de Online License
            int hresult = initializeEngine(customerProjectId, licensePath, licensePassword, 
                "", "", false, ref engine);
            Marshal.ThrowExceptionForHR(hresult);
        }
        catch (Exception)
        {
            // Libere la biblioteca FREngine.dll
            engine = null;
            // Elimine todos los objetos antes de llamar a FreeLibrary
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            FreeLibrary(dllHandle);
            dllHandle = IntPtr.Zero;
            initializeEngine = null;
            deinitializeEngine = null;
            dllCanUnloadNow = null;
            throw;
        }
    }
    // Funciones de 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);
    // Funciones de 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();
    // Variables privadas
    private FREngine.IEngine engine = null;
    // Identificador de FREngine.dll
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}
Cree un objeto ClassificationEngine, que actúa como factoría de otros objetos de la API de clasificación. Use el método CreateClassificationEngine del objeto Engine.

C#

FREngine.IEngine engine;
FREngine.IClassificationEngine classEngine = engine.CreateClassificationEngine();
Los métodos de entrenamiento y clasificación funcionan con un tipo especial de objeto creado a partir de un documento o de una página: ClassificationObject, que contiene toda la información relevante para la clasificación.Para preparar un documento para usarlo en un escenario de clasificación, haga lo siguiente:
  1. Cargue las imágenes que se van a procesar. Hay varias formas de hacerlo; por ejemplo, puede crear el objeto FRDocument con el método CreateFRDocument del objeto Engine y, a continuación, agregar imágenes desde un archivo al objeto FRDocument creado mediante el método AddImageFile.
  2. Si va a entrenar o usar un clasificador de un tipo que tiene en cuenta características de texto (CT_Combined, CT_Text), primero reconozca el documento con cualquier método adecuado. Usaremos los métodos Analyze y Recognize del objeto FRDocument. La síntesis del documento no es necesaria para la clasificación.
Aunque la clasificación en sí no admite procesamiento en paralelo, puede necesitarlo para el reconocimiento previo de los documentos en Windows y Linux. Si el número de documentos que va a clasificar es elevado, recomendamos usar Batch Processor u otros métodos de procesamiento en paralelo descritos en Parallel Processing with ABBYY FineReader Engine.
  1. Use el método CreateObjectFromDocument del objeto ClassificationEngine para crear un objeto ClassificationObject que contenga la información de la primera página del documento. Si necesita usar otra página del documento, llame al método CreateObjectFromPage.
  2. La propiedad Description de ClassificationObject está vacía de forma predeterminada. Especifique esta propiedad si necesita una descripción pertinente.
En ocasiones, puede suceder que el documento o la página reconocidos no contengan texto reconocido (por ejemplo, si por error se usó una página vacía). En este caso, el ClassificationObject no puede usarse con clasificadores que requieren características de texto. Puede usar su propiedad SuitableClassifiers como comprobación adicional.

C#

// Crear el objeto FRDocument
FREngine.IFRDocument frDocument = engine.CreateFRDocument();
// Agregar las imágenes
frDocument.AddImageFile( "C:\\MyImage.tif", null, null );
// Opcional: analizar y reconocer el documento
frDocument.Analyze( null, null, null );
frDocument.Recognize( null, null );
// Crear el objeto de clasificación
FREngine.IClassificationObject clObject = classEngine.CreateObjectFromDocument( frDocument );
// Vamos a poner en la descripción la categoría a la que pertenece el objeto
clObject.Description = "CategoryA_Object1";
Para entrenar un clasificador que distinga entre varios tipos de documentos, necesita un conjunto de datos categorizado que contenga muestras de cada tipo. Use el objeto TrainingData para completar y administrar este conjunto de datos:
  1. Cree un objeto vacío con el método CreateTrainingData del objeto ClassificationEngine.
  2. Acceda a la colección de categorías a través de la propiedad Categories.
  3. Use varias veces el método AddNew del objeto Categories para agregar una categoría por cada tipo de documento que desee clasificar. El método requiere una string con la etiqueta de la categoría como parámetro de entrada. La etiqueta será devuelta por los métodos de clasificación, por lo que debe ser única dentro del conjunto de categorías.
  4. Para cada objeto Category recién agregado, abra la colección de objetos de clasificación mediante la propiedad Objects. Con el método IClassificationObjects::Add, agregue los objetos de clasificación correspondientes a esta categoría.
    Ninguna categoría puede quedar vacía. Por razones obvias, se requieren al menos dos categorías para el entrenamiento.
  5. Una vez configurado el conjunto de datos de entrenamiento, puede que desee guardarlo en un archivo del disco para usarlo más adelante; por ejemplo, si la precisión del modelo entrenado resulta inaceptable y desea agregar o corregir datos para mejorar la calidad. El objeto TrainingData proporciona el método SaveToFile.

C#

FREngine.ITrainingData trainingData = classEngine.CreateTrainingData();
FREngine.ICategories categories = trainingData.Categories;
// Agregar la primera categoría
FREngine.ICategory category = categories.AddNew( "CategoryA" );
// Agregar los objetos de clasificación preparados en el paso 3
category.Objects.Add( clObject ); // repetir para todos los objetos de esta categoría
...
// Repetir para todas las categorías
...
// Después de agregar todas las categorías, guardar el conjunto de datos de entrenamiento
trainingData.SaveToFile( "C:\\trainingData.dat" );
La funcionalidad para el entrenamiento de modelos la proporciona el objeto Trainer. Use el método CreateTrainer del objeto ClassificationEngine para crearlo.Contiene todos los ajustes para el tipo de clasificador y el procedimiento de entrenamiento, en dos subobjetos: TrainingParams y ValidationParams. Determine qué ajustes necesita y modifique las propiedades correspondientes:
  • El tipo de clasificador (ITrainingParams::ClassifierType). Esta configuración determina qué características del documento se tienen en cuenta al asignar una categoría: las características de la imagen, el contenido del texto reconocido o ambas. Para seleccionar un tipo que utilice el contenido del texto, debe asegurarse de que todos los objetos de clasificación del conjunto de datos de entrenamiento se hayan creado a partir de documentos reconocidos previamente.
  • El modo de entrenamiento (ITrainingParams::TrainingMode). Esta configuración determina si el proceso de entrenamiento debe favorecer una alta precisión (cuántos de los elementos seleccionados son correctos), una alta cobertura (cuántos de los elementos correctos se seleccionan) o un equilibrio entre ambas.
  • Si se debe usar la validación cruzada de k pliegues (IValidationParams::ShouldPerformValidation). Recomendamos usar la validación cruzada cuando la muestra de entrenamiento no es muy grande, ya que permite entrenar varios modelos con las distintas particiones de la misma muestra y seleccionar el mejor. Si dispone de una gran cantidad de datos categorizados, puede ser mejor desactivar la validación, entrenar el modelo con toda la muestra de entrenamiento y, a continuación, usar los métodos de clasificación (Paso 6) para probar el modelo con otra muestra y calcular por su cuenta las métricas de rendimiento.
  • Los parámetros de la validación cruzada de k pliegues: el número de partes en las que se divide la muestra de entrenamiento (IValidationParams::FoldsCount) y el número de iteraciones (IValidationParams::RepeatCount). Tenga en cuenta que el número mínimo requerido de objetos en el conjunto de entrenamiento en cada iteración no puede ser inferior a 4 para el clasificador de texto ni a 8 para el clasificador combinado. Asegúrese de que su muestra de entrenamiento contenga suficientes objetos.
Ya está listo para entrenar un modelo. Pase el objeto TrainingData que configuró en el paso 4 al método TrainModel del objeto Trainer. Este devuelve una colección TrainingResults que, con la funcionalidad disponible actualmente, contiene únicamente un TrainingResult. Si eligió realizar una validación cruzada, revise las puntuaciones de rendimiento en su subobjeto ValidationResult.
El entrenamiento y la clasificación de modelos se realizarán en modo secuencial en Linux y Windows, independientemente del valor de IMultiProcessingParams::MultiProcessingMode.
La propiedad ITrainingResult::Model proporciona acceso al modelo de clasificación entrenado. Puede guardarlo en un archivo mediante el método SaveToFile o utilizarlo directamente para clasificar documentos (continúe con el Paso 6).

C#

// Crear el objeto trainer y configurar los parámetros
FREngine.ITrainer trainer = classEngine.CreateTrainer();
trainer.TrainingParams.ClassifierType = (int)FREngine.ClassifierTypeEnum.CT_Image; // el clasificador solo usará las características de imagen
// Dejaremos el resto de la configuración con los valores predeterminados y entrenaremos el modelo directamente
FREngine.ITrainingResults results = trainer.TrainModel ( trainingData );
// Comprobar la puntuación F1 del modelo
double F1 = results[0].ValidationResult.FMeasure;
// Obtener el modelo de clasificación
FREngine.IModel model = results[0].Model;
// Guardar el modelo para uso posterior
model.SaveToFile( "C:\\model.dat" );
Para usar el modelo entrenado para clasificar documentos:
  1. Si el modelo no está cargado actualmente, llame al método CreateModelFromFile del objeto ClassificationEngine para cargarlo desde un archivo en el disco.
  2. Prepare los objetos de clasificación a partir de los documentos que necesita clasificar, como se describe en el Paso 3.
  3. Para cada objeto de clasificación, llame al método Classify del objeto Model, pasando ClassificationObject como parámetro de entrada. El método devuelve una colección de objetos ClassificationResult, cada uno de los cuales contiene la etiqueta de la categoría y su probabilidad. Los resultados se ordenan por probabilidad, de mayor a menor. Recupere el resultado y compruebe que el nivel de probabilidad sea aceptable para usted.
    Si el clasificador no puede asignar una categoría, se devuelve null en lugar de la colección de resultados.
El entrenamiento y la clasificación del modelo se realizarán en modo secuencial en Linux y Windows, independientemente del valor de IMultiProcessingParams::MultiProcessingMode.

C#

// Abrir el modelo entrenado
FREngine.IModel model = classEngine.CreateModelFromFile( "C:\\model.dat" );
// Clasificar el objeto
FREngine.IClassificationResults classResults = model.Classify( clObject );
// Acceder al mejor resultado y a su probabilidad
string label = classResults[0].CategoryLabel;
double probability = classResults[0].Probability;
Después de terminar de trabajar con ABBYY FineReader Engine, debe descargar el objeto Engine. Para ello, use la función exportada DeinitializeEngine.

C#

public class EngineLoader : IDisposable
{
    // Descargar FineReader Engine
    public void Dispose()
    {
        if (engine == null)
        {
            // Engine no se cargó
            return;
        }
        engine = null;
        // Eliminar todos los objetos antes de la llamada 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;
        // generar una excepción después de la limpieza
        Marshal.ThrowExceptionForHR(hresult);
    }
    // Funciones de 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);
    // Funciones de 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();
    // variables privadas
    private FREngine.IEngine engine = null;
    // Identificador de FREngine.dll
    private IntPtr dllHandle = IntPtr.Zero;
    private InitializeEngine initializeEngine = null;
    private DeinitializeEngine deinitializeEngine = null;
    private DllCanUnloadNow dllCanUnloadNow = null;
}

Recursos necesarios

Puede utilizar el archivo FREngineDistribution.csv para crear automáticamente una lista de los archivos necesarios para que su aplicación funcione. Para procesar con este escenario, seleccione en la columna 5 (RequiredByModule) los siguientes valores: 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 Si modifica el escenario estándar, cambie los módulos necesarios según corresponda. También debe especificar los idiomas de la interfaz, los idiomas de reconocimiento y cualquier función adicional que utilice su aplicación (como, por ejemplo, Opening.PDF si necesita abrir archivos PDF, o Processing.OCR.CJK si necesita reconocer textos en idiomas CJK). Consulte Working with the FREngineDistribution.csv File para obtener más información.

Optimización adicional

Puede encontrar más información sobre cómo configurar las distintas etapas de procesamiento en estos artículos:

Consulte también

Implementación de escenarios de uso básicos