Учебник. Прогнозирование цен с помощью регрессии с ML.NET

В этом учебнике показано, как создать модель регрессии с помощью ML.NET, чтобы прогнозировать цены, в частности плату за проезд в такси по Нью-Йорку.

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

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

Предварительные требования

  • Visual Studio 2022 с установленной рабочей нагрузкой "Разработка классических приложений .NET".

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

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

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

  3. Создайте каталог с именем Data в проекте для хранения набора данных и файлов модели.

  4. Установите пакеты NuGet Microsoft.ML и Microsoft.ML.FastTree:

    Примечание

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

    В обозревателе решений щелкните проект правой кнопкой мыши и выберите Управление пакетами NuGet. Выберите в качестве источника пакета "nuget.org", откройте вкладку Обзор, найдите Microsoft.ML, выберите пакет в списке и нажмите кнопку Установить. Нажмите кнопку ОК в диалоговом окне Предварительный просмотр изменений, а затем нажмите кнопку Принимаю в диалоговом окне Принятие условий лицензионного соглашения, если вы согласны с указанными условиями лицензионного соглашения для выбранных пакетов. Сделайте то же самое для пакета Microsoft.ML.FastTree в NuGet.

Подготовка и анализ данных

  1. Скачайте наборы данных taxi-fare-train.csv и taxi-fare-test.csv, сохранив их в созданной на предыдущем шаге папке Data. Эти наборы данных используются для обучения модели машинного обучения и последующей оценки точности этой модели. Эти наборы данных взяты из наборов данных NYC TLC Taxi Trip.

  2. В Обозреватель решений щелкните правой кнопкой мыши каждый из файлов *.csv и выберите Свойства. В разделе Дополнительно для параметра Копировать в выходной каталог установите значение Копировать более позднюю версию.

  3. Откройте набор данных taxi-fare-train.csv и просмотрите заголовки столбцов в первой строке. Теперь изучите каждый из столбцов. Разберитесь, какие данные в них хранятся, и определите, какие столбцы являются признаками, а в каком содержится метка.

label — это столбец, который необходимо спрогнозировать. Определены Featuresвходные данные, которые вы предоставляете модели для прогнозирования Label.

Предоставленный набор данных содержит следующие столбцы:

  • vendor_id: идентификатор поставщика услуг такси — это признак.
  • rate_code: тип тарифа для поездки на такси — это признак.
  • passenger_count: число пассажиров в поездке — это признак.
  • trip_time_in_secs: время, затраченное на поездку. Вам требуется спрогнозировать плату за одну поездку до того, как поездка будет завершена. На этот момент вам неизвестна продолжительность поездки. Таким образом, продолжительность поездки не является признаком и соответствующий столбец следует исключить из модели.
  • trip_distance: расстояние поездки — это признак.
  • payment_type: метод оплаты (наличные или кредитная карта) — это признак.
  • fare_amount: общая сумма, уплаченная за поездку на такси, — это метка.

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

Создайте классы для входных данных и прогнозов:

  1. В обозревателе решений щелкните проект правой кнопкой мыши и выберите пункты Добавить>Новый элемент.

  2. В диалоговом окне Добавление нового элемента выберите Класс и измените значение поля Имя на TaxiTrip.cs. Теперь нажмите кнопку Добавить.

  3. Добавьте следующие директивы using в новый файл.

    using Microsoft.ML.Data;
    

Удалите из файла TaxiTrip.cs существующее определение класса и добавьте следующий код с двумя классами TaxiTrip и TaxiTripFarePrediction:

public class TaxiTrip
{
    [LoadColumn(0)]
    public string? VendorId;

    [LoadColumn(1)]
    public string? RateCode;

    [LoadColumn(2)]
    public float PassengerCount;

    [LoadColumn(3)]
    public float TripTime;

    [LoadColumn(4)]
    public float TripDistance;

    [LoadColumn(5)]
    public string? PaymentType;

    [LoadColumn(6)]
    public float FareAmount;
}

public class TaxiTripFarePrediction
{
    [ColumnName("Score")]
    public float FareAmount;
}

Класс TaxiTrip содержит входные данные и определения для каждого из столбцов в этом наборе данных. Используйте атрибут LoadColumnAttribute, чтобы указать индексы исходных столбцов в наборе данных.

Класс TaxiTripFarePrediction представляет результаты прогнозирования. Он содержит одно поле типа float FareAmount, к которому применен атрибут ScoreColumnNameAttribute. Для задачи регрессии столбец Оценка содержит прогнозируемые значения метки.

Примечание

Используйте тип float для представления значений с плавающей запятой в классах данных ввода и прогнозирования.

Определение путей к данным и модели

Добавьте следующие новые операторы using в начало файла Program.cs:

using Microsoft.ML;
using TaxiFarePrediction;

Необходимо создать три поля, которые будут содержать пути к файлам с наборами данных и к файлу для сохранения модели:

  • _trainDataPath содержит путь к файлу с набором данных, используемым для обучения модели.
  • _testDataPath содержит путь к файлу с набором данных, используемым для оценки модели.
  • _modelPath содержит путь к файлу для сохранения обучаемой модели.

Добавьте следующий код прямо под разделом с операторами using, чтобы указать эти пути и переменную _textLoader:

string _trainDataPath = Path.Combine(Environment.CurrentDirectory, "Data", "taxi-fare-train.csv");
string _testDataPath = Path.Combine(Environment.CurrentDirectory, "Data", "taxi-fare-test.csv");
string _modelPath = Path.Combine(Environment.CurrentDirectory, "Data", "Model.zip");

Все операции ML.NET запускаются в классе MLContext. Инициализация mlContext создает новую среду ML.NET, которую могут совместно использовать объекты рабочего процесса создания модели. По существу он аналогичен классу DBContext в Entity Framework.

Инициализация переменных

Замените строку Console.WriteLine("Hello World!") следующим кодом, чтобы объявить и инициализировать переменную mlContext:

MLContext mlContext = new MLContext(seed: 0);

Добавьте следующий код в качестве следующей строки кода для вызова метода Train:

var model = Train(mlContext, _trainDataPath);

Метод Train() выполняет следующие задачи:

  • загрузка данных;
  • извлечение и преобразование данных;
  • обучение модели;
  • возвращение модели.

Метод Train обучает модель. Создайте этот метод сразу под оператором using, используя следующий код:

ITransformer Train(MLContext mlContext, string dataPath)
{

}

Загрузка и преобразование данных

ML.NET использует интерфейс IDataView в качестве гибкого и эффективного средства описания числовых или текстовых табличных данных. IDataView может загружать данные из текстового файла или в режиме реального времени (например, из базы данных SQL или файлов журнала). Добавьте следующий код в первую строку метода Train():

IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(dataPath, hasHeader: true, separatorChar: ',');

Так как вам нужно спрогнозировать плату за такси, столбец FareAmount является меткой Label, которую вы будете прогнозировать (выходные данные модели). Используйте класс преобразования CopyColumnsEstimator, чтобы скопировать FareAmount, и добавьте следующий код:

var pipeline = mlContext.Transforms.CopyColumns(outputColumnName: "Label", inputColumnName:"FareAmount")

Алгоритм, который обучает модель, принимает числовые признаки, поэтому значения категориальных данных (VendorId, RateCode и PaymentType) нужно преобразовать в числа (VendorIdEncoded, RateCodeEncoded и PaymentTypeEncoded). Для этого используйте класс преобразования OneHotEncodingTransformer, который присваивает разные числовые значения ключа разным значениям в каждом из столбцов, и добавьте следующий код:

.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "VendorIdEncoded", inputColumnName:"VendorId"))
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "RateCodeEncoded", inputColumnName: "RateCode"))
.Append(mlContext.Transforms.Categorical.OneHotEncoding(outputColumnName: "PaymentTypeEncoded", inputColumnName: "PaymentType"))

Последний шаг на этапе подготовки данных заключается в объединении всех столбцов признаков в столбце Features с помощью класса преобразования mlContext.Transforms.Concatenate. По умолчанию алгоритм обучения обрабатывает только признаки, представленные в столбце Features. Добавьте следующий код:

.Append(mlContext.Transforms.Concatenate("Features", "VendorIdEncoded", "RateCodeEncoded", "PassengerCount", "TripDistance", "PaymentTypeEncoded"))

Выбор алгоритма обучения

Задача заключается в прогнозировании стоимости поездки в такси в Нью-Йорке. На первый взгляд может показаться, что плата зависит только от расстояния. Но службы такси в Нью-Йорке изменяют плату с учетом еще нескольких факторов, таких как наличие дополнительных пассажиров или оплата кредитной картой вместо наличных. Вы хотите спрогнозировать стоимость, которая является реальным значением, зависящим от других факторов в наборе данных. Для этого нужно выбрать задачу машинного обучения регрессия.

Добавьте задачу машинного обучения FastTreeRegressionTrainer к определениям преобразований данных, добавив в Train() следующую строку кода:

.Append(mlContext.Regression.Trainers.FastTree());

Обучение модели

Согласуйте модель с учебным dataview и получите обученную модель, добавив в метод Train() следующую строку кода:

var model = pipeline.Fit(dataView);

Метод Fit() обучает модель путем преобразования набора данных и применения обучения.

Возвратите обученную модель с помощью следующей строки кода в методе Train():

return model;

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

Затем оцените эффективность модели с помощью тестовых данных для контроля качества и проверки. Создайте метод Evaluate() сразу после Train(), используя следующий код:

void Evaluate(MLContext mlContext, ITransformer model)
{

}

Метод Evaluate выполняет следующие задачи:

  • загрузка тестового набора данных;
  • создание средства оценки регрессии;
  • оценка модели и создание метрик;
  • отображение метрик.

Добавьте вызов нового метода сразу под вызовом метода Train, используя следующий код:

Evaluate(mlContext, model);

Загрузите тестовый набор данных с помощью метода LoadFromTextFile(). Оцените модель с помощью этого набора данных для проверки ее качества, добавив следующий код в метод Evaluate:

IDataView dataView = mlContext.Data.LoadFromTextFile<TaxiTrip>(_testDataPath, hasHeader: true, separatorChar: ',');

Затем преобразуйте данные Test, добавив следующий код для Evaluate():

var predictions = model.Transform(dataView);

Метод Transform() осуществляет прогнозирование для входных строк тестового набора данных.

Метод RegressionContext.Evaluate вычисляет метрики качества для PredictionModel на основе указанного набора данных. Возвращает объект RegressionMetrics, который содержит общие метрики, вычисляемые с помощью средств оценки регрессии.

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

var metrics = mlContext.Regression.Evaluate(predictions, "Label", "Score");

После получения прогноза метод Evaluate() оценивает модель, сравнивая спрогнозированные значения с фактическими метками (Labels) в тестовом наборе данных, а затем возвращает метрики эффективности модели.

Добавьте следующий код, чтобы оценить модель и создать для нее метрики оценки:

Console.WriteLine();
Console.WriteLine($"*************************************************");
Console.WriteLine($"*       Model quality metrics evaluation         ");
Console.WriteLine($"*------------------------------------------------");

Также в качестве метрики для оценки регрессионных моделей используется коэффициент детерминации. Коэффициент детерминации может иметь значения в диапазоне от 0 до 1. Чем ближе его значение к 1, тем лучше модель. Чтобы отображать значение коэффициента детерминации, добавьте следующий код в метод Evaluate:

Console.WriteLine($"*       RSquared Score:      {metrics.RSquared:0.##}");

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

Console.WriteLine($"*       Root Mean Squared Error:      {metrics.RootMeanSquaredError:#.##}");

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

Создайте метод TestSinglePrediction сразу после метода Evaluate, вставив в него следующий код:

void TestSinglePrediction(MLContext mlContext, ITransformer model)
{

}

Метод TestSinglePrediction выполняет следующие задачи:

  • создание отдельного комментария тестовых данных;
  • прогнозирование суммы оплаты на основе тестовых данных;
  • объединение тестовых данных и прогнозов для создания отчетов;
  • отображение результатов прогнозирования.

Добавьте вызов нового метода сразу под вызовом метода Evaluate, используя следующий код:

TestSinglePrediction(mlContext, model);

Для прогнозирования цены используйте PredictionEngine, добавив в TestSinglePrediction() следующий код:

var predictionFunction = mlContext.Model.CreatePredictionEngine<TaxiTrip, TaxiTripFarePrediction>(model);

Класс PredictionEngine представляет собой удобный API, позволяющий осуществить прогнозирование на основе единственного экземпляра данных. PredictionEngine не является потокобезопасным. Допустимо использовать в средах прототипов или средах с одним потоком. Для улучшенной производительности и потокобезопасности в рабочей среде используйте службу PredictionEnginePool, которая создает ObjectPool объектов PredictionEngine для использования во всем приложении. См. руководство по использованию PredictionEnginePool в веб-API ASP.NET Core.

Примечание

Расширение службы PredictionEnginePool сейчас доступно в предварительной версии.

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

var taxiTripSample = new TaxiTrip()
{
    VendorId = "VTS",
    RateCode = "1",
    PassengerCount = 1,
    TripTime = 1140,
    TripDistance = 3.75f,
    PaymentType = "CRD",
    FareAmount = 0 // To predict. Actual/Observed = 15.5
};

Затем спрогнозируйте стоимость на основе отдельного экземпляра данных о поездке на такси и передайте ее в PredictionEngine, добавив следующие строки кода в метод TestSinglePrediction():

var prediction = predictionFunction.Predict(taxiTripSample);

Функция Predict() создает прогноз по одному экземпляру данных.

Чтобы отобразить прогнозируемую плату за указанную поездку, добавьте в метод TestSinglePrediction следующий код:

Console.WriteLine($"**********************************************************************");
Console.WriteLine($"Predicted fare: {prediction.FareAmount:0.####}, actual fare: 15.5");
Console.WriteLine($"**********************************************************************");

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

Поздравляем! Вы успешно создали модель машинного обучения для прогнозирования платы за проезд в такси, оценили ее точность и использовали ее для получения прогнозов. Исходный код для этого руководства можно найти в репозитории GitHub dotnet/samples.

Следующие шаги

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

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

Переходите к следующему руководству.