Tutorial: entrenamiento de un modelo ML.NET de clasificación para clasificar imágenes

Aprenda a entrenar un modelo de clasificación para clasificar imágenes mediante un modelo de TensorFlow entrenado previamente para el procesamiento de imágenes.

El modelo de TensorFlow se entrenó para clasificar las imágenes en mil categorías. Dado que el modelo de TensorFlow sabe cómo reconocer patrones en imágenes, el modelo ML.NET puede hacer uso de parte de él en su canalización para convertir imágenes sin procesar en características o entradas para entrenar un modelo de clasificación.

En este tutorial aprenderá a:

  • Entender el problema
  • Incorporación del modelo de TensorFlow entrenado previamente en la canalización de ML.NET
  • Entrenamiento y evaluación del modelo de ML.NET
  • Clasificación de una imagen de prueba

Puede encontrar el código fuente para este tutorial en el repositorio dotnet/samples. De forma predeterminada, la configuración del proyecto de .NET de este tutorial tiene como destino .NET Core 2.2.

Requisitos previos

Selección de la tarea de aprendizaje automático adecuada

Aprendizaje profundo

El aprendizaje profundo es un subconjunto de Machine Learning, que está revolucionando áreas como Computer Vision y reconocimiento de voz.

Los modelos de aprendizaje profundo se entrenan mediante el uso de grandes conjuntos de datos etiquetados y redes neuronales que contienen varias capas de conocimiento. Aprendizaje profundo:

  • Funciona mejor en algunas tareas como Computer Vision.
  • Requiere enormes cantidades de datos de entrenamiento.

La clasificación de imágenes es una tarea que nos permite clasificar imágenes automáticamente en categorías como:

  • Detectar o no una cara humana en una imagen.
  • Detectar gatos o perros.

O bien, como en las imágenes siguientes, determinar si una imagen es un alimento, un juguete o un dispositivo:

imagen de una pizzaimagen de un oso de pelucheimagen de una tostadora

Nota

Las imágenes anteriores pertenecen a Wikimedia Commons y tienen los siguientes atributos:

Entrenar un modelo de clasificación de imágenes desde cero requiere establecer millones de parámetros, una enorme cantidad de datos de aprendizaje etiquetados y una gran cantidad de recursos informáticos (cientos de horas de GPU). Si bien no es tan eficaz como entrenar un modelo entrenado previamente, el aprendizaje por transferencia permite acortar este proceso al trabajar con miles de imágenes frente a millones de imágenes etiquetadas y crear un modelo personalizado con bastante rapidez (menos de una hora en una máquina sin GPU). En este tutorial se amplía aún más ese proceso, con solo una docena de imágenes de aprendizaje.

Inception model está entrenado para clasificar imágenes en miles de categorías, pero, en este tutorial, el usuario deberá clasificar las imágenes en un conjunto de categorías más pequeño y solo en esas categorías. Puede usar la capacidad de Inception model para reconocer y clasificar imágenes en las nuevas categorías limitadas del clasificador personalizado de imágenes.

  • Alimentos
  • Juguetes
  • Dispositivos

En este tutorial se usa el modelo de aprendizaje profundo del Inicio de TensorFlow, un modelo de reconocimiento de imágenes popular entrenado en el conjunto de datos ImageNet. El modelo de TensorFlow clasifica imágenes completas en miles de clases como "Paraguas", "Jersey" y "Lavavajillas".

Como Inception model se ha entrenado previamente en miles de imágenes distintas, contiene internamente las características de imagen necesarias para la identificación de imágenes. Podemos usar estas características de imagen internas en el modelo para entrenar un modelo nuevo con muchas menos clases.

Como se muestra en el diagrama siguiente, puede agregar una referencia a los paquetes NuGet de ML.NET en las aplicaciones de .NET Core o .NET Framework. En segundo plano, ML.NET incluye y hace referencia a la biblioteca nativa de TensorFlow que permite escribir código que carga un archivo de modelo de TensorFlow entrenado existente.

Diagrama de la arquitectura de ML.NET de transformación de TensorFlow

Clasificación multiclase

Después de usar el modelo de inicio de TensorFlow para extraer características adecuadas como entrada para un algoritmo de aprendizaje automático clásico, se agrega un clasificador multiclase de ML.NET.

El entrenador específico que se usa en este caso es el algoritmo de regresión logística multinomial.

El algoritmo que implementa este entrenador funciona con un gran número de características, que es el caso de un modelo de aprendizaje profundo que funciona con datos de imagen.

Consulte Aprendizaje profundo frente a aprendizaje automático para obtener más información.

Datos

Hay dos orígenes de datos: el archivo .tsv y los archivos de imagen. El archivo tags.tsv contiene dos columnas: la primera está definida como ImagePath y la segunda es la Label que corresponde a la imagen. El archivo de ejemplo siguiente no tiene una fila de encabezado y se ve así:

broccoli.jpg	food
pizza.jpg	food
pizza2.jpg	food
teddy2.jpg	toy
teddy3.jpg	toy
teddy4.jpg	toy
toaster.jpg	appliance
toaster2.png	appliance

Las imágenes de entrenamiento y prueba se encuentran en las carpetas de recursos que descargará en un archivo ZIP. Estas imágenes pertenecen a Wikimedia Commons.

Wikimedia Commons, el repositorio multimedia gratuito. Recuperado a las 10:48 del 17 de octubre de 2018 desde: https://commons.wikimedia.org/wiki/Pizzahttps://commons.wikimedia.org/wiki/Toasterhttps://commons.wikimedia.org/wiki/Teddy_bear

Programa de instalación

Crear un proyecto

  1. Cree una aplicación de consola en C# llamada "TransferLearningTF". Haga clic en el botón Next (Siguiente).

  2. Seleccione .NET 6 como marco de trabajo que va a usarse. Haga clic en el botón Crear.

  3. Instale el paquete NuGet Microsoft.ML:

    Nota

    En este ejemplo se usa la versión estable más reciente de los paquetes NuGet mencionados, a menos que se indique lo contrario.

    • En el Explorador de soluciones, haga clic con el botón derecho en Administrar paquetes NuGet.
    • Elija "nuget.org" como origen del paquete, seleccione la pestaña Examinar y busque Microsoft.ML.
    • Seleccione el botón Instalar.
    • Seleccione el botón Aceptar en el cuadro de diálogo Vista previa de los cambios.
    • Haga clic en el botón Acepto en el cuadro de diálogo Aceptación de la licencia si está de acuerdo con los términos de licencia de los paquetes que aparecen.
    • Repita estos pasos para Microsoft.ML.ImageAnalytics, SciSharp.TensorFlow.Redist y Microsoft.ML.TensorFlow.

Descarga de activos

  1. Descargue el archivo ZIP del directorio de recursos del proyecto y descomprímalo.

  2. Copie el directorio assets en el directorio de proyecto TransferLearningTF. Este directorio y sus subdirectorios contienen los datos y los archivos auxiliares (excepto los del modelo de inicio, que descargará y agregará en el paso siguiente) que se necesitan en este tutorial.

  3. Descargue el modelo de inicio y descomprímalo.

  4. Copie el contenido del directorio inception5h que acaba de descomprimir en el directorio assets/inception del proyecto TransferLearningTF. En este directorio está el modelo y los archivos auxiliares adicionales que se necesitan en este tutorial, tal como se muestra en la imagen siguiente:

    Contenido del directorio de inicio

  5. En el Explorador de soluciones, haga clic con el botón derecho en cada uno de los archivos del directorio de recursos y los subdirectorios y seleccione Propiedades. En Avanzadas, cambie el valor de Copiar en el directorio de salida por Copiar si es posterior.

Crear clases y definir rutas de acceso

  1. Agregue las siguientes instrucciones using adicionales a la parte superior del archivo Program.cs:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    
  2. Agregue el código siguiente a la línea justo debajo de las instrucciones using para especificar las rutas de acceso del recurso:

    string _assetsPath = Path.Combine(Environment.CurrentDirectory, "assets");
    string _imagesFolder = Path.Combine(_assetsPath, "images");
    string _trainTagsTsv = Path.Combine(_imagesFolder, "tags.tsv");
    string _testTagsTsv = Path.Combine(_imagesFolder, "test-tags.tsv");
    string _predictSingleImage = Path.Combine(_imagesFolder, "toaster3.jpg");
    string _inceptionTensorFlowModel = Path.Combine(_assetsPath, "inception", "tensorflow_inception_graph.pb");
    
  3. Cree clases para los datos de entrada y las predicciones.

    public class ImageData
    {
        [LoadColumn(0)]
        public string? ImagePath;
    
        [LoadColumn(1)]
        public string? Label;
    }
    

    ImageData es la clase de datos de imagen de entrada y tiene los campos String siguientes:

    • ImagePath contiene el nombre del archivo de imagen.
    • Label contiene un valor para la etiqueta de imagen.
  4. Agregue una clase nueva al proyecto para ImagePrediction:

    public class ImagePrediction : ImageData
    {
        public float[]? Score;
    
        public string? PredictedLabelValue;
    }
    

    ImagePrediction es la clase de predicción de imagen y tiene los campos siguientes:

    • Score contiene el porcentaje de confianza de una clasificación de imágenes determinada.
    • PredictedLabelValue contiene un valor para la etiqueta de clasificación de imágenes prevista.

    ImagePrediction es la clase usada para la predicción una vez entrenado el modelo. Tiene una string (ImagePath) para la ruta de acceso a la imagen. Label se usa para reutilizar y entrenar el modelo. PredictedLabelValue se usa durante la predicción y la evaluación. Para la evaluación, se utiliza una entrada con los datos de entrenamiento, los valores de predicción y el modelo.

Inicialización de variables

  1. Inicialice la variable mlContext con una instancia nueva de MLContext. Reemplace la línea Console.WriteLine("Hello World!") por el código siguiente:

    MLContext mlContext = new MLContext();
    

    La clase MLContext es un punto de partida para todas las operaciones de ML.NET. Al inicializar mlContext se crea un entorno de ML.NET que se puede compartir entre los objetos del flujo de trabajo de creación de modelos. Como concepto, se parece a DBContext en Entity Framework.

Creación de una estructura para los parámetros del modelo de inicio

  1. El modelo de inicio tiene varios parámetros que se deben transmitir. Cree una estructura para asignar los valores de parámetros a nombres descriptivos con el código siguiente, justo después de inicializar la variable mlContext:

    struct InceptionSettings
    {
        public const int ImageHeight = 224;
        public const int ImageWidth = 224;
        public const float Mean = 117;
        public const float Scale = 1;
        public const bool ChannelsLast = true;
    }
    

Crear un método de utilidad para mostrar

Dado que se mostrarán los datos de imagen y las predicciones relacionadas más de una vez, cree un método de utilidad para mostrar a fin de controlar la visualización de los resultados de la imagen y la predicción.

  1. Cree el método DisplayResults() justo después de la estructura InceptionSettings con el código siguiente:

    void DisplayResults(IEnumerable<ImagePrediction> imagePredictionData)
    {
    
    }
    
  2. Rellene el cuerpo del método DisplayResults:

    foreach (ImagePrediction prediction in imagePredictionData)
    {
        Console.WriteLine($"Image: {Path.GetFileName(prediction.ImagePath)} predicted as: {prediction.PredictedLabelValue} with score: {prediction.Score?.Max()} ");
    }
    

Creación de un método para hacer una predicción

  1. Cree el método ClassifySingleImage(), justo antes del método DisplayResults(), mediante el código siguiente:

    void ClassifySingleImage(MLContext mlContext, ITransformer model)
    {
    
    }
    
  2. Cree un objeto ImageData que contenga la ruta de acceso completa y el nombre del archivo de imagen para la ImagePath única. Agregue el código siguiente como las siguientes líneas en el método ClassifySingleImage():

    var imageData = new ImageData()
    {
        ImagePath = _predictSingleImage
    };
    
  3. Para hacer una predicción única, agregue el código siguiente como la línea siguiente en el método ClassifySingleImage:

    // Make prediction function (input = ImageData, output = ImagePrediction)
    var predictor = mlContext.Model.CreatePredictionEngine<ImageData, ImagePrediction>(model);
    var prediction = predictor.Predict(imageData);
    

    Para obtener la predicción, use el método Predict(). PredictionEngine es una API de conveniencia, que le permite realizar una predicción en una única instancia de datos. PredictionEngine no es seguro para subprocesos. Es aceptable usarlo en entornos de un solo subproceso o prototipo. Para mejorar el rendimiento y la seguridad para subprocesos en entornos de producción, use el servicio PredictionEnginePool, que crea un ObjectPool de objetos de PredictionEngine para su uso en toda la aplicación. Consulte esta guía sobre cómo usar PredictionEnginePool en una API web de ASP.NET Core.

    Nota

    La extensión del servicio PredictionEnginePool está actualmente en versión preliminar.

  4. Muestre el resultado de la predicción como la línea de código siguiente en el método ClassifySingleImage():

    Console.WriteLine($"Image: {Path.GetFileName(imageData.ImagePath)} predicted as: {prediction.PredictedLabelValue} with score: {prediction.Score?.Max()} ");
    

Construcción de la canalización del modelo de ML.NET

Una canalización del modelo de ML.NET es una cadena de estimadores. No se produce ninguna ejecución durante la construcción de la canalización. Se crean los objetos del estimador, pero no se ejecutan.

  1. Agregue un método para generar el modelo

    Este método es la parte central del tutorial. Crea una canalización para el modelo y entrena la canalización para generar el modelo de ML.NET. También evalúa el modelo con respecto a algunos datos de prueba previamente desconocidos.

    Cree el método GenerateModel() justo después de la estructura InceptionSettings y antes del método DisplayResults() con el código siguiente:

    ITransformer GenerateModel(MLContext mlContext)
    {
    
    }
    
  2. Agregue los estimadores para cargar, cambiar el tamaño y extraer los píxeles de los datos de la imagen:

    IEstimator<ITransformer> pipeline = mlContext.Transforms.LoadImages(outputColumnName: "input", imageFolder: _imagesFolder, inputColumnName: nameof(ImageData.ImagePath))
                    // The image transforms transform the images into the model's expected format.
                    .Append(mlContext.Transforms.ResizeImages(outputColumnName: "input", imageWidth: InceptionSettings.ImageWidth, imageHeight: InceptionSettings.ImageHeight, inputColumnName: "input"))
                    .Append(mlContext.Transforms.ExtractPixels(outputColumnName: "input", interleavePixelColors: InceptionSettings.ChannelsLast, offsetImage: InceptionSettings.Mean))
    

    Los datos de la imagen se deben procesar en el formato que el modelo de TensorFlow espera. En este caso, las imágenes se cargan en la memoria, se cambia el tamaño a un tamaño coherente y los píxeles se extraen en un vector numérico.

  3. Agregue el estimador para cargar el modelo de TensorFlow y puntúelo:

    .Append(mlContext.Model.LoadTensorFlowModel(_inceptionTensorFlowModel).
        ScoreTensorFlowModel(outputColumnNames: new[] { "softmax2_pre_activation" }, inputColumnNames: new[] { "input" }, addBatchDimensionInput: true))
    

    Esta fase de la canalización carga el modelo de TensorFlow en la memoria y, a continuación, procesa el vector de valores de píxeles a través de la red del modelo de TensorFlow. La aplicación de entradas a un modelo de aprendizaje profundo y la generación de una salida mediante el modelo se conoce como Puntuación. Al utilizar el modelo en su totalidad, la puntuación hace una inferencia o predicción.

    En este caso, se usa todo el modelo de TensorFlow, excepto la última capa, que es la que realiza la inferencia. El resultado de la penúltima capa tiene la etiqueta softmax_2_preactivation. La salida de esta capa es, de hecho, un vector de las características que caracterizan las imágenes de entrada originales.

    Este vector de características generado por el modelo de TensorFlow se usará como entrada para un algoritmo de aprendizaje de ML.NET.

  4. Agregue el estimador para asignar las etiquetas de cadena en los datos de entrenamiento a los valores de clave entera:

    .Append(mlContext.Transforms.Conversion.MapValueToKey(outputColumnName: "LabelKey", inputColumnName: "Label"))
    

    El entrenador de ML.NET que se anexa a continuación requiere que sus etiquetas estén en formato key en lugar de cadenas arbitrarias. Una clave es un número que tiene una asignación de uno a uno a un valor de cadena.

  5. Agregue el algoritmo de aprendizaje de ML.NET:

    .Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(labelColumnName: "LabelKey", featureColumnName: "softmax2_pre_activation"))
    
  6. Agregue el estimador para asignar el valor de clave de predicción a una cadena:

    .Append(mlContext.Transforms.Conversion.MapKeyToValue("PredictedLabelValue", "PredictedLabel"))
    .AppendCacheCheckpoint(mlContext);
    

Entrenar el modelo

  1. Cargue los datos de entrenamiento con el contenedor LoadFromTextFile. Agregue el siguiente código como la siguiente línea en el método GenerateModel():

    IDataView trainingData = mlContext.Data.LoadFromTextFile<ImageData>(path:  _trainTagsTsv, hasHeader: false);
    

    Los datos de ML.NET se representan como una interfaz IDataView. IDataView es una manera flexible y eficiente de describir datos tabulares (numéricos y de texto). Los datos se pueden cargar desde un archivo de texto o en tiempo real (por ejemplo, archivos de registro o base de datos SQL) en un objeto IDataView.

  2. Entrene el modelo con los datos cargados anteriormente:

    ITransformer model = pipeline.Fit(trainingData);
    

    El método Fit() entrena el modelo mediante la aplicación del conjunto de datos de entrenamiento a la canalización.

Evaluación de la precisión del modelo

  1. Cargue y transforme los datos de prueba agregando el código siguiente a la siguiente línea del método GenerateModel:

    IDataView testData = mlContext.Data.LoadFromTextFile<ImageData>(path: _testTagsTsv, hasHeader: false);
    IDataView predictions = model.Transform(testData);
    
    // Create an IEnumerable for the predictions for displaying results
    IEnumerable<ImagePrediction> imagePredictionData = mlContext.Data.CreateEnumerable<ImagePrediction>(predictions, true);
    DisplayResults(imagePredictionData);
    

    Hay algunas imágenes de ejemplo que puede usar para evaluar el modelo. Al igual que los datos de entrenamiento, deben cargarse en IDataView, de modo que el modelo pueda transformarlos.

  2. Agregue el código siguiente al método GenerateModel() para evaluar el modelo:

    MulticlassClassificationMetrics metrics =
        mlContext.MulticlassClassification.Evaluate(predictions,
            labelColumnName: "LabelKey",
            predictedLabelColumnName: "PredictedLabel");
    

    Una vez que establece la predicción, el método Evaluate():

    • Evalúa el modelo (compara los valores previstos con el conjunto de datos labels de prueba).
    • Devuelve las métricas de rendimiento del modelo.
  3. Visualización de las métricas de precisión del modelo

    Utilice el código siguiente para mostrar las métricas, compartir los resultados y, a continuación, trabajar con ellos:

    Console.WriteLine($"LogLoss is: {metrics.LogLoss}");
    Console.WriteLine($"PerClassLogLoss is: {String.Join(" , ", metrics.PerClassLogLoss.Select(c => c.ToString()))}");
    

    Las siguiente métricas se evalúan para la clasificación de imágenes:

    • Log-loss: consulte Pérdida de registro. Le recomendamos que la pérdida logarítmica esté lo más cerca posible de 0.
    • Per class Log-loss. Le recomendamos que la pérdida logarítmica por clase esté lo más cerca posible de 0.
  4. Agregue el siguiente código para devolver el modelo entrenado, como la siguiente línea:

    return model;
    

Ejecución de la aplicación

  1. Agregue la llamada a GenerateModel después de la creación de la clase MLContext:

    ITransformer model = GenerateModel(mlContext);
    
  2. Agregue la llamada al método ClassifySingleImage() después de la llamada al método GenerateModel():

    ClassifySingleImage(mlContext, model);
    
  3. Ejecute la aplicación de consola (Ctrl + F5). Los resultados deberían ser similares a la salida siguiente. (Es posible que vea advertencias o mensajes de procesamiento, si bien se han quitado de los resultados siguientes para mayor claridad).

    =============== Training classification model ===============
    Image: broccoli2.jpg predicted as: food with score: 0.8955513
    Image: pizza3.jpg predicted as: food with score: 0.9667718
    Image: teddy6.jpg predicted as: toy with score: 0.9797683
    =============== Classification metrics ===============
    LogLoss is: 0.0653774699265059
    PerClassLogLoss is: 0.110315812569315 , 0.0204391272836966 , 0
    =============== Making single image classification ===============
    Image: toaster3.jpg predicted as: appliance with score: 0.9646884
    

¡Enhorabuena! Ahora ha creado correctamente un modelo de clasificación en ML.NET para clasificar imágenes mediante un TensorFlow entrenado previamente para el procesamiento de imágenes.

Puede encontrar el código fuente para este tutorial en el repositorio dotnet/samples.

En este tutorial ha aprendido a:

  • Entender el problema
  • Incorporación del modelo de TensorFlow entrenado previamente en la canalización de ML.NET
  • Entrenamiento y evaluación del modelo de ML.NET
  • Clasificación de una imagen de prueba

Consulte el repositorio de GitHub con ejemplos de Machine Learning para explorar un ejemplo expandido de clasificación de imágenes.