Tutorial: Treinar um modelo de classificação ML.NET para categorizar imagens

Saiba como treinar um modelo de classificação para categorizar imagens usando um modelo pré-treinado do TensorFlow para processamento de imagens.

O modelo TensorFlow foi treinado para classificar imagens em mil categorias. Como o modelo TensorFlow sabe reconhecer padrões em imagens, o modelo ML.NET pode usar parte dele em seu pipeline para converter imagens brutas em recursos ou entradas para treinar um modelo de classificação.

Neste tutorial, você aprenderá a:

  • Compreender o problema
  • Incorpore o modelo pré-treinado do TensorFlow ao pipeline ML.NET
  • Treinar e avaliar o modelo ML.NET
  • Classificar uma imagem de teste

Você pode encontrar o código-fonte para este tutorial no repositório dotnet/samples. Por padrão, a configuração do projeto .NET para este tutorial tem como alvo o .NET core 2.2.

Pré-requisitos

Selecione a tarefa de aprendizado de máquina correta

Aprendizado

Aprendizado profundo é um subconjunto do aprendizado de máquina, que está revolucionando áreas como Pesquisa Visual Computacional e Reconhecimento de Fala.

Os modelos de aprendizado profundo são treinados usando grandes conjuntos de dados rotulados e redes neurais que contêm várias camadas de aprendizado. Aprendizado profundo:

  • Executa melhor em algumas tarefas, como visão computacional.
  • Requer grandes quantidades de dados de treinamento.

A classificação de imagens é uma tarefa de classificação específica que nos permite classificar automaticamente as imagens em categorias como:

  • Detectar uma face humana em uma imagem ou não.
  • Detecção de gatos vs. cães.

Ou como nas imagens a seguir, determinando se uma imagem é um alimento, brinquedo ou aparelho:

imagem de pizzaimagem de ursinho de pelúciaimagem de torradeira

Observação

As imagens anteriores pertencem ao Wikimedia Commons e são atribuídas da seguinte forma:

Treinar um modelo de classificação de imagem do zero requer a configuração de milhões de parâmetros, uma tonelada de dados de treinamento rotulados e uma grande quantidade de recursos de computação (centenas de horas de GPU). Embora não seja tão eficaz quanto treinar um modelo personalizado do zero, o uso de um modelo pré-treinado permite abreviar esse processo trabalhando com milhares de imagens versus milhões de imagens rotuladas e criar um modelo personalizado com bastante rapidez (dentro de uma hora em uma máquina sem GPU). Este tutorial reduz ainda mais esse processo, usando apenas uma dúzia de imagens de treinamento.

O Inception model é treinado para classificar imagens em mil categorias, mas para este tutorial, você precisa classificar as imagens em um conjunto de categorias menor e apenas essas categorias. Você pode usar a capacidade de Inception model de reconhecer e classificar imagens para as novas categorias limitadas do seu classificador de imagens personalizado.

  • Alimentos
  • Brinquedos
  • Dispositivo

Este tutorial usa o modelo de aprendizado profundo TensorFlow Inception, um modelo popular de reconhecimento de imagem treinado no ImageNet conjunto de dados. O modelo TensorFlow classifica imagens inteiras em mil classes, como “Umbrella”, “Jersey” e “Dishwasher”.

Como o Inception model já foi pré-treinado em milhares de imagens diferentes, internamente ele contém os recursos de imagem necessários para a identificação da imagem. Podemos usar esses recursos de imagem interna no modelo para treinar um novo modelo com muito menos classes.

Conforme mostrado no diagrama a seguir, você adiciona uma referência aos pacotes ML.NET NuGet em seus aplicativos .NET Core ou .NET Framework. Nos bastidores, o ML.NET inclui e faz referência à TensorFlowbiblioteca nativa que permite escrever código que carrega um TensorFlow arquivo de modelo treinado existente.

Diagrama de arco da transformação do TensorFlow do ML.NET

Classificação multiclasse

Depois de usar o modelo inicial do TensorFlow para extrair recursos adequados como entrada para um algoritmo clássico de aprendizado de máquina, adicionamos um classificador multiclasse ML.NET.

O treinador específico usado neste caso é o algoritmo de regressão logística multinomial.

O algoritmo implementado por este treinador funciona bem em problemas com um grande número de recursos, como é o caso de um modelo de aprendizado profundo operando em dados de imagem.

Consulte Aprendizado profundo versus aprendizado de máquina para obter mais informações.

Dados

Há duas fontes de dados: o arquivo .tsv e os arquivos de imagem. O arquivo tags.tsv contém duas colunas: a primeira é definida como ImagePath e a segunda é a Label correspondente à imagem. O arquivo de exemplo a seguir não possui uma linha de cabeçalho e tem a seguinte aparência:

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

As imagens de treinamento e teste estão localizadas nas pastas de recursos que você baixará em um arquivo zip. Essas imagens pertencem ao Wikimedia Commons.

Wikimedia Commons, o repositório de mídia gratuito. Recuperado em 10:48, 17 de outubro de 2018 de: https://commons.wikimedia.org/wiki/Pizzahttps://commons.wikimedia.org/wiki/Toasterhttps://commons.wikimedia.org/wiki/Teddy_bear

Configuração

Criar um projeto

  1. Crie um aplicativo de console C# chamado "TransferLearningTF". Clique no botão Avançar.

  2. Escolha o .NET 6 como a estrutura a ser usada. Selecione o botão Criar.

  3. Instalar o Pacote NuGet Microsoft.ML:

    Observação

    Este exemplo usa a versão estável mais recente dos pacotes NuGet mencionados, salvo indicação em contrário.

    • No Gerenciador de Soluções, clique com o botão direito do mouse no seu projeto e selecione Gerenciar Pacotes NuGet.
    • Escolha "nuget.org" como a fonte do pacote, selecione a guia Browse, procure por Microsoft.ML.
    • Selecione o botão Instalar.
    • Clique no botão OK na caixa de diálogo Pré-visualização de alterações.
    • Selecione o botão Aceito na caixa de diálogo Aceitação de Licença se você concordar com os termos de licença para os pacotes listados.
    • Repita essas etapas para Microsoft.ML.ImageAnalytics, SciSharp.TensorFlow.Redist e Microsoft.ML.TensorFlow.

Baixar ativos

  1. Baixe o arquivo zip do diretório de materiais do projeto e descompacte.

  2. Copie o diretórioassets no diretório do projeto TransferLearningTF. Este diretório e seus subdiretórios contêm os arquivos de dados e de suporte (exceto o modelo Concepção, que você irá baixar e adicionar na próxima etapa) necessários para este tutorial.

  3. Baixe o modelo Concepção e descompacte-o.

  4. Copie o conteúdo do diretório inception5h apenas descompactado em seu diretório de projeto TransferLearningTFassets/inception. Este diretório contém o modelo e os arquivos de suporte adicionais necessários para este tutorial, conforme mostrado na imagem a seguir:

    Conteúdo do diretório de concepção

  5. No Gerenciador de Soluções, clique com o botão direito do mouse em cada um dos arquivos no diretório e nos subdiretórios do ativo e selecionePropriedades. Em Avançado, altere o valor de Copiar para Diretório de Saída para Copiar se for mais novo.

Criar classes e definir demarcadores

  1. Adicione as seguintes instruções using adicionais ao início do arquivo Program.cs:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    
  2. Adicione o seguinte código à linha logo abaixo das instruções de uso para especificar os caminhos dos ativos:

    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. Crie classes para seus dados de entrada e previsões.

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

    ImageData é a classe de conjunto de dados de entrada e tem os seguintes campos String:

    • ImagePath contém o nome do arquivo de imagem.
    • Label contém um valor para o rótulo da imagem.
  4. Adicione uma nova classe ao seu projeto para ImagePrediction:

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

    ImagePrediction é a classe de previsão de imagem e possui os seguintes campos:

    • Score contém a porcentagem de confiança para uma determinada classificação de imagem.
    • PredictedLabelValue contém um valor para o rótulo de classificação de imagem previsto.

    ImagePrediction é a classe usada para previsão depois que o modelo foi treinado. Tem um string (ImagePath) para o demarcador da imagem. O Label é usado para reutilizar e treinar o modelo. O PredictedLabelValue é usado durante a previsão e avaliação. Para avaliação, uma entrada com dados de treinamento, os valores previstos e o modelo são usados.

Inicializar variáveis

  1. Inicialize a variável mlContext com uma nova instância de MLContext. Substitua a linha Console.WriteLine("Hello World!") pelo código a seguir:

    MLContext mlContext = new MLContext();
    

    A MLContext classe é um ponto de partida para todas as operações ML.NET, e a inicialização mlContext cria um novo ambiente ML.NET que pode ser compartilhado entre os objetos de fluxo de trabalho de criação de modelo. Ele é semelhante, conceitualmente, a DBContext no Entity Framework.

Criar uma estrutura para os parâmetros do modelo Inception

  1. O modelo Inception tem vários parâmetros que você precisa verificar. Crie uma estrutura para mapear os valores de parâmetro para nomes amigáveis com o seguinte código, logo após inicializar a mlContext variável:

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

Criar um método de utilitário de exibição

Uma vez que você exibirá os dados de imagem e as previsões relacionadas mais de uma vez, crie um método de utilitário de exibição para lidar com a exibição dos resultados de imagem e previsão.

  1. Crie o método DisplayResults(), logo após o struct InceptionSettings, usando o seguinte código:

    void DisplayResults(IEnumerable<ImagePrediction> imagePredictionData)
    {
    
    }
    
  2. Preencha o corpo do DisplayResults método:

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

Criar um método para fazer uma previsão

  1. Crie o método ClassifySingleImage(), logo antes do método DisplayResults(), usando o seguinte código:

    void ClassifySingleImage(MLContext mlContext, ITransformer model)
    {
    
    }
    
  2. Crie um ImageDataobjeto que contenha o caminho totalmente qualificado e o nome do arquivo de imagem para o arquivo ImagePath. Adicione o seguinte código como as próximas linhas no ClassifySingleImage()método:

    var imageData = new ImageData()
    {
        ImagePath = _predictSingleImage
    };
    
  3. Faça uma única previsão, adicionando o código a seguir como a próxima linha no ClassifySingleImage método:

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

    Para obter a previsão, use o método Predict(). O PredictionEngine é uma API de conveniência, que permite executar uma previsão em uma instância individual de dados. PredictionEngine não é thread-safe. É aceitável usá-lo em ambientes de thread único ou de protótipo. Para aprimorar o desempenho e o acesso thread-safe em ambientes de produção, use o serviço PredictionEnginePool, que cria um ObjectPool de objetos PredictionEngine para uso em todo o aplicativo. Confira este guia sobre como usar o PredictionEnginePool em uma API Web ASP.NET Core.

    Observação

    A extensão de serviço PredictionEnginePool está atualmente em versão prévia.

  4. Exiba o resultado da previsão como a próxima linha de código no método ClassifySingleImage():

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

Construir o pipeline do modelo ML.NET

Um pipeline de modelo ML.NET é uma cadeia de estimadores. Nenhuma execução acontece durante a construção do pipeline. Os objetos do estimador são criados, mas não executados.

  1. Adicionar um método para gerar o modelo

    Este método é o coração do tutorial. Ele cria um pipeline para o modelo e treina o pipeline para produzir o modelo ML.NET. Ele também avalia o modelo em relação a alguns dados de teste não vistos anteriormente.

    Crie o método GenerateModel() logo após o struct InceptionSettings e antes do método DisplayResults(), usando o seguinte código:

    ITransformer GenerateModel(MLContext mlContext)
    {
    
    }
    
  2. Adicione os estimadores para carregar, redimensionar e extrair os pixels dos dados da imagem:

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

    Os dados da imagem precisam ser processados no formato que o modelo do TensorFlow espera. Nesse caso, as imagens são carregadas na memória, redimensionadas para um tamanho consistente e os pixels são extraídos em um vetor numérico.

  3. Adicione o estimador para carregar o modelo TensorFlow e marque-o:

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

    Esse estágio no pipeline carrega o modelo do TensorFlow na memória e processa o vetor de valores de pixel por meio da rede do modelo do TensorFlow. Aplicar entradas a um modelo de aprendizado profundo e gerar uma saída usando o modelo é chamado de Pontuação . Ao usar o modelo em sua totalidade, a pontuação faz uma inferência ou previsão.

    Nesse caso, você usa todo o modelo do TensorFlow, exceto a última camada, que é a camada que faz a inferência. A saída da penúltima camada é rotulada softmax_2_preactivation. A saída desta camada é efetivamente um vetor de características que caracterizam as imagens de entrada originais.

    Esse vetor de recursos gerado pelo modelo TensorFlow será usado como entrada para um algoritmo de treinamento ML.NET.

  4. Adicione o estimador para mapear os rótulos de string nos dados de treinamento para valores de chave inteiros:

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

    O treinador ML.NET que é anexado a seguir exige que seus rótulos estejam em key formato em vez de cadeias de caracteres arbitrárias. Uma chave é um número que tem um mapeamento de um para um para uma cadeia de caracteres.

  5. Adicione o algoritmo de treinamento ML.NET:

    .Append(mlContext.MulticlassClassification.Trainers.LbfgsMaximumEntropy(labelColumnName: "LabelKey", featureColumnName: "softmax2_pre_activation"))
    
  6. Adicione o estimador para mapear o valor da chave previsto de volta em uma cadeia de caracteres:

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

Treinar o modelo

  1. Carregue os dados de treinamento usando o wrapper LoadFromTextFile. Adicione o seguinte código ao método GenerateModel() como a linha seguinte:

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

    Os dados no ML.NET são representados como uma interface IDataView. IDataView é uma maneira flexível e eficiente de descrever dados tabulares (numéricos e texto). Os dados podem ser carregados de um arquivo de texto ou em tempo real (por exemplo, banco de dados SQL ou arquivos de log) para um objeto IDataView.

  2. Treine o modelo com os dados carregados acima:

    ITransformer model = pipeline.Fit(trainingData);
    

    O Fit() método treina seu modelo aplicando o conjunto de dados de treinamento ao pipeline.

Avaliar a precisão do modelo

  1. Carregue e transforme os dados de teste, adicionando o seguinte código à próxima linha do GenerateModel método:

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

    Existem algumas imagens de amostra que você pode usar para avaliar o modelo. Assim como os dados de treinamento, eles precisam ser carregados em um IDataView, para que possam ser transformados pelo modelo.

  2. Adicione o seguinte código ao GenerateModel() método para avaliar o modelo:

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

    Depois de determinar a previsão, o método Evaluate ():

    • Avalia o modelo (compara os valores previstos com o conjunto de dados de testelabels).
    • Retorna as métricas de desempenho do modelo.
  3. Exibir as métricas de precisão do modelo

    Use o código a seguir para exibir as métricas, compartilhar os resultados e, em seguida, agir com relação a eles:

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

    As seguintes métricas são avaliadas para classificação de imagem:

    • Log-loss - confira Perda de log. Convém que a Perda de log seja tão próxima de zero quanto possível.
    • Per class Log-loss. Convém que a Perda de log por classe seja tão próxima de zero quanto possível.
  4. Adicione o seguinte código para retornar o modelo treinado como a próxima linha:

    return model;
    

Execute o aplicativo!

  1. Adicione a chamada após GenerateModel a criação da MLContext classe:

    ITransformer model = GenerateModel(mlContext);
    
  2. Adicione a chamada ao ClassifySingleImage() método após a chamada ao GenerateModel() método:

    ClassifySingleImage(mlContext, model);
    
  3. Execute seu aplicativo de console (Ctrl + CtrlF5). O resultado deverá ser semelhante à seguinte saída. (Você poderá ver avisos ou mensagens de processamento, mas essas mensagens foram removidas dos resultados a seguir para maior clareza.)

    =============== 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
    

Parabéns! Agora você criou com sucesso um modelo de classificação no ML.NET para categorizar imagens usando um TensorFlow pré-treinado para processamento de imagens.

Você pode encontrar o código-fonte para este tutorial no repositório dotnet/samples.

Neste tutorial, você aprendeu a:

  • Compreender o problema
  • Incorpore o modelo pré-treinado do TensorFlow ao pipeline ML.NET
  • Treinar e avaliar o modelo ML.NET
  • Classificar uma imagem de teste

Conferir o repositório GitHub de Amostras de Aprendizado de Máquina para explorar uma amostra de classificação de imagem expandida.