ML - очередной аудит
This commit is contained in:
parent
2294ce5844
commit
be48d378be
|
|
@ -54,6 +54,7 @@ type
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses MLExceptions;
|
uses MLExceptions;
|
||||||
|
uses MLUtilsABC;
|
||||||
|
|
||||||
const
|
const
|
||||||
ER_SCORE_FUNC_NULL =
|
ER_SCORE_FUNC_NULL =
|
||||||
|
|
@ -90,9 +91,8 @@ begin
|
||||||
|
|
||||||
var resultVec := new Vector(p);
|
var resultVec := new Vector(p);
|
||||||
|
|
||||||
var baseSeed :=
|
var userProvidedSeed: boolean;
|
||||||
if seed >= 0 then seed
|
var baseSeed := ResolveRandomSeed(seed, userProvidedSeed);
|
||||||
else System.Environment.TickCount and integer.MaxValue;
|
|
||||||
|
|
||||||
for var j := 0 to p - 1 do
|
for var j := 0 to p - 1 do
|
||||||
begin
|
begin
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
// Используется дисперсия генеральной совокупности (деление на n),
|
// Используется дисперсия генеральной совокупности (деление на n),
|
||||||
// как принято в численных методах и алгоритмах машинного обучения.
|
// как принято в численных методах и алгоритмах машинного обучения.
|
||||||
//
|
//
|
||||||
|
// Исключение: Matrix.PCA использует (n-1) для ковариационной матрицы
|
||||||
|
// согласно статистической политике MLABC (пункт 3).
|
||||||
|
//
|
||||||
// См. статистическую политику в модуле MLABC.
|
// См. статистическую политику в модуле MLABC.
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
||||||
|
|
@ -339,7 +342,8 @@ const
|
||||||
'Матрица вырождена или плохо обусловлена!!Matrix is singular or ill-conditioned';
|
'Матрица вырождена или плохо обусловлена!!Matrix is singular or ill-conditioned';
|
||||||
ER_EMPTY_MATRIX =
|
ER_EMPTY_MATRIX =
|
||||||
'Матрица пуста!!Matrix is empty';
|
'Матрица пуста!!Matrix is empty';
|
||||||
|
ER_EIGEN_NOT_CONVERGED =
|
||||||
|
'EigenSymmetric: не сошлось за {0} итераций (off={1}, tol={2})!!EigenSymmetric did not converge in {0} iterations (off={1}, tol={2})';
|
||||||
|
|
||||||
type
|
type
|
||||||
MLNotSPDException = class(MLException);
|
MLNotSPDException = class(MLException);
|
||||||
|
|
@ -1276,10 +1280,11 @@ begin
|
||||||
var M := Clone;
|
var M := Clone;
|
||||||
var V := Matrix.Identity(n);
|
var V := Matrix.Identity(n);
|
||||||
|
|
||||||
|
var off := 0.0;
|
||||||
for var iter := 0 to maxIter - 1 do
|
for var iter := 0 to maxIter - 1 do
|
||||||
begin
|
begin
|
||||||
// --- Норма вне-диагонали
|
// --- Норма вне-диагонали
|
||||||
var off := 0.0;
|
off := 0.0;
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
for var j := i + 1 to n - 1 do
|
for var j := i + 1 to n - 1 do
|
||||||
off += M[i, j] * M[i, j];
|
off += M[i, j] * M[i, j];
|
||||||
|
|
@ -1358,6 +1363,12 @@ begin
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
var offNorm := Sqrt(off);
|
||||||
|
|
||||||
|
if offNorm >= tol then
|
||||||
|
Error(ER_EIGEN_NOT_CONVERGED, maxIter, offNorm, tol);
|
||||||
|
|
||||||
|
|
||||||
// --- Eigenvalues
|
// --- Eigenvalues
|
||||||
var values := new Vector(n);
|
var values := new Vector(n);
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
|
|
@ -1449,7 +1460,8 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- Eigen
|
// --- Eigen
|
||||||
var (values, V) := C.EigenSymmetric;
|
var adaptiveIter := Max(100, n * n * 10);
|
||||||
|
var (values, V) := C.EigenSymmetric(1e-12, adaptiveIter);
|
||||||
|
|
||||||
// --- Выбор k компонент
|
// --- Выбор k компонент
|
||||||
var components := new Matrix(n, k);
|
var components := new Matrix(n, k);
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,10 @@ type
|
||||||
function Fit(X: Matrix): IUnsupervisedTransformer;
|
function Fit(X: Matrix): IUnsupervisedTransformer;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
IColumnExpander = interface
|
||||||
|
function GetExpandedColumns(sourceColumn: string): array of string;
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
@ -88,7 +88,9 @@ type
|
||||||
/// • clusterStd — базовое стандартное отклонение
|
/// • clusterStd — базовое стандартное отклонение
|
||||||
/// • clusterStdVar — разброс std между кластерами (0 → одинаковые)
|
/// • clusterStdVar — разброс std между кластерами (0 → одинаковые)
|
||||||
/// • centerBox — диапазон генерации центров [-centerBox, centerBox]
|
/// • centerBox — диапазон генерации центров [-centerBox, centerBox]
|
||||||
/// • classBalance — равномерность кластеров (0..1, 1 = равномерно)
|
/// • classBalance — равномерность распределения объектов по кластерам (0..1]
|
||||||
|
/// • classBalance = 1.0 — строго равномерное распределение (детерминированное, не зависит от seed)
|
||||||
|
/// • classBalance < 1.0 — случайное распределение; чем меньше значение, тем выше дисбаланс в среднем
|
||||||
/// • noisePoints — число шумовых точек (outliers)
|
/// • noisePoints — число шумовых точек (outliers)
|
||||||
/// • shuffle — перемешивание
|
/// • shuffle — перемешивание
|
||||||
/// • seed — генератор (seed < 0 → случайный)
|
/// • seed — генератор (seed < 0 → случайный)
|
||||||
|
|
@ -748,7 +750,7 @@ begin
|
||||||
// --- вероятности кластеров
|
// --- вероятности кластеров
|
||||||
var probs := new real[centers];
|
var probs := new real[centers];
|
||||||
|
|
||||||
if classBalance = 1 then
|
if Abs(classBalance - 1.0) < 1e-12 then
|
||||||
begin
|
begin
|
||||||
for var c := 0 to centers - 1 do
|
for var c := 0 to centers - 1 do
|
||||||
probs[c] := 1.0 / centers;
|
probs[c] := 1.0 / centers;
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ const
|
||||||
ER_XY_SIZE_MISMATCH =
|
ER_XY_SIZE_MISMATCH =
|
||||||
'Размеры X и y не совпадают: X={0}, y={1}!!X and y size mismatch: X={0}, y={1}';
|
'Размеры X и y не совпадают: X={0}, y={1}!!X and y size mismatch: X={0}, y={1}';
|
||||||
ER_FEATURE_COUNT_MISMATCH =
|
ER_FEATURE_COUNT_MISMATCH =
|
||||||
'Число признаков не совпадает!!Feature count mismatch';
|
'Число признаков не совпадает: {0} и {1}!!Feature count mismatch: {0}, {1}';
|
||||||
ER_NAN_IN_X =
|
ER_NAN_IN_X =
|
||||||
'X содержит NaN (пропуски)!!X contains NaN';
|
'X содержит NaN (пропуски)!!X contains NaN';
|
||||||
ER_NAN_IN_Y =
|
ER_NAN_IN_Y =
|
||||||
|
|
|
||||||
|
|
@ -2475,7 +2475,15 @@ type
|
||||||
|
|
||||||
{$endregion Utility functions}
|
{$endregion Utility functions}
|
||||||
|
|
||||||
type
|
type
|
||||||
|
/// Проверять ли входные данные моделей на NaN и Infinity.
|
||||||
|
///
|
||||||
|
/// По умолчанию: True — модели валидируют вход и выбрасывают исключение.
|
||||||
|
///
|
||||||
|
/// Установка в False отключает проверки ГЛОБАЛЬНО для всех моделей.
|
||||||
|
/// Использовать только если данные гарантированно очищены.
|
||||||
|
///
|
||||||
|
/// При наличии NaN/Inf поведение моделей не определено
|
||||||
MLConfig = static class
|
MLConfig = static class
|
||||||
public
|
public
|
||||||
/// Проверять ли входные данные моделей на NaN, Inf
|
/// Проверять ли входные данные моделей на NaN, Inf
|
||||||
|
|
@ -2624,9 +2632,9 @@ const
|
||||||
ER_LEAFL2_INVALID =
|
ER_LEAFL2_INVALID =
|
||||||
'leafL2 должно быть >= 0 ({0}).!!' +
|
'leafL2 должно быть >= 0 ({0}).!!' +
|
||||||
'leafL2 must be >= 0 ({0}).';
|
'leafL2 must be >= 0 ({0}).';
|
||||||
ER_MIN_LEAF_GE_SPLIT =
|
ER_MIN_LEAF_GT_SPLIT =
|
||||||
'minSamplesLeaf ({0}) должно быть меньше minSamplesSplit ({1}).!!' +
|
'minSamplesSplit ({1}) должен быть > 2 * minSamplesLeaf ({0}).!!' +
|
||||||
'minSamplesLeaf ({0}) must be less than minSamplesSplit ({1}).';
|
'minSamplesSplit ({1}) must be > 2 * minSamplesLeaf ({0}).';
|
||||||
ER_OOB_NOT_ENABLED =
|
ER_OOB_NOT_ENABLED =
|
||||||
'OOB score не включен для этой модели. Установите computeOOB = true в конструкторе.!!' +
|
'OOB score не включен для этой модели. Установите computeOOB = true в конструкторе.!!' +
|
||||||
'OOB score is not enabled for this model. Set computeOOB = true in the constructor.';
|
'OOB score is not enabled for this model. Set computeOOB = true in the constructor.';
|
||||||
|
|
@ -3252,10 +3260,6 @@ begin
|
||||||
|
|
||||||
// --- init
|
// --- init
|
||||||
fW := new Matrix(p, fClassCount);
|
fW := new Matrix(p, fClassCount);
|
||||||
var scale := 0.01;
|
|
||||||
for var j := 0 to p - 1 do
|
|
||||||
for var k := 0 to fClassCount - 1 do
|
|
||||||
fW.Data[j,k] := (Random - 0.5) * 2 * scale;
|
|
||||||
|
|
||||||
fIntercept := new Vector(fClassCount);
|
fIntercept := new Vector(fClassCount);
|
||||||
|
|
||||||
|
|
@ -3718,7 +3722,7 @@ begin
|
||||||
if fClassLabels = nil then
|
if fClassLabels = nil then
|
||||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||||
|
|
||||||
Result := fClassLabels;
|
Result := Copy(fClassLabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function GiniCriterion.Impurity(y: Vector; indices: array of integer): real;
|
function GiniCriterion.Impurity(y: Vector; indices: array of integer): real;
|
||||||
|
|
@ -4375,11 +4379,9 @@ begin
|
||||||
if minSamplesLeaf < 1 then
|
if minSamplesLeaf < 1 then
|
||||||
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
||||||
|
|
||||||
if 2 * minSamplesLeaf >= minSamplesSplit then
|
|
||||||
ArgumentOutOfRangeError(
|
if minSamplesSplit < 2 * minSamplesLeaf then
|
||||||
ER_MIN_LEAF_GE_SPLIT,
|
ArgumentOutOfRangeError(ER_MIN_LEAF_GT_SPLIT, minSamplesLeaf, minSamplesSplit);
|
||||||
minSamplesLeaf, minSamplesSplit
|
|
||||||
);
|
|
||||||
|
|
||||||
// --- parameters valid → assign
|
// --- parameters valid → assign
|
||||||
|
|
||||||
|
|
@ -4694,8 +4696,8 @@ begin
|
||||||
if minSamplesLeaf < 1 then
|
if minSamplesLeaf < 1 then
|
||||||
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
||||||
|
|
||||||
if minSamplesLeaf >= minSamplesSplit then
|
if minSamplesSplit < 2 * minSamplesLeaf then
|
||||||
ArgumentOutOfRangeError(ER_MIN_LEAF_GE_SPLIT, minSamplesLeaf, minSamplesSplit);
|
ArgumentOutOfRangeError(ER_MIN_LEAF_GT_SPLIT, minSamplesLeaf, minSamplesSplit);
|
||||||
|
|
||||||
if maxFeatures < 0 then
|
if maxFeatures < 0 then
|
||||||
ArgumentOutOfRangeError(ER_MAX_FEATURES_INVALID);
|
ArgumentOutOfRangeError(ER_MAX_FEATURES_INVALID);
|
||||||
|
|
@ -4753,22 +4755,17 @@ begin
|
||||||
var classes: array of integer;
|
var classes: array of integer;
|
||||||
var yEncArr := EncodeLabelsInt(yInt, classes);
|
var yEncArr := EncodeLabelsInt(yInt, classes);
|
||||||
|
|
||||||
if fCriterion = nil then
|
|
||||||
fCriterion := new GiniCriterion(classes.Length);
|
|
||||||
|
|
||||||
if classes.Length < 2 then
|
if classes.Length < 2 then
|
||||||
ArgumentError(ER_NEED_AT_LEAST_TWO_CLASSES);
|
ArgumentError(ER_NEED_AT_LEAST_TWO_CLASSES);
|
||||||
|
|
||||||
fIndexToClass := classes;
|
|
||||||
|
|
||||||
// --- criterion
|
// --- criterion
|
||||||
if fCriterion = nil then
|
if fCriterion = nil then
|
||||||
fCriterion := new GiniCriterion(classes.Length);
|
fCriterion := new GiniCriterion(classes.Length);
|
||||||
|
|
||||||
|
fIndexToClass := classes;
|
||||||
|
|
||||||
// --- encoded vector
|
// --- encoded vector
|
||||||
var yEncoded := new Vector(m);
|
var yEncoded := new Vector(yEncArr);
|
||||||
for var i := 0 to m - 1 do
|
|
||||||
yEncoded[i] := yEncArr[i];
|
|
||||||
|
|
||||||
// --- Core
|
// --- Core
|
||||||
fCore := new DecisionTreeCore(
|
fCore := new DecisionTreeCore(
|
||||||
|
|
@ -4854,7 +4851,7 @@ begin
|
||||||
if fClassLabels = nil then
|
if fClassLabels = nil then
|
||||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||||
|
|
||||||
Result := fClassLabels;
|
Result := Copy(fClassLabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// DecisionTreeRegressor
|
// DecisionTreeRegressor
|
||||||
|
|
@ -5003,9 +5000,7 @@ begin
|
||||||
CheckXForPredict(X);
|
CheckXForPredict(X);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureImportances.Length then
|
if X.ColCount <> fFeatureImportances.Length then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH,
|
DimensionError(ER_FEATURE_COUNT_MISMATCH, X.ColCount, fFeatureImportances.Length);
|
||||||
X.ColCount,
|
|
||||||
fFeatureImportances.Length);
|
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
Result := new Vector(n);
|
Result := new Vector(n);
|
||||||
|
|
@ -5066,8 +5061,8 @@ begin
|
||||||
if minSamplesLeaf < 1 then
|
if minSamplesLeaf < 1 then
|
||||||
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
||||||
|
|
||||||
if minSamplesLeaf >= minSamplesSplit then
|
if minSamplesSplit < 2 * minSamplesLeaf then
|
||||||
ArgumentOutOfRangeError(ER_MIN_LEAF_GE_SPLIT, minSamplesLeaf, minSamplesSplit);
|
ArgumentOutOfRangeError(ER_MIN_LEAF_GT_SPLIT, minSamplesLeaf, minSamplesSplit);
|
||||||
|
|
||||||
fNTrees := nTrees;
|
fNTrees := nTrees;
|
||||||
fMaxDepth := maxDepth;
|
fMaxDepth := maxDepth;
|
||||||
|
|
@ -5680,7 +5675,7 @@ begin
|
||||||
if fClassLabels = nil then
|
if fClassLabels = nil then
|
||||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||||
|
|
||||||
Result := fClassLabels;
|
Result := Copy(fClassLabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
@ -5764,6 +5759,9 @@ begin
|
||||||
if minSamplesLeaf < 1 then
|
if minSamplesLeaf < 1 then
|
||||||
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
||||||
|
|
||||||
|
if minSamplesSplit < 2 * minSamplesLeaf then
|
||||||
|
ArgumentOutOfRangeError(ER_MIN_LEAF_GT_SPLIT, minSamplesLeaf, minSamplesSplit);
|
||||||
|
|
||||||
if maxDepth > MAX_ALLOWED_TREE_DEPTH then
|
if maxDepth > MAX_ALLOWED_TREE_DEPTH then
|
||||||
ArgumentOutOfRangeError(ER_MAX_DEPTH_TOO_LARGE, maxDepth);
|
ArgumentOutOfRangeError(ER_MAX_DEPTH_TOO_LARGE, maxDepth);
|
||||||
|
|
||||||
|
|
@ -6026,10 +6024,10 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if XVal.RowCount <> yVal.Length then
|
if XVal.RowCount <> yVal.Length then
|
||||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
DimensionError(ER_XY_SIZE_MISMATCH,XVal.RowCount,yVal.Length);
|
||||||
|
|
||||||
if XVal.ColCount <> XTrain.ColCount then
|
if XVal.ColCount <> XTrain.ColCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,XVal.ColCount,XTrain.ColCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- OOB checks ---
|
// --- OOB checks ---
|
||||||
|
|
@ -6241,7 +6239,7 @@ begin
|
||||||
CheckXForPredict(X);
|
CheckXForPredict(X);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var yPred := new Vector(n);
|
var yPred := new Vector(n);
|
||||||
|
|
@ -6473,7 +6471,7 @@ begin
|
||||||
ArgumentError(ER_EMPTY_DATASET);
|
ArgumentError(ER_EMPTY_DATASET);
|
||||||
|
|
||||||
if XTrain.RowCount <> yTrain.Length then
|
if XTrain.RowCount <> yTrain.Length then
|
||||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
DimensionError(ER_XY_SIZE_MISMATCH,XTrain.RowCount,yTrain.Length);
|
||||||
|
|
||||||
if useValidation then
|
if useValidation then
|
||||||
begin
|
begin
|
||||||
|
|
@ -6490,10 +6488,10 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if XVal.RowCount <> yVal.Length then
|
if XVal.RowCount <> yVal.Length then
|
||||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
DimensionError(ER_XY_SIZE_MISMATCH,XVal.RowCount,yVal.Length);
|
||||||
|
|
||||||
if XVal.ColCount <> XTrain.ColCount then
|
if XVal.ColCount <> XTrain.ColCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,XVal.ColCount,XTrain.ColCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- reset state
|
// --- reset state
|
||||||
|
|
@ -7007,6 +7005,9 @@ begin
|
||||||
if minSamplesLeaf < 1 then
|
if minSamplesLeaf < 1 then
|
||||||
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
||||||
|
|
||||||
|
if minSamplesSplit < 2 * minSamplesLeaf then
|
||||||
|
ArgumentOutOfRangeError(ER_MIN_LEAF_GT_SPLIT, minSamplesLeaf, minSamplesSplit);
|
||||||
|
|
||||||
if (subsample <= 0) or (subsample > 1) then
|
if (subsample <= 0) or (subsample > 1) then
|
||||||
ArgumentOutOfRangeError(ER_SUBSAMPLE_OUT_OF_RANGE, subsample);
|
ArgumentOutOfRangeError(ER_SUBSAMPLE_OUT_OF_RANGE, subsample);
|
||||||
|
|
||||||
|
|
@ -7050,7 +7051,7 @@ begin
|
||||||
CheckXForPredict(X);
|
CheckXForPredict(X);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var nSamples := X.RowCount;
|
var nSamples := X.RowCount;
|
||||||
var classCount := fClassCount;
|
var classCount := fClassCount;
|
||||||
|
|
@ -7127,7 +7128,7 @@ begin
|
||||||
CheckXForPredict(X);
|
CheckXForPredict(X);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var total := fEstimators.Count;
|
var total := fEstimators.Count;
|
||||||
|
|
||||||
|
|
@ -7318,7 +7319,7 @@ begin
|
||||||
if fClassLabels = nil then
|
if fClassLabels = nil then
|
||||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||||
|
|
||||||
Result := fClassLabels;
|
Result := Copy(fClassLabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
@ -7344,7 +7345,7 @@ begin
|
||||||
CheckXForPredict(X);
|
CheckXForPredict(X);
|
||||||
|
|
||||||
if X.ColCount <> fXTrain.ColCount then
|
if X.ColCount <> fXTrain.ColCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fXTrain.ColCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function KNNBase.SquaredL2(trainRow: integer; XTest: Matrix; testRow: integer): double;
|
function KNNBase.SquaredL2(trainRow: integer; XTest: Matrix; testRow: integer): double;
|
||||||
|
|
@ -7532,7 +7533,7 @@ begin
|
||||||
ArgumentNullError(ER_Y_NULL);
|
ArgumentNullError(ER_Y_NULL);
|
||||||
|
|
||||||
if X.RowCount <> y.Length then
|
if X.RowCount <> y.Length then
|
||||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
DimensionError(ER_XY_SIZE_MISMATCH,X.RowCount,y.Length);
|
||||||
|
|
||||||
if X.RowCount = 0 then
|
if X.RowCount = 0 then
|
||||||
ArgumentError(ER_EMPTY_DATASET);
|
ArgumentError(ER_EMPTY_DATASET);
|
||||||
|
|
@ -7900,7 +7901,7 @@ begin
|
||||||
if fClassLabels = nil then
|
if fClassLabels = nil then
|
||||||
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
ArgumentError(ER_CLASSES_NOT_AVAILABLE);
|
||||||
|
|
||||||
Result := fClassLabels;
|
Result := Copy(fClassLabels);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7922,7 +7923,7 @@ begin
|
||||||
ArgumentNullError(ER_Y_NULL);
|
ArgumentNullError(ER_Y_NULL);
|
||||||
|
|
||||||
if X.RowCount <> y.Length then
|
if X.RowCount <> y.Length then
|
||||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
DimensionError(ER_XY_SIZE_MISMATCH,X.RowCount,y.Length);
|
||||||
|
|
||||||
if X.RowCount = 0 then
|
if X.RowCount = 0 then
|
||||||
ArgumentError(ER_EMPTY_DATASET);
|
ArgumentError(ER_EMPTY_DATASET);
|
||||||
|
|
@ -8076,13 +8077,8 @@ begin
|
||||||
fNInit := nInit;
|
fNInit := nInit;
|
||||||
|
|
||||||
// --- seed (единый стиль)
|
// --- seed (единый стиль)
|
||||||
fRandomSeed := seed;
|
fRandomSeed := ResolveRandomSeed(seed, fUserProvidedSeed);
|
||||||
fUserProvidedSeed := seed >= 0;
|
fRng := new System.Random(fRandomSeed);
|
||||||
|
|
||||||
if fUserProvidedSeed then
|
|
||||||
fRng := new System.Random(seed)
|
|
||||||
else
|
|
||||||
fRng := new System.Random;
|
|
||||||
|
|
||||||
// --- state
|
// --- state
|
||||||
fFitted := False;
|
fFitted := False;
|
||||||
|
|
@ -8636,7 +8632,7 @@ begin
|
||||||
ArgumentNullError(ER_Y_NULL);
|
ArgumentNullError(ER_Y_NULL);
|
||||||
|
|
||||||
if X.RowCount <> y.Length then
|
if X.RowCount <> y.Length then
|
||||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
DimensionError(ER_XY_SIZE_MISMATCH,X.RowCount,y.Length);
|
||||||
|
|
||||||
if X.RowCount = 0 then
|
if X.RowCount = 0 then
|
||||||
ArgumentError(ER_EMPTY_DATASET);
|
ArgumentError(ER_EMPTY_DATASET);
|
||||||
|
|
@ -8962,11 +8958,6 @@ begin
|
||||||
fMean := X.ColumnMeans;
|
fMean := X.ColumnMeans;
|
||||||
fStd := X.ColumnStd;
|
fStd := X.ColumnStd;
|
||||||
|
|
||||||
// защита от нулевой дисперсии
|
|
||||||
for var j := 0 to fStd.Length - 1 do
|
|
||||||
if Abs(fStd[j]) < 1e-12 then
|
|
||||||
fStd[j] := 1.0;
|
|
||||||
|
|
||||||
fFitted := true;
|
fFitted := true;
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
@ -8980,7 +8971,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
@ -8988,11 +8979,11 @@ begin
|
||||||
Result := new Matrix(n, p);
|
Result := new Matrix(n, p);
|
||||||
|
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
for var j := 0 to p - 1 do
|
for var j := 0 to p - 1 do
|
||||||
if fStd[j] <> 0 then
|
if Abs(fStd[j]) < 1e-12 then
|
||||||
Result[i,j] := (X[i,j] - fMean[j]) / fStd[j]
|
Result[i,j] := 0.0
|
||||||
else
|
else
|
||||||
Result[i,j] := 0.0;
|
Result[i,j] := (X[i,j] - fMean[j]) / fStd[j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function StandardScaler.InverseTransform(X: Matrix): Matrix;
|
function StandardScaler.InverseTransform(X: Matrix): Matrix;
|
||||||
|
|
@ -9004,7 +8995,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
@ -9074,7 +9065,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
@ -9113,7 +9104,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
@ -9198,7 +9189,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
@ -9275,7 +9266,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
if fSelected = nil then
|
if fSelected = nil then
|
||||||
Error(ER_MODEL_NOT_INITIALIZED);
|
Error(ER_MODEL_NOT_INITIALIZED);
|
||||||
|
|
@ -9602,7 +9593,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
if fSelected = nil then
|
if fSelected = nil then
|
||||||
Error(ER_MODEL_NOT_INITIALIZED);
|
Error(ER_MODEL_NOT_INITIALIZED);
|
||||||
|
|
@ -9671,7 +9662,7 @@ begin
|
||||||
ArgumentNullError(ER_X_NULL);
|
ArgumentNullError(ER_X_NULL);
|
||||||
|
|
||||||
if X.ColCount <> fFeatureCount then
|
if X.ColCount <> fFeatureCount then
|
||||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
DimensionError(ER_FEATURE_COUNT_MISMATCH,X.ColCount,fFeatureCount);
|
||||||
|
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
|
||||||
|
|
@ -334,16 +334,35 @@ begin
|
||||||
|
|
||||||
foreach var f in fFeatures do
|
foreach var f in fFeatures do
|
||||||
begin
|
begin
|
||||||
if current.HasColumn(f) then
|
var expanded := false;
|
||||||
|
|
||||||
|
// ищем expander-ы В ПРЯМОМ порядке pipeline
|
||||||
|
for var i := 0 to fDataSteps.Count - 1 do
|
||||||
begin
|
begin
|
||||||
feats.Add(f);
|
var expander := fDataSteps[i] as IColumnExpander;
|
||||||
continue;
|
if expander = nil then
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var cols := expander.GetExpandedColumns(f);
|
||||||
|
if (cols <> nil) and (cols.Length > 0) then
|
||||||
|
begin
|
||||||
|
foreach var c in cols do
|
||||||
|
if current.HasColumn(c) then
|
||||||
|
if not feats.Contains(c) then
|
||||||
|
feats.Add(c);
|
||||||
|
|
||||||
|
expanded := true;
|
||||||
|
break;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
foreach var c in current.Schema.ColumnNames do
|
if expanded then
|
||||||
if c.StartsWith(f + '_') then
|
continue;
|
||||||
if not feats.Contains(c) then
|
|
||||||
feats.Add(c);
|
// fallback: обычный столбец
|
||||||
|
if current.HasColumn(f) then
|
||||||
|
if not feats.Contains(f) then
|
||||||
|
feats.Add(f);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if feats.Count = 0 then
|
if feats.Count = 0 then
|
||||||
|
|
|
||||||
|
|
@ -1681,10 +1681,10 @@ end;
|
||||||
static function Metrics.AdjustedRandIndex(yTrue, yPred: Vector): real;
|
static function Metrics.AdjustedRandIndex(yTrue, yPred: Vector): real;
|
||||||
begin
|
begin
|
||||||
if yTrue = nil then
|
if yTrue = nil then
|
||||||
ArgumentNullError(ER_Y_NULL);
|
ArgumentNullError(ER_ARG_NULL, 'yTrue');
|
||||||
|
|
||||||
if yPred = nil then
|
if yPred = nil then
|
||||||
ArgumentNullError(ER_Y_NULL);
|
ArgumentNullError(ER_ARG_NULL, 'yPred');
|
||||||
|
|
||||||
var n := yTrue.Length;
|
var n := yTrue.Length;
|
||||||
if n <> yPred.Length then
|
if n <> yPred.Length then
|
||||||
|
|
@ -1774,56 +1774,6 @@ begin
|
||||||
Result := (sumNij - expected) / denom;
|
Result := (sumNij - expected) / denom;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{static function Metrics.AdjustedRandIndex(yTrue, yPred: Vector): real;
|
|
||||||
begin
|
|
||||||
if yTrue.Length <> yPred.Length then
|
|
||||||
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
|
|
||||||
|
|
||||||
var n := yTrue.Length;
|
|
||||||
|
|
||||||
if n < 2 then
|
|
||||||
Result := 1
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
var tp := 0;
|
|
||||||
var tn := 0;
|
|
||||||
var fp := 0;
|
|
||||||
var fn := 0;
|
|
||||||
|
|
||||||
for var i := 0 to n-2 do
|
|
||||||
for var j := i+1 to n-1 do
|
|
||||||
begin
|
|
||||||
var sameTrue := yTrue[i] = yTrue[j];
|
|
||||||
var samePred := yPred[i] = yPred[j];
|
|
||||||
|
|
||||||
if sameTrue and samePred then
|
|
||||||
tp += 1
|
|
||||||
else if (not sameTrue) and (not samePred) then
|
|
||||||
tn += 1
|
|
||||||
else if (not sameTrue) and samePred then
|
|
||||||
fp += 1
|
|
||||||
else
|
|
||||||
fn += 1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
var total := tp + tn + fp + fn;
|
|
||||||
|
|
||||||
var ri := (tp + tn) / total;
|
|
||||||
|
|
||||||
var a := tp + fp;
|
|
||||||
var b := tp + fn;
|
|
||||||
var c := fn + tn;
|
|
||||||
var d := fp + tn;
|
|
||||||
|
|
||||||
var expected := ((a*b) + (c*d)) / (total*total);
|
|
||||||
|
|
||||||
if 1 - expected = 0 then
|
|
||||||
Result := 0
|
|
||||||
else
|
|
||||||
Result := (ri - expected) / (1 - expected);
|
|
||||||
end;
|
|
||||||
end;}
|
|
||||||
|
|
||||||
function R(s: string; w: integer): string;
|
function R(s: string; w: integer): string;
|
||||||
begin
|
begin
|
||||||
Result := s.PadLeft(w);
|
Result := s.PadLeft(w);
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ type
|
||||||
/// Категории фиксируются при Fit
|
/// Категории фиксируются при Fit
|
||||||
/// Неизвестные категории кодируются нулями
|
/// Неизвестные категории кодируются нулями
|
||||||
/// Пропущенные значения (NA) кодируются нулями
|
/// Пропущенные значения (NA) кодируются нулями
|
||||||
OneHotEncoder = class(IPreprocessor, IColumnBoundStep)
|
OneHotEncoder = class(IPreprocessor, IColumnBoundStep, IColumnExpander)
|
||||||
private
|
private
|
||||||
col: string;
|
col: string;
|
||||||
categories: array of string;
|
categories: array of string;
|
||||||
|
|
@ -115,6 +115,8 @@ type
|
||||||
function ToString: string; override;
|
function ToString: string; override;
|
||||||
|
|
||||||
property ColumnName: string read col;
|
property ColumnName: string read col;
|
||||||
|
|
||||||
|
function GetExpandedColumns(sourceColumn: string): array of string;
|
||||||
|
|
||||||
/// Создаёт копию препроцессора с той же конфигурацией.
|
/// Создаёт копию препроцессора с той же конфигурацией.
|
||||||
///
|
///
|
||||||
|
|
@ -251,7 +253,13 @@ const
|
||||||
ER_IMPUTER_STRATEGY_NOT_SUPPORTED =
|
ER_IMPUTER_STRATEGY_NOT_SUPPORTED =
|
||||||
'Стратегия импутации {0} не поддерживается!!Imputation strategy {0} is not supported';
|
'Стратегия импутации {0} не поддерживается!!Imputation strategy {0} is not supported';
|
||||||
ER_UNSUPPORTED_IMPUTE_STRATEGY =
|
ER_UNSUPPORTED_IMPUTE_STRATEGY =
|
||||||
'Неподдерживаемая стратегия заполнения: {0}!!Unsupported impute strategy: {0}';
|
'Неподдерживаемая стратегия заполнения: {0}!!Unsupported impute strategy: {0}';
|
||||||
|
ER_ONEHOT_NAME_EQUALS_SOURCE =
|
||||||
|
'Сгенерированная колонка совпадает с исходной: {0}!!Generated column equals source column: {0}';
|
||||||
|
ER_ONEHOT_COLUMN_COLLISION =
|
||||||
|
'Конфликт имён колонок: {0}!!Column name collision: {0}';
|
||||||
|
ER_ONEHOT_DUPLICATE_COLUMN =
|
||||||
|
'Дублирующаяся сгенерированная колонка: {0}!!Duplicate generated column: {0}';
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
@ -407,6 +415,29 @@ begin
|
||||||
Error(ER_ONEHOT_EMPTY_COLUMN, col);
|
Error(ER_ONEHOT_EMPTY_COLUMN, col);
|
||||||
|
|
||||||
categories := values.ToArray;
|
categories := values.ToArray;
|
||||||
|
|
||||||
|
// --- проверка коллизий имён колонок
|
||||||
|
var used := new HashSet<string>;
|
||||||
|
|
||||||
|
for var i := 0 to categories.Length - 1 do
|
||||||
|
begin
|
||||||
|
var newName := col + '_' + categories[i];
|
||||||
|
|
||||||
|
// 1. совпадение с исходным именем
|
||||||
|
if newName = col then
|
||||||
|
Error(ER_ONEHOT_NAME_EQUALS_SOURCE, newName);
|
||||||
|
|
||||||
|
// 2. коллизия с существующими колонками DataFrame
|
||||||
|
if df.HasColumn(newName) then
|
||||||
|
Error(ER_ONEHOT_COLUMN_COLLISION, newName);
|
||||||
|
|
||||||
|
// 3. дубликаты среди сгенерированных колонок
|
||||||
|
if used.Contains(newName) then
|
||||||
|
Error(ER_ONEHOT_DUPLICATE_COLUMN, newName);
|
||||||
|
|
||||||
|
used.Add(newName);
|
||||||
|
end;
|
||||||
|
|
||||||
fitted := true;
|
fitted := true;
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
@ -465,6 +496,21 @@ begin
|
||||||
Result := Transform(df);
|
Result := Transform(df);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function OneHotEncoder.GetExpandedColumns(sourceColumn: string): array of string;
|
||||||
|
begin
|
||||||
|
if not fitted then
|
||||||
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
||||||
|
if sourceColumn <> col then
|
||||||
|
exit(nil);
|
||||||
|
|
||||||
|
var res := new string[categories.Length];
|
||||||
|
for var i := 0 to categories.Length - 1 do
|
||||||
|
res[i] := col + '_' + categories[i];
|
||||||
|
|
||||||
|
Result := res;
|
||||||
|
end;
|
||||||
|
|
||||||
function OneHotEncoder.ToString: string;
|
function OneHotEncoder.ToString: string;
|
||||||
begin
|
begin
|
||||||
Result := 'OneHotEncoder(column=' + col + ')';
|
Result := 'OneHotEncoder(column=' + col + ')';
|
||||||
|
|
@ -615,13 +661,15 @@ begin
|
||||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||||
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
||||||
|
|
||||||
|
var capturedIdx := idx;
|
||||||
|
|
||||||
case strategy of
|
case strategy of
|
||||||
isMean:
|
isMean:
|
||||||
begin
|
begin
|
||||||
var m := means[i];
|
var m := means[i];
|
||||||
res := res.ReplaceColumnFloat(
|
res := res.ReplaceColumnFloat(
|
||||||
name,
|
name,
|
||||||
c -> (if c.IsValid(idx) then c.Float(idx) else m)
|
c -> (if c.IsValid(capturedIdx) then c.Float(capturedIdx) else m)
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -665,7 +713,7 @@ begin
|
||||||
|
|
||||||
res := res.ReplaceColumnFloat(
|
res := res.ReplaceColumnFloat(
|
||||||
name,
|
name,
|
||||||
c -> (if c.IsValid(idx) then c.Float(idx) else r)
|
c -> (if c.IsValid(capturedIdx) then c.Float(capturedIdx) else r)
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
@ -675,7 +723,7 @@ begin
|
||||||
var m := medians[i];
|
var m := medians[i];
|
||||||
res := res.ReplaceColumnFloat(
|
res := res.ReplaceColumnFloat(
|
||||||
name,
|
name,
|
||||||
c -> (if c.IsValid(idx) then c.Float(idx) else m)
|
c -> (if c.IsValid(capturedIdx) then c.Float(capturedIdx) else m)
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,24 @@ interface
|
||||||
uses LinearAlgebraML, MLCoreABC;
|
uses LinearAlgebraML, MLCoreABC;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
/// Методы для разбиения данных и оценки моделей.
|
||||||
|
///
|
||||||
|
/// Содержит утилиты для:
|
||||||
|
/// • разделения выборки на обучающую и тестовую (TrainTestSplit)
|
||||||
|
/// • k-fold кросс-валидации (KFold)
|
||||||
|
/// • стратифицированной кросс-валидации (StratifiedKFold)
|
||||||
|
/// • оценки моделей через кросс-валидацию (CrossValidate, StratifiedCrossValidate)
|
||||||
|
///
|
||||||
|
/// Методы возвращают индексы или подвыборки без изменения исходных данных.
|
||||||
|
///
|
||||||
|
/// • KFold — простое разбиение без учёта распределения классов
|
||||||
|
/// • StratifiedKFold — сохраняет пропорции классов в каждом fold (для классификации)
|
||||||
|
///
|
||||||
|
/// Для стратифицированных методов требуется:
|
||||||
|
/// • целочисленные метки классов
|
||||||
|
/// • число объектов каждого класса ≥ числа фолдов
|
||||||
|
///
|
||||||
|
/// Все методы используют генератор случайных чисел (seed) для воспроизводимости
|
||||||
Validation = static class
|
Validation = static class
|
||||||
private
|
private
|
||||||
static function CrossValidateCore(model: ISupervisedModel;
|
static function CrossValidateCore(model: ISupervisedModel;
|
||||||
|
|
@ -93,6 +111,7 @@ type
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses MLExceptions;
|
uses MLExceptions;
|
||||||
|
uses MLUtilsABC;
|
||||||
|
|
||||||
const
|
const
|
||||||
ER_DIM_MISMATCH_TRAIN_TEST =
|
ER_DIM_MISMATCH_TRAIN_TEST =
|
||||||
|
|
@ -114,7 +133,9 @@ const
|
||||||
'At least 2 samples are required for {0}';
|
'At least 2 samples are required for {0}';
|
||||||
ER_STRATIFIED_CLASS_TOO_SMALL =
|
ER_STRATIFIED_CLASS_TOO_SMALL =
|
||||||
'Класс {0} содержит {1} объектов, что меньше числа фолдов ({2})!!Class {0} has {1} samples, which is less than the number of folds ({2})';
|
'Класс {0} содержит {1} объектов, что меньше числа фолдов ({2})!!Class {0} has {1} samples, which is less than the number of folds ({2})';
|
||||||
|
ER_STRATIFIED_K_TOO_LARGE =
|
||||||
|
'Stratified CV: число фолдов ({0}) превышает минимальный размер класса ({1})!!Stratified CV: number of folds ({0}) exceeds smallest class size ({1})';
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// Validation
|
// Validation
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
@ -263,12 +284,8 @@ begin
|
||||||
var trainSize := n - size;
|
var trainSize := n - size;
|
||||||
var trainIdx := new integer[trainSize];
|
var trainIdx := new integer[trainSize];
|
||||||
|
|
||||||
if start > 0 then
|
System.Array.Copy(idx, 0, trainIdx, 0, start);
|
||||||
System.Array.Copy(idx, 0, trainIdx, 0, start);
|
System.Array.Copy(idx, start + size, trainIdx, start, n - (start + size));
|
||||||
|
|
||||||
var tailCount := n - (start + size);
|
|
||||||
if tailCount > 0 then
|
|
||||||
System.Array.Copy(idx, start + size, trainIdx, start, tailCount);
|
|
||||||
|
|
||||||
yield (trainIdx, testIdx);
|
yield (trainIdx, testIdx);
|
||||||
|
|
||||||
|
|
@ -442,6 +459,17 @@ begin
|
||||||
|
|
||||||
if (k < 2) or (k > X.RowCount) then
|
if (k < 2) or (k > X.RowCount) then
|
||||||
ArgumentError(ER_K_INVALID_STRATIFIED, k, X.RowCount);
|
ArgumentError(ER_K_INVALID_STRATIFIED, k, X.RowCount);
|
||||||
|
|
||||||
|
var labels := y.ToIntArray;
|
||||||
|
var classCounts := labels.EachCount;
|
||||||
|
|
||||||
|
var minCount := integer.MaxValue;
|
||||||
|
foreach var pair in classCounts do
|
||||||
|
if pair.Value < minCount then
|
||||||
|
minCount := pair.Value;
|
||||||
|
|
||||||
|
if k > minCount then
|
||||||
|
ArgumentError(ER_STRATIFIED_K_TOO_LARGE, k, minCount);
|
||||||
|
|
||||||
var baseSeed :=
|
var baseSeed :=
|
||||||
if seed >= 0 then seed
|
if seed >= 0 then seed
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue