Учебник. Прогнозирование цен с помощью регрессии с ML.NET
В этом учебнике показано, как создать модель регрессии с помощью ML.NET, чтобы прогнозировать цены, в частности плату за проезд в такси по Нью-Йорку.
В этом руководстве вы узнаете, как:
- Подготовка и анализ данных
- Загрузка и преобразование данных
- Выбор алгоритма обучения
- Обучение модели
- Оценка модели
- Использование модели для прогнозирования
Предварительные требования
- Visual Studio 2022 с установленной рабочей нагрузкой "Разработка классических приложений .NET".
Создание консольного приложение
Создайте консольное приложение C# с именем TaxiFarePrediction.
Выберите .NET 6 в качестве используемой платформы. Нажмите кнопку Создать .
Создайте каталог с именем Data в проекте для хранения набора данных и файлов модели.
Установите пакеты NuGet Microsoft.ML и Microsoft.ML.FastTree:
Примечание
В этом примере используется последняя стабильная версия пакетов NuGet, упомянутых выше, если не указано иное.
В обозревателе решений щелкните проект правой кнопкой мыши и выберите Управление пакетами NuGet. Выберите в качестве источника пакета "nuget.org", откройте вкладку Обзор, найдите Microsoft.ML, выберите пакет в списке и нажмите кнопку Установить. Нажмите кнопку ОК в диалоговом окне Предварительный просмотр изменений, а затем нажмите кнопку Принимаю в диалоговом окне Принятие условий лицензионного соглашения, если вы согласны с указанными условиями лицензионного соглашения для выбранных пакетов. Сделайте то же самое для пакета Microsoft.ML.FastTree в NuGet.
Подготовка и анализ данных
Скачайте наборы данных taxi-fare-train.csv и taxi-fare-test.csv, сохранив их в созданной на предыдущем шаге папке Data. Эти наборы данных используются для обучения модели машинного обучения и последующей оценки точности этой модели. Эти наборы данных взяты из наборов данных NYC TLC Taxi Trip.
В Обозреватель решений щелкните правой кнопкой мыши каждый из файлов *.csv и выберите Свойства. В разделе Дополнительно для параметра Копировать в выходной каталог установите значение Копировать более позднюю версию.
Откройте набор данных taxi-fare-train.csv и просмотрите заголовки столбцов в первой строке. Теперь изучите каждый из столбцов. Разберитесь, какие данные в них хранятся, и определите, какие столбцы являются признаками, а в каком содержится метка.
label
— это столбец, который необходимо спрогнозировать. Определены Features
входные данные, которые вы предоставляете модели для прогнозирования Label
.
Предоставленный набор данных содержит следующие столбцы:
- vendor_id: идентификатор поставщика услуг такси — это признак.
- rate_code: тип тарифа для поездки на такси — это признак.
- passenger_count: число пассажиров в поездке — это признак.
- trip_time_in_secs: время, затраченное на поездку. Вам требуется спрогнозировать плату за одну поездку до того, как поездка будет завершена. На этот момент вам неизвестна продолжительность поездки. Таким образом, продолжительность поездки не является признаком и соответствующий столбец следует исключить из модели.
- trip_distance: расстояние поездки — это признак.
- payment_type: метод оплаты (наличные или кредитная карта) — это признак.
- fare_amount: общая сумма, уплаченная за поездку на такси, — это метка.
Создание классов данных
Создайте классы для входных данных и прогнозов:
В обозревателе решений щелкните проект правой кнопкой мыши и выберите пункты Добавить>Новый элемент.
В диалоговом окне Добавление нового элемента выберите Класс и измените значение поля Имя на TaxiTrip.cs. Теперь нажмите кнопку Добавить.
Добавьте следующие директивы
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
, к которому применен атрибут Score
ColumnNameAttribute. Для задачи регрессии столбец Оценка содержит прогнозируемые значения метки.
Примечание
Используйте тип 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.
Следующие шаги
В этом руководстве вы узнали, как:
- Подготовка и анализ данных
- Создание конвейера обучения
- Загрузка и преобразование данных
- Выбор алгоритма обучения
- Обучение модели
- Оценка модели
- Использование модели для прогнозирования
Переходите к следующему руководству.