Compartilhar via


Tutorial: Prever a demanda do serviço de aluguel de bicicletas com análise de série temporal e ML.NET

Saiba como prever a demanda por um serviço de aluguel de bicicletas usando análise de série temporal não variável em dados armazenados em um banco de dados do SQL Server com ML.NET.

Neste tutorial, você aprenderá como:

  • Compreender o problema
  • Carregar dados de um banco de dados
  • Criar um modelo de previsão
  • Avaliar o modelo de previsão
  • Salvar um modelo de previsão
  • Usar um modelo de previsão

Pré-requisitos

Visão geral do exemplo de previsão de série temporal

Este exemplo é um aplicativo de console C# que prevê a demanda por aluguel de bicicletas usando um algoritmo de análise de série temporal não variável conhecido como Análise de Espectro Singular. O código deste exemplo pode ser encontrado no repositório dotnet/machinelearning-samples no GitHub.

Compreender o problema

Para executar uma operação eficiente, o gerenciamento de inventário desempenha um papel fundamental. Ter muito de um produto em estoque significa produtos não vendidos sentados nas prateleiras não gerando nenhuma receita. Ter muito pouco produto leva a vendas perdidas e clientes comprando de concorrentes. Portanto, a pergunta constante é: qual é a quantidade ideal de inventário a ser mantida em mãos? A análise de série temporal ajuda a fornecer uma resposta para essas perguntas examinando dados históricos, identificando padrões e usando essas informações para prever valores em algum momento no futuro.

A técnica para analisar os dados usados neste tutorial é uma análise de série temporal não variável. A análise de série temporal univariada analisa uma única observação numérica durante um período de tempo em intervalos específicos, como vendas mensais.

O algoritmo usado neste tutorial é Singular Spectrum Analysis (SSA). O SSA funciona decompondo uma série temporal em um conjunto de componentes principais. Esses componentes podem ser interpretados como partes de um sinal que correspondem a tendências, ruído, sazonalidade e muitos outros fatores. Em seguida, esses componentes são reconstruídos e usados para prever valores em algum momento no futuro.

Criar aplicativo de console

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

  2. Escolha .NET 8 como a estrutura a ser usada. Clique no botão Criar .

  3. Instale o pacote NuGet da versão Microsoft.ML

    Observação

    Este exemplo usa a versão estável mais recente dos pacotes NuGet mencionados, a menos que indicado de outra forma.

    1. No Gerenciador de Soluções, clique com o botão direito do mouse em seu projeto e selecione Gerenciar Pacotes NuGet.
    2. Escolha "nuget.org" como a origem do pacote, selecione a guia Procurar , pesquise Microsoft.ML.
    3. Marque a caixa de seleção Incluir pré-lançamento .
    4. Selecione o botão Instalar.
    5. Selecione o botão OK na caixa de diálogo Alterações de Visualização e, em seguida, selecione o botão I Accept na caixa de diálogo Aceitação da Licença se você concordar com os termos de licença dos pacotes listados.
    6. Repita estas etapas para System.Data.SqlClient e Microsoft.ML.TimeSeries.

Preparar e entender os dados

  1. Crie um diretório chamado Dados.
  2. Baixe o arquivo de banco de dados DailyDemand.mdf e salve-o no diretório de dados.

Observação

Os dados usados neste tutorial são provenientes do conjunto de dados de compartilhamento de bicicletas da UCI. Hadi Fanaee-T e João Gama, 'Rotulagem de eventos combinando detectores de conjuntos e conhecimento em segundo plano', Progresso em Inteligência Artificial (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.

O conjunto de dados original contém várias colunas correspondentes à sazonalidade e ao clima. Para fins de brevidade e como o algoritmo usado neste tutorial requer apenas os valores de uma única coluna numérica, o conjunto de dados original foi condensado para incluir apenas as seguintes colunas:

  • dteday: a data da observação.
  • ano: o ano codificado da observação (0=2011, 1=2012).
  • cnt: O número total de aluguéis de bicicletas para aquele dia.

O conjunto de dados original é mapeado para uma tabela de banco de dados com o esquema a seguir em um banco de dados do SQL Server.

CREATE TABLE [Rentals] (
    [RentalDate] DATE NOT NULL,
    [Year] INT NOT NULL,
    [TotalRentals] INT NOT NULL
);

Veja a seguir um exemplo dos dados:

RentalDate Ano TotalRentals
1/1/2011 0 985
1/2/2011 0 801
1/3/2011 0 1349

Criar classes de entrada e saída

  1. Abra Program.cs arquivo e substitua as diretivas existentes using pelo seguinte:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms.TimeSeries;
    using System.Data.SqlClient;
    
  2. Criar ModelInput classe. Abaixo da Program classe, adicione o código a seguir.

    public class ModelInput
    {
        public DateTime RentalDate { get; set; }
    
        public float Year { get; set; }
    
        public float TotalRentals { get; set; }
    }
    

    A ModelInput classe contém as seguintes colunas:

    • RentalDate: a data da observação.
    • Ano: o ano codificado da observação (0=2011, 1=2012).
    • TotalRentals: O número total de aluguéis de bicicletas para aquele dia.
  3. Crie ModelOutput uma classe abaixo da classe recém-criada ModelInput .

    public class ModelOutput
    {
        public float[] ForecastedRentals { get; set; }
    
        public float[] LowerBoundRentals { get; set; }
    
        public float[] UpperBoundRentals { get; set; }
    }
    

    A ModelOutput classe contém as seguintes colunas:

    • ForecastedRentals: Os valores previstos para o período previsto.
    • LowerBoundRentals: os valores mínimos previstos para o período previsto.
    • UpperBoundRentals: os valores máximos previstos para o período previsto.

Definir caminhos e inicializar variáveis

  1. Abaixo das using diretivas, defina variáveis para armazenar o local de seus dados, cadeia de conexão e onde salvar o modelo treinado.

    string rootDir = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../"));
    string dbFilePath = Path.Combine(rootDir, "Data", "DailyDemand.mdf");
    string modelPath = Path.Combine(rootDir, "MLModel.zip");
    var connectionString = $"Data Source=(LocalDB)\\MSSQLLocalDB;AttachDbFilename={dbFilePath};Integrated Security=True;Connect Timeout=30;";
    
  2. Inicialize a variável mlContext com uma nova instância de MLContext adicionando a linha a seguir depois de definir os caminhos.

    MLContext mlContext = new MLContext();
    

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

Carregar os dados

  1. Crie DatabaseLoader que carrega registros do tipo ModelInput.

    DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
    
  2. Defina a consulta para carregar os dados do banco de dados.

    string query = "SELECT RentalDate, CAST(Year as REAL) as Year, CAST(TotalRentals as REAL) as TotalRentals FROM Rentals";
    

    ML.NET algoritmos esperam que os dados sejam do tipo Single. Portanto, os valores numéricos provenientes do banco de dados que não são do tipo Real, um valor de ponto flutuante de precisão única, precisam ser convertidos Realem .

    As colunas Year e TotalRental são do tipo inteiro no banco de dados. Usando a CAST função embutida, ambos são convertidos para Real.

  3. Crie um DatabaseSource para se conectar ao banco de dados e executar a consulta.

    DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
                                    connectionString,
                                    query);
    
  4. Carregue os dados em um IDataView.

    IDataView dataView = loader.Load(dbSource);
    
  5. O conjunto de dados contém dois anos de dados. Somente os dados do primeiro ano são usados para treinamento, o segundo ano é mantido para comparar os valores reais com a previsão produzida pelo modelo. Filtre os dados usando a FilterRowsByColumn transformação.

    IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1);
    IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
    

    Para o primeiro ano, somente os valores na Year coluna menor que 1 são selecionados definindo o upperBound parâmetro como 1. Por outro lado, para o segundo ano, valores maiores ou iguais a 1 são selecionados definindo o lowerBound parâmetro como 1.

Definir o pipeline de análise de série temporal

  1. Defina um pipeline que usa o SsaForecastingEstimator para prever valores em um conjunto de dados de série temporal.

    var forecastingPipeline = mlContext.Forecasting.ForecastBySsa(
        outputColumnName: "ForecastedRentals",
        inputColumnName: "TotalRentals",
        windowSize: 7,
        seriesLength: 30,
        trainSize: 365,
        horizon: 7,
        confidenceLevel: 0.95f,
        confidenceLowerBoundColumn: "LowerBoundRentals",
        confidenceUpperBoundColumn: "UpperBoundRentals");
    

    O forecastingPipeline usa 365 pontos de dados para o primeiro ano e amostra ou divide o conjunto de dados de série temporal em intervalos de 30 dias (mensais), conforme especificado pelo seriesLength parâmetro. Cada um desses exemplos é analisado por meio de uma janela semanal ou de sete dias. Ao determinar qual é o valor previsto para o próximo período, os valores dos sete dias anteriores são usados para fazer uma previsão. O modelo está definido para prever sete períodos no futuro, conforme definido pelo horizon parâmetro. Como uma previsão é um palpite informado, nem sempre é 100% precisa. Portanto, é bom saber o intervalo de valores nos melhores e piores cenários, conforme definido pelos limites superior e inferior. Nesse caso, o nível de confiança para os limites inferior e superior é definido como 95%. O nível de confiança pode ser aumentado ou reduzido adequadamente. Quanto maior o valor, maior o intervalo entre os limites superior e inferior para atingir o nível de confiança desejado.

  2. Use o Fit método para treinar o modelo e ajustar os dados ao definido forecastingPipelineanteriormente.

    SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
    

Avaliar o modelo

Avalie o desempenho do modelo ao prever os dados do próximo ano e compará-lo com os valores reais.

  1. Crie um novo método de utilitário chamado Evaluate na parte inferior do arquivo Program.cs .

    Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
    {
    
    }
    
  2. Dentro do Evaluate método, prevea os dados do segundo ano usando o Transform método com o modelo treinado.

    IDataView predictions = model.Transform(testData);
    
  3. Obtenha os valores reais dos dados usando o CreateEnumerable método.

    IEnumerable<float> actual =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, true)
            .Select(observed => observed.TotalRentals);
    
  4. Obtenha os valores de previsão usando o CreateEnumerable método.

    IEnumerable<float> forecast =
        mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true)
            .Select(prediction => prediction.ForecastedRentals[0]);
    
  5. Calcule a diferença entre os valores reais e de previsão, geralmente chamados de erro.

    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    
  6. Medir o desempenho calculando os valores de erro médio absoluto absoluto e erro quadrado médio raiz.

    var MAE = metrics.Average(error => Math.Abs(error)); // Mean Absolute Error
    var RMSE = Math.Sqrt(metrics.Average(error => Math.Pow(error, 2))); // Root Mean Squared Error
    

    Para avaliar o desempenho, as seguintes métricas são usadas:

    • Erro absoluto médio: mede o quão próximas são as previsões do valor real. Esse valor varia entre 0 e infinito. Quanto mais próximo de 0, melhor a qualidade do modelo.
    • Erro quadrado médio raiz: resume o erro no modelo. Esse valor varia entre 0 e infinito. Quanto mais próximo de 0, melhor a qualidade do modelo.
  7. Exiba as métricas no console.

    Console.WriteLine("Evaluation Metrics");
    Console.WriteLine("---------------------");
    Console.WriteLine($"Mean Absolute Error: {MAE:F3}");
    Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
    
  8. Chame o Evaluate método abaixo chamando o Fit() método.

    Evaluate(secondYearData, forecaster, mlContext);
    

Salvar o modelo

Se você estiver satisfeito com seu modelo, salve-o para uso posterior em outros aplicativos.

  1. Abaixo do Evaluate() método, crie um TimeSeriesPredictionEngine. TimeSeriesPredictionEngine é um método de conveniência para fazer previsões simples.

    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    
  2. Salve o modelo em um arquivo chamado MLModel.zip conforme especificado pela variável definida modelPath anteriormente. Use o Checkpoint método para salvar o modelo.

    forecastEngine.CheckPoint(mlContext, modelPath);
    

Usar o modelo para prever a demanda

  1. Abaixo do Evaluate método, crie um novo método utilitário chamado Forecast.

    void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
    {
    
    }
    
  2. Dentro do Forecast método, use o Predict método para prever aluguéis para os próximos sete dias.

    ModelOutput forecast = forecaster.Predict();
    
  3. Alinhe os valores reais e de previsão para sete períodos.

    IEnumerable<string> forecastOutput =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, reuseRowObject: false)
            .Take(horizon)
            .Select((ModelInput rental, int index) =>
            {
                string rentalDate = rental.RentalDate.ToShortDateString();
                float actualRentals = rental.TotalRentals;
                float lowerEstimate = Math.Max(0, forecast.LowerBoundRentals[index]);
                float estimate = forecast.ForecastedRentals[index];
                float upperEstimate = forecast.UpperBoundRentals[index];
                return $"Date: {rentalDate}\n" +
                $"Actual Rentals: {actualRentals}\n" +
                $"Lower Estimate: {lowerEstimate}\n" +
                $"Forecast: {estimate}\n" +
                $"Upper Estimate: {upperEstimate}\n";
            });
    
  4. Iterar pelo resultado da previsão e mostrá-lo no terminal.

    Console.WriteLine("Rental Forecast");
    Console.WriteLine("---------------------");
    foreach (var prediction in forecastOutput)
    {
        Console.WriteLine(prediction);
    }
    

Executar o aplicativo

  1. Abaixo, chamar o Checkpoint() método chama o Forecast método.

    Forecast(secondYearData, 7, forecastEngine, mlContext);
    
  2. Execute o aplicativo. Uma saída semelhante à abaixo deve aparecer no console. Por brevidade, o resultado foi condensado.

    Evaluation Metrics
    ---------------------
    Mean Absolute Error: 726.416
    Root Mean Squared Error: 987.658
    
    Rental Forecast
    ---------------------
    Date: 1/1/2012
    Actual Rentals: 2294
    Lower Estimate: 1197.842
    Forecast: 2334.443
    Upper Estimate: 3471.044
    
    Date: 1/2/2012
    Actual Rentals: 1951
    Lower Estimate: 1148.412
    Forecast: 2360.861
    Upper Estimate: 3573.309
    

A inspeção dos valores reais e previstos mostra as seguintes relações:

Comparação real versus previsão

Embora os valores previstos não estejam prevendo o número exato de aluguéis, eles fornecem um intervalo mais estreito de valores que permite que uma operação otimize o uso de recursos.

Parabéns! Agora você criou com êxito um modelo de machine learning de série temporal para prever a demanda de aluguel de bicicletas.

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

Próximas etapas