diff --git a/InstallerSamples/MachineLearning/01_GettingStarted/03_FirstClustering_DataFrame.pas b/InstallerSamples/MachineLearning/01_GettingStarted/03_FirstClustering_DataFrame.pas index a0f43ada6..d7267e078 100644 --- a/InstallerSamples/MachineLearning/01_GettingStarted/03_FirstClustering_DataFrame.pas +++ b/InstallerSamples/MachineLearning/01_GettingStarted/03_FirstClustering_DataFrame.pas @@ -43,10 +43,10 @@ begin var labels := model.PredictLabels(X); // Получаем названия городов - var cities := df.GetStrColumn('city'); + var cities := df.Str('city'); // Получаем численность населения городов - var populations := df.GetFloatColumn('population'); + var populations := df.Float('population'); // Выводим, сколько кластеров нашёл алгоритм Println($'Число кластеров: {model.ClustersCount}'); diff --git a/InstallerSamples/MachineLearning/03_Preprocessing/03_Imputer_OrdinalEncoder.pas b/InstallerSamples/MachineLearning/03_Preprocessing/03_Imputer_OrdinalEncoder.pas index 86e34436c..14693f64b 100644 --- a/InstallerSamples/MachineLearning/03_Preprocessing/03_Imputer_OrdinalEncoder.pas +++ b/InstallerSamples/MachineLearning/03_Preprocessing/03_Imputer_OrdinalEncoder.pas @@ -1,4 +1,4 @@ -// Пример табличной предобработки: +// Пример табличной предобработки: // сначала заполняем пропуски в числовом столбце, // затем кодируем категориальный столбец числами. uses MLABC; @@ -18,13 +18,13 @@ city,population,region Println; // Заполняем пропуск в числовом столбце средним значением - var imputer := new Imputer(['population']); - df := imputer.FitTransform(df); + var imp := new Imputer(['population']); + df := imp.FitTransform(df); // Кодируем названия регионов числами 0, 1, 2, ... var encoder := new OrdinalEncoder('region'); df := encoder.FitTransform(df); - Println('После Imputer и LabelEncoder:'); + Println('После Imputer и OrdinalEncoder:'); df.Print; end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/Iris/08_ConfusionMatrix.pas b/InstallerSamples/MachineLearning/08_Datasets/Iris/08_ConfusionMatrix.pas new file mode 100644 index 000000000..1e3bbd9e7 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/Iris/08_ConfusionMatrix.pas @@ -0,0 +1,33 @@ +// Confusion matrix для классификации датасета Iris. + +uses MLABC, PlotML; + +begin + var ds := Datasets.Iris; + + var (train, test) := ds.StratifiedTrainTestSplit(0.2, 42); + + var Xtrain := train.Data.ToMatrix(train.Features); + var Xtest := test.Data.ToMatrix(test.Features); + + var scaler := new StandardScaler; + Xtrain := scaler.FitTransform(Xtrain); + Xtest := scaler.Transform(Xtest); + + var encoder := new LabelEncoder; + var ytrain := encoder.FitTransform(train); + var ytest := encoder.Transform(test); + + var model := new LogisticRegression; + model.Fit(Xtrain, ytrain); + + var pred := model.Predict(Xtest); + + var cm := new ConfusionMatrix(ytest, pred); + var acc := Metrics.Accuracy(ytest, pred); + + Plot.ConfusionMatrix(cm, encoder.Classes{, normalize := MatrixNormalization.Rows}); + Plot.Title := $'Iris: LogisticRegression, accuracy = {acc:F3}'; + Plot.XLabel := 'Предсказанный класс'; + Plot.YLabel := 'Истинный класс'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/Iris/08_ConfusionMatrix1.pas b/InstallerSamples/MachineLearning/08_Datasets/Iris/08_ConfusionMatrix1.pas new file mode 100644 index 000000000..e29732f81 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/Iris/08_ConfusionMatrix1.pas @@ -0,0 +1,29 @@ +// Confusion matrix для Iris с использованием DataPipeline. +// Рекомендуемый стиль - он проще и явно показывает намерения + +uses MLABC, PlotML; + +begin + var ds := Datasets.Iris; + var (train, test) := ds.StratifiedTrainTestSplit(0.2, 42); + + var pipe := DataPipeline.Build( + TaskKind.tkClassification, + ds.Target, + ds.Features, + new StandardScaler, + new LogisticRegression + ); + + pipe.Fit(train.Data); + + var pred := pipe.Predict(test.Data); + var ytest := pipe.GetEncodedLabels(test.Data); + var cm := new ConfusionMatrix(ytest, pred); + var acc := Metrics.Accuracy(ytest, pred); + + Plot.ConfusionMatrix(cm, pipe.GetClassLabels{, normalize := MatrixNormalization.Rows}); + Plot.Title := $'Iris: DataPipeline, accuracy = {acc:F3}'; + Plot.XLabel := 'Предсказанный класс'; + Plot.YLabel := 'Истинный класс'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/02_PriceHistogram.pas b/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/02_PriceHistogram.pas index 53ba5c3f3..f3171fe25 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/02_PriceHistogram.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/02_PriceHistogram.pas @@ -8,6 +8,6 @@ begin Plot.Hist(price, bins := 40); Plot.Title := 'Распределение цен на квартиры в Москве'; - Plot.XLabel('Цена (руб)'); - Plot.YLabel('Количество'); -end. \ No newline at end of file + Plot.XLabel := 'Цена (руб)'; + Plot.YLabel := 'Количество'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/03_AreaPriceScatter.pas b/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/03_AreaPriceScatter.pas index 9b6f8788d..2f62e66f8 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/03_AreaPriceScatter.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/03_AreaPriceScatter.pas @@ -9,7 +9,7 @@ begin Plot.Points(area, price, size := 3); - Plot.XLabel('Площадь (м²)'); - Plot.YLabel('Цена (руб)'); + Plot.XLabel := 'Площадь (м²)'; + Plot.YLabel := 'Цена (руб)'; Plot.Title := 'Цена квартиры vs площадь'; -end. \ No newline at end of file +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/04_MetroPriceScatter.pas b/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/04_MetroPriceScatter.pas index 8618eaa1c..bd9ae6baf 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/04_MetroPriceScatter.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/MoscowHousing/04_MetroPriceScatter.pas @@ -9,7 +9,7 @@ begin Plot.Points(metro.Data.ConvertAll(x -> x*1.0), price.Data, size := 3); - Plot.XLabel('Минуты до метро'); - Plot.YLabel('Цена (руб)'); + Plot.XLabel := 'Минуты до метро'; + Plot.YLabel := 'Цена (руб)'; Plot.Title := 'Зависимость цены квартиры от расстояния до метро'; -end. \ No newline at end of file +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/RussianCities/01_Info.pas b/InstallerSamples/MachineLearning/08_Datasets/RussianCities/01_Info.pas index 8f5411a75..4cbb85b65 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/RussianCities/01_Info.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/RussianCities/01_Info.pas @@ -8,5 +8,7 @@ begin df.Schema.Println; df.PrintInfo; Println; + ds.Info; + Println; df.Print; end. \ No newline at end of file diff --git a/InstallerSamples/MachineLearning/08_Datasets/RussianCities/02_PointsByClaster.pas b/InstallerSamples/MachineLearning/08_Datasets/RussianCities/02_PointsByClaster.pas index 5907f7f14..e3248cd1b 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/RussianCities/02_PointsByClaster.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/RussianCities/02_PointsByClaster.pas @@ -32,7 +32,7 @@ begin marker := MarkerType.Circle ); - Plot.XLabel('Долгота'); - Plot.YLabel('Широта'); + Plot.XLabel := 'Долгота'; + Plot.YLabel := 'Широта'; Plot.Title := 'Кластеры городов России'; -end. \ No newline at end of file +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/RussianCities/04_Clusters_DBScan.pas b/InstallerSamples/MachineLearning/08_Datasets/RussianCities/04_Clusters_DBScan.pas index f73b6e462..e208a2b6a 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/RussianCities/04_Clusters_DBScan.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/RussianCities/04_Clusters_DBScan.pas @@ -76,9 +76,7 @@ begin var labels := model.PredictLabels(X); - var counts := new Dictionary(); - foreach var l in labels do - counts[l] := counts.Get(l, 0) + 1; + var counts := labels.EachCount; var n := df.RowCount; diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/02_AgeFareScatter.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/02_AgeFareScatter.pas index 96cc90765..801f97382 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/02_AgeFareScatter.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/02_AgeFareScatter.pas @@ -7,10 +7,10 @@ begin var age := df.ToVector('Возраст'); var fare := df.ToVector('ЦенаБилета'); - var y := df.GetIntColumn('Выжил'); + var y := df.Int('Выжил'); Plot.Points(age, fare, y, size := 5); - Plot.XLabel('Возраст'); - Plot.YLabel('Цена билета'); + Plot.XLabel := 'Возраст'; + Plot.YLabel := 'Цена билета'; Plot.Title := 'Титаник: возраст, цена билета и выживание'; end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/04_AgeHistogramBySurvival.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/04_AgeHistogramBySurvival.pas index 73d2a0e54..be79626cd 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/04_AgeHistogramBySurvival.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/04_AgeHistogramBySurvival.pas @@ -18,6 +18,6 @@ begin Plot.HistMany([diedAges,survivedAges], bins := 20, colors := [Colors.IndianRed, Colors.SteelBlue], alpha := 0.45, legend := ['не выжили','выжили']); Plot.Title := 'Титаник: возраст выживших (голубой) и невыживших (красный)'; - Plot.XLabel('Возраст'); - Plot.YLabel('Число пассажиров'); + Plot.XLabel := 'Возраст'; + Plot.YLabel := 'Число пассажиров'; end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/06_ModelComparison.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/06_ModelComparison.pas index 3c434dddd..f1656991f 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/06_ModelComparison.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/06_ModelComparison.pas @@ -4,45 +4,39 @@ begin var ds := Datasets.TitanicRu; var df := ds.Data.Drop(['Id', 'Имя']); - // Заполняем пропуски. - var ageImputer := new Imputer(['Возраст']); - df := ageImputer.FitTransform(df); - - var portImputer := new Imputer('Саутгемптон', ['ПортПосадки']); - df := portImputer.FitTransform(df); - - // Кодируем категориальные признаки числами. - var sexEncoder := new OrdinalEncoder('Пол'); - df := sexEncoder.FitTransform(df); - - var portEncoder := new OrdinalEncoder('ПортПосадки'); - df := portEncoder.FitTransform(df); - var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки']; - var X := df.ToMatrix(features); - var y := df.GetIntColumn('Выжил'); + var target := 'Выжил'; - var (Xtrain, Xtest, ytrain, ytest) := Validation.TrainTestSplit(X, y, testRatio := 0.2, seed := 42); + var (trainDf, testDf) := + df.StratifiedTrainTestSplit(ds.Target, testRatio := 0.2, seed := 42); - var scaler := new StandardScaler; - scaler.Fit(Xtrain); - var XtrainScaled := scaler.Transform(Xtrain); - var XtestScaled := scaler.Transform(Xtest); + var prep := + DataPipeline.BuildPreprocessing( + TaskKind.tkClassification, + target, + features, + new Imputer(['Возраст']), + new Imputer('Саутгемптон', ['ПортПосадки']), + new OneHotEncoder('Пол'), + new OneHotEncoder('ПортПосадки'), + new StandardScaler + ); - var lr := new LogisticRegression(learningRate := 0.01, epochs := 2000); - lr.Fit(XtrainScaled, ytrain); - var predLR := lr.Predict(XtestScaled); + var pipeLR := prep.WithModel(new LogisticRegression(learningRate := 0.01, epochs := 2000)); + pipeLR.Fit(trainDf); + var predLR := pipeLR.Predict(testDf); + var y := pipeLR.GetEncodedLabels(testDf); - var tree := new DecisionTreeClassifier(maxDepth := 5, minSamplesLeaf := 3, minSamplesSplit := 6); - tree.Fit(Xtrain, ytrain); - var predTree := tree.Predict(Xtest); + var pipeTree := prep.WithModel(new DecisionTreeClassifier(maxDepth := 5, minSamplesLeaf := 3, minSamplesSplit := 6)); + pipeTree.Fit(trainDf); + var predTree := pipeTree.Predict(testDf); - var forest := new RandomForestClassifier(nTrees := 100, maxDepth := 6, minSamplesLeaf := 3, minSamplesSplit := 6); - forest.Fit(Xtrain, ytrain); - var predForest := forest.Predict(Xtest); + var pipeForest := prep.WithModel(new RandomForestClassifier(nTrees := 100, maxDepth := 6, minSamplesLeaf := 3, minSamplesSplit := 6)); + pipeForest.Fit(trainDf); + var predForest := pipeForest.Predict(testDf); Println('Сравнение моделей на TitanicRu'); - Println($'LogisticRegression: Accuracy = {Metrics.Accuracy(ytest, predLR):F3}'); - Println($'DecisionTreeClassifier: Accuracy = {Metrics.Accuracy(ytest, predTree):F3}'); - Println($'RandomForestClassifier: Accuracy = {Metrics.Accuracy(ytest, predForest):F3}'); + Println($'LogisticRegression: Accuracy = {Metrics.Accuracy(y, predLR):F3}'); + Println($'DecisionTreeClassifier: Accuracy = {Metrics.Accuracy(y, predTree):F3}'); + Println($'RandomForestClassifier: Accuracy = {Metrics.Accuracy(y, predForest):F3}'); end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/08_CrossValidation.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/08_CrossValidation.pas index 99aa63799..98d10f105 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/08_CrossValidation.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/08_CrossValidation.pas @@ -32,7 +32,7 @@ begin var testAccuracy := Metrics.Accuracy(y, pred); var total := 0.0; - var folds := Validation.StratifiedKFold(df.GetIntColumn('Выжил'), 5, seed := 1); + var folds := Validation.StratifiedKFold(df.Int('Выжил'), 5, seed := 1); var foldsCount := 0; foreach var (trainIdx, testIdx) in folds do diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/09_FeatureImportance.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/09_FeatureImportance.pas index f9a04e8db..22c149efc 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/09_FeatureImportance.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/09_FeatureImportance.pas @@ -18,7 +18,7 @@ begin var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки']; var X := df.ToMatrix(features); - var y := df.GetIntColumn('Выжил'); + var y := df.Int('Выжил'); var model := new RandomForestClassifier(nTrees := 100, maxDepth := 6, minSamplesLeaf := 3, minSamplesSplit := 6, seed := 42); model.Fit(X, y); diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/10_SurvivalBySexAndClass.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/10_SurvivalBySexAndClass.pas index 6f6061c9f..6875196da 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/10_SurvivalBySexAndClass.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/10_SurvivalBySexAndClass.pas @@ -2,7 +2,7 @@ uses MLABC; function SurvivalRate(df: DataFrame): real; begin - Result := df.GetIntColumn('Выжил').Average; + Result := df.Int('Выжил').Average; end; begin diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/11_ConfusionMatrix.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/11_ConfusionMatrix.pas new file mode 100644 index 000000000..8cab772a6 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/11_ConfusionMatrix.pas @@ -0,0 +1,35 @@ +uses MLABC, PlotML; + +begin + var ds := Datasets.TitanicRu; + var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки']; + var df := ds.Data.Drop(['Id', 'Имя']); + + var (trainDf, testDf) := + df.StratifiedTrainTestSplit(ds.Target, testRatio := 0.2, seed := 42); + + var pipe := + DataPipeline.Build( + TaskKind.tkClassification, + ds.Target, + features, + new Imputer(['Возраст']), + new Imputer('Саутгемптон', ['ПортПосадки']), + new OneHotEncoder('Пол'), + new OneHotEncoder('ПортПосадки'), + new StandardScaler, + new LogisticRegression(learningRate := 0.1, epochs := 2000) + ); + + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := pipe.GetEncodedLabels(testDf); + var cm := new ConfusionMatrix(y, pred); + var acc := Metrics.Accuracy(y, pred); + + Plot.ConfusionMatrix(cm, ['не выжил', 'выжил']); + Plot.Title := $'TitanicRu: DataPipeline, accuracy = {acc:F3}'; + Plot.XLabel := 'Предсказанный класс'; + Plot.YLabel := 'Истинный класс'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/12_PrecisionRecallF1.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/12_PrecisionRecallF1.pas new file mode 100644 index 000000000..e17648fee --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/12_PrecisionRecallF1.pas @@ -0,0 +1,39 @@ +uses MLABC; + +begin + var ds := Datasets.TitanicRu; + var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки']; + var df := ds.Data.Drop(['Id', 'Имя']); + + var (trainDf, testDf) := + df.StratifiedTrainTestSplit(ds.Target, testRatio := 0.2, seed := 42); + + var pipe := + DataPipeline.Build( + TaskKind.tkClassification, + ds.Target, + features, + new Imputer(['Возраст']), + new Imputer('Саутгемптон', ['ПортПосадки']), + new OneHotEncoder('Пол'), + new OneHotEncoder('ПортПосадки'), + new StandardScaler, + new RandomForestClassifier(nTrees := 100, maxDepth := 6, minSamplesLeaf := 3, minSamplesSplit := 6) + ); + + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := pipe.GetEncodedLabels(testDf); + + Println('TitanicRu: Precision, Recall и F1'); + Println; + Println('Precision — какая доля пассажиров, предсказанных как "выжил", действительно выжила.'); + Println('Recall — какую долю реально выживших пассажиров модель нашла.'); + Println('F1 — общий баланс между Precision и Recall.'); + Println; + Println($'Accuracy = {Metrics.Accuracy(y, pred):F3}'); + Println($'Precision = {Metrics.Precision(y, pred):F3}'); + Println($'Recall = {Metrics.Recall(y, pred):F3}'); + Println($'F1 = {Metrics.F1(y, pred):F3}'); +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/13_MisclassifiedPassengers.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/13_MisclassifiedPassengers.pas new file mode 100644 index 000000000..8ba41850d --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/13_MisclassifiedPassengers.pas @@ -0,0 +1,65 @@ +uses MLABC; + +function LabelText(v: integer): string; +begin + Result := if v = 1 then 'выжил' else 'не выжил'; +end; + +begin + var ds := Datasets.TitanicRu; + var features := ['Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки']; + + var fullDf := ds.Data; + var (trainFull, testFull) := + fullDf.StratifiedTrainTestSplit(ds.Target, testRatio := 0.2, seed := 42); + + var trainDf := trainFull.Drop(['Id', 'Имя']); + var testDf := testFull.Drop(['Id', 'Имя']); + + var pipe := + DataPipeline.Build( + TaskKind.tkClassification, + ds.Target, + features, + new Imputer(['Возраст']), + new Imputer('Саутгемптон', ['ПортПосадки']), + new OneHotEncoder('Пол'), + new OneHotEncoder('ПортПосадки'), + new StandardScaler, + new RandomForestClassifier(nTrees := 100, maxDepth := 6, minSamplesLeaf := 3, minSamplesSplit := 6) + ); + + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := pipe.GetEncodedLabels(testDf); + var predInt := pred.ToIntArray; + var yInt := y.ToIntArray; + var cur := testFull.GetCursor; + + Println('TitanicRu: пассажиры, классифицированные неправильно'); + Println; + + var shown := 0; + while cur.MoveNext do + begin + var i := cur.Position; + if yInt[i] <> predInt[i] then + begin + var id := cur.Int('Id'); + var name := cur.Str('Имя'); + var sex := cur.Str('Пол'); + var pclass := cur.Int('Класс'); + var age := cur.Float('Возраст'); + var fare := cur.Float('ЦенаБилета'); + + Println($'Id={id}, {name}'); + Println($' Пол={sex}, класс={pclass}, возраст={age:F1}, билет={fare:F1}'); + Println($' Истина: {LabelText(yInt[i])}, прогноз: {LabelText(predInt[i])}'); + Println; + + shown += 1; + if shown >= 10 then break; + end; + end; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/14_FeatureAblation.pas b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/14_FeatureAblation.pas new file mode 100644 index 000000000..630ee4cc6 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/TitanicRu/14_FeatureAblation.pas @@ -0,0 +1,52 @@ +uses MLABC; + +function BuildPipe(features: array of string): DataPipeline; +begin + Result := + DataPipeline.Build( + TaskKind.tkClassification, + 'Выжил', + features, + new Imputer(['Возраст']), + new Imputer('Саутгемптон', ['ПортПосадки']), + new OneHotEncoder('Пол'), + new OneHotEncoder('ПортПосадки'), + new StandardScaler, + new RandomForestClassifier(nTrees := 100, maxDepth := 6, minSamplesLeaf := 3, minSamplesSplit := 6) + ); +end; + +function TestAccuracy(df: DataFrame; features: array of string): real; +begin + var (trainDf, testDf) := + df.StratifiedTrainTestSplit('Выжил', testRatio := 0.2, seed := 42); + + var pipe := BuildPipe(features); + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := pipe.GetEncodedLabels(testDf); + Result := Metrics.Accuracy(y, pred); +end; + +begin + var ds := Datasets.TitanicRu; + var df := ds.Data.Drop(['Id', 'Имя']); + + var allFeatures := + SetOf('Класс', 'Пол', 'Возраст', 'БратьяИСупруги', 'РодителиИДети', 'ЦенаБилета', 'ПортПосадки'); + var noSex := (allFeatures - ['Пол']).ToArray; + var noClass := (allFeatures - ['Класс']).ToArray; + + var accAll := TestAccuracy(df, allFeatures.ToArray); + var accNoSex := TestAccuracy(df, noSex); + var accNoClass := TestAccuracy(df, noClass); + + Println('TitanicRu: влияние признаков на качество модели'); + Println; + Println('Идея примера: убираем один важный признак и смотрим, насколько хуже становится классификация.'); + Println; + Println($'Все признаки: Accuracy = {accAll:F3}'); + Println($'Без признака "Пол": Accuracy = {accNoSex:F3}'); + Println($'Без признака "Класс": Accuracy = {accNoClass:F3}'); +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/01_Info.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/01_Info.pas new file mode 100644 index 000000000..1ca60462a --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/01_Info.pas @@ -0,0 +1,11 @@ +uses MLABC; + +begin + Datasets.Language := 'ru'; + + var ds := Datasets.UsedCarsPrice; + ds.Info; + Println; + + ds.Data.Print(20); +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/02_PriceHistogram.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/02_PriceHistogram.pas new file mode 100644 index 000000000..eebbc8219 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/02_PriceHistogram.pas @@ -0,0 +1,12 @@ +uses MLABC, PlotML; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data.Filter(r -> r.Float('price_k_rub') <= 6000); + var price := df.ToVector('price_k_rub'); + + Plot.Hist(price, bins := 30); + Plot.XLabel := 'Цена, тыс. руб.'; + Plot.YLabel := 'Число автомобилей'; + Plot.Title := 'UsedCars: распределение цен до 6000 тыс. руб.'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/03_MileagePriceScatter.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/03_MileagePriceScatter.pas new file mode 100644 index 000000000..4f3047dcb --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/03_MileagePriceScatter.pas @@ -0,0 +1,14 @@ +uses MLABC, PlotML; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data.Filter(r -> r.Float('price_k_rub') <= 6000); + + var mileage := df.ToVector('mileage_km'); + var price := df.ToVector('price_k_rub'); + + Plot.Points(mileage, price, size := 4); + Plot.XLabel := 'Пробег, км'; + Plot.YLabel := 'Цена, тыс. руб.'; + Plot.Title := 'UsedCars: цена и пробег'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/04_PairPlotByPrice.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/04_PairPlotByPrice.pas new file mode 100644 index 000000000..40dc35bf0 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/04_PairPlotByPrice.pas @@ -0,0 +1,17 @@ +uses MLABC, PlotML; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data.Filter(r -> r.Float('price_k_rub') <= 6000); + + var names := Arr('year', 'mileage_1000km', 'l_100km', 'engineSize'); + var X := df.ToMatrix(Arr('year', 'mileage_km', 'l_100km', 'engineSize')); + + for var i := 0 to X.RowCount - 1 do + X[i, 1] := X[i, 1] / 1000.0; + + var price := df.ToVector('price_k_rub'); + + Plot.PairPlot(X.Data, price.Data, names, bins := 6, maxPoints := 2000); + Plot.Title := 'UsedCars: признаки, окрашенные по цене'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/05_YearPriceScatter.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/05_YearPriceScatter.pas new file mode 100644 index 000000000..128bf0e54 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/05_YearPriceScatter.pas @@ -0,0 +1,15 @@ +uses MLABC, PlotML; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data.Filter(r -> r.Float('price_k_rub') <= 6000); + + var year := df.ToVector('year'); + var price := df.ToVector('price_k_rub'); + var make := df.EncodeLabels('Make'); + + Plot.Points(year, price, make, size := 4); + Plot.XLabel := 'Год выпуска'; + Plot.YLabel := 'Цена, тыс. руб.'; + Plot.Title := 'UsedCars: цена по годам и маркам'; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/06_RegressionPipeline.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/06_RegressionPipeline.pas new file mode 100644 index 000000000..40a53c00b --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/06_RegressionPipeline.pas @@ -0,0 +1,45 @@ +uses MLABC; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data; + + var features := [ + 'model', + 'year', + 'transmission', + 'mileage_km', + 'fuelType', + 'l_100km', + 'engineSize', + 'Make' + ]; + + var target := 'price_k_rub'; + + var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42); + + var pipe := + DataPipeline.Build( + TaskKind.tkRegression, + target, + features, + new OrdinalEncoder('model'), + new OneHotEncoder('transmission'), + new OneHotEncoder('fuelType'), + new OneHotEncoder('Make'), + new StandardScaler, + new DecisionTreeRegressor(10) + ); + + + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := testDf.ToVector(target); + + Println('Прогнозирование цены автомобилей (DecisionTreeRegressor)'); + Println($'MAE = {Metrics.MAE(y, pred):F0}'); + Println($'RMSE = {Metrics.RMSE(y, pred):F0}'); + Println($'R² = {Metrics.R2(y, pred):F3}'); +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/07_ModelComparison.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/07_ModelComparison.pas new file mode 100644 index 000000000..8209f136b --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/07_ModelComparison.pas @@ -0,0 +1,48 @@ +uses MLABC; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data; + + var features := [ + 'model', + 'year', + 'transmission', + 'mileage_km', + 'fuelType', + 'l_100km', + 'engineSize', + 'Make' + ]; + + var target := 'price_k_rub'; + var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42); + + var prep := + DataPipeline.BuildPreprocessing( + TaskKind.tkRegression, + target, + features, + new OrdinalEncoder('model'), + new OneHotEncoder('transmission'), + new OneHotEncoder('fuelType'), + new OneHotEncoder('Make'), + new StandardScaler + ); + + var pipeTree := prep.WithModel(new DecisionTreeRegressor(12)); + + pipeTree.Fit(trainDf); + var predTree := pipeTree.Predict(testDf); + + var pipeForest := prep.WithModel(new RandomForestRegressor(12, 50)); + + pipeForest.Fit(trainDf); + var predForest := pipeForest.Predict(testDf); + + var y := testDf.ToVector(target); + + Println('Сравнение моделей регрессии'); + Println($'DecisionTreeRegressor: R² = {Metrics.R2(y, predTree):F3}'); + Println($'RandomForestRegressor: R² = {Metrics.R2(y, predForest):F3}'); +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/08_CrossValidation.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/08_CrossValidation.pas new file mode 100644 index 000000000..1408bdd5f --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/08_CrossValidation.pas @@ -0,0 +1,64 @@ +uses MLABC; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data; + + var features := [ + 'model', + 'year', + 'transmission', + 'mileage_km', + 'fuelType', + 'l_100km', + 'engineSize', + 'Make' + ]; + + var target := 'price_k_rub'; + var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42); + + var prep := + DataPipeline.BuildPreprocessing( + TaskKind.tkRegression, + target, + features, + new OrdinalEncoder('model'), + new OneHotEncoder('transmission'), + new OneHotEncoder('fuelType'), + new OneHotEncoder('Make'), + new StandardScaler + ); + + var pipe := prep.WithModel(new RandomForestRegressor(12, 50)); + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := testDf.ToVector(target); + var testR2 := Metrics.R2(y, pred); + + var total := 0.0; + var folds := Validation.KFold(df.RowCount, 5, seed := 1); + var foldsCount := 0; + + foreach var (trainIdx, testIdx) in folds do + begin + var foldTrain := df.TakeRows(trainIdx); + var foldTest := df.TakeRows(testIdx); + + var foldPipe := prep.WithModel(new RandomForestRegressor(12, 50)); + foldPipe.Fit(foldTrain); + + var foldPred := foldPipe.Predict(foldTest); + var foldY := foldTest.ToVector(target); + + total += Metrics.R2(foldY, foldPred); + foldsCount += 1; + end; + + var cvR2 := total / foldsCount; + + Println('Оценка RandomForestRegressor двумя способами'); + Println($'R² на тестовой выборке: {testR2:F3}'); + Println($'Средний R² по кросс-валидации: {cvR2:F3}'); +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/UsedCars/09_Residuals.pas b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/09_Residuals.pas new file mode 100644 index 000000000..65a6d9562 --- /dev/null +++ b/InstallerSamples/MachineLearning/08_Datasets/UsedCars/09_Residuals.pas @@ -0,0 +1,71 @@ +uses MLABC; + +begin + var ds := Datasets.UsedCarsPrice; + var df := ds.Data; + + // В этом примере смотрим не только на R², + // но и на конкретные ошибки модели для отдельных автомобилей. + // Остаток = истинная цена - предсказанная цена. + // Если остаток положительный, модель занизила цену. + // Если отрицательный, модель завысила цену. + + var features := [ + 'model', + 'year', + 'transmission', + 'mileage_km', + 'fuelType', + 'l_100km', + 'engineSize', + 'Make' + ]; + + var target := 'price_k_rub'; + + var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42); + + var pipe := + DataPipeline.Build( + TaskKind.tkRegression, + target, + features, + new OrdinalEncoder('model'), + new OneHotEncoder('transmission'), + new OneHotEncoder('fuelType'), + new OneHotEncoder('Make'), + new StandardScaler, + new RandomForestRegressor(12, 50) + ); + + pipe.Fit(trainDf); + + var pred := pipe.Predict(testDf); + var y := testDf.ToVector(target); + + var models := testDf.Str('model'); + var makes := testDf.Str('Make'); + var years := testDf.Int('year'); + + var idx := Arr(0..pred.Length - 1); + idx := idx.OrderByDescending(i -> Abs(y[i] - pred[i])).ToArray; + + Println('UsedCars: самые большие ошибки модели'); + Println; + Println('Остаток = истинная цена - предсказанная цена.'); + Println('Положительный остаток означает, что модель занизила цену.'); + Println('Отрицательный остаток означает, что модель завысила цену.'); + Println; + + for var k := 0 to Min(9, idx.Length - 1) do + begin + var i := idx[k]; + var residual := y[i] - pred[i]; + + Println($'{makes[i]} {models[i]}, {years[i]}'); + Println($' Истинная цена: {y[i]:F0} тыс. руб.'); + Println($' Прогноз: {pred[i]:F0} тыс. руб.'); + Println($' Остаток: {residual:F0} тыс. руб.'); + Println; + end; +end. diff --git a/InstallerSamples/MachineLearning/08_Datasets/_Synthetic/03_MakeRegression.pas b/InstallerSamples/MachineLearning/08_Datasets/_Synthetic/03_MakeRegression.pas index 86ecce750..4c1bc95f1 100644 --- a/InstallerSamples/MachineLearning/08_Datasets/_Synthetic/03_MakeRegression.pas +++ b/InstallerSamples/MachineLearning/08_Datasets/_Synthetic/03_MakeRegression.pas @@ -13,8 +13,8 @@ begin // --- визуализация Plot.Title('MakeRegression синтетический датасет'); - Plot.XLabel('признак'); - Plot.YLabel('целевая переменная'); + Plot.XLabel := 'признак'; + Plot.YLabel := 'целевая переменная'; Plot.Points(X.Col(0), y.ToArray, size := 3); -end. \ No newline at end of file +end. diff --git a/InstallerSamples/MachineLearning/09_Visualization/01_Scatter_Iris.pas b/InstallerSamples/MachineLearning/09_Visualization/01_Scatter_Iris.pas index 76e41786d..3c165e33d 100644 --- a/InstallerSamples/MachineLearning/09_Visualization/01_Scatter_Iris.pas +++ b/InstallerSamples/MachineLearning/09_Visualization/01_Scatter_Iris.pas @@ -1,4 +1,4 @@ -// В этом примере строится scatter plot для двух признаков датасета Iris. +// В этом примере строится scatter plot для двух признаков датасета Iris. // // По оси X откладывается длина чашелистика, // по оси Y — ширина чашелистика. diff --git a/InstallerSamples/MachineLearning/09_Visualization/08_PCA_Iris.pas b/InstallerSamples/MachineLearning/09_Visualization/08_PCA_Iris.pas new file mode 100644 index 000000000..587422883 --- /dev/null +++ b/InstallerSamples/MachineLearning/09_Visualization/08_PCA_Iris.pas @@ -0,0 +1,20 @@ +uses MLABC, PlotML; + +begin + var ds := Datasets.Iris; + var X := ds.Data.ToMatrix(ds.Features); + var y := ds.Data.EncodeLabels(ds.Target); + + var scaler := new StandardScaler; + scaler.Fit(X); + X := scaler.Transform(X); + + var pca := new PCATransformer(2); + pca.Fit(X); + var X2 := pca.Transform(X); + + Plot.Points(X2.Col(0), X2.Col(1), y, size := 5); + Plot.XLabel := 'Первая главная компонента'; + Plot.YLabel := 'Вторая главная компонента'; + Plot.Title := 'Iris: проекция на две главные компоненты'; +end. diff --git a/InstallerSamples/MachineLearning/09_Visualization/09_Bars.pas b/InstallerSamples/MachineLearning/09_Visualization/09_Bars.pas new file mode 100644 index 000000000..79840a9c8 --- /dev/null +++ b/InstallerSamples/MachineLearning/09_Visualization/09_Bars.pas @@ -0,0 +1,25 @@ +uses PlotML; + +begin + var fig := Plot.Grid(2, 2); + + var labels := ['sedan', 'suv', 'hatchback', 'wagon']; + + fig[0, 0].Bar(labels, [120.0, 95.0, 70.0, 40.0]); + fig[0, 0].Title := 'Cars A'; + fig[0, 0].XLabel := 'count'; + + fig[0, 1].Bar(labels, [80.0, 110.0, 55.0, 30.0]); + fig[0, 1].Title := 'Cars B'; + fig[0, 1].XLabel := 'count'; + + fig[1, 0].Bar(labels, [45.0, 65.0, 100.0, 25.0]); + fig[1, 0].Title := 'Cars C'; + fig[1, 0].XLabel := 'count'; + + fig[1, 1].Bar(labels, [30.0, 50.0, 75.0, 90.0]); + fig[1, 1].Title := 'Cars D'; + fig[1, 1].XLabel := 'count'; + + Plot.Title := 'Bar plots 2x2'; +end. diff --git a/InstallerSamples/MachineLearning/09_Visualization/10_ConfusionMatrix.pas b/InstallerSamples/MachineLearning/09_Visualization/10_ConfusionMatrix.pas new file mode 100644 index 000000000..42e55c03b --- /dev/null +++ b/InstallerSamples/MachineLearning/09_Visualization/10_ConfusionMatrix.pas @@ -0,0 +1,28 @@ +// В этом примере строится confusion matrix для классификации Iris. + +uses MLABC, PlotML; + +begin + var ds := Datasets.Iris; + + var (train, test) := ds.StratifiedTrainTestSplit(0.2, 42); + + var Xtrain := train.Data.ToMatrix(train.Features); + var Xtest := test.Data.ToMatrix(test.Features); + + var encoder := new LabelEncoder; + var ytrain := encoder.FitTransform(train); + var ytest := encoder.Transform(test); + + var model := new LogisticRegression; + model.Fit(Xtrain, ytrain); + + var pred := model.Predict(Xtest); + var cm := new ConfusionMatrix(ytest, pred); + var acc := Metrics.Accuracy(ytest, pred); + + Plot.ConfusionMatrix(cm, encoder.Classes, normalize := MatrixNormalization.Rows); + Plot.Title := $'Iris: LogisticRegression, accuracy = {acc:F3}'; + Plot.XLabel := 'Predicted'; + Plot.YLabel := 'Actual'; +end. diff --git a/InstallerSamples/MachineLearning/09_Visualization/11_ConfusionMatrices.pas b/InstallerSamples/MachineLearning/09_Visualization/11_ConfusionMatrices.pas new file mode 100644 index 000000000..e1c330f1d --- /dev/null +++ b/InstallerSamples/MachineLearning/09_Visualization/11_ConfusionMatrices.pas @@ -0,0 +1,49 @@ +uses PlotML; + +begin + var labels := ['cat', 'dog', 'fox']; + + var m1 := Matr( + [3.0, 0, 0], + [1.0, 2, 1], + [0.0, 1, 2] + ); + + var m2 := Matr( + [4.0, 1, 0], + [0.0, 3, 1], + [1.0, 0, 2] + ); + + var m3 := Matr( + [2.0, 1, 1], + [0.0, 4, 0], + [1.0, 1, 3] + ); + + var m4 := Matr( + [5.0, 0, 1], + [1.0, 3, 0], + [0.0, 2, 4] + ); + + var fig := Plot.Grid(2, 2); + + fig[0, 0].ConfusionMatrix(m1, labels); + fig[0, 0].Title := 'Model A'; + fig[0, 0].YLabel := 'Actual'; + + fig[0, 1].ConfusionMatrix(m2, labels); + fig[0, 1].Title := 'Model B'; + + fig[1, 0].ConfusionMatrix(m3, labels); + fig[1, 0].Title := 'Model C'; + fig[1, 0].XLabel := 'Predicted'; + fig[1, 0].YLabel := 'Actual'; + + fig[1, 1].ConfusionMatrix(m4, labels); + fig[1, 1].Title := 'Model D'; + fig[1, 1].XLabel := 'Predicted'; + + Plot.Title := 'Confusion Matrices'; +end.