Вопрос или проблема
У меня есть файлы с меткой (3 варианта) в первом столбце, за которыми следуют ~300 уже нормализованных столбцов (числа в диапазоне от 0 до 1).
Я знаю, что мне нужно:
- Загрузить мои данные в DataView
- Разделить данные на обучающую и тестовую выборки
- Построить конвейер (нужна помощь здесь)
- Обучить модель
- Сохранить и оценить ее
Класс данных называется “Frame”.
internal class Frame
{
[LoadColumn(0), ColumnName("Label")] public string Label { get; set; }
[LoadColumn(1, 302), VectorType(302)] public float[] Features { get; set; }
}
Класс результата называется “OutcomePrediction”.
internal class OutcomePrediction
{
[ColumnName("PredictedLabel")] public string PredictedLabel { get; set; }
}
Я не понимаю, как не нормализовать мои данные снова. У меня возникают трудности с определением, где использовать преобразования ключ/значение. Мое понимание на данный момент очень смутное.
Я достиг этого этапа. Я в основном пытался следовать документации:
https://learn.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/load-data-ml-net
https://learn.microsoft.com/en-us/dotnet/machine-learning/how-to-guides/train-machine-learning-model-ml-net
private const string DIR = @"C:\Users\<username>\Desktop\Sample";
private static readonly DirectoryInfo directory = new(DIR);
private static readonly MLContext mlContext = new(seed: 0);
static void Main(string[] args)
{
// §0 Загрузка из файлов (вероятно, нормально)
TextLoader textLoader = mlContext.Data.CreateTextLoader<Frame>(separatorChar: ',', hasHeader: true);
IDataView data = textLoader.Load(directory.GetFiles().Select(file => file.FullName).ToArray());
// §1 Разделение данных (вероятно, нормально)
TrainTestData dataSplit = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);
IDataView trainData = dataSplit.TrainSet;
IDataView testData = dataSplit.TestSet;
// §2 Моя попытка разбора данных (вероятно, ошибочно)
EstimatorChain<RegressionPredictionTransformer<LinearRegressionModelParameters>> dataPrepEstimator =
mlContext.Transforms.Concatenate("Features", "Features")
.Append(mlContext.Transforms.Conversion.MapValueToKey(outputColumnName: "LabelKey", inputColumnName: "Label"))
.Append(mlContext.Regression.Trainers.Sdca(labelColumnName: "LabelKey"));
// §3 Преобразователь, хотя мне на самом деле не нужно ничего преобразовывать, кроме "Label" (в ключ)
// выбрасывает System.ArgumentOutOfRangeException:
// 'Несоответствие схемы для столбца меток 'LabelKey': ожидалось Single, получен Key<UInt32> Arg_ParamName_Name'
TransformerChain<RegressionPredictionTransformer<LinearRegressionModelParameters>> dataPrepTransformer =
dataPrepEstimator.Fit(trainData);
// §4 Не уверен, что это необходимо
IDataView transformedTrainingData = dataPrepTransformer.Transform(trainData);
// §5 Тренер (вероятно, нормально)
SdcaRegressionTrainer sdcaEstimator = mlContext.Regression.Trainers.Sdca();
// §6 Обучение (возможно, нормально)
RegressionPredictionTransformer<LinearRegressionModelParameters> trainedModel =
sdcaEstimator.Fit(transformedTrainingData);
LinearRegressionModelParameters trainedModelParameters = trainedModel.Model;
// §7 Тестирование (вероятно, нормально)
IDataView transformedTestData = dataPrepTransformer.Transform(testData);
IDataView testDataPredictions = trainedModel.Transform(transformedTestData);
RegressionMetrics trainedModelMetrics = mlContext.Regression.Evaluate(testDataPredictions);
double rSquared = trainedModelMetrics.RSquared;
Console.WriteLine($"r²: {rSquared}");
}
Как вы можете видеть в §3, возникает ошибка. Скорее всего, это связано с ошибкой в §2, где я строю EstimatorChain. Ошибка касается преобразования ключ/значение атрибута “Label”.
Ответ или решение
ML.NET Классификация уже нормализованных данных
В вашей задаче требуется классификация данных с помощью ML.NET, где у вас имеется файл с метками и около 300 нормализованных столбцов. Давайте последовательно разберем каждый шаг вашего процесса и предоставим четкий подход к решению.
1. Загрузка данных в DataView
Вы правильно используете TextLoader
для загрузки данных. Ваш код в этой части выглядит корректно:
TextLoader textLoader = mlContext.Data.CreateTextLoader<Frame>(separatorChar: ',', hasHeader: true);
IDataView data = textLoader.Load(directory.GetFiles().Select(file => file.FullName).ToArray());
2. Разделение данных на обучающую и тестовую выборки
Разделение данных осуществляется корректно. Это важный шаг для оценки качества модели.
TrainTestData dataSplit = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);
IDataView trainData = dataSplit.TrainSet;
IDataView testData = dataSplit.TestSet;
3. Построение пайплайна
Здесь вам действительно нужно внести изменения. Поскольку ваши данные уже нормализованы, нет необходимости повторно их нормализовать. Вместо этого сосредоточьтесь на кодировании меток.
Вам нужно использовать MapValueToKey
только для вашей метки, но не для Features
. Ваша цепочка преобразований должна выглядеть следующим образом:
var dataPrepEstimator = mlContext.Transforms.Conversion.MapValueToKey("LabelKey", "Label")
.Append(mlContext.MultiClassClassification.Trainers.LightGbm(labelColumnName: "LabelKey", maximumNumberOfLeaves: 50));
В данном случае вы используете LightGbm
, так как это популярный метод для многоклассовой классификации, и он хорошо справляется с большими объемами данных. Не забудьте изменить обратный вызов модели на MultiClassPredictionTransformer
:
var model = dataPrepEstimator.Fit(trainData);
4. Обучение модели
Обучение модели осуществляется корректно, но нужно учесть, что теперь вы используете другой тренер:
IDataView transformedTrainingData = model.Transform(trainData);
5. Сохранение и оценка модели
Ваш код для тестирования и оценки также корректен, единственное, используйте соответствующие методы для многоклассовой классификации:
IDataView transformedTestData = model.Transform(testData);
var predictions = mlContext.Data.CreateEnumerable<OutcomePrediction>(transformedTestData, reuseRowObject: false).ToArray();
Для оценки модели используйте MulticlassClassificationMetrics
:
var metrics = mlContext.MulticlassClassification.Evaluate(predictions);
Console.WriteLine($"Accuracy: {metrics.Accuracy}");
Полный пример кода
Вот как будет выглядеть полный код с учётом предложенных изменений:
internal class Frame
{
[LoadColumn(0), ColumnName("Label")] public string Label { get; set; }
[LoadColumn(1, 302), VectorType(302)] public float[] Features { get; set; }
}
internal class OutcomePrediction
{
[ColumnName("PredictedLabel")] public string PredictedLabel { get; set; }
}
private const string DIR = @"C:\Users\<username>\Desktop\Sample";
private static readonly MLContext mlContext = new(seed: 0);
static void Main(string[] args)
{
var textLoader = mlContext.Data.CreateTextLoader<Frame>(separatorChar: ',', hasHeader: true);
IDataView data = textLoader.Load(Directory.GetFiles(DIR));
var dataSplit = mlContext.Data.TrainTestSplit(data, testFraction: 0.2);
IDataView trainData = dataSplit.TrainSet;
IDataView testData = dataSplit.TestSet;
var dataPrepEstimator = mlContext.Transforms.Conversion.MapValueToKey("LabelKey", "Label")
.Append(mlContext.MultiClassClassification.Trainers.LightGbm(labelColumnName: "LabelKey", maximumNumberOfLeaves: 50));
var model = dataPrepEstimator.Fit(trainData);
IDataView transformedTestData = model.Transform(testData);
var predictions = mlContext.Data.CreateEnumerable<OutcomePrediction>(transformedTestData, reuseRowObject: false).ToArray();
var metrics = mlContext.MulticlassClassification.Evaluate(predictions);
Console.WriteLine($"Accuracy: {metrics.Accuracy}");
}
Заключение
Следуйте вышеприведенным рекомендациям, и вы сможете успешно классифицировать ваши данные с использованием ML.NET. Если у вас возникнут дополнительные вопросы по работе с ML.NET или уточнения по коду, не стесняйтесь спрашивать.