Tutorial: Analisar sentimento de comentários de um site com classificação binária em ML.NET

Este tutorial mostra como criar um aplicativo de console do .NET Core que classifica sentimentos de comentários de um site e executa a ação adequada. O classificador binário de sentimento usa C# no Visual Studio 2022.

Neste tutorial, você aprenderá a:

  • Criar um aplicativo de console
  • Preparar dados
  • Carregar os dados
  • Criar e treinar o modelo
  • Avaliar o modelo
  • Usar o modelo para fazer uma previsão
  • Confira os resultados

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

Pré-requisitos

Criar um aplicativo de console

  1. Crie um Aplicativo de console em C# chamado "SentimentAnalysis". 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.

Preparar seus dados

Observação

Os conjuntos de dados deste tutorial são de "From Group to Individual Labels using Deep Features”, Kotzias et. al,. KDD 2015, e hospedados no UCI Machine Learning Repository – Dua, D. e Karra Taniskidou, E. (2017). UCI Machine Learning Repository [http://archive.ics.uci.edu/ml]. Irvine, CA: University of California, School of Information and Computer Science.

  1. Baixe o arquivo zip do conjunto de dados de Sentenças de sentimentos rotuladas da UCI e descompacte-o.

  2. Copie o arquivo yelp_labelled.txt para o diretório Dados que você criou.

  3. No Gerenciador de Soluções, clique com o botão direito do mouse no arquivo yelp_labeled.txt e selecione Propriedades. 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;
    using SentimentAnalysis;
    using static Microsoft.ML.DataOperationsCatalog;
    
  2. Adicione o seguinte código à linha logo abaixo das instruções using para criar um campo para manter o caminho de arquivo do conjunto de dados recentemente baixado:

    string _dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "yelp_labelled.txt");
    
  3. Em seguida, crie classes para os dados de entrada e as previsões. Adicione uma nova classe ao seu projeto:

    • No Gerenciador de Soluções, clique com o botão direito do mouse no projeto e selecione Adicionar>Novo Item.

    • Na caixa de diálogo Adicionar Novo Item, selecione Classe e altere o campo Nome para SentimentData.cs. Em seguida, selecione o botão Adicionar.

  4. O arquivo SentimentData.cs é aberto no editor de códigos. Adicione a seguinte instrução using acima de SentimentData.cs:

    using Microsoft.ML.Data;
    
  5. Remova a definição de classe existente e adicione o seguinte código, que possui duas classes SentimentData e SentimentPrediction, ao arquivo SentimentData.cs:

    public class SentimentData
    {
        [LoadColumn(0)]
        public string? SentimentText;
    
        [LoadColumn(1), ColumnName("Label")]
        public bool Sentiment;
    }
    
    public class SentimentPrediction : SentimentData
    {
    
        [ColumnName("PredictedLabel")]
        public bool Prediction { get; set; }
    
        public float Probability { get; set; }
    
        public float Score { get; set; }
    }
    

Como os dados foram preparados

A classe de conjunto de dados de entrada, SentimentData, tem um string para comentários do usuário (SentimentText) e um valor bool (Sentiment) de 1 (positivo) ou 0 (negativo) para sentimento. Ambos os campos têm atributos LoadColumn anexados a eles, que descrevem a ordem do arquivo de dados de cada campo. Além disso, a propriedade Sentiment tem um atributo ColumnName para designá-lo como o campo Label. O arquivo de exemplo a seguir não possui uma linha de cabeçalho e tem a seguinte aparência:

SentimentText Sentimento (rótulo)
A garçonete foi um pouco lenta no serviço. 0
A torrada não é boa. 0
Uau! Adorei esse lugar. 1
O serviço era muito rápido. 1

SentimentPrediction é a classe de previsão usada após o treinamento do modelo. Ela herda SentimentData de modo que a entrada SentimentText possa ser exibida junto com a previsão de saída. O booliano Prediction é o valor que o modelo prevê quando fornecido com a nova entrada SentimentText.

A classe de saída SentimentPrediction contém duas outras propriedades calculadas pelo modelo: Score – a pontuação bruta calculada pelo modelo e Probability – a pontuação calibrada para a probabilidade de o texto ter sentimento positivo.

Para este tutorial, a propriedade mais importante é Prediction.

Carregar os dados

Os dados do 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.

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.

Você prepara o aplicativo e, em seguida, carrega os dados:

  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. Adicione o seguinte como a linha seguinte do código:

    TrainTestData splitDataView = LoadData(mlContext);
    
  3. Crie um método LoadData() na parte inferior do arquivo Program.cs usando o seguinte código:

    TrainTestData LoadData(MLContext mlContext)
    {
    
    }
    

    O método LoadData() executa as seguintes tarefas:

    • Carrega os dados.
    • Divide o conjunto de dados carregado em conjuntos de treinamento e teste.
    • Retorna os conjuntos de treinamento e teste.
  4. Adicione o seguinte código como a primeira linha do método LoadData():

    IDataView dataView = mlContext.Data.LoadFromTextFile<SentimentData>(_dataPath, hasHeader: false);
    

    O método LoadFromTextFile() define o esquema de dados e lê o arquivo. Ele usa as variáveis de caminho de dados e retorna uma IDataView.

Dividir o conjunto de dados para o modelo de treinamento e de teste

Ao preparar um modelo, você usa parte do conjunto de dados para treiná-lo e parte do conjunto de dados para testar a precisão do modelo.

  1. Para dividir os dados carregados nos conjuntos de dados necessários, adicione o seguinte código como a próxima linha no método LoadData():

    TrainTestData splitDataView = mlContext.Data.TrainTestSplit(dataView, testFraction: 0.2);
    

    O código anterior usa o método TrainTestSplit() para dividir o conjunto de dados carregado em conjuntos de dados de treino e de teste e retorna-os na classe DataOperationsCatalog.TrainTestData. Especifique o percentual de dados do conjunto de teste com o parâmetro testFraction. O padrão é 10% e, nesse caso, você usa 20% para avaliar mais dados.

  2. Retorne o splitDataView no final do método LoadData():

    return splitDataView;
    

Criar e treinar o modelo

  1. Adicione a seguinte chamada ao método BuildAndTrainModel abaixo da chamada ao método LoadData:

    ITransformer model = BuildAndTrainModel(mlContext, splitDataView.TrainSet);
    

    O método BuildAndTrainModel() executa as seguintes tarefas:

    • Extrai e transforma os dados.
    • Treina o modelo.
    • Prevê o sentimento com base nos dados de teste.
    • Retorna o modelo.
  2. Crie o método BuildAndTrainModel(), abaixo do método LoadData(), usando o seguinte código:

    ITransformer BuildAndTrainModel(MLContext mlContext, IDataView splitTrainSet)
    {
    
    }
    

Extrair e transformar os dados

  1. Chame FeaturizeText como a próxima linha de código:

    var estimator = mlContext.Transforms.Text.FeaturizeText(outputColumnName: "Features", inputColumnName: nameof(SentimentData.SentimentText))
    

    O método FeaturizeText() no código anterior converte a coluna de texto (SentimentText) em uma coluna Features do tipo chave numérica usada pelo algoritmo de aprendizado de máquina, adicionando-a como uma nova coluna do conjunto de dados:

    SentimentText Sentimento Recursos
    A garçonete foi um pouco lenta no serviço. 0 [0,76, 0,65, 0,44, …]
    A torrada não é boa. 0 [0,98, 0,43, 0,54, …]
    Uau! Adorei esse lugar. 1 [0,35, 0,73, 0,46, …]
    O serviço era muito rápido. 1 [0,39, 0, 0,75, …]

Adicionar um algoritmo de aprendizado

Este aplicativo usa um algoritmo de classificação que categoriza itens ou linhas de dados. O aplicativo categoriza comentários de site como positivos ou negativos, portanto, use a tarefa de classificação binária.

Acrescente a tarefa de aprendizado de máquina às definições de transformação de dados adicionando o seguinte como a próxima linha de código em BuildAndTrainModel():

.Append(mlContext.BinaryClassification.Trainers.SdcaLogisticRegression(labelColumnName: "Label", featureColumnName: "Features"));

O SdcaLogisticRegressionBinaryTrainer é o algoritmo de treinamento de classificação. Ele é acrescentado ao estimator e aceita o parâmetro SentimentText (Features) personalizado e o parâmetro de entrada Label para aprender com os dados históricos.

Treinar o modelo

Ajuste o modelo aos dados splitTrainSet e retorne o modelo treinado adicionando o seguinte como a próxima linha de código no método BuildAndTrainModel():

Console.WriteLine("=============== Create and Train the Model ===============");
var model = estimator.Fit(splitTrainSet);
Console.WriteLine("=============== End of training ===============");
Console.WriteLine();

O método Fit() treina o modelo transformando o conjunto de dados e aplicando o treinamento.

Retornar o modelo treinado a ser usado para avaliação

Retorne o modelo no final do método BuildAndTrainModel():

return model;

Avaliar o modelo

Depois que o modelo é treinado, use os dados de teste para validar o desempenho do modelo.

  1. Crie o método Evaluate(), logo após BuildAndTrainModel(), com o código a seguir:

    void Evaluate(MLContext mlContext, ITransformer model, IDataView splitTestSet)
    {
    
    }
    

    O método Evaluate() executa as seguintes tarefas:

    • Carrega o conjunto de dados de teste.
    • Cria o avaliador BinaryClassification.
    • Avalia o modelo e cria métricas.
    • Exibe as métricas.
  2. Adicione uma chamada ao novo método abaixo da chamada do método BuildAndTrainModel usando o seguinte código:

    Evaluate(mlContext, model, splitDataView.TestSet);
    
  3. Transforme os dados splitTestSet adicionando o seguinte código a Evaluate():

    Console.WriteLine("=============== Evaluating Model accuracy with Test data===============");
    IDataView predictions = model.Transform(splitTestSet);
    

    O código anterior usa o método Transform para fazer previsões para várias linhas de entrada fornecidas por um conjunto de dados de teste.

  4. Avalie o modelo adicionando o seguinte como a próxima linha de código no método Evaluate():

    CalibratedBinaryClassificationMetrics metrics = mlContext.BinaryClassification.Evaluate(predictions, "Label");
    

Uma vez que você tem o conjunto de previsão (predictions), o método Evaluate() avalia o modelo, que compara os valores previstos com os Labels reais no conjunto de dados de teste e retorna um objeto CalibratedBinaryClassificationMetrics sobre o desempenho do modelo.

Exibir as métricas para validação de modelo

Use o código a seguir para exibir as métricas:

Console.WriteLine();
Console.WriteLine("Model quality metrics evaluation");
Console.WriteLine("--------------------------------");
Console.WriteLine($"Accuracy: {metrics.Accuracy:P2}");
Console.WriteLine($"Auc: {metrics.AreaUnderRocCurve:P2}");
Console.WriteLine($"F1Score: {metrics.F1Score:P2}");
Console.WriteLine("=============== End of model evaluation ===============");
  • A métrica Accuracy obtém a precisão de um modelo, que é a proporção de previsões corretas no conjunto de teste.

  • A métrica AreaUnderRocCurve indica a confiabilidade do modelo ao classificar corretamente as classes positivas e negativas. Você deseja que o AreaUnderRocCurve seja o mais próximo possível de um.

  • A métrica F1Score obtém a pontuação F1 do modelo, que é uma medida do equilíbrio entre precisão e recall. Você deseja que o F1Score seja o mais próximo possível de um.

Prever o resultado dos dados de teste

  1. Crie o método UseModelWithSingleItem(), logo após o método Evaluate(), usando o seguinte código:

    void UseModelWithSingleItem(MLContext mlContext, ITransformer model)
    {
    
    }
    

    O método UseModelWithSingleItem() executa as seguintes tarefas:

    • Cria um único comentário dos dados de teste.
    • Prevê o sentimento com base nos dados de teste.
    • Combina dados de teste e previsões para relatórios.
    • Exibe os resultados previstos.
  2. Adicione uma chamada ao novo método logo abaixo da chamada do método Evaluate() usando o seguinte código:

    UseModelWithSingleItem(mlContext, model);
    
  3. Adicione o seguinte código para criar a primeira linha no método UseModelWithSingleItem():

    PredictionEngine<SentimentData, SentimentPrediction> predictionFunction = mlContext.Model.CreatePredictionEngine<SentimentData, SentimentPrediction>(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.

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

    SentimentData sampleStatement = new SentimentData
    {
        SentimentText = "This was a very bad steak"
    };
    
  5. Passe os dados de comentário de teste para o PredictionEngine adicionando o seguinte como as próximas linhas do código no método UseModelWithSingleItem():

    var resultPrediction = predictionFunction.Predict(sampleStatement);
    

    A função Predict() faz uma previsão em uma única coluna de dados.

  6. Exiba SentimentText e a previsão de sentimento correspondente usando o código a seguir:

    Console.WriteLine();
    Console.WriteLine("=============== Prediction Test of model with a single sample and test dataset ===============");
    
    Console.WriteLine();
    Console.WriteLine($"Sentiment: {resultPrediction.SentimentText} | Prediction: {(Convert.ToBoolean(resultPrediction.Prediction) ? "Positive" : "Negative")} | Probability: {resultPrediction.Probability} ");
    
    Console.WriteLine("=============== End of Predictions ===============");
    Console.WriteLine();
    

Usar o modelo para previsão

Implantar e prever itens em lotes

  1. Crie o método UseModelWithBatchItems(), logo após o método UseModelWithSingleItem(), usando o seguinte código:

    void UseModelWithBatchItems(MLContext mlContext, ITransformer model)
    {
    
    }
    

    O método UseModelWithBatchItems() executa as seguintes tarefas:

    • Cria dados de teste em lote.
    • Prevê o sentimento com base nos dados de teste.
    • Combina dados de teste e previsões para relatórios.
    • Exibe os resultados previstos.
  2. Adicione uma chamada ao novo método logo abaixo da chamada do método UseModelWithSingleItem() usando o seguinte código:

    UseModelWithBatchItems(mlContext, model);
    
  3. Adicione alguns comentários para testar as previsões do modelo treinado no método UseModelWithBatchItems():

    IEnumerable<SentimentData> sentiments = new[]
    {
        new SentimentData
        {
            SentimentText = "This was a horrible meal"
        },
        new SentimentData
        {
            SentimentText = "I love this spaghetti."
        }
    };
    

Prever sentimento de comentário

Use o modelo para prever o sentimento dos dados de comentários usando o método Transform:

IDataView batchComments = mlContext.Data.LoadFromEnumerable(sentiments);

IDataView predictions = model.Transform(batchComments);

// Use model to predict whether comment data is Positive (1) or Negative (0).
IEnumerable<SentimentPrediction> predictedResults = mlContext.Data.CreateEnumerable<SentimentPrediction>(predictions, reuseRowObject: false);

Combinar e exibir as previsões

Crie um cabeçalho para as previsões usando o seguinte código:

Console.WriteLine();

Console.WriteLine("=============== Prediction Test of loaded model with multiple samples ===============");

Como SentimentPrediction é herdado de SentimentData, o método Transform() preencheu SentimentText com os campos previstos. Conforme o processo do ML.NET processa, cada componente adiciona colunas e isso facilita a exibição dos resultados:

foreach (SentimentPrediction prediction  in predictedResults)
{
    Console.WriteLine($"Sentiment: {prediction.SentimentText} | Prediction: {(Convert.ToBoolean(prediction.Prediction) ? "Positive" : "Negative")} | Probability: {prediction.Probability} ");
}
Console.WriteLine("=============== End of predictions ===============");

Resultados

Seus resultados devem ser semelhantes aos seguintes. Durante o processamento, as mensagens são exibidas. Você pode ver avisos ou mensagens de processamento. Eles foram removidos dos seguintes resultados para maior clareza.

Model quality metrics evaluation
--------------------------------
Accuracy: 83.96%
Auc: 90.51%
F1Score: 84.04%

=============== End of model evaluation ===============

=============== Prediction Test of model with a single sample and test dataset ===============

Sentiment: This was a very bad steak | Prediction: Negative | Probability: 0.1027377
=============== End of Predictions ===============

=============== Prediction Test of loaded model with a multiple samples ===============

Sentiment: This was a horrible meal | Prediction: Negative | Probability: 0.1369192
Sentiment: I love this spaghetti. | Prediction: Positive | Probability: 0.9960636
=============== End of predictions ===============

=============== End of process ===============
Press any key to continue . . .

Parabéns! Agora você criou com sucesso um modelo de aprendizado de máquina para classificar e prever o sentimento das mensagens.

A criação de modelos bem-sucedidos é um processo iterativo. Esse modelo tem qualidade inicial inferior, pois o tutorial usa conjuntos de dados pequenos para fornecer um treinamento rápido do modelo. Se você não estiver satisfeito com a qualidade do modelo, tente melhorá-lo fornecendo conjuntos de dados de treinamento maiores ou escolhendo diferentes algoritmos de treinamento com diferentes hiperparâmetros para cada algoritmo.

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

Próximas etapas

Neste tutorial, você aprendeu a:

  • Criar um aplicativo de console
  • Preparar dados
  • Carregar os dados
  • Criar e treinar o modelo
  • Avaliar o modelo
  • Usar o modelo para fazer uma previsão
  • Confira os resultados

Avançar para o próximo tutorial para saber mais