Tutorial: Analisar o sentimento de avaliações de filmes usando um modelo pré-treinado do TensorFlow no ML.NET

Este tutorial mostra como usar um modelo pré-treinado do TensorFlow para classificar o sentimento nos comentários do site. O classificador de sentimento binário é um aplicativo de console C# desenvolvido por meio do Visual Studio.

O modelo do TensorFlow usado neste tutorial foi treinado com avaliações de filmes do banco de dados do IMDB. Depois de terminar de desenvolver o aplicativo, você poderá fornecer um texto de avaliações de filmes e o aplicativo informará se a avaliação tem um sentimento positivo ou negativo.

Neste tutorial, você aprenderá a:

  • Carregar um modelo pré-treinado do TensorFlow
  • Transformar o texto de comentário do site em recursos adequados para o modelo
  • Usar o modelo para fazer uma previsão

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

Pré-requisitos

  • Visual Studio 2022 com a carga de trabalho "Desenvolvimento com a área de trabalho do .NET" instalada.

Configuração

Criar o aplicativo

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

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

  3. Crie um diretório chamado Data no seu projeto para salvar os arquivos do conjunto de dados.

  4. 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 origem do pacote e selecione a guia Procurar. Procure Microsoft.ML, escolha o pacote desejado e selecione o botão Instalar. Prossiga com a instalação concordando com os termos de licença do pacote que você escolher. Repita estas etapas para Microsoft.ML.TensorFlow, Microsoft.ML.SampleUtils e SciSharp.TensorFlow.Redist.

Adicionar o modelo do TensorFlow ao projeto

Observação

O modelo deste tutorial foi obtido do repositório GitHub dotnet/machinelearning-testdata. Ele está no formato SavedModel do TensorFlow.

  1. Baixe o arquivo zip sentiment_model e descompacte-o.

    O arquivo zip contém:

    • saved_model.pb: o próprio modelo do TensorFlow. O modelo usa uma matriz de inteiro de comprimento fixo (tamanho 600) de recursos que representam o texto em uma cadeia de caracteres de avaliações do IMDB e gera duas probabilidades que somam 1: a probabilidade de que a avaliação de entrada tenha um sentimento positivo e a probabilidade de que a avaliação de entrada tenha um sentimento negativo.
    • imdb_word_index.csv: um mapeamento de palavras individuais para um valor inteiro. O mapeamento é usado para gerar os recursos de entrada para o modelo do TensorFlow.
  2. Copie o conteúdo do diretório sentiment_model mais interno no diretório sentiment_model do projeto TextClassificationTF. 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 sentiment_model

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

Adicionar instruções using e variáveis globais

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

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms;
    
  2. Crie uma variável global logo após as instruções using para manter o caminho do arquivo de modelo salvo.

    string _modelPath = Path.Combine(Environment.CurrentDirectory, "sentiment_model");
    
    • _modelPath é o caminho do arquivo do modelo treinado.

Modelar os dados

As avaliações de filmes são textos de forma livre. Seu aplicativo converte o texto no formato de entrada esperado pelo modelo em várias fases discretas.

A primeira é dividir o texto em palavras separadas e usar o arquivo de mapeamento fornecido para mapear cada palavra em uma codificação de inteiro. O resultado dessa transformação é uma matriz de inteiro de comprimento variável com um comprimento correspondente ao número de palavras na frase.

Propriedade Valor Tipo
ReviewText este filme é realmente bom string
VariableLengthFeatures 14, 22, 9, 66, 78 etc. int[]

A matriz de recursos de comprimento variável é redimensionada para um comprimento fixo de 600. Esse é o comprimento esperado pelo modelo do TensorFlow.

Propriedade Valor Tipo
ReviewText este filme é realmente bom string
VariableLengthFeatures 14, 22, 9, 66, 78 etc. int[]
Recursos 14, 22, 9, 66, 78 etc. int[600]
  1. Crie uma classe para seus dados de entrada na parte inferior do arquivo Program.cs:

    /// <summary>
    /// Class to hold original sentiment data.
    /// </summary>
    public class MovieReview
    {
        public string? ReviewText { get; set; }
    }
    

    A classe de dados de entrada, MovieReview, tem uma string para comentários do usuário (ReviewText).

  2. Crie uma classe para os recursos de comprimento variável após a classe MovieReview:

    /// <summary>
    /// Class to hold the variable length feature vector. Used to define the
    /// column names used as input to the custom mapping action.
    /// </summary>
    public class VariableLength
    {
        /// <summary>
        /// This is a variable length vector designated by VectorType attribute.
        /// Variable length vectors are produced by applying operations such as 'TokenizeWords' on strings
        /// resulting in vectors of tokens of variable lengths.
        /// </summary>
        [VectorType]
        public int[]? VariableLengthFeatures { get; set; }
    }
    

    A propriedade VariableLengthFeatures tem um atributo VectorType para designá-la como um vetor. Todos os elementos de vetor precisam ser do mesmo tipo. Em conjuntos de dados com um grande número de colunas, carregar várias colunas como um só vetor reduz o número de dados transmitidos quando você aplica transformações de dados.

    Essa classe é usada na ação ResizeFeatures. Os nomes das propriedades (neste caso, apenas uma) são usados para indicar quais colunas na DataView podem ser usadas como a entrada para a ação de mapeamento personalizada.

  3. Crie uma classe para os recursos de comprimento fixo, após a classe VariableLength:

    /// <summary>
    /// Class to hold the fixed length feature vector. Used to define the
    /// column names used as output from the custom mapping action,
    /// </summary>
    public class FixedLength
    {
        /// <summary>
        /// This is a fixed length vector designated by VectorType attribute.
        /// </summary>
        [VectorType(Config.FeatureLength)]
        public int[]? Features { get; set; }
    }
    

    Essa classe é usada na ação ResizeFeatures. Os nomes das propriedades (neste caso, apenas uma) são usados para indicar quais colunas na DataView podem ser usadas como a saída da ação de mapeamento personalizada.

    Observe que o nome da propriedade Features é determinado pelo modelo do TensorFlow. Não é possível alterar o nome dessa propriedade.

  4. Crie uma classe para a previsão após a classe FixedLength:

    /// <summary>
    /// Class to contain the output values from the transformation.
    /// </summary>
    public class MovieReviewSentimentPrediction
    {
        [VectorType(2)]
        public float[]? Prediction { get; set; }
    }
    

    MovieReviewSentimentPrediction é a classe de previsão usada após o treinamento do modelo. MovieReviewSentimentPrediction tem uma só matriz float (Prediction) e um atributo VectorType.

  5. Crie outra classe para manter os valores de configuração, como o comprimento do vetor do recurso:

    static class Config
    {
        public const int FeatureLength = 600;
    }
    

Criar o MLContext, o dicionário de pesquisa e a ação para redimensionar os recursos

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

  1. Substitua a linha Console.WriteLine("Hello World!") pelo seguinte código para declarar e inicializar a variável mlContext:

    MLContext mlContext = new MLContext();
    
  2. Crie um dicionário para codificar palavras como inteiros usando o método LoadFromTextFile para carregar dados de mapeamento de um arquivo, conforme visto na seguinte tabela:

    Word Índice
    kids 362
    desejar 181
    errado 355
    effects 302
    sentimento 547

    Adicione o código abaixo para criar o mapa de pesquisa:

    var lookupMap = mlContext.Data.LoadFromTextFile(Path.Combine(_modelPath, "imdb_word_index.csv"),
        columns: new[]
            {
                new TextLoader.Column("Words", DataKind.String, 0),
                new TextLoader.Column("Ids", DataKind.Int32, 1),
            },
        separatorChar: ','
        );
    
  3. Adicione uma Action para redimensionar a matriz de inteiro de palavras de comprimento variável para uma matriz de inteiro de tamanho fixo, com as próximas linhas de código:

    Action<VariableLength, FixedLength> ResizeFeaturesAction = (s, f) =>
    {
        var features = s.VariableLengthFeatures;
        Array.Resize(ref features, Config.FeatureLength);
        f.Features = features;
    };
    

Carregar o modelo pré-treinado do TensorFlow

  1. Adicione o código para carregar o modelo do TensorFlow:

    TensorFlowModel tensorFlowModel = mlContext.Model.LoadTensorFlowModel(_modelPath);
    

    Depois que o modelo for carregado, você poderá extrair o esquema de entrada e de saída. Os esquemas são exibidos somente para interesse e aprendizado. Você não precisa desse código para que o aplicativo final funcione:

    DataViewSchema schema = tensorFlowModel.GetModelSchema();
    Console.WriteLine(" =============== TensorFlow Model Schema =============== ");
    var featuresType = (VectorDataViewType)schema["Features"].Type;
    Console.WriteLine($"Name: Features, Type: {featuresType.ItemType.RawType}, Size: ({featuresType.Dimensions[0]})");
    var predictionType = (VectorDataViewType)schema["Prediction/Softmax"].Type;
    Console.WriteLine($"Name: Prediction/Softmax, Type: {predictionType.ItemType.RawType}, Size: ({predictionType.Dimensions[0]})");
    
    

    O esquema de entrada é a matriz de comprimento fixo de palavras codificadas em inteiro. O esquema de saída é uma matriz flutuante de probabilidades que indicam se o sentimento de uma avaliação é negativo ou positivo. Esses valores somam 1, pois a probabilidade de ser positivo é o complemento da probabilidade de o sentimento ser negativo.

Criar o pipeline do ML.NET

  1. Crie o pipeline e divida o texto de entrada em palavras usando a transformação TokenizeIntoWords para dividir o texto em palavras como a próxima linha de código:

    IEstimator<ITransformer> pipeline =
        // Split the text into individual words
        mlContext.Transforms.Text.TokenizeIntoWords("TokenizedWords", "ReviewText")
    

    A transformação TokenizeIntoWords usa espaços para analisar o texto/a cadeia de caracteres em palavras. Ela cria uma coluna e divide cada cadeia de caracteres de entrada em um vetor de subcadeias de caracteres com base no separador definido pelo usuário.

  2. Mapeie as palavras para a codificação de inteiro usando a tabela de pesquisa que você declarou acima:

    // Map each word to an integer value. The array of integer makes up the input features.
    .Append(mlContext.Transforms.Conversion.MapValue("VariableLengthFeatures", lookupMap,
        lookupMap.Schema["Words"], lookupMap.Schema["Ids"], "TokenizedWords"))
    
  3. Redimensione as codificações de inteiro de comprimento variável para a de comprimento fixo necessária para o modelo:

    // Resize variable length vector to fixed length vector.
    .Append(mlContext.Transforms.CustomMapping(ResizeFeaturesAction, "Resize"))
    
  4. Classifique a entrada com o modelo do TensorFlow carregado:

    // Passes the data to TensorFlow for scoring
    .Append(tensorFlowModel.ScoreTensorFlowModel("Prediction/Softmax", "Features"))
    

    A saída do modelo do TensorFlow é chamada Prediction/Softmax. Observe que o nome Prediction/Softmax é determinado pelo modelo do TensorFlow. Não é possível modificar esse elemento.

  5. Crie uma coluna para a previsão de saída:

    // Retrieves the 'Prediction' from TensorFlow and copies to a column
    .Append(mlContext.Transforms.CopyColumns("Prediction", "Prediction/Softmax"));
    

    Você precisa copiar a coluna Prediction/Softmax em um com um nome que possa ser usado como uma propriedade em uma classe C#: Prediction. O caractere / não é permitido em um nome de propriedade C#.

Criar o modelo ML.NET com base no pipeline

  1. Adicione o código para criar o modelo com base no pipeline:

    // Create an executable model from the estimator pipeline
    IDataView dataView = mlContext.Data.LoadFromEnumerable(new List<MovieReview>());
    ITransformer model = pipeline.Fit(dataView);
    

    Um modelo ML.NET é criado com base na cadeia de estimativas no pipeline chamando o método Fit. Nesse caso, não estamos ajustando nenhum dado para criar o modelo, pois o modelo do TensorFlow já foi treinado anteriormente. Fornecemos um objeto de exibição de dados vazio para atender aos requisitos do método Fit.

Usar o modelo para fazer uma previsão

  1. Adicione o método PredictSentiment acima da classe MovieReview:

    void PredictSentiment(MLContext mlContext, ITransformer model)
    {
    
    }
    
  2. Adicione o seguinte código para criar o PredictionEngine como a primeira linha no método PredictSentiment():

    var engine = mlContext.Model.CreatePredictionEngine<MovieReview, MovieReviewSentimentPrediction>(model);
    

    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.

  3. Adicione um comentário para testar as previsões do modelo treinado no método Predict() ao criar uma instância de MovieReview:

    var review = new MovieReview()
    {
        ReviewText = "this film is really good"
    };
    
  4. Transmita os dados de comentário de teste para o Prediction Engine adicionando as próximas linhas de código ao método PredictSentiment():

    var sentimentPrediction = engine.Predict(review);
    
  5. A função Predict() faz uma previsão em uma só linha de dados:

    Propriedade Valor Tipo
    Previsão [0,5459937, 0,454006255] float[]
  6. Veja a previsão de sentimento usando o seguinte código:

    Console.WriteLine($"Number of classes: {sentimentPrediction.Prediction?.Length}");
    Console.WriteLine($"Is sentiment/review positive? {(sentimentPrediction.Prediction?[1] > 0.5 ? "Yes." : "No.")}");
    
  7. Adicione uma chamada a PredictSentiment depois de chamar o método Fit():

    PredictSentiment(mlContext, model);
    

Resultados

Compile e execute seu aplicativo.

Seus resultados devem ser semelhantes aos seguintes. Durante o processamento, as mensagens são exibidas. Você pode ver avisos ou mensagens de processamento. Essas mensagens foram removidas dos resultados a seguir para ficar mais claro.

Number of classes: 2
Is sentiment/review positive ? Yes

Parabéns! Você acabou de criar com sucesso um modelo de machine learning para classificar e prever o sentimento das mensagens reutilizando um modelo TensorFlow pré-treinado no ML.NET.

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

Neste tutorial, você aprendeu a:

  • Carregar um modelo pré-treinado do TensorFlow
  • Transformar o texto de comentário do site em recursos adequados para o modelo
  • Usar o modelo para fazer uma previsão