Поделиться через


Руководство по прогнозированию спроса на обслуживание велосипедов с помощью анализа временных рядов и ML.NET

Узнайте, как прогнозировать спрос на аренду велосипедов с помощью единовременного анализа временных рядов данных, хранящихся в базе данных SQL Server с ML.NET.

В этом руководстве вы узнаете, как:

  • Определение проблемы
  • Загрузка данных из базы данных
  • Создание модели прогнозирования
  • Оценка модели прогнозирования
  • Сохранение модели прогнозирования
  • Использование модели прогнозирования

Предпосылки

Обзор примера прогнозирования временных рядов

Этот пример является консольным приложением C# , которое прогнозирует спрос на прокат велосипедов с помощью единого алгоритма анализа временных рядов, известного как Анализ сингулярного спектра. Код для этого примера можно найти в репозитории dotnet/machinelearning-samples на сайте GitHub.

Определение проблемы

Для эффективного выполнения операции управление инвентаризацией играет ключевую роль. Наличие слишком большого количества продукта на складе означает нераспроданные продукты, сидящие на полках, не генерируя никакой выручки. Слишком мало продукта приводит к потере продаж и клиентов покупки от конкурентов. Таким образом, постоянный вопрос состоит в определении оптимального объема запасов, который следует иметь в наличии. Анализ временных рядов помогает ответить на эти вопросы, просматривая исторические данные, определяя шаблоны и используя эти сведения для прогнозирования значений в будущем.

Метод анализа данных, используемых в этом руководстве, является унивариативным анализом временных рядов. Одномерный анализ временных рядов смотрит на одно числовое наблюдение за периодом времени с определенными интервалами, такими как ежемесячные продажи.

Алгоритм, используемый в этом руководстве, — это анализ сингулярным спектром (SSA). SSA работает путем разложения временных рядов в набор основных компонентов. Эти компоненты можно интерпретировать как части сигнала, которые соответствуют тенденциям, шуму, сезонности и многим другим факторам. Затем эти компоненты восстанавливаются и используются для прогнозирования значений в будущем.

Создание консольного приложения

  1. Создайте консольное приложение C# с именем BikeDemandForecasting. Нажмите кнопку Далее.

  2. Выберите .NET 8 в качестве платформы для использования. Нажмите кнопку Создать.

  3. Установка пакета NuGet версии Microsoft.ML

    Замечание

    В этом примере используется последняя стабильная версия упомянутых пакетов NuGet, если не указано иное.

    1. В обозревателе решений щелкните проект правой кнопкой мыши и выберите пункт "Управление пакетами NuGet".
    2. Выберите "nuget.org" в качестве источника пакета, перейдите на вкладку "Обзор ", найдите Microsoft.ML.
    3. Установите флажок "Включить предварительную версию ".
    4. Нажмите кнопку Установить.
    5. Нажмите кнопку "ОК " в диалоговом окне "Предварительные изменения" , а затем нажмите кнопку "Принять" в диалоговом окне принятия лицензий, если вы согласны с условиями лицензии для перечисленных пакетов.
    6. Повторите эти действия для System.Data.SqlClient и Microsoft.ML.TimeSeries.

Подготовка и понимание данных

  1. Создайте каталог с именем Data.
  2. Скачайте файл базы данных DailyDemand.mdf и сохраните его в каталоге данных.

Замечание

Данные, используемые в этом руководстве, поступают из набора данных Bike Sharing UCI. Hadi Fanaee-T и João Gama, "Обозначение событий, сочетание ансамблевых детекторов и фоновых знаний", Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, Web Link.

Исходный набор данных содержит несколько столбцов, соответствующих сезонности и погоде. Для краткости и из-за того, что алгоритм, используемый в этом руководстве, требует только значений из одного числового столбца, исходный набор данных был сокращен, чтобы включить только следующие столбцы:

  • dteday: дата наблюдения.
  • год: закодированный год наблюдения (0=2011, 1=2012).
  • cnt: Общее количество прокатов велосипедов в течение этого дня.

Исходный набор данных сопоставляется с таблицей базы данных со следующей схемой в базе данных SQL Server.

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

Ниже приведен пример данных:

RentalDate Год TotalRentals
1/1/2011 0 985
1/2/2011 0 801
1/3/2011 0 1349

Создание входных и выходных классов

  1. Откройте файл Program.cs и замените существующие using директивы следующими:

    using Microsoft.ML;
    using Microsoft.ML.Data;
    using Microsoft.ML.Transforms.TimeSeries;
    using System.Data.SqlClient;
    
  2. Создание ModelInput класса. Под классом Program добавьте следующий код.

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

    Класс ModelInput содержит следующие столбцы:

    • RentalDate: Дата наблюдения.
    • Год: закодированный год наблюдения (0=2011, 1=2012).
    • TotalRentals: Общее количество прокатов велосипедов в течение этого дня.
  3. Создайте ModelOutput класс под вновь созданным ModelInput классом.

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

    Класс ModelOutput содержит следующие столбцы:

    • ForecastedRentals: прогнозируемые значения для прогнозируемого периода.
    • LowerBoundRentals: прогнозируемые минимальные значения для прогнозируемого периода.
    • UpperBoundRentals: прогнозируемые максимальные значения для прогнозируемого периода.

Определение путей и инициализация переменных

  1. using Под директивами определяются переменные для хранения расположения данных, строки подключения и места сохранения обученной модели.

    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. Инициализируйте переменную mlContext новым экземпляром MLContext, добавив следующую строку после определения путей.

    MLContext mlContext = new MLContext();
    

    Класс MLContext является отправной точкой для всех операций ML.NET, и инициализация mlContext создает новую среду ML.NET, которую можно совместно использовать для объектов рабочего процесса создания модели. Это концептуально похоже на DBContext в Entity Framework.

Загрузка данных

  1. Создайте DatabaseLoader записи типа ModelInput.

    DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<ModelInput>();
    
  2. Определите запрос для загрузки данных из базы данных.

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

    ML.NET алгоритмы ожидают, что данные будут иметь тип Single. Таким образом, числовые значения, поступающие из базы данных, не имеющие типа Real, значение с плавающей запятой с одной точностью, необходимо преобразовать в Real.

    Столбцы Year и TotalRental являются целочисленными типами в базе данных. Используя встроенную функцию CAST, они оба преобразованы в Real.

  3. DatabaseSource Создайте объект для подключения к базе данных и выполните запрос.

    DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance,
                                    connectionString,
                                    query);
    
  4. Загрузка данных в объект IDataView.

    IDataView dataView = loader.Load(dbSource);
    
  5. Набор данных содержит два года данных. Для обучения используются только данные первого года, второй год проводится для сравнения фактических значений с прогнозом, созданным моделью. Отфильтруйте данные с помощью FilterRowsByColumn преобразования.

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

    В течение первого года только значения в Year столбце меньше 1 выбираются, задав upperBound параметру значение 1. И наоборот, в течение второго года значения, превышающие или равные 1, выбираются путем задания lowerBound параметра равным 1.

Определение конвейера анализа временных рядов

  1. Определите конвейер, использующий SsaForecastingEstimator для прогнозирования значений в наборе данных временных рядов.

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

    forecastingPipeline принимает 365 точек данных за первый год и разбивает или выбирает временные интервалы набора данных временных рядов на 30-дневные (ежемесячные), как это указано параметром seriesLength. Каждый из этих примеров анализируется еженедельно или через 7-дневное окно. При определении прогнозируемого значения для следующих периодов значения, полученные за предыдущие семь дней, используются для прогнозирования. Модель устанавливается для прогнозирования семи периодов в будущем, как определено параметром horizon . Так как прогноз является информированным предположением, это не всегда 100% точно. Поэтому хорошо знать диапазон значений в лучших и худших сценариях, как определено верхними и нижними границами. В этом случае уровень достоверности для нижних и верхних границ имеет значение 95%. Уровень достоверности может быть увеличен или уменьшен соответствующим образом. Чем выше значение, тем шире диапазон между верхними и нижними границами для достижения требуемого уровня достоверности.

  2. Fit Используйте метод для обучения модели и соответствия данным ранее определеннымforecastingPipeline.

    SsaForecastingTransformer forecaster = forecastingPipeline.Fit(firstYearData);
    

Оценка модели

Оцените, насколько хорошо модель выполняется путем прогнозирования данных следующего года и сравнения ее с фактическими значениями.

  1. Создайте новый метод служебной программы, называемый Evaluate в нижней части файла Program.cs .

    Evaluate(IDataView testData, ITransformer model, MLContext mlContext)
    {
    
    }
    
  2. В методе Evaluate прогнозируйте данные второго года с помощью Transform метода с обученной моделью.

    IDataView predictions = model.Transform(testData);
    
  3. Получение фактических значений из данных с помощью CreateEnumerable метода.

    IEnumerable<float> actual =
        mlContext.Data.CreateEnumerable<ModelInput>(testData, true)
            .Select(observed => observed.TotalRentals);
    
  4. Получение значений прогноза CreateEnumerable с помощью метода.

    IEnumerable<float> forecast =
        mlContext.Data.CreateEnumerable<ModelOutput>(predictions, true)
            .Select(prediction => prediction.ForecastedRentals[0]);
    
  5. Вычислите разницу между фактическими и прогнозными значениями, которые обычно называются ошибкой.

    var metrics = actual.Zip(forecast, (actualValue, forecastValue) => actualValue - forecastValue);
    
  6. Измеряйте производительность путем вычисления значений средней абсолютной ошибки и корневой среднеквадратической ошибки.

    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
    

    Для оценки производительности используются следующие метрики:

    • Средняя абсолютная ошибка: измеряет, как близкие прогнозы относятся к фактическому значению. Это значение диапазонов от 0 до бесконечности. Чем ближе к 0, тем лучше качество модели.
    • Среднеквадратическая ошибка: суммирует ошибку в модели. Это значение диапазонов от 0 до бесконечности. Чем ближе к 0, тем лучше качество модели.
  7. Выводит метрики в консоль.

    Console.WriteLine("Evaluation Metrics");
    Console.WriteLine("---------------------");
    Console.WriteLine($"Mean Absolute Error: {MAE:F3}");
    Console.WriteLine($"Root Mean Squared Error: {RMSE:F3}\n");
    
  8. Вызовите приведенный ниже метод, вызывающий EvaluateFit() метод.

    Evaluate(secondYearData, forecaster, mlContext);
    

Сохранение модели

Если вы удовлетворены моделью, сохраните ее для дальнейшего использования в других приложениях.

  1. Под методом Evaluate()TimeSeriesPredictionEngineсоздается . TimeSeriesPredictionEngine — это удобный метод для создания отдельных прогнозов.

    var forecastEngine = forecaster.CreateTimeSeriesEngine<ModelInput, ModelOutput>(mlContext);
    
  2. Сохраните модель в файл, который называется MLModel.zip указанной ранее modelPath переменной. Checkpoint Используйте метод для сохранения модели.

    forecastEngine.CheckPoint(mlContext, modelPath);
    

Использование модели для прогнозирования спроса

  1. Под методом Evaluate создайте новый метод Forecastслужебной программы.

    void Forecast(IDataView testData, int horizon, TimeSeriesPredictionEngine<ModelInput, ModelOutput> forecaster, MLContext mlContext)
    {
    
    }
    
  2. В методе Forecast используйте Predict метод для прогнозирования аренды в течение следующих семи дней.

    ModelOutput forecast = forecaster.Predict();
    
  3. Выровняйте фактические и прогнозные значения за семь периодов.

    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. Выполните итерацию по выходным данным прогноза и отобразите его на консоли.

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

Запуск приложения

  1. Под вызовом Checkpoint()Forecast метода вызывается метод.

    Forecast(secondYearData, 7, forecastEngine, mlContext);
    
  2. Запустите приложение. Выходные данные, аналогичные приведенному ниже, должны отображаться в консоли. Для краткости выходные данные были сокращены.

    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
    

Проверка фактических и прогнозируемых значений показывает следующие связи:

Фактическое сравнение прогнозов и прогнозов

Хотя прогнозируемые значения не прогнозируют точное количество аренды, они предоставляют более узкий диапазон значений, что позволяет операции оптимизировать их использование ресурсов.

Поздравляю! Теперь вы успешно создали модель машинного обучения временных рядов для прогнозирования спроса на прокат велосипедов.

Исходный код этого руководства можно найти в репозитории dotnet/machinelearning-samples .

Дальнейшие шаги