ML тесты поправлены

Небольшие правки ML-модулей
This commit is contained in:
Mikhalkovich Stanislav 2026-05-28 23:56:46 +03:00
parent b08e8b6c0a
commit 3938120846
43 changed files with 279 additions and 233 deletions

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
begin
var ds := Datasets.TitanicRu;
@ -11,8 +11,7 @@ begin
df.StratifiedTrainTestSplit(ds.Target, testRatio := 0.2, seed := 42);
var prep :=
DataPipeline.BuildPreprocessing(
TaskKind.tkClassification,
DataPipeline.BuildClassificationPreprocessing(
target,
features,
new Imputer(['Возраст']),

View file

@ -1,6 +1,6 @@
uses MLABC;
uses MLABC;
function BuildPipe(features: array of string): DataPipeline;
function BuildPipe(features: array of string): ClassificationDataPipeline;
begin
Result :=
DataPipeline.BuildClassification(

View file

@ -32,9 +32,9 @@ begin
var pred := pipe.Predict(testDf);
var y := pipe.GetEncodedLabels(testDf);
var predInt := pred.ToIntArray;
var yInt := y.ToIntArray;
var testPrepared := pipe.Transform(testDf);
var cur := testFull.GetCursor;
var curPrepared := testPrepared.GetCursor;
Println('TitanicRu: пассажиры, классифицированные неправильно');
Println;
@ -42,19 +42,22 @@ begin
var shown := 0;
while cur.MoveNext do
begin
if not curPrepared.MoveNext then
break;
var i := cur.Position;
if yInt[i] <> predInt[i] then
if y[i] <> pred[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 age := curPrepared.Float('Возраст');
var fare := cur.Float('ЦенаБилета');
Println($'Id={id}, {name}');
Println($' Пол={sex}, класс={pclass}, возраст={age:F1}, билет={fare:F1}');
Println($' Истина: {LabelText(yInt[i])}, прогноз: {LabelText(predInt[i])}');
Println($' Истина: {LabelText(y[i])}, прогноз: {LabelText(pred[i])}');
Println;
shown += 1;

View file

@ -1,6 +1,6 @@
uses MLABC;
function BuildPipe(features: array of string): DataPipeline;
function BuildPipe(features: array of string): ClassificationDataPipeline;
begin
Result :=
DataPipeline.BuildClassification(

View file

@ -19,8 +19,7 @@ begin
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42);
var prep :=
DataPipeline.BuildPreprocessing(
TaskKind.tkRegression,
DataPipeline.BuildRegressionPreprocessing(
target,
features,
new OrdinalEncoder('model'),

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
begin
var ds := Datasets.UsedCarsPrice;
@ -19,8 +19,7 @@ begin
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 42);
var prep :=
DataPipeline.BuildPreprocessing(
TaskKind.tkRegression,
DataPipeline.BuildRegressionPreprocessing(
target,
features,
new OrdinalEncoder('model'),

View file

@ -1,15 +1,21 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
var y := new Vector(Arr(0.0, 0.9999999999999, 2.0000000000001));
var labels := LabelsToInts(y);
var ds := new Dataset;
ds.Task := TaskType.Classification;
ds.Target := 'Target';
ds.Data := new DataFrame;
ds.Data.AddStrColumn('Target', Arr('cat', 'dog', 'cat'));
Check(labels.Length = 3, 'Length mismatch');
Check(labels[0] = 0, 'First label mismatch');
Check(labels[1] = 1, 'Second label mismatch');
Check(labels[2] = 2, 'Third label mismatch');
var enc := new LabelEncoder;
var y := enc.FitTransform(ds);
var bad := new Vector(Arr(0.0, 1.2, 2.0));
CheckRaises(procedure -> begin var tmp := LabelsToInts(bad); end, 'LabelsToInts must reject non-integer labels');
Check(y.Length = 3, 'Length mismatch');
Check(y[0] = 0, 'First label mismatch');
Check(y[1] = 1, 'Second label mismatch');
Check(y[2] = 0, 'Third label mismatch');
Check(enc.Classes.Length = 2, 'Class count mismatch');
Check(enc.Classes[0] = 'cat', 'First class mismatch');
Check(enc.Classes[1] = 'dog', 'Second class mismatch');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -6,6 +6,6 @@ begin
var yPred := new Vector(Arr(0.51, 1.0));
CheckRaises(procedure -> begin
var a := Metrics.Accuracy(yTrue, yPred);
var a := Metrics.Accuracy(yTrue.ToIntArray, yPred.ToIntArray);
end, 'Accuracy must reject non-integer class labels');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -6,6 +6,6 @@ begin
var yPred := new Vector(Arr(real.NaN, 1.0));
CheckRaises(procedure -> begin
var a := Metrics.Accuracy(yTrue, yPred);
var a := Metrics.Accuracy(yTrue.ToIntArray, yPred.ToIntArray);
end, 'Accuracy must reject NaN and Infinity in class labels');
end.

View file

@ -0,0 +1,29 @@
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
var df := new DataFrame;
df.AddBoolColumn('Flag', Arr(true, false, true, false));
df.AddBoolColumn('IsNew', Arr(false, false, true, true));
df.AddFloatColumn('X', Arr(1.5, 2.5, 3.5, 4.5));
df.AddIntColumn('Target', Arr(0, 1, 0, 1));
df := df.SetCategorical(['Target']);
var X := df.ToMatrix(['Flag', 'IsNew', 'X']);
var y := df.ToVector('Target').ToIntArray;
Check(X.RowCount = 4, 'ToMatrix row count mismatch');
Check(X.ColCount = 3, 'ToMatrix col count mismatch');
Check(Abs(X[0,0] - 1.0) < 1e-12, 'Flag=true must encode as 1');
Check(Abs(X[1,0] - 0.0) < 1e-12, 'Flag=false must encode as 0');
Check(Abs(X[0,1] - 0.0) < 1e-12, 'IsNew=false must encode as 0');
Check(Abs(X[2,1] - 1.0) < 1e-12, 'IsNew=true must encode as 1');
Check(Abs(X[3,2] - 4.5) < 1e-12, 'Numeric column must stay unchanged');
var model := new DecisionTreeClassifier(maxDepth := 3);
model.Fit(X, y);
var pred := model.Predict(X);
Check(pred.Length = df.RowCount, 'Manual bool-feature model prediction length mismatch');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -6,8 +6,7 @@ begin
var df := ds.Data;
var pipe :=
DataPipeline.Build(
TaskKind.tkClassification,
DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new StandardScaler,
@ -25,3 +24,4 @@ begin
Check(y.Length = testDf.RowCount, 'Encoded labels length mismatch');
Check(Metrics.Accuracy(y, pred) > 0.8, 'Pipeline accuracy is unexpectedly low');
end.

View file

@ -1,11 +1,11 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
var ds := Datasets.Iris;
var pipe :=
UDataPipeline.Build(
DataPipeline.BuildClustering(
ds.Features,
new StandardScaler,
new KMeans(3, seed := 1)
@ -15,3 +15,4 @@ begin
Check(labels.Length = ds.Data.RowCount, 'UDataPipeline labels length mismatch');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,8 +7,7 @@ begin
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 3);
var pipe :=
DataPipeline.Build(
TaskKind.tkClassification,
DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new StandardScaler,
@ -42,3 +41,4 @@ begin
Check(classes[yi] = trueLabels[i], $'GetEncodedLabels[{i}] does not decode to true target');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -21,9 +21,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check(Abs(pred[i] - pi) < 1e-12, $'Predict[{i}] is not an original integer label');
Check((labels[i] >= 0) and (labels[i] < modelClasses.Length), $'PredictLabels[{i}] out of range');
Check(modelClasses[labels[i]] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
var pi := pred[i];
Check((pi >= 0) and (pi < modelClasses.Length), $'Predict[{i}] out of range');
Check(labels[i] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -21,9 +21,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check(Abs(pred[i] - pi) < 1e-12, $'Predict[{i}] is not an original integer label');
Check((labels[i] >= 0) and (labels[i] < modelClasses.Length), $'PredictLabels[{i}] out of range');
Check(modelClasses[labels[i]] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
var pi := pred[i];
Check((pi >= 0) and (pi < modelClasses.Length), $'Predict[{i}] out of range');
Check(labels[i] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -21,9 +21,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check(Abs(pred[i] - pi) < 1e-12, $'Predict[{i}] is not an original integer label');
Check((labels[i] >= 0) and (labels[i] < modelClasses.Length), $'PredictLabels[{i}] out of range');
Check(modelClasses[labels[i]] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
var pi := pred[i];
Check((pi >= 0) and (pi < modelClasses.Length), $'Predict[{i}] out of range');
Check(labels[i] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -21,9 +21,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check(Abs(pred[i] - pi) < 1e-12, $'Predict[{i}] is not an original integer label');
Check((labels[i] >= 0) and (labels[i] < modelClasses.Length), $'PredictLabels[{i}] out of range');
Check(modelClasses[labels[i]] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
var pi := pred[i];
Check((pi >= 0) and (pi < modelClasses.Length), $'Predict[{i}] out of range');
Check(labels[i] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC, PreprocessorABC;
uses MLABC, PreprocessorABC;
uses TestHelpers in '..\TestHelpers.pas';
type
@ -32,8 +32,7 @@ end;
begin
var ds := Datasets.Iris;
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new BadDropper,
@ -43,3 +42,4 @@ begin
CheckRaises(procedure -> begin pipe.Fit(ds.Data); end,
'Pipeline must reject DataFrame preprocessors that change RowCount');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -21,9 +21,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check(Abs(pred[i] - pi) < 1e-12, $'Predict[{i}] is not an original integer label');
Check((labels[i] >= 0) and (labels[i] < modelClasses.Length), $'PredictLabels[{i}] out of range');
Check(modelClasses[labels[i]] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
var pi := pred[i];
Check((pi >= 0) and (pi < modelClasses.Length), $'Predict[{i}] out of range');
Check(labels[i] = pi.ToString, $'Predict[{i}] and PredictLabels[{i}] are inconsistent');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -12,8 +12,7 @@ begin
testDf.AddStrColumn('Target', Arr('cat', 'fox'));
testDf := testDf.SetCategorical(['Target']);
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
'Target',
Arr($'X'),
new LogisticRegression
@ -24,3 +23,4 @@ begin
CheckRaises(procedure -> begin var y := pipe.GetEncodedLabels(testDf); end,
'GetEncodedLabels must reject unseen target classes');
end.

View file

@ -1,4 +1,4 @@
uses MLABC, PreprocessorABC;
uses MLABC, PreprocessorABC;
uses TestHelpers in '..\TestHelpers.pas';
type
@ -32,7 +32,7 @@ end;
begin
var ds := Datasets.Iris;
var pipe := UDataPipeline.Build(
var pipe := DataPipeline.BuildClustering(
ds.Features,
new BadDropper,
new KMeans(3, seed := 1)
@ -41,3 +41,4 @@ begin
CheckRaises(procedure -> begin var labels := pipe.FitPredict(ds.Data); end,
'UDataPipeline must reject DataFrame preprocessors that change RowCount');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,8 +7,7 @@ begin
df.AddStrColumn('Target', Arr('low', 'mid', 'high'));
df := df.SetCategorical(['Target']);
var pipe := DataPipeline.Build(
TaskKind.tkRegression,
var pipe := DataPipeline.BuildRegression(
'Target',
Arr($'X'),
new LinearRegression
@ -17,3 +16,4 @@ begin
CheckRaises(procedure -> begin pipe.Fit(df); end,
'Regression pipeline must reject non-numeric target');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -6,8 +6,7 @@ begin
df.AddFloatColumn('X', Arr(1.0, 2.0, 3.0, 4.0));
df.AddStrColumn('Target', Arr('cat', 'dog', 'cat', 'dog'));
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
'Target',
Arr($'X'),
new LogisticRegression
@ -16,3 +15,4 @@ begin
CheckRaises(procedure -> begin pipe.Fit(df); end,
'Classification pipeline must require categorical target');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -6,8 +6,7 @@ begin
var df := ds.Data;
var pipe :=
DataPipeline.Build(
TaskKind.tkClassification,
DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new StandardScaler,
@ -24,3 +23,4 @@ begin
Check(proba.ColCount = classes.Length, 'Probability class count mismatch');
CheckProbabilityRowsSumToOne(proba);
end.

View file

@ -1,10 +1,9 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
CheckRaises(procedure -> begin
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
'Target',
Arr($'X', $'Target'),
new LogisticRegression
@ -12,3 +11,4 @@ begin
end,
'DataPipeline.Build must reject target inside features');
end.

View file

@ -1,12 +1,13 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
CheckRaises(procedure -> begin
var pipe := UDataPipeline.Build(
var pipe := DataPipeline.BuildClustering(
Arr($'X', $'X'),
new KMeans(3, seed := 1)
);
end,
'UDataPipeline.Build must reject duplicate features');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,8 +7,7 @@ begin
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 3);
var pipe :=
DataPipeline.Build(
TaskKind.tkClassification,
DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new StandardScaler,
@ -25,3 +24,4 @@ begin
for var i := 0 to labels.Length - 1 do
Check(labels[i] in classes, $'PredictLabels[{i}] must be one of pipeline class labels');
end.

View file

@ -1,4 +1,4 @@
uses MLABC, PreprocessorABC, MLCoreABC;
uses MLABC, PreprocessorABC, MLCoreABC;
uses TestHelpers in '..\TestHelpers.pas';
type
@ -41,8 +41,7 @@ end;
begin
CheckRaises(procedure -> begin
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
'Target',
Arr($'X'),
new BadTargetStep('Target'),
@ -51,3 +50,4 @@ begin
end,
'DataPipeline must reject a bound preprocessor that targets the target column');
end.

View file

@ -1,4 +1,4 @@
uses MLABC, PreprocessorABC, MLCoreABC;
uses MLABC, PreprocessorABC, MLCoreABC;
uses TestHelpers in '..\TestHelpers.pas';
type
@ -41,8 +41,7 @@ end;
begin
CheckRaises(procedure -> begin
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
'Target',
Arr($'X'),
new BadTargetColumnsStep(Arr($'X', $'Target')),
@ -51,3 +50,4 @@ begin
end,
'DataPipeline must reject a multi-column bound preprocessor that includes the target column');
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,8 +7,7 @@ begin
df.AddIntColumn('Target', Arr(10, 20, 10, 20));
df := df.SetCategorical(['Target']);
var pipe := DataPipeline.Build(
TaskKind.tkClassification,
var pipe := DataPipeline.BuildClassification(
'Target',
Arr($'X'),
new LogisticRegression
@ -39,3 +38,4 @@ begin
Check(predLabels[i] = classes[pi], $'PredictLabels[{i}] mismatch');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,7 +7,7 @@ begin
X[2,0] := 1.0; X[3,0] := 1.1;
X[4,0] := 2.0; X[5,0] := 2.1;
var y := new Vector(Arr(10.0, 10.0, 20.0, 20.0, 30.0, 30.0));
var y := Arr(10, 10, 20, 20, 30, 30);
var model := new LogisticRegression;
model.Fit(X, y);
@ -30,9 +30,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check((labels[i] >= 0) and (labels[i] < classes.Length), $'PredictLabels[{i}] out of range');
var pi := pred[i];
Check((pi = 10) or (pi = 20) or (pi = 30), $'Predict[{i}] must return original sparse label');
Check(classes[labels[i]] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
Check(labels[i] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,7 +7,7 @@ begin
X[2,0] := 1.0; X[3,0] := 1.1;
X[4,0] := 2.0; X[5,0] := 2.1;
var y := new Vector(Arr(10.0, 10.0, 20.0, 20.0, 30.0, 30.0));
var y := Arr(10, 10, 20, 20, 30, 30);
var model := new RandomForestClassifier(20, maxDepth := 6, seed := 1);
model.Fit(X, y);
@ -25,9 +25,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check((labels[i] >= 0) and (labels[i] < classes.Length), $'PredictLabels[{i}] out of range');
var pi := pred[i];
Check((pi = 10) or (pi = 20) or (pi = 30), $'Predict[{i}] must return original sparse label');
Check(classes[labels[i]] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
Check(labels[i] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,7 +7,7 @@ begin
X[2,0] := 1.0; X[3,0] := 1.1;
X[4,0] := 2.0; X[5,0] := 2.1;
var y := new Vector(Arr(10.0, 10.0, 20.0, 20.0, 30.0, 30.0));
var y := Arr(10, 10, 20, 20, 30, 30);
var model := new GradientBoostingClassifier(20, learningRate := 0.1, maxDepth := 3, seed := 1);
model.Fit(X, y);
@ -25,9 +25,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check((labels[i] >= 0) and (labels[i] < classes.Length), $'PredictLabels[{i}] out of range');
var pi := pred[i];
Check((pi = 10) or (pi = 20) or (pi = 30), $'Predict[{i}] must return original sparse label');
Check(classes[labels[i]] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
Check(labels[i] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,7 +7,7 @@ begin
X[2,0] := 1.0; X[3,0] := 1.1;
X[4,0] := 2.0; X[5,0] := 2.1;
var y := new Vector(Arr(10.0, 10.0, 20.0, 20.0, 30.0, 30.0));
var y := Arr(10, 10, 20, 20, 30, 30);
var model := new DecisionTreeClassifier(maxDepth := 6, seed := 1);
model.Fit(X, y);
@ -23,9 +23,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check((labels[i] >= 0) and (labels[i] < classes.Length), $'PredictLabels[{i}] out of range');
var pi := pred[i];
Check((pi = 10) or (pi = 20) or (pi = 30), $'Predict[{i}] must return original sparse label');
Check(classes[labels[i]] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
Check(labels[i] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,7 +7,7 @@ begin
X[2,0] := 1.0; X[3,0] := 1.1;
X[4,0] := 2.0; X[5,0] := 2.1;
var y := new Vector(Arr(10.0, 10.0, 20.0, 20.0, 30.0, 30.0));
var y := Arr(10, 10, 20, 20, 30, 30);
var model := new KNNClassifier(3);
model.Fit(X, y);
@ -25,9 +25,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
Check((labels[i] >= 0) and (labels[i] < classes.Length), $'PredictLabels[{i}] out of range');
var pi := pred[i];
Check((pi = 10) or (pi = 20) or (pi = 30), $'Predict[{i}] must return original sparse label');
Check(classes[labels[i]] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
Check(labels[i] = pi.ToString, $'Predict and PredictLabels mismatch at {i}');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,8 +7,7 @@ begin
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 3);
var pipe :=
DataPipeline.Build(
TaskKind.tkClassification,
DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new StandardScaler,
@ -30,3 +29,4 @@ begin
Check((pi >= 0) and (pi < classes.Length), $'Predict[{i}] out of dense class range');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,8 +7,7 @@ begin
var (trainDf, testDf) := df.TrainTestSplit(0.2, seed := 3);
var pipe :=
DataPipeline.Build(
TaskKind.tkClassification,
DataPipeline.BuildClassification(
ds.Target,
ds.Features,
new StandardScaler,
@ -33,3 +32,4 @@ begin
Check(predLabels[i] = classes[pi], $'PredictLabels[{i}] must decode Predict[{i}]');
end;
end.

View file

@ -1,4 +1,4 @@
uses MLABC;
uses MLABC;
uses TestHelpers in '..\TestHelpers.pas';
begin
@ -7,7 +7,7 @@ begin
X[2,0] := 1.0; X[3,0] := 1.1;
X[4,0] := 2.0; X[5,0] := 2.1;
var y := new Vector(Arr(10.0, 10.0, 20.0, 20.0, 30.0, 30.0));
var y := Arr(10, 10, 20, 20, 30, 30);
var model := new LogisticRegression;
model.Fit(X, y);
@ -20,9 +20,8 @@ begin
for var i := 0 to X.RowCount - 1 do
begin
var pi := Round(pred[i]);
var pi := pred[i];
Check((pi = 10) or (pi = 20) or (pi = 30), $'Predict[{i}] must return original class label');
Check((labels[i] >= 0) and (labels[i] < classes.Length), $'PredictLabels[{i}] out of range');
Check(classes[labels[i]] = pi.ToString, $'PredictLabels[{i}] must point to Predict[{i}]');
Check(labels[i] = pi.ToString, $'PredictLabels[{i}] must match Predict[{i}]');
end;
end.

View file

@ -22,12 +22,15 @@ unit MLABC;
// =============================================================
// =============================================================
// PIPELINES
// КОНВЕЙЕРЫ
//
// DataFrame-based:
// Табличные:
// DataPipeline
// ClassificationDataPipeline
// RegressionDataPipeline
// ClusteringDataPipeline
//
// Matrix/Vector-based:
// Матричные:
// MatrixPipeline
// ClassificationMatrixPipeline
// RegressionMatrixPipeline
@ -37,6 +40,24 @@ unit MLABC;
// для построения специализированных конвейеров.
// =============================================================
// =============================================================
// ПОЛИТИКА PREDICT
//
// 1. Классификация:
// • Predict возвращает метки классов в том же виде,
// в каком они были поданы модели при обучении.
// • PredictLabels возвращает строковые метки классов.
//
// 2. Регрессия:
// • Predict возвращает вектор числовых предсказаний.
//
// 3. Кластеризация:
// • Predict и FitPredict возвращают номера кластеров.
//
// Внутреннее кодирование классов может использоваться внутри модели,
// но наружу через Predict оно не выдаётся.
// =============================================================
interface
uses LinearAlgebraML;
@ -68,6 +89,9 @@ type
ConfusionMatrix = MetricsABC.ConfusionMatrix;
DataPipeline = MLPipelineABC.DataPipeline;
ClassificationDataPipeline = MLPipelineABC.ClassificationDataPipeline;
RegressionDataPipeline = MLPipelineABC.RegressionDataPipeline;
ClusteringDataPipeline = MLPipelineABC.ClusteringDataPipeline;
DataFrame = DataFrameABC.DataFrame;
DataFrameCursor = DataFrameABCCore.DataFrameCursor;
@ -134,6 +158,7 @@ type
Datasets = MLDatasets.Datasets;
Dataset = MLDatasets.Dataset;
LabelEncoder = MLDatasets.LabelEncoder;
TaskType = MLDatasets.TaskType;
IModel = MLCoreABC.IModel;
IUnsupervisedModel = MLCoreABC.IUnsupervisedModel;

View file

@ -54,35 +54,45 @@ type
property IsFitted: boolean read;
end;
/// Интерфейс модели без учителя (Unsupervised Model).
/// Интерфейс модели без учителя.
/// Наследуется от базового интерфейса IModel.
/// Предназначен для алгоритмов, обучающихся только по признакам X
/// без использования целевых значений (y)
/// без использования целевых значений y.
IUnsupervisedModel = interface(IModel)
function Fit(X: Matrix): IUnsupervisedModel;
end;
/// Интерфейс алгоритма кластеризации (unsupervised learning).
/// Интерфейс алгоритма кластеризации.
/// Предназначен для моделей, разбивающих объекты на кластеры
/// без использования целевой переменной.
/// Основной сценарий использования — FitPredict, возвращающий
/// индекс кластера для каждого объекта обучающей выборки
/// номер кластера для каждого объекта обучающей выборки.
///
/// Важно:
/// • номер кластера — это внутренний идентификатор алгоритма;
/// • он не связан с внешними метками классов, как в классификации;
/// • одинаковые по смыслу кластеры в разных запусках могут получить
/// разные номера.
IClusterer = interface(IUnsupervisedModel)
// function PredictLabels(X: Matrix): array of integer;
/// Выполняет кластеризацию данных и возвращает массив индексов кластеров
/// Выполняет кластеризацию данных и возвращает массив номеров кластеров
/// (0,1,2,...) для каждого объекта. Значение -1 может использоваться
/// для обозначения шума (например, в DBSCAN).
///
/// Возвращаемые значения являются внутренними номерами кластеров,
/// а не внешними метками, заданными пользователем.
function FitPredict(X: Matrix): array of integer;
end;
/// Интерфейс кластеризатора, поддерживающего предсказание для новых данных.
/// Используется для алгоритмов, задающих явное отображение X → cluster
/// (например, KMeans через ближайший центр кластера)
/// Используется для алгоритмов, задающих явное отображение X → кластер
/// (например, KMeans через ближайший центр кластера).
IPredictiveClusterer = interface(IClusterer)
/// Возвращает внутренний индекс кластера для каждого объекта из X
/// Возвращает номер кластера для каждого объекта из X
/// без повторного обучения модели.
/// Требует предварительного вызова Fit или FitPredict.
///
/// Возвращаемые значения являются внутренними номерами кластеров,
/// а не внешними метками классов.
function Predict(X: Matrix): array of integer;
end;

View file

@ -351,7 +351,7 @@ const
ER_VALUECOUNTS_ONLY_CLASSIFICATION =
'ValueCounts доступны только для задач классификации!!ValueCounts are only available for classification datasets';
ER_DATASET_TARGET_MISSING =
'Target обязателен для задач обучения с учителем!!Target is required for supervised learning datasets';
'Target обязателен для задач с учителем!!Target is required for supervised learning datasets';
ER_PARAM_LE =
'Параметр {0} должен быть <= допустимого максимума!!Parameter {0} must be <= the allowed maximum value';
ER_PARAM_RANGE_01 =

View file

@ -1366,9 +1366,10 @@ type
/// Используется для анализа обучения.
function PredictStageProba(X: Matrix; m: integer): Matrix;
/// Предсказывает классы по первым m итерациям бустинга.
/// Предсказывает метки классов по первым m итерациям бустинга.
/// Возвращает их в том же виде, в каком они были поданы модели при обучении.
/// Удобно для построения learning curve.
function PredictStage(X: Matrix; m: integer): Vector;
function PredictStage(X: Matrix; m: integer): array of integer;
/// Текущее количество деревьев в ансамбле.
function TreeCount: integer := fEstimators.Count;
@ -1458,7 +1459,6 @@ type
private
// ==== classification state ====
fYEnc: array of integer;
fClasses: array of double;
fClassCount: integer;
fLabels: ClassifierLabelHelper;
@ -1594,7 +1594,7 @@ type
/// • обученное состояние модели (центры кластеров) НЕ копируется методом Clone
function Fit(X: Matrix): IUnsupervisedModel;
/// Возвращает внутренний индекс кластера для каждого объекта из X.
/// Возвращает номер кластера для каждого объекта из X.
/// Требует предварительного вызова Fit.
function Predict(X: Matrix): array of integer;
@ -1684,23 +1684,23 @@ type
{$endregion Models}
{$region MatrixPipeline}
/// Последовательный конвейер машинного обучения (supervised).
/// Общая база для матричных конвейеров с учителем.
/// Гарантирует строгий порядок выполнения шагов:
/// [преобразователи] → [модель].
///
/// Поддерживает:
/// • преобразователи без учёта целевой переменной (unsupervised);
/// • преобразователи с учётом целевой переменной (supervised);
/// • преобразователи без учёта целевой переменной;
/// • преобразователи с учётом целевой переменной;
/// • одну финальную модель с учителем.
///
/// Все преобразователи применяются последовательно к признакам X,
/// после чего финальная модель обучается на (X, y).
///
/// Обеспечивает единый интерфейс Fit(X, y) / Predict(X)
/// и воспроизводимость полного процесса обучения
/// и воспроизводимость полного процесса обучения.
///
/// MatrixPipeline используется, когда данные уже представлены
/// в виде Matrix X и Vector y
/// в виде Matrix X и вектора y.
MatrixPipeline = class(IModel)
protected
fTransformers: List<ITransformer>;
@ -1775,8 +1775,8 @@ type
/// Строит матричный конвейер для задачи классификации.
/// Шаги указываются в порядке выполнения:
/// сначала преобразователи, затем модель.
/// Последний шаг должен быть моделью.
/// сначала преобразователи, затем модель.
/// Последний шаг должен быть моделью.
static function BuildClassification(params steps: array of IPipelineStep): ClassificationMatrixPipeline;
/// Строит матричный конвейер для задачи регрессии.
@ -1801,7 +1801,7 @@ type
/// X — матрица m × n (m объектов, n признаков).
/// y — вектор длины m с целевыми значениями.
/// Последовательно обучает все трансформеры и применяет их к данным,
/// после чего обучает финальную supervised-модель.
/// после чего обучает финальную модель с учителем.
///
/// Примечание:
/// • обученное состояние пайплайна НЕ копируется методом Clone
@ -1855,47 +1855,36 @@ type
function Fit(X: Matrix; y: Vector): IRegressor; reintroduce;
end;
/// Последовательный конвейер машинного обучения (unsupervised).
/// Гарантирует строгий порядок выполнения шагов:
/// [преобразователи].
/// Общая база для матричных конвейеров без учителя.
///
/// Поддерживает:
/// • преобразователи без учёта целевой переменной (unsupervised).
///
/// Все преобразователи применяются последовательно к признакам X.
///
/// Обеспечивает единый интерфейс Fit(X) / Transform(X)
/// и воспроизводимость полного процесса преобразования данных.
///
/// Используется, когда данные уже представлены
/// в виде Matrix X и отсутствует целевая переменная
/// Поддерживает последовательность матричных преобразователей
/// и одну финальную модель без учителя.
/// Используется как базовый класс для специализированных
/// матричных конвейеров, например ClusteringMatrixPipeline.
UnsupervisedMatrixPipelineBase = class(IUnsupervisedModel)
protected
fTransformers: List<ITransformer>;
fModel: IModel;
fFitted: boolean;
public
/// Создаёт конвейер машинного обучения для заданной модели:
/// • model — модель, которая будет применена
/// после последовательного применения всех преобразователей.
/// Модель должна реализовывать интерфейс IModel (без учёта целевой переменной)
/// Создаёт конвейер машинного обучения для заданной модели.
/// Модель применяется после последовательного выполнения всех преобразователей.
constructor Create(model: IModel);
/// Создаёт пустой пайплайн (конвейер машинного обучения).
/// Модель должна быть установлена через SetModel
/// Создаёт пустой конвейер.
/// Модель должна быть установлена позже через SetModel.
constructor Create;
/// Строит конвейер машинного обучения из последовательности шагов.
/// Шаги указываются в порядке выполнения:
/// сначала преобразователи, затем модель.
/// Последний шаг обязан быть моделью (IModel).
/// Возвращает сконструированный конвейер.
/// Строит матричный конвейер для задачи кластеризации.
/// Шаги указываются в порядке выполнения:
/// сначала преобразователи, затем модель.
/// Последний шаг должен быть моделью.
static function Build(params steps: array of IPipelineStep): ClusteringMatrixPipeline;
/// Устанавливает или заменяет модель.
/// Устанавливает или заменяет модель.
function SetModel(m: IModel): UnsupervisedMatrixPipelineBase;
/// Добавляет преобразование в конец пайплайна
/// Добавляет преобразование в конец пайплайна.
function Add(t: ITransformer): UnsupervisedMatrixPipelineBase;
/// Обучает пайплайн на данных.
@ -1938,6 +1927,12 @@ type
function Name: string := Self.GetType.Name;
end;
/// Матричный конвейер для задачи кластеризации.
/// FitPredict работает с любым кластеризатором.
/// Predict для новых данных доступен только тогда,
/// когда финальная модель реализует IPredictiveClusterer
/// (например, KMeans).
/// Для алгоритмов вроде DBSCAN используйте FitPredict.
ClusteringMatrixPipeline = class(UnsupervisedMatrixPipelineBase)
end;
@ -2093,7 +2088,7 @@ type
/// Трансформер, удаляющий признаки с малой дисперсией.
/// Удаляет столбцы X_j, для которых Var(X_j) < threshold.
/// Не использует целевую переменную (unsupervised)
/// Не использует целевую переменную.
VarianceThreshold = class(IUnsupervisedTransformer)
private
fThreshold: real;
@ -2465,8 +2460,8 @@ const
'Метки классов недоступны. Убедитесь, что модель обучена и метки установлены!!Class labels are not available. Ensure the model is fitted and class labels are set';
ER_LABEL_INDEX_OUT_OF_RANGE =
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})';
ER_PIPELINE_LAST_NOT_SUPERVISED_MODEL =
'Последний шаг Pipeline должен быть supervised-моделью!!' +
ER_PIPELINE_LAST_NOT_SUPERVISED_MODEL =
'Последний шаг Pipeline должен быть моделью с учителем!!' +
'Last Pipeline step must be a supervised model';
ER_INTERNAL_INVALID_MODEL_CLONE =
'Внутренняя ошибка: Clone модели вернул несовместимый тип!!' +
@ -2636,7 +2631,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForFit(X);
CheckYForFit(y);
end;
var m := X.RowCount;
@ -2761,7 +2755,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForFit(X);
CheckYForFit(y);
end;
if X.RowCount = 0 then
@ -3306,7 +3299,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForFit(X);
CheckYForFit(y);
end;
if X.RowCount = 0 then
@ -4758,7 +4750,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForFit(X);
CheckYForFit(y);
end;
if X.RowCount = 0 then
@ -5992,7 +5983,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForFit(XTrain);
CheckYForFit(yTrain);
end;
// --- shape checks ---
@ -6013,7 +6003,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForPredict(XVal);
CheckYForFit(yVal);
end;
if XVal.RowCount <> yVal.Length then
@ -7123,12 +7112,12 @@ begin
Result := probs;
end;
function GradientBoostingClassifier.PredictStage(X: Matrix; m: integer): Vector;
function GradientBoostingClassifier.PredictStage(X: Matrix; m: integer): array of integer;
begin
var probs := PredictStageProba(X, m);
var n := probs.RowCount;
var resultVec := new Vector(n);
SetLength(Result, n);
for var i := 0 to n - 1 do
begin
@ -7142,10 +7131,8 @@ begin
best := cls;
end;
resultVec[i] := fClasses[best];
Result[i] := fClasses[best];
end;
Result := resultVec;
end;
function GradientBoostingClassifier.FeatureImportances: Vector;
@ -7506,11 +7493,6 @@ begin
if fClassCount < 2 then
ArgumentError(ER_NEED_AT_LEAST_TWO_CLASSES);
// сохранить оригинальные метки (double API)
SetLength(fClasses, fClassCount);
for var i := 0 to fClassCount - 1 do
fClasses[i] := classesInt[i];
if fLabels = nil then
fLabels := new ClassifierLabelHelper;
fLabels.SetClassValues(classesInt);
@ -7867,7 +7849,6 @@ begin
if MLConfig.ValidateFiniteInputs then
begin
CheckXForFit(X);
CheckYForFit(y);
end;
fXTrain := X.Clone;
@ -8701,6 +8682,7 @@ begin
ArgumentError(ER_EMPTY_DATASET);
var Xt := X;
var yVec: Vector := nil;
for var i := 0 to fTransformers.Count - 1 do
begin
@ -8709,8 +8691,12 @@ begin
if t = nil then
ArgumentError(ER_PIPELINE_STEP_NULL);
if t is ISupervisedTransformer then
ArgumentError(ER_PIPELINE_INVALID_STEP_ORDER)
if t is ISupervisedTransformer(var sup) then
begin
if yVec = nil then
yVec := new Vector(y);
fTransformers[i] := sup.Fit(Xt, yVec);
end
else if t is IUnsupervisedTransformer(var unsup) then
fTransformers[i] := unsup.Fit(Xt)
else

View file

@ -38,7 +38,7 @@ type
/// Возвращает только те столбцы, которые реально присутствуют в current,
/// без повторов и в итоговом порядке для построения матрицы признаков.
///
/// Если после preprocessing не осталось ни одного признака,
/// Если после предобработки не осталось ни одного признака,
/// выбрасывается исключение
function ResolveFinalFeatures(current: DataFrame): array of string;
@ -116,8 +116,8 @@ type
/// Добавляет шаг в конец конвейера.
/// Принимает:
/// • IPreprocessor (DataFrame-уровень)
/// • ITransformer (Matrix-уровень)
/// • IPreprocessor (табличный шаг)
/// • ITransformer (матричный шаг)
/// • IModel (модель, должна быть последней)
/// Запрещено добавлять шаги после вызова Fit.
function Add(step: IPipelineStep): SupervisedDataPipelineBase;
@ -126,7 +126,7 @@ type
///
/// Матричные шаги и модель при этом не используются.
/// Метод предназначен в основном для просмотра и анализа промежуточного
/// результата preprocessing.
/// результата предобработки.
///
/// Полученный DataFrame обычно не следует повторно подавать в тот же pipeline,
/// так как DataFrame-преобразования будут выполнены ещё раз.
@ -169,22 +169,17 @@ type
end;
/// Общая база для табличных конвейеров без учителя.
///
/// UnsupervisedDataPipelineBase — конвейер подготовки данных и обучения модели без учителя на DataFrame.
///
/// Поддерживает два уровня шагов:
/// • DataFrame-уровень: IPreprocessor (Fit/Transform над DataFrame)
/// • Matrix-уровень: IUnsupervisedTransformer и IUnsupervisedModel
/// (после преобразования DataFrame → Matrix)
///
/// Правила порядка шагов:
/// • Сначала идут только DataFrame-шаги (IPreprocessor).
/// • Затем — матричные шаги (IUnsupervisedTransformer).
/// • Модель (IUnsupervisedModel) добавляется последней и может быть только одна.
/// Поддерживает два типа шагов:
/// • табличные преобразователи;
/// • матричные преобразователи и финальную модель без учителя.
///
/// В конвейере используются:
/// • features — признаки (целевая переменная отсутствует).
/// Порядок шагов строгий:
/// • сначала идут табличные преобразователи;
/// • затем матричные преобразователи;
/// • модель без учителя добавляется последней и может быть только одна.
///
/// Конвейер работает только с признаками.
UnsupervisedDataPipelineBase = class(PipelineBase, IModel)
protected
fModel: IUnsupervisedModel;
@ -202,8 +197,8 @@ type
/// Добавляет шаг в конец конвейера.
/// Принимает:
/// • IPreprocessor (DataFrame-уровень)
/// • ITransformer (Matrix-уровень)
/// • IPreprocessor (табличный шаг)
/// • ITransformer (матричный шаг)
/// • IUnsupervisedModel (модель, должна быть последней)
function Add(step: IPipelineStep): UnsupervisedDataPipelineBase;
@ -211,7 +206,7 @@ type
///
/// Матричные шаги и модель при этом не используются.
/// Метод предназначен в основном для просмотра и анализа промежуточного
/// результата preprocessing.
/// результата предобработки.
///
/// Полученный DataFrame обычно не следует повторно подавать в тот же pipeline,
/// так как DataFrame-преобразования будут выполнены ещё раз.
@ -226,7 +221,7 @@ type
end;
/// Табличный конвейер для задачи кластеризации.
/// Predict возвращает внутренние индексы кластеров.
/// Predict возвращает номера кластеров.
ClusteringDataPipeline = class(UnsupervisedDataPipelineBase)
public
function WithModel(model: IUnsupervisedModel): ClusteringDataPipeline;
@ -234,7 +229,7 @@ type
/// Обучает конвейер и сразу возвращает метки кластеров.
function FitPredict(df: DataFrame): array of integer;
/// Делает предсказание кластеров для объектов из DataFrame.
/// Возвращает внутренние индексы кластеров.
/// Возвращает номера кластеров.
function Predict(df: DataFrame): array of integer;
end;
@ -280,9 +275,11 @@ const
ER_PIPELINE_FINALFEATURES =
'Внутренняя ошибка pipeline: итоговый набор признаков не определён. Возможно, Fit не был выполнен корректно!!Pipeline internal error: final feature set is not defined. Fit may not have been executed correctly';
ER_PIPELINE_TARGET_REMOVED =
'Целевая переменная "{0}" была удалена на этапе preprocessing pipeline!!Target column "{0}" was removed during pipeline preprocessing';
'Целевая переменная "{0}" была удалена на этапе предобработки конвейера!!' +
'Target column "{0}" was removed during pipeline preprocessing';
ER_PIPELINE_NO_FEATURES =
'После preprocessing pipeline не осталось признаков для обучения модели!!No features remain after pipeline preprocessing';
'После предобработки конвейера не осталось признаков для обучения модели!!' +
'No features remain after pipeline preprocessing';
ER_MATRIXSTEP_NO_FIT =
'Шаг матричного конвейера #{0} не поддерживает Fit!!Matrix step #{0} does not support Fit';
ER_Model_NoFit =
@ -307,7 +304,8 @@ const
ER_LABEL_INDEX_OUT_OF_RANGE =
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})';
ER_ORDINALENCODER_TARGET_NOT_ALLOWED =
'OrdinalEncoder нельзя применять к целевой переменной — кодирование выполняется внутри модели!!OrdinalEncoder cannot be applied to target — encoding is handled internally by the model';
'OrdinalEncoder нельзя применять к целевой переменной: её кодирование выполняется внутри модели!!' +
'OrdinalEncoder cannot be applied to the target because target encoding is handled inside the model';
ER_ENCODELABELS_NOT_CATEGORICAL =
'Целевой столбец должен быть категориальным для задач классификации!!Target column must be categorical for classification tasks';
ER_CLASSIFICATION_TARGET_MUST_BE_CATEGORICAL_STR_OR_INT =
@ -327,13 +325,13 @@ const
'Pipeline не содержит шагов!!' +
'Pipeline must contain at least one step';
ER_PIPELINE_LAST_NOT_UNSUPERVISED_MODEL =
'Последний шаг Pipeline должен быть unsupervised-моделью!!' +
'Последний шаг Pipeline должен быть моделью без учителя!!' +
'Last Pipeline step must be an unsupervised model';
ER_PIPELINE_INVALID_STEP_ORDER =
'Неверный порядок шагов в Pipeline: модель должна быть последней!!' +
'Invalid pipeline step order: model must be the last step';
ER_PIPELINE_LAST_NOT_SUPERVISED_MODEL =
'Последний шаг Pipeline должен быть supervised-моделью!!' +
'Последний шаг Pipeline должен быть моделью с учителем!!' +
'Last Pipeline step must be a supervised model';
ER_MODEL_NOT_CLASSIFIER =
'Модель не является классификатором!!' +