Tutorial : Detectar anomalias na venda de produtos com o ML.NET

Saiba como criar um aplicativo de detecção de anomalias para dados de vendas do produto. Este tutorial cria um aplicativo de console .NET Core usando C# no Visual Studio.

Neste tutorial, você aprenderá a:

  • Carregar os dados
  • Criar uma transformação para detecção de anomalias de pico
  • Detectar anomalias de pico com a transformação
  • Criar uma transformação para detecção de anomalias de ponto de alteração
  • Detectar anomalias de ponto de alteração com a transformação

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

Pré-requisitos

Observação

Os dados de formato em product-sales.csv baseiam-se no conjunto de dados "Vendas de xampu no período de três anos" originalmente adquiridos do DataMarket e fornecidos pela TSDL (Biblioteca de Dados de Série Temporal), criado por Rob Hyndman. Conjunto de dados de "Vendas de xampu no período de três anos" licenciado sob a Licença Aberta Padrão do DataMarket.

Criar um aplicativo de console

  1. Crie um Aplicativo de Console C# chamado "ProductSalesAnomalyDetection". 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, selecione a guia Procurar, pesquise Microsoft.ML e selecione o botão Instalar. Selecione o botão OK na caixa de diálogo Visualizar Alterações e selecione o botão Aceito na caixa de diálogo Aceitação da Licença, se concordar com o termos de licença para os pacotes listados. Repita essas etapas para Microsoft.ML.TimeSeries.

  5. Adicione as seguintes instruções using à parte superior do arquivo Program.cs:

    using Microsoft.ML;
    using ProductSalesAnomalyDetection;
    

Baixar seus dados

  1. Baixe o conjunto de dados e salve-o na pasta Data criada anteriormente:

    • Clique com o botão direito do mouse em product-sales.csv e selecione "Salvar Link (ou destino) como…"

      Salve o arquivo *.csv na pasta Data ou, depois de salvá-lo em outro lugar, mova o arquivo *.csv para a pasta Data.

  2. No Gerenciador de Soluções, clique com o botão direito do mouse no arquivo *.csv e selecione Propriedades. Em Avançado, altere o valor de Copiar para Diretório de Saída para Copiar se for mais novo.

A seguinte tabela é uma versão prévia dos dados do seu arquivo *.csv:

Mês ProductSales
1-Jan 271
2-Jan 150.9
..... .....
1-Feb 199.3
..... .....

Criar classes e definir demarcadores

Em seguida, defina suas estruturas de dados de classe de entrada e de previsão.

Adicione uma nova classe ao seu projeto:

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

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

    O arquivo ProductSalesData.cs é aberto no editor de códigos.

  3. Adicione a seguinte instrução using à parte superior do ProductSalesData.cs:

    using Microsoft.ML.Data;
    
  4. Remova a definição de classe existente e adicione o seguinte código, que tem duas classes, ProductSalesData e ProductSalesPrediction, ao arquivo ProductSalesData.cs:

    public class ProductSalesData
    {
        [LoadColumn(0)]
        public string? Month;
    
        [LoadColumn(1)]
        public float numSales;
    }
    
    public class ProductSalesPrediction
    {
        //vector to hold alert,score,p-value values
        [VectorType(3)]
        public double[]? Prediction { get; set; }
    }
    

    ProductSalesData especifica uma classe de dados de entrada. O atributo LoadColumn especifica quais colunas (por índice de coluna) no conjunto de dados devem ser carregadas.

    ProductSalesPrediction especifica a classe de dados de previsão. Para detecção de anomalias, a previsão consiste em um alerta para indicar se há uma anomalia, uma pontuação bruta e um valor p. Quanto mais próximo o valor p for de 0, maior será a probabilidade de que uma anomalia tenha ocorrido.

  5. Crie dois campos globais para armazenar o caminho do arquivo de conjunto de dados baixado recentemente e o caminho do arquivo de modelo salvo:

    • _dataPath tem o demarcador para o conjunto de dados usado para treinar o modelo.
    • O _docsize tem o número de registros no arquivo de conjunto de dados. Você usará _docSize para calcular pvalueHistoryLength.
  6. Adicione o seguinte código à linha logo abaixo das instruções de uso para especificar estes caminhos:

    string _dataPath = Path.Combine(Environment.CurrentDirectory, "Data", "product-sales.csv");
    //assign the Number of records in dataset file to constant variable
    const int _docsize = 36;
    

Inicializar variáveis

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

    MLContext mlContext = new MLContext();
    

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

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 de outras fontes (por exemplo, banco de dados SQL ou arquivos de log) para um objeto IDataView.

  1. Adicione o seguinte código após a criação da variável mlContext:

    IDataView dataView = mlContext.Data.LoadFromTextFile<ProductSalesData>(path: _dataPath, hasHeader: true, separatorChar: ',');
    

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

Detecção de anomalias de série temporal

A detecção de anomalias sinaliza comportamentos ou eventos incomuns ou inesperados. Ela dá dicas de em que local procurar problemas e ajuda a responder à pergunta "Isso é estranho?".

Exemplo da detecção de anomalias

A detecção de anomalias é o processo de detectar exceções de dados de série temporal; pontos em uma série temporal de entrada em que o comportamento não é o esperado ou "estranho".

A detecção de anomalias pode ser útil de várias maneiras. Por exemplo:

Se você tem um carro, você pode querer saber: a leitura deste medidor de óleo está normal ou há um vazamento? Se você estiver monitorando o consumo de energia, vai querer saber: há uma interrupção?

Há dois tipos de anomalias de série temporal que podem ser detectados:

  • Picos indicam intermitências temporárias de comportamentos anormais no sistema.

  • Pontos de alteração indicam o início de alterações persistentes ao longo do tempo no sistema.

No ML.NET, os algoritmos de Detecção de Pico IID ou Detecção de Ponto de Alteração IID são adequados para conjuntos de dados independentes e distribuídos de forma idêntica. Eles pressupõem que os dados inseridos sejam uma sequência de pontos de dados amostrados independentemente de uma distribuição estacionária.

Ao contrário dos modelos nos outros tutoriais, as transformações do detector de anomalias de série temporal operam diretamente nos dados de entrada. O método IEstimator.Fit() não precisa de dados de treinamento para produzir a transformação. No entanto, ele precisa do esquema de dados, que é fornecido por uma exibição de dados gerada de uma lista vazia de ProductSalesData.

Você vai analisar os mesmos dados de vendas do produto para detectar picos e pontos de alteração. O processo de criar e treinar o modelo é o mesmo para detecção de pico e detecção de ponto de alteração; a principal diferença é o algoritmo de detecção específico usado.

Detecção de pico

A meta da detecção de pico é identificar picos repentinos, mas temporários, que diferem significativamente da maioria dos valores de dados de série temporal. É importante detectar esses itens raros, eventos ou observações suspeitos de maneira oportuna para que sejam minimizados. A abordagem a seguir pode ser usada para detectar uma variedade de anomalias, como: interrupções, ataques cibernéticos ou conteúdo da Web viral. A imagem a seguir é um exemplo de picos em um conjunto de dados de série temporal:

Captura de tela que mostra duas detecções de pico.

Adicionar o método CreateEmptyDataView()

Adicione o seguinte método a Program.cs:

IDataView CreateEmptyDataView(MLContext mlContext) {
    // Create empty DataView. We just need the schema to call Fit() for the time series transforms
    IEnumerable<ProductSalesData> enumerableData = new List<ProductSalesData>();
    return mlContext.Data.LoadFromEnumerable(enumerableData);
}

O CreateEmptyDataView() produz um objeto de exibição de dados vazio com o esquema correto a ser usado como entrada para o método IEstimator.Fit().

Crie o método DetectSpike()

O método DetectSpike():

  • Cria a transformação do estimador.
  • Detecta picos com base em dados históricos de vendas.
  • Exibe os resultados.
  1. Crie o método DetectSpike() na parte inferior do arquivo Program.cs usando o seguinte código:

    DetectSpike(MLContext mlContext, int docSize, IDataView productSales)
    {
    
    }
    
  2. Use IidSpikeEstimator para treinar o modelo para a detecção de pico. Adicione-o ao método DetectSpike() com o seguinte código:

    var iidSpikeEstimator = mlContext.Transforms.DetectIidSpike(outputColumnName: nameof(ProductSalesPrediction.Prediction), inputColumnName: nameof(ProductSalesData.numSales), confidence: 95d, pvalueHistoryLength: docSize / 4);
    
  3. Crie a transformação detecção de pico adicionando o seguinte como a próxima linha de código no método DetectSpike():

    Dica

    Os parâmetros confidence e pvalueHistoryLength afetam como os picos são detectados. confidence determina a sensibilidade do seu modelo a picos. Quanto menor a confiança, maior a probabilidade de o algoritmo detectar picos "menores". O parâmetro pvalueHistoryLength define o número de pontos de dados em uma janela deslizante. O valor desse parâmetro geralmente é um percentual de todo o conjunto de dados. Quanto menor o pvalueHistoryLength, mais rápido o modelo esquece picos grandes anteriores.

    ITransformer iidSpikeTransform = iidSpikeEstimator.Fit(CreateEmptyDataView(mlContext));
    
  4. Adicione a seguinte linha de código para transformar os dados productSales como a próxima linha no método DetectSpike():

    IDataView transformedData = iidSpikeTransform.Transform(productSales);
    

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

  5. Converta seu transformedData em um IEnumerable fortemente tipado para exibição mais fácil usando o método CreateEnumerable() com o seguinte código:

    var predictions = mlContext.Data.CreateEnumerable<ProductSalesPrediction>(transformedData, reuseRowObject: false);
    
  6. Crie uma linha de cabeçalho de exibição usando o seguinte código Console.WriteLine():

    Console.WriteLine("Alert\tScore\tP-Value");
    

    Você exibirá as informações a seguir nos resultados da detecção de pico:

    • Alert indica um alerta de pico para um ponto de dados específico.
    • Score é o valor ProductSales para um dado ponto de dados no conjunto de dados.
    • P-Value O "P" significa probabilidade. Quanto mais próximo o valor p for de 0, maior será a probabilidade de que o ponto de dados seja uma anomalia.
  7. Use o seguinte código para iterar por predictionsIEnumerable e exibir os resultados:

    foreach (var p in predictions)
    {
        if (p.Prediction is not null)
        {
            var results = $"{p.Prediction[0]}\t{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}";
    
            if (p.Prediction[0] == 1)
            {
                results += " <-- Spike detected";
            }
    
            Console.WriteLine(results);
        }
    }
    Console.WriteLine("");
    
  8. Adicione a chamada ao método DetectSpike() abaixo da chamada ao método LoadFromTextFile():

    DetectSpike(mlContext, _docsize, dataView);
    

Resultados da detecção de pico

Seus resultados devem ser semelhantes aos seguintes. Durante o processamento, as mensagens são exibidas. Você pode ver avisos ou mensagens de processamento. Algumas das mensagens foram removidas dos resultados a seguir para fins de clareza.

Detect temporary changes in pattern
=============== Training the model ===============
=============== End of training process ===============
Alert   Score   P-Value
0       271.00  0.50
0       150.90  0.00
0       188.10  0.41
0       124.30  0.13
0       185.30  0.47
0       173.50  0.47
0       236.80  0.19
0       229.50  0.27
0       197.80  0.48
0       127.90  0.13
1       341.50  0.00 <-- Spike detected
0       190.90  0.48
0       199.30  0.48
0       154.50  0.24
0       215.10  0.42
0       278.30  0.19
0       196.40  0.43
0       292.00  0.17
0       231.00  0.45
0       308.60  0.18
0       294.90  0.19
1       426.60  0.00 <-- Spike detected
0       269.50  0.47
0       347.30  0.21
0       344.70  0.27
0       445.40  0.06
0       320.90  0.49
0       444.30  0.12
0       406.30  0.29
0       442.40  0.21
1       580.50  0.00 <-- Spike detected
0       412.60  0.45
1       687.00  0.01 <-- Spike detected
0       480.30  0.40
0       586.30  0.20
0       651.90  0.14

Detecção de ponto de alteração

Change points são alterações persistentes em uma distribuição de valores de fluxo de evento de série temporal, como alterações de nível e tendências. Essas alterações persistentes duram muito mais que spikes e podem indicar eventos catastróficos. Change points normalmente não estão visíveis a olho nu, mas podem ser detectados em seus dados usando abordagens como o método a seguir. A imagem a seguir é um exemplo de uma detecção de ponto de alteração:

Captura de tela que mostra uma detecção de ponto de alteração.

Crie o método DetectChangepoint()

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

  • Cria a transformação do estimador.
  • Detecta pontos de alteração com base em dados históricos de vendas.
  • Exibe os resultados.
  1. Crie o método DetectChangepoint(), logo após a declaração do método DetectSpike(), usando o seguinte código:

    void DetectChangepoint(MLContext mlContext, int docSize, IDataView productSales)
    {
    
    }
    
  2. Crie o iidChangePointEstimator no método DetectChangepoint() com o seguinte código:

    var iidChangePointEstimator = mlContext.Transforms.DetectIidChangePoint(outputColumnName: nameof(ProductSalesPrediction.Prediction), inputColumnName: nameof(ProductSalesData.numSales), confidence: 95d, changeHistoryLength: docSize / 4);
    
  3. Como você fez anteriormente, crie a transformação do estimador adicionando a seguinte linha de código ao método DetectChangePoint():

    Dica

    A detecção de pontos de alteração ocorre com um pequeno atraso, pois o modelo precisa verificar se o desvio atual é uma alteração persistente e não apenas alguns picos aleatórios antes de criar um alerta. A quantidade desse atraso é igual ao parâmetro changeHistoryLength. Ao aumentar o valor desse parâmetro, a detecção de alteração alerta sobre alterações mais persistentes, mas ao custo de um atraso maior.

    var iidChangePointTransform = iidChangePointEstimator.Fit(CreateEmptyDataView(mlContext));
    
  4. Use o método Transform() para transformar os dados adicionando o seguinte código a DetectChangePoint():

    IDataView transformedData = iidChangePointTransform.Transform(productSales);
    
  5. Como você fez antes, converta seu transformedData em um IEnumerable fortemente tipado para exibição mais fácil usando o método CreateEnumerable() com o seguinte código:

    var predictions = mlContext.Data.CreateEnumerable<ProductSalesPrediction>(transformedData, reuseRowObject: false);
    
  6. Crie um cabeçalho de exibição com o código a seguir como a próxima linha no método DetectChangePoint():

    Console.WriteLine("Alert\tScore\tP-Value\tMartingale value");
    

    Você exibirá as informações a seguir nos resultados da detecção de ponto de alteração:

    • Alert indica um alerta de ponto de alteração para um ponto de dados específico.
    • Score é o valor ProductSales para um dado ponto de dados no conjunto de dados.
    • P-Value O "P" significa probabilidade. Quanto mais próximo o valor P for de 0, maior será a probabilidade de que o ponto de dados seja uma anomalia.
    • Martingale value é usado para identificar o quão "estranho" um ponto de dados é com base na sequência de valores de P.
  7. Itere por predictionsIEnumerable e exiba os resultados com o código a seguir:

    foreach (var p in predictions)
    {
        if (p.Prediction is not null)
        {
            var results = $"{p.Prediction[0]}\t{p.Prediction[1]:f2}\t{p.Prediction[2]:F2}\t{p.Prediction[3]:F2}";
    
            if (p.Prediction[0] == 1)
            {
                results += " <-- alert is on, predicted changepoint";
            }
            Console.WriteLine(results);
        }
    }
    Console.WriteLine("");
    
  8. Adicione a seguinte chamada ao método DetectChangepoint()após a chamada ao método DetectSpike():

    DetectChangepoint(mlContext, _docsize, dataView);
    

Resultados da detecção de ponto de alteração

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

Detect Persistent changes in pattern
=============== Training the model Using Change Point Detection Algorithm===============
=============== End of training process ===============
Alert   Score   P-Value Martingale value
0       271.00  0.50    0.00
0       150.90  0.00    2.33
0       188.10  0.41    2.80
0       124.30  0.13    9.16
0       185.30  0.47    9.77
0       173.50  0.47    10.41
0       236.80  0.19    24.46
0       229.50  0.27    42.38
1       197.80  0.48    44.23 <-- alert is on, predicted changepoint
0       127.90  0.13    145.25
0       341.50  0.00    0.01
0       190.90  0.48    0.01
0       199.30  0.48    0.00
0       154.50  0.24    0.00
0       215.10  0.42    0.00
0       278.30  0.19    0.00
0       196.40  0.43    0.00
0       292.00  0.17    0.01
0       231.00  0.45    0.00
0       308.60  0.18    0.00
0       294.90  0.19    0.00
0       426.60  0.00    0.00
0       269.50  0.47    0.00
0       347.30  0.21    0.00
0       344.70  0.27    0.00
0       445.40  0.06    0.02
0       320.90  0.49    0.01
0       444.30  0.12    0.02
0       406.30  0.29    0.01
0       442.40  0.21    0.01
0       580.50  0.00    0.01
0       412.60  0.45    0.01
0       687.00  0.01    0.12
0       480.30  0.40    0.08
0       586.30  0.20    0.03
0       651.90  0.14    0.09

Parabéns! Você agora criou com êxito os modelos de machine learning para detectar anomalias de pontos de alteração e picos nos dados de vendas.

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

Neste tutorial, você aprendeu a:

  • Carregar os dados
  • Treinar o modelo para detecção de anomalias de pico
  • Detectar anomalias de pico com o modelo treinado
  • Treinar o modelo para detecção de anomalias de ponto de alteração
  • Detectar anomalias de ponto de alteração com o modo treinado

Próximas etapas

Confira o repositório do GitHub de exemplos de Machine Learning para explorar um exemplo de detecção de anomalia de dados de sazonalidade.