Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Aprenda como prever a procura por um serviço de aluguer de bicicletas usando análise univariada de séries temporais em dados armazenados numa base de dados SQL Server com ML.NET.
Neste tutorial, aprenderás como:
- Entenda o problema
- Carregar dados a partir de uma base de dados
- Criar um modelo de previsão
- Avaliar o modelo de previsão
- Guardar um modelo de previsão
- Utilizar um modelo de previsão
Pré-requisitos
- Visual Studio 2022 ou versões posteriores com a carga de trabalho de desenvolvimento .NET Desktop instalada.
Visão geral do exemplo de previsão de séries temporais
Este exemplo é uma aplicação de consola C# que prevê a procura por aluguer de bicicletas usando um algoritmo de análise de séries temporais univariadas conhecido como Análise de Espectro Singular. O código deste exemplo pode ser encontrado no repositório dotnet/machinelearning-samples no GitHub.
Entenda o problema
Para gerir uma operação eficiente, a gestão de inventário desempenha um papel fundamental. Ter um produto a mais em stock significa que os produtos não vendidos ficam nas prateleiras sem gerar receita. Ter produto insuficiente leva à perda de vendas e à compra de clientes a concorrentes. Portanto, a questão constante é: qual é a quantidade ótima de inventário para manter disponível? A análise de séries temporais ajuda a fornecer uma resposta a estas questões, analisando dados históricos, identificando padrões e utilizando essa informação para prever valores para algum momento futuro.
A técnica para analisar dados utilizada neste tutorial é a análise univariada de séries temporais. A análise de séries temporais univariadas 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 é a Análise do Espectro Singular (SSA). A SSA funciona decompondo 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. Depois, estes componentes são reconstruídos e usados para prever valores em algum momento futuro.
Criar aplicação de consola
Crie uma aplicação de consola C# chamada "BikeDemandForecasting". Clique no botão Seguinte.
Escolhe o .NET 8 como framework a usar. Clique no botão Criar .
Instalar a versão do pacote NuGet Microsoft.ML
Observação
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 no seu projeto e selecione Gerir Pacotes NuGet.
- Escolhe "nuget.org" como fonte do Pacote, seleciona o separador Explorar , pesquisa por Microsoft.ML.
- Assinala a opção Incluir pré-lançamento .
- Selecione o botão Instalar.
- Selecione o botão OK na janela de Pré-visualização de Alterações e depois selecione o botão Aceitar na janela de Aceitação de Licença se concordar com os termos da licença dos pacotes listados.
- Repita estes passos para System.Data.SqlClient e Microsoft.ML.TimeSeries.
Prepare e compreenda os dados
- Crie um diretório chamado Data.
- Descarregue o ficheiro DailyDemand.mdf base de dados e guarde-o no diretório de Dados .
Observação
Os dados usados neste tutorial provêm do Conjunto de Dados de Partilha de Bicicletas da UCI. Hadi Fanaee-T e João Gama, 'Rotulagem de eventos que combina detetores de conjunto e conhecimento de fundo', Progress in Artificial Intelligence (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 brevidade e porque o algoritmo utilizado 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 bicicletas alugadas nesse 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 uma amostra dos dados:
| Data de Aluguer | Ano | TotalRentals |
|---|---|---|
| 1/1/2011 | 0 | 985 |
| 1/2/2011 | 0 | 801 |
| 1/3/2011 | 0 | 1349 |
Criar classes de entrada e saída
Abra Program.cs ficheiro e substitua as diretivas existentes
usingpelas seguintes:using Microsoft.ML; using Microsoft.ML.Data; using Microsoft.ML.Transforms.TimeSeries; using System.Data.SqlClient;Cria
ModelInputclasse. Por baixo daProgramturma, adicione o seguinte código.public class ModelInput { public DateTime RentalDate { get; set; } public float Year { get; set; } public float TotalRentals { get; set; } }A
ModelInputclasse contém as seguintes colunas:- Data de Observação: 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 nesse dia.
Crie
ModelOutputuma classe abaixo da nova classe criadaModelInput.public class ModelOutput { public float[] ForecastedRentals { get; set; } public float[] LowerBoundRentals { get; set; } public float[] UpperBoundRentals { get; set; } }A
ModelOutputclasse contém as seguintes colunas:- Previsões de Alugueres: Os valores previstos para o período de previsão.
- LowerLimitsRentals: 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
usingdiretivas definem variáveis para armazenar a localização dos seus dados, cadeia de ligação e onde guardar 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;";Inicialize a
mlContextvariável com uma nova instância deMLContextadicionando a linha seguinte após definir os caminhos.MLContext mlContext = new MLContext();A
MLContextclasse é um ponto de partida para todas as operações ML.NET, e inicializar o mlContext cria um novo ambiente ML.NET que pode ser partilhado entre os objetos do fluxo de trabalho de criação de modelos. É semelhante, do ponto de vista conceptual, aoDBContextno Entity Framework.
Carregar os dados
Cria
DatabaseLoaderque 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. Portanto, valores numéricos provenientes da base de dados que não são do tipoReal, um valor de ponto flutuante de precisão simples, têm de ser convertidos paraReal.As
Yearcolunas eTotalRentalsão ambos tipos inteiros na base de dados. Usando a função incorporadaCAST, ambos são convertidos paraReal.Crie um
DatabaseSourcepara ligar à base de dados e execute a consulta.DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, query);Carregue os dados num
IDataViewarquivo .IDataView dataView = loader.Load(dbSource);O conjunto de dados contém dois anos de dados. Apenas os dados do primeiro ano são usados para o treino, o segundo ano é reservado para comparar os valores reais com a previsão produzida pelo modelo. Filtra os dados usando a
FilterRowsByColumntransformada.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
Yearcoluna inferior a 1 são selecionados definindo oupperBoundparâmetro para 1. Por outro lado, para o segundo ano, valores maiores ou iguais a 1 são selecionados definindo olowerBoundparâmetro como 1.
Definir fluxo de análise de séries temporais
Defina um pipeline que utiliza o SsaForecastingEstimator para prever valores num conjunto de dados de séries temporais.
var forecastingPipeline = mlContext.Forecasting.ForecastBySsa( outputColumnName: "ForecastedRentals", inputColumnName: "TotalRentals", windowSize: 7, seriesLength: 30, trainSize: 365, horizon: 7, confidenceLevel: 0.95f, confidenceLowerBoundColumn: "LowerBoundRentals", confidenceUpperBoundColumn: "UpperBoundRentals");forecastingPipelinetoma 365 pontos de dados para o primeiro ano e amostra ou divide o conjunto de dados de séries temporais em intervalos mensais de 30 dias, conforme especificado pelo parâmetroseriesLength. Cada uma destas amostras é analisada numa janela semanal ou de 7 dias. Ao determinar qual é o valor previsto para o(s) próximo(s) período(s), os valores dos sete dias anteriores são usados para fazer uma previsão. O modelo está configurado para prever sete períodos no futuro, conforme definido pelohorizonparâmetro. Como uma previsão é um palpite informado, nem sempre é 100% precisa. Por isso, é 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 é definido para 95%. O nível de confiança pode ser aumentado ou diminuído em conformidade. Quanto maior for o valor, maior é o intervalo entre os limites superior e inferior para atingir o nível desejado de confiança.Use o
Fitmétodo para treinar o modelo e ajuste os dados ao .forecastingPipelineSsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
Avaliar o modelo
Avalie o desempenho do modelo, prevendo os dados do próximo ano e comparando-os com os valores reais.
Crie um novo método utilitário chamado
Evaluateno final do ficheiro Program.cs .Evaluate(IDataView testData, ITransformer model, MLContext mlContext) { }Dentro do
Evaluatemétodo, prevê os dados do segundo ano utilizando oTransformmétodo com o modelo treinado.IDataView predictions = model.Transform(testData);Obtenha os valores reais dos dados usando o
CreateEnumerablemétodo.IEnumerable<float> actual = mlContext.Data.CreateEnumerable<ModelInput>(testData, true) .Select(observed => observed.TotalRentals);Obtenha os valores das previsões usando o
CreateEnumerablemé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, comumente referidos como o erro.
var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);Mede o desempenho calculando os valores do Erro Absoluto Médio e do Erro Quadrático 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 ErrorPara avaliar o desempenho, são usadas as seguintes métricas:
- Erro Absoluto Médio: Mede quão próximas estão as previsões do valor real. Este valor varia entre 0 e infinito. Quanto mais próximo de 0, melhor a qualidade do modelo.
- Erro Quadrático Médio Raiz: Resume o erro no modelo. Este valor varia entre 0 e infinito. Quanto mais próximo de 0, melhor a qualidade do modelo.
Envia 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");Chama o
Evaluatemétodo abaixo a chamar oFit()método.Evaluate(secondYearData, forecaster, mlContext);
Guardar o modelo
Se estiver satisfeito com o seu modelo, guarde-o para uso posterior noutras aplicações.
Abaixo do
Evaluate()método, crie umTimeSeriesPredictionEngine.TimeSeriesPredictionEngineé um método de conveniência para fazer previsões únicas.var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);Guardar o modelo num ficheiro chamado
MLModel.zipconforme especificado pela variável previamente definidamodelPath. Use oCheckpointmétodo para guardar o modelo.forecastEngine.CheckPoint(mlContext, modelPath);
Use o modelo para prever a procura
Por baixo do
Evaluatemétodo, crie um novo método utilitário chamadoForecast.void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext) { }No
Forecastprocesso, utilize-oPredictpara prever as rendas para os próximos sete dias.ModelOutput forecast = forecaster.Predict();Alinhe os valores reais e previstos 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"; });Percorrer o resultado da previsão e mostrá-lo na consola.
Console.WriteLine("Rental Forecast"); Console.WriteLine("---------------------"); foreach (var prediction in forecastOutput) { Console.WriteLine(prediction); }
Execute o aplicativo
Abaixo, chamar o
Checkpoint()método chama oForecastmétodo.Forecast(secondYearData, 7, forecastEngine, mlContext);Execute o aplicativo. Uma saída semelhante à abaixo deverá aparecer na consola. Para ser breve, 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:
Embora os valores previstos não prevejam o número exato de arrendamentos, fornecem uma gama mais restrita de valores que permite a uma operação otimizar a utilização dos recursos.
Parabéns! Agora construiu com sucesso um modelo de aprendizagem automática em séries temporais para prever a procura de aluguer de bicicletas.
Pode encontrar o código-fonte deste tutorial no repositório dotnet/machinelearning-samples .