Tutorial: Previsão da procura do serviço de aluguer de bicicletas com análise de série temporal e ML.NET
Saiba como prever a procura de um serviço de aluguer de bicicletas com uma análise de série temporal nãoiva em dados armazenados numa base de dados SQL Server com ML.NET.
Neste tutorial, ficará a saber como:
- Compreender o problema
- Carregar dados a partir de uma base de dados
- Criar um modelo de previsão
- Avaliar modelo de previsão
- Guardar um modelo de previsão
- Utilizar um modelo de previsão
Pré-requisitos
- Visual Studio 2022 com a carga de trabalho ".NET Desktop Development" instalada.
Descrição geral do exemplo de previsão de série temporal
Este exemplo é uma aplicação de consola .NET Core C# que prevê a procura de alugueres de bicicletas através de um algoritmo de análise de série temporal indisiva conhecido como Singular Spectrum Analysis. O código para este exemplo pode ser encontrado no repositório dotnet/machinelearning-samples no GitHub.
Compreender o problema
Para executar uma operação eficiente, a gestão de inventário desempenha uma função fundamental. Ter demasiado produto em stock significa que os produtos não vendidos nas prateleiras não geram receitas. Ter pouco produto leva à perda de vendas e aos clientes que compram a concorrentes. Portanto, a pergunta constante é: qual é a quantidade ideal de inventário a manter disponível? A análise de séries temporizadas ajuda a fornecer uma resposta a estas perguntas ao analisar dados históricos, identificar padrões e utilizar estas informações para prever valores no futuro.
A técnica para analisar os dados utilizados neste tutorial é a análise de série temporal inivariada. A análise de série temporal nãoivada analisa uma única observação numérica ao longo de um período de tempo em intervalos específicos, como vendas mensais.
O algoritmo utilizado neste tutorial é Singular Spectrum Analysis(SSA). O SSA funciona ao decompor uma série temporal num conjunto de componentes principais. Estes componentes podem ser interpretados como as partes de um sinal que correspondem a tendências, ruído, sazonalidade e muitos outros fatores. Em seguida, estes componentes são reconstruídos e utilizados para prever valores no futuro.
Criar aplicação da consola
Crie uma Aplicação de Consola C# denominada "BikeDemandForecasting". Clique no botão Seguinte .
Selecione .NET 6 como a arquitetura a utilizar. Clique no botão Criar.
Instalar Microsoft.ML versão do pacote NuGet
Nota
Este exemplo utiliza a versão estável mais recente dos pacotes NuGet mencionados, salvo indicação em contrário.
- No Explorador de Soluções, clique com o botão direito do rato no projeto e selecione Gerir Pacotes NuGet.
- Selecione "nuget.org" como origem do pacote, selecione o separador Procurar, procure Microsoft.ML.
- Selecione a caixa de verificação Incluir pré-lançamento .
- Selecione o botão Instalar .
- Selecione o botão OK na caixa de diálogo Pré-visualizar Alterações e, em seguida, selecione o botão Aceito na caixa de diálogo Aceitação da Licença se concordar com os termos de licença dos pacotes listados.
- Repita estes passos para System.Data.SqlClient e Microsoft.ML.TimeSeries.
Preparar e compreender os dados
- Crie um diretório denominado Dados.
- Transfira o ficheiro de base de dados DailyDemand.mdf e guarde-o no diretório Dados.
Nota
Os dados utilizados neste tutorial são provenientes do Conjunto de Dados de Partilha de Bicicletas UCI. Fanaee-T, Hadi e Gama, João, "Etiquetagem de eventos que combina detetores de conjuntos e conhecimentos de fundo", Progresso na 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 às condições meteorológicas. Para a brevidade e porque o algoritmo utilizado neste tutorial apenas requer 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 alugueres de bicicletas para esse dia.
O conjunto de dados original é mapeado para uma tabela de base de dados com o seguinte esquema numa base de dados SQL Server.
CREATE TABLE [Rentals] (
[RentalDate] DATE NOT NULL,
[Year] INT NOT NULL,
[TotalRentals] INT NOT NULL
);
Segue-se um exemplo dos dados:
RentalDate | Anual | TotalRentals |
---|---|---|
1/1/2011 | 0 | 985 |
1/2/2011 | 0 | 801 |
1/3/2011 | 0 | 1349 |
Criar classes de entrada e saída
Abra o ficheiro Program.cs e substitua as instruções existentes
using
pelo seguinte:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;
Crie a classe
ModelInput
. Abaixo daProgram
classe, adicione o seguinte código.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 alugueres de bicicletas para esse dia.
Crie
ModelOutput
a classe abaixo da classe recentemente criadaModelInput
.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
Abaixo das instruções de utilização, defina variáveis para armazenar a localização dos seus dados, cadeia de ligação e onde guardar o modelo preparado.
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;";
Inicialize a
mlContext
variável com uma nova instância deMLContext
ao adicionar a seguinte linha 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 inicializar mlContext cria um novo ambiente de ML.NET que pode ser partilhado entre os objetos de fluxo de trabalho de criação de modelos. É semelhante, conceptualmente, aDBContext
no Entity Framework.
Carregar os dados
Criar
DatabaseLoader
que carrega registos do tipoModelInput
.DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
Defina a consulta para carregar os dados da base 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
. Por conseguinte, os valores numéricos provenientes da base de dados que não são do tipoReal
, um valor de vírgula flutuante de precisão única, têm de ser convertidos emReal
.As
Year
colunas eTotalRental
são ambos tipos inteiros na base de dados. Com aCAST
função incorporada, ambos são fundidos emReal
.Crie um
DatabaseSource
para ligar à base de dados e executar a consulta.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);
Carregue os dados para um
IDataView
.IDataView dataView = loader.Load(dbSource);
O conjunto de dados contém dois anos de dados. Apenas são utilizados dados do primeiro ano para preparação, sendo que o segundo ano é mantido para comparar os valores reais com a previsão produzida pelo modelo. Filtre os dados com a transformação
FilterRowsByColumn
.IDataView firstYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", upperBound: 1); IDataView secondYearData = mlContext.Data.FilterRowsByColumn(dataView, "Year", lowerBound: 1);
No primeiro ano, apenas os valores na coluna inferior a
Year
1 são selecionados ao definir oupperBound
parâmetro como 1. Por outro lado, para o segundo ano, os valores maiores ou iguais a 1 são selecionados ao definir olowerBound
parâmetro como 1.
Definir o pipeline de análise de série temporal
Defina um pipeline que utiliza o SsaForecastingEstimator para prever valores num 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
utiliza 365 pontos de dados para o primeiro ano e exemplos ou divide o conjunto de dados da série temporal em intervalos de 30 dias (mensais), conforme especificado peloseriesLength
parâmetro. Cada um destes exemplos é analisado através de uma janela semanal ou de 7 dias. Ao determinar qual é o valor previsto para o(s) período(s) seguinte, os valores dos sete dias anteriores são utilizados para fazer uma predição. O modelo está definido para prever sete períodos no futuro, conforme definido pelohorizon
parâmetro. Como uma previsão é uma estimativa informada, nem sempre é 100% precisa. Por conseguinte, é bom conhecer o intervalo de valores nos melhores e piores cenários, conforme definido pelos limites superior e inferior. Neste caso, o nível de confiança para os limites inferior e superior está definido como 95%. O nível de confiança pode ser aumentado ou diminuído em conformidade. Quanto maior for o valor, maior será o intervalo entre os limites superior e inferior para alcançar o nível de confiança pretendido.Utilize o
Fit
método para preparar o modelo e ajustar os dados ao definido anteriormenteforecastingPipeline
.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.
Crie um novo método utilitário chamado
Evaluate
na parte inferior do ficheiro Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }
Dentro do
Evaluate
método, preveja os dados do segundo ano com oTransform
método com o modelo preparado.IDataView predictions = model.Transform(testData);
Obtenha os valores reais dos dados com o
CreateEnumerable
método .IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);
Obtenha os valores de previsão com o
CreateEnumerable
método .IEnumerable<float> forecast = mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true) .Select(prediction => prediction.ForecastedRentals[0]);
Calcule a diferença entre os valores reais e previstos, normalmente referidos como o erro.
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
Medir o desempenho ao calcular os valores Erro Absoluto Médio Absoluto e Erro Quadrado Médio de 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, são utilizadas as seguintes métricas:
- Erro Absoluto Médio: mede a proximidade das predições com o valor real. Este valor varia entre 0 e infinito. Quanto mais perto de 0, melhor será a qualidade do modelo.
- Erro Quadrado Médio de Raiz: resume o erro no modelo. Este valor varia entre 0 e infinito. Quanto mais perto de 0, melhor será a qualidade do modelo.
Exportar as métricas para a consola.
Console.WriteLine("Evaluation Metrics"); Console.WriteLine("---------------------"); Console.WriteLine($"Mean Absolute Error: {MAE:F3}"); Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
Chame o
Evaluate
método abaixo ao chamar oFit()
método.Evaluate(secondYearData, forecaster, mlContext);
Guardar o modelo
Se estiver satisfeito com o seu modelo, guarde-o para utilização posterior noutras aplicações.
Por baixo do
Evaluate()
método, crie umTimeSeriesPredictionEngine
.TimeSeriesPredictionEngine
é um método de conveniência para fazer predições individuais.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
Guarde o modelo num ficheiro chamado
MLModel.zip
conforme especificado pela variável definidamodelPath
anteriormente. Utilize oCheckpoint
método para guardar o modelo.forecastEngine.CheckPoint(mlContext, modelPath);
Utilizar o modelo para prever a procura
Por baixo do
Evaluate
método, crie um novo método utilitário chamadoForecast
.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }
Dentro do
Forecast
método, utilize oPredict
método para prever alugueres para os próximos sete dias.ModelOutput forecast = forecaster.Predict();
Alinhe os valores reais e previstos durante 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"; });
Itera através do resultado da previsão e apresente-o na consola do .
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
Executar a aplicação
Abaixo, chamar o
Checkpoint()
método chama oForecast
método.Forecast(secondYearData, 7, forecastEngine, mlContext);
Execute a aplicação. O resultado semelhante ao apresentado abaixo deverá aparecer na consola do . Por questões de brevidade, a saída foi condensada.
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:
Embora os valores previstos não estejam a prever o número exato de alugueres, fornecem um intervalo de valores mais estreito que permite que uma operação otimize a utilização dos recursos.
Parabéns! Criou com êxito um modelo de machine learning de série temporal para prever a procura de aluguer de bicicletas.
Pode encontrar o código fonte deste tutorial no repositório dotnet/machinelearning-samples .