Правки ML
This commit is contained in:
parent
13a1804dda
commit
10c929fa1c
|
|
@ -15,7 +15,7 @@ internal static class RevisionClass
|
|||
public const string Major = "3";
|
||||
public const string Minor = "11";
|
||||
public const string Build = "1";
|
||||
public const string Revision = "3805";
|
||||
public const string Revision = "3808";
|
||||
|
||||
public const string MainVersion = Major + "." + Minor;
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%MINOR%=11
|
||||
%REVISION%=3805
|
||||
%COREVERSION%=1
|
||||
%REVISION%=3808
|
||||
%MINOR%=11
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.1.3805
|
||||
3.11.1.3808
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.1.3805'
|
||||
!define VERSION '3.11.1.3808'
|
||||
|
|
|
|||
|
|
@ -25,9 +25,6 @@ const
|
|||
'Столбец "{0}" должен быть категориальным для EncodeLabels!!Column "{0}" must be categorical for EncodeLabels';
|
||||
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
||||
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
||||
ER_UNKNOWN_CLASS_IN_TRANSFORM =
|
||||
'Неизвестное значение класса "{0}" при преобразовании меток!!Unknown class value "{0}" in TransformLabels';
|
||||
|
||||
|
||||
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
|
||||
begin
|
||||
|
|
@ -180,49 +177,25 @@ begin
|
|||
if not Self.IsCategorical(target) then
|
||||
ArgumentError(ER_ENCODELABELS_NOT_CATEGORICAL, target);
|
||||
|
||||
// --- строим mapping
|
||||
var map := new Dictionary<string, integer>;
|
||||
for var i := 0 to classes.Length - 1 do
|
||||
map[classes[i]] := i;
|
||||
|
||||
case Self.GetColumnType(target) of
|
||||
|
||||
|
||||
ColumnType.ctStr:
|
||||
begin
|
||||
var data := Self.GetStrColumn(target).ToArray;
|
||||
var res := new integer[data.Length];
|
||||
|
||||
for var i := 0 to data.Length - 1 do
|
||||
begin
|
||||
var lbl := data[i];
|
||||
|
||||
if not map.ContainsKey(lbl) then
|
||||
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
|
||||
|
||||
res[i] := map[lbl];
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
Result := TransformLabels(data, classes);
|
||||
end;
|
||||
|
||||
|
||||
ColumnType.ctInt:
|
||||
begin
|
||||
var data := Self.GetIntColumn(target).ToArray;
|
||||
var res := new integer[data.Length];
|
||||
|
||||
var strData := new string[data.Length];
|
||||
|
||||
for var i := 0 to data.Length - 1 do
|
||||
begin
|
||||
var lbl := data[i].ToString;
|
||||
|
||||
if not map.ContainsKey(lbl) then
|
||||
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
|
||||
|
||||
res[i] := map[lbl];
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
strData[i] := data[i].ToString;
|
||||
|
||||
Result := TransformLabels(strData, classes);
|
||||
end;
|
||||
|
||||
|
||||
else
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
end;
|
||||
|
|
@ -252,25 +225,9 @@ begin
|
|||
if Self.GetColumnType(target) <> ColumnType.ctInt then
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
|
||||
// --- mapping: значение → индекс
|
||||
var map := new Dictionary<integer, integer>;
|
||||
for var i := 0 to classes.Length - 1 do
|
||||
map[classes[i]] := i;
|
||||
|
||||
var data := Self.GetIntColumn(target).ToArray;
|
||||
var res := new integer[data.Length];
|
||||
|
||||
for var i := 0 to data.Length - 1 do
|
||||
begin
|
||||
var v := data[i];
|
||||
|
||||
if not map.ContainsKey(v) then
|
||||
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, v);
|
||||
|
||||
res[i] := map[v];
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
Result := TransformLabelsInt(data, classes);
|
||||
end;
|
||||
|
||||
/// Кодирует значения целочисленного категориального столбца DataFrame
|
||||
|
|
|
|||
|
|
@ -6464,9 +6464,6 @@ static function CSVLoader.Load(filename: string;
|
|||
begin
|
||||
var enc := encoding;
|
||||
|
||||
//if enc = nil then
|
||||
// enc := System.Text.Encoding.Utf8;
|
||||
|
||||
if enc = nil then
|
||||
enc := DetectEncoding(filename);
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,10 @@ type
|
|||
rightPrefix: string
|
||||
): DataFrameSchema;
|
||||
|
||||
{ --- debug --- }
|
||||
{ --- DEBUG ONLY ---
|
||||
Проверка внутренних инвариантов схемы.
|
||||
Использует Assert и выполняется только в debug-сборке.
|
||||
Не предназначена для обработки пользовательских ошибок. }
|
||||
procedure AssertConsistent;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -90,11 +90,6 @@ begin
|
|||
|
||||
var resultVec := new Vector(p);
|
||||
|
||||
// Базовый seed для всех параметров.
|
||||
// Все модели оцениваются на одинаковых фолдах,
|
||||
// что обеспечивает корректное сравнение.
|
||||
// При seed = -1 разбиение случайное, но фиксируется
|
||||
// один раз для всего GridSearch.
|
||||
var baseSeed :=
|
||||
if seed >= 0 then seed
|
||||
else System.Environment.TickCount and integer.MaxValue;
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ type
|
|||
|
||||
function Clone: Vector;
|
||||
|
||||
function Normalize: Vector;
|
||||
function Normalized: Vector;
|
||||
|
||||
function ToArray: array of real;
|
||||
function ToIntArray: array of integer;
|
||||
|
||||
|
|
@ -69,7 +72,9 @@ type
|
|||
|
||||
// ---------- Основные методы ----------
|
||||
function Sum: real;
|
||||
/// Среднее. Синоним Mean
|
||||
function Average: real;
|
||||
/// Среднее
|
||||
function Mean: real;
|
||||
function Norm2: real;
|
||||
function Norm: real;
|
||||
|
|
@ -295,8 +300,6 @@ const
|
|||
'Вектор пуст!!Vector is empty';
|
||||
ER_VECTOR_DIVIDE_BY_ZERO =
|
||||
'Деление на ноль при делении вектора на скаляр!!Division by zero in Vector / scalar';
|
||||
ER_DIM_MISMATCH =
|
||||
'Несоответствие размерностей: {0} и {1}!!Dimension mismatch: {0} and {1}';
|
||||
ER_MATRIX_SIZE_NEGATIVE =
|
||||
'Размеры матрицы должны быть неотрицательными!!Matrix size must be non-negative';
|
||||
ER_MATRIX_SIZE_MISMATCH =
|
||||
|
|
@ -384,6 +387,25 @@ begin
|
|||
Result := new Vector(fdata);
|
||||
end;
|
||||
|
||||
function Vector.Normalize: Vector;
|
||||
begin
|
||||
var sum := 0.0;
|
||||
for var i := 0 to Length - 1 do
|
||||
sum += Self[i];
|
||||
|
||||
if sum > 0 then
|
||||
for var i := 0 to Length - 1 do
|
||||
Self[i] /= sum;
|
||||
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function Vector.Normalized: Vector;
|
||||
begin
|
||||
Result := Self.Clone;
|
||||
Result.Normalize;
|
||||
end;
|
||||
|
||||
static procedure Vector.CheckSameLength(a, b: Vector);
|
||||
begin
|
||||
if a.Length <> b.Length then
|
||||
|
|
@ -428,6 +450,7 @@ static function Vector.operator /(v: Vector; alpha: real): Vector;
|
|||
begin
|
||||
if alpha = 0.0 then
|
||||
raise new System.DivideByZeroException(GetTranslation(ER_VECTOR_DIVIDE_BY_ZERO));
|
||||
|
||||
Result := new Vector(v.Length);
|
||||
var inv := 1.0 / alpha;
|
||||
for var i := 0 to v.Length - 1 do
|
||||
|
|
@ -479,12 +502,10 @@ begin
|
|||
Result := s;
|
||||
end;
|
||||
|
||||
// Алиас Mean
|
||||
function Vector.Average: real;
|
||||
begin
|
||||
if Length = 0 then
|
||||
ArgumentError(ER_VECTOR_EMPTY);
|
||||
|
||||
Result := Sum / Length;
|
||||
Result := Mean;
|
||||
end;
|
||||
|
||||
function Vector.Mean: real;
|
||||
|
|
@ -541,6 +562,7 @@ constructor Matrix.Create(r, c: integer);
|
|||
begin
|
||||
if (r < 0) or (c < 0) then
|
||||
ArgumentOutOfRangeError(ER_MATRIX_SIZE_NEGATIVE);
|
||||
|
||||
fdata := new real[r, c];
|
||||
end;
|
||||
|
||||
|
|
@ -580,8 +602,10 @@ end;
|
|||
function Matrix.ColumnMeans: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
if n = 0 then
|
||||
exit( new Vector(ColCount) );
|
||||
|
||||
if n = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
Result := ColumnSums / n;
|
||||
end;
|
||||
|
||||
|
|
@ -598,20 +622,28 @@ end;
|
|||
|
||||
function Matrix.RowMeans: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
|
||||
if n = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if p = 0 then
|
||||
exit (new Vector(RowCount));
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
Result := RowSums / p;
|
||||
end;
|
||||
|
||||
function Matrix.ColumnVariances: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
|
||||
if n = 0 then
|
||||
exit (new Vector(ColCount));
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
var means := ColumnMeans;
|
||||
var p := ColCount;
|
||||
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
|
|
@ -631,12 +663,17 @@ end;
|
|||
|
||||
function Matrix.RowVariances: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
|
||||
if n = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if p = 0 then
|
||||
exit (new Vector(RowCount));
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
var means := RowMeans;
|
||||
var n := RowCount;
|
||||
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
|
|
@ -658,10 +695,10 @@ function Matrix.ColumnMins: Vector;
|
|||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
|
||||
if n = 0 then
|
||||
exit(new Vector(p));
|
||||
|
||||
if n = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
|
|
@ -678,10 +715,10 @@ function Matrix.ColumnMaxs: Vector;
|
|||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
|
||||
if n = 0 then
|
||||
exit(new Vector(p));
|
||||
|
||||
if n = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
|
|
@ -700,8 +737,11 @@ begin
|
|||
var p := ColCount;
|
||||
|
||||
if n = 0 then
|
||||
exit(new Vector(p));
|
||||
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if p = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
|
|
@ -720,8 +760,11 @@ begin
|
|||
var p := ColCount;
|
||||
|
||||
if n = 0 then
|
||||
exit(new Vector(p));
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if p = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
|
|
@ -736,6 +779,15 @@ end;
|
|||
|
||||
function Matrix.RowArgMin(i: integer): integer;
|
||||
begin
|
||||
if RowCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if ColCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if (i < 0) or (i >= RowCount) then
|
||||
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE, i, RowCount);
|
||||
|
||||
var minVal := fdata[i,0];
|
||||
var arg := 0;
|
||||
|
||||
|
|
@ -756,6 +808,15 @@ end;
|
|||
|
||||
function Matrix.RowArgMax(i: integer): integer;
|
||||
begin
|
||||
if RowCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if ColCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if (i < 0) or (i >= RowCount) then
|
||||
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE, i, RowCount);
|
||||
|
||||
var maxVal := fdata[i,0];
|
||||
var arg := 0;
|
||||
|
||||
|
|
@ -776,6 +837,12 @@ end;
|
|||
|
||||
function Matrix.RowSum(i: integer): real;
|
||||
begin
|
||||
if RowCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
if (i < 0) or (i >= RowCount) then
|
||||
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE, i, RowCount);
|
||||
|
||||
var sum := 0.0;
|
||||
|
||||
for var j := 0 to ColCount - 1 do
|
||||
|
|
@ -810,6 +877,9 @@ end;
|
|||
|
||||
function Matrix.ColumnArgMin(j: integer): integer;
|
||||
begin
|
||||
if (j < 0) or (j >= ColCount) then
|
||||
ArgumentOutOfRangeError(ER_COL_INDEX_OUT_OF_RANGE, j, ColCount);
|
||||
|
||||
var n := RowCount;
|
||||
|
||||
if n = 0 then
|
||||
|
|
@ -835,6 +905,9 @@ end;
|
|||
|
||||
function Matrix.ColumnArgMax(j: integer): integer;
|
||||
begin
|
||||
if (j < 0) or (j >= ColCount) then
|
||||
ArgumentOutOfRangeError(ER_COL_INDEX_OUT_OF_RANGE, j, ColCount);
|
||||
|
||||
var n := RowCount;
|
||||
|
||||
if n = 0 then
|
||||
|
|
@ -895,6 +968,9 @@ end;
|
|||
|
||||
function Matrix.FrobeniusNorm: real;
|
||||
begin
|
||||
if RowCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
var s := 0.0;
|
||||
for var i := 0 to RowCount - 1 do
|
||||
for var j := 0 to ColCount - 1 do
|
||||
|
|
@ -905,6 +981,10 @@ end;
|
|||
procedure Matrix.AddScaledIdentity(lambda: real);
|
||||
begin
|
||||
var n := RowCount;
|
||||
|
||||
if RowCount <> ColCount then
|
||||
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
fdata[i, i] += lambda;
|
||||
end;
|
||||
|
|
@ -1060,6 +1140,7 @@ function Matrix.GetRow(i: integer): Vector;
|
|||
begin
|
||||
if (i < 0) or (i >= RowCount) then
|
||||
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE, i, RowCount);
|
||||
|
||||
Result := new Vector(ColCount);
|
||||
for var j := 0 to ColCount - 1 do
|
||||
Result[j] := fdata[i, j];
|
||||
|
|
@ -1067,8 +1148,14 @@ end;
|
|||
|
||||
function Matrix.TakeRows(indices: array of integer): Matrix;
|
||||
begin
|
||||
if indices = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'indices');
|
||||
|
||||
var n := indices.Length;
|
||||
var p := ColCount;
|
||||
|
||||
if RowCount = 0 then
|
||||
Error(ER_EMPTY_MATRIX);
|
||||
|
||||
var res := new Matrix(n, p);
|
||||
|
||||
|
|
@ -1124,6 +1211,7 @@ function Matrix.GetCol(j: integer): Vector;
|
|||
begin
|
||||
if (j < 0) or (j >= ColCount) then
|
||||
ArgumentOutOfRangeError(ER_COL_INDEX_OUT_OF_RANGE, j, ColCount);
|
||||
|
||||
Result := new Vector(RowCount);
|
||||
for var i := 0 to RowCount - 1 do
|
||||
Result[i] := fdata[i, j];
|
||||
|
|
@ -1141,6 +1229,12 @@ end;
|
|||
|
||||
static function Matrix.OuterProduct(a, b: Vector): Matrix;
|
||||
begin
|
||||
if a = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'a');
|
||||
|
||||
if b = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'b');
|
||||
|
||||
var m := a.Length;
|
||||
var n := b.Length;
|
||||
Result := new Matrix(m, n);
|
||||
|
|
|
|||
|
|
@ -295,10 +295,10 @@ const
|
|||
ER_PARAM_BETWEEN_01 =
|
||||
'Параметр {0} должен быть в диапазоне (0,1)!!Parameter {0} must be in range (0,1)';
|
||||
ER_DATASET_NO_TARGET =
|
||||
'У датасета нет целевой переменной (задача кластеризации).' +
|
||||
'У датасета нет целевой переменной (задача кластеризации).!!' +
|
||||
'Dataset has no target variable (clustering task).';
|
||||
ER_DATASET_TARGET_NOT_FOUND =
|
||||
'Целевая переменная "{0}" не найдена в таблице.' +
|
||||
'Целевая переменная "{0}" не найдена в таблице.!!' +
|
||||
'Target column "{0}" not found in DataFrame';
|
||||
ER_DATASET_META_NOT_FOUND =
|
||||
'Файл метаданных датасета "{0}" не найден!!Dataset meta file "{0}" not found';
|
||||
|
|
@ -324,8 +324,6 @@ const
|
|||
'Параметр {0} должен быть в диапазоне (0, 1)!!Parameter {0} must be in range (0, 1)';
|
||||
ER_PARAM_LT =
|
||||
'Параметр {0} должен быть меньше допустимого значения!!Parameter {0} must be less than allowed value';
|
||||
ER_TEST_RATIO_INVALID =
|
||||
'Некорректное значение testRatio (должно быть между 0 и 1)!!Invalid testRatio (must be between 0 and 1)';
|
||||
ER_GROUPBY_UNSUPPORTED_KEY_TYPE =
|
||||
'Неподдерживаемый тип ключа для группировки!!Unsupported key type for grouping';
|
||||
ER_STRATIFIED_ONLY_FOR_CLASSIFICATION =
|
||||
|
|
@ -334,7 +332,9 @@ const
|
|||
'Слишком малое значение classBalance: {0}. Минимально допустимое значение — 1e-3!!classBalance is too small: {0}. Minimum allowed value is 1e-3';
|
||||
ER_UNSUPPORTED_TARGET_TYPE =
|
||||
'Неподдерживаемый тип целевого столбца: {0}!!Unsupported target column type: {0}';
|
||||
|
||||
ER_ENCODELABELS_UNSUPPORTED_TYPE =
|
||||
'Неподдерживаемый тип столбца для кодирования меток: {0}!!' +
|
||||
'Unsupported column type for label encoding: {0}';
|
||||
|
||||
C_DATASET = 'Датасет: {0}!!Dataset: {0}';
|
||||
C_DESCRIPTION = 'Описание:!!Description:';
|
||||
|
|
@ -613,15 +613,39 @@ begin
|
|||
if Task <> TaskType.Classification then
|
||||
ArgumentError(ER_VALUECOUNTS_ONLY_CLASSIFICATION);
|
||||
|
||||
var labels := Data.GetStrColumn(Target);
|
||||
|
||||
var dict := new Dictionary<string,integer>;
|
||||
|
||||
foreach var v in labels do
|
||||
if dict.ContainsKey(v) then
|
||||
dict[v] += 1
|
||||
case Data.GetColumnType(Target) of
|
||||
|
||||
ColumnType.ctStr:
|
||||
begin
|
||||
var labels := Data.GetStrColumn(Target);
|
||||
|
||||
foreach var v in labels do
|
||||
if dict.ContainsKey(v) then
|
||||
dict[v] += 1
|
||||
else
|
||||
dict[v] := 1;
|
||||
end;
|
||||
|
||||
ColumnType.ctInt:
|
||||
begin
|
||||
var labels := Data.GetIntColumn(Target);
|
||||
|
||||
foreach var v in labels do
|
||||
begin
|
||||
var s := v.ToString;
|
||||
|
||||
if dict.ContainsKey(s) then
|
||||
dict[s] += 1
|
||||
else
|
||||
dict[s] := 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
else
|
||||
dict[v] := 1;
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, Target);
|
||||
end;
|
||||
|
||||
Result := dict;
|
||||
end;
|
||||
|
|
@ -879,7 +903,7 @@ begin
|
|||
ArgumentOutOfRangeError(ER_PARAM_GE_ZERO, 'nInformative');
|
||||
|
||||
if nInformative > nFeatures then
|
||||
nInformative := nFeatures;
|
||||
ArgumentOutOfRangeError(ER_PARAM_LE, 'nInformative');
|
||||
|
||||
if noise < 0 then
|
||||
ArgumentOutOfRangeError(ER_PARAM_GE_ZERO, 'noise');
|
||||
|
|
@ -1065,7 +1089,26 @@ begin
|
|||
X[row,1] := r * Sin(t) + noise * Normal(rnd);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
// --- обработка хвоста (если n не делится на classes)
|
||||
for var i := classes * perClass to n - 1 do
|
||||
begin
|
||||
var row := idx[i];
|
||||
|
||||
var c := i mod classes; // равномернее, чем rnd
|
||||
|
||||
var u := rnd.NextDouble;
|
||||
|
||||
var r := radius * u;
|
||||
|
||||
var t := turns * 2 * Pi * u + c * 2 * Pi / classes + noise * 0.5 * Normal(rnd);
|
||||
|
||||
y[row] := c;
|
||||
|
||||
X[row,0] := r * Cos(t) + noise * Normal(rnd);
|
||||
X[row,1] := r * Sin(t) + noise * Normal(rnd);
|
||||
end;
|
||||
|
||||
Result := (X, y);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const
|
|||
ER_Y_NULL =
|
||||
'y не может быть nil!!y cannot be nil';
|
||||
ER_XY_SIZE_MISMATCH =
|
||||
'Размерности X и y не согласованы!!X and y size mismatch';
|
||||
'Размеры X и y не совпадают: X={0}, y={1}!!X and y size mismatch: X={0}, y={1}';
|
||||
ER_FEATURE_COUNT_MISMATCH =
|
||||
'Число признаков не совпадает!!Feature count mismatch';
|
||||
ER_NAN_IN_X =
|
||||
|
|
@ -50,8 +50,13 @@ const
|
|||
'Данные для предсказания содержат Infinity. Проверьте данные на выбросы.!!' +
|
||||
'Prediction data contains Infinity. Please check for extreme values.';
|
||||
ER_MODEL_NOT_FITTED =
|
||||
'Модель "{0}" не обучена. Сначала вызовите Fit()|Model "{0}" is not fitted. Call Fit() first';
|
||||
|
||||
'Модель "{0}" не обучена. Сначала вызовите Fit()!!Model "{0}" is not fitted. Call Fit() first';
|
||||
ER_TEST_RATIO_INVALID =
|
||||
'Параметр testRatio должен быть в интервале (0,1), получено {0}!!' +
|
||||
'Parameter testRatio must be in (0,1), got {0}';
|
||||
ER_PREDICT_NOT_SUPPORTED =
|
||||
'Модель не поддерживает операцию Predict!!' +
|
||||
'Model does not support Predict operation';
|
||||
|
||||
type
|
||||
/// Базовое исключение ML-библиотеки
|
||||
|
|
|
|||
|
|
@ -283,6 +283,8 @@ type
|
|||
fAlpha: real;
|
||||
fMaxIter: integer;
|
||||
fTol: real;
|
||||
|
||||
function GetIsFitted: boolean;
|
||||
public
|
||||
/// Создаёт модель Lasso-регрессии.
|
||||
/// alpha — коэффициент L1-регуляризации.
|
||||
|
|
@ -326,7 +328,7 @@ type
|
|||
|
||||
/// Показывает, была ли модель обучена.
|
||||
/// После вызова Fit значение становится True.
|
||||
property IsFitted: boolean read fModel.fFitted;
|
||||
property IsFitted: boolean read GetIsFitted;
|
||||
|
||||
function Name: string := Self.GetType.Name;
|
||||
end;
|
||||
|
|
@ -557,8 +559,10 @@ type
|
|||
procedure Fit(X: Matrix; y: Vector); // y уже 0..K-1
|
||||
function Predict(X: Matrix): Vector; // возвращает 0..K-1
|
||||
function PredictRow(X: Matrix; row: integer): integer;
|
||||
|
||||
|
||||
private
|
||||
function GetFeatureImportances: Vector;
|
||||
|
||||
function CreateLeaf(y: Vector; indices: array of integer): DecisionTreeNode;
|
||||
|
||||
function BuildNode(X: Matrix; y: Vector; indices: array of integer; depth: integer): DecisionTreeNode;
|
||||
|
|
@ -573,9 +577,9 @@ type
|
|||
var Xr: Matrix; var yr: Vector
|
||||
);
|
||||
|
||||
property FeatureImportances: Vector read fFeatureImportances;
|
||||
|
||||
function MajorityClass(y: Vector; indices: array of integer): integer;
|
||||
public
|
||||
property FeatureImportances: Vector read GetFeatureImportances;
|
||||
|
||||
function PredictOne(x: Vector; node: DecisionTreeNode): integer;
|
||||
function Clone: DecisionTreeCore;
|
||||
|
|
@ -1566,6 +1570,9 @@ type
|
|||
fMark: array of integer;
|
||||
fTouched: array of integer;
|
||||
fEpoch: integer;
|
||||
|
||||
fSampleIdx: array of integer;
|
||||
fSampleSize: integer;
|
||||
|
||||
public
|
||||
/// Создаёт классификатор kNN.
|
||||
|
|
@ -1675,7 +1682,6 @@ type
|
|||
fMaxIter: integer;
|
||||
fTol: real;
|
||||
fNInit: integer;
|
||||
fSeed: integer;
|
||||
|
||||
fFitted: boolean;
|
||||
fFeatureCount: integer;
|
||||
|
|
@ -1751,8 +1757,6 @@ type
|
|||
property Tol: real read fTol;
|
||||
/// Количество запусков алгоритма.
|
||||
property NInit: integer read fNInit;
|
||||
/// Используемый seed.
|
||||
property Seed: integer read fSeed;
|
||||
/// Матрица центроидов (k × p).
|
||||
property ClusterCenters: Matrix read fCenters;
|
||||
/// Значение инерции (сумма квадратов расстояний до центроидов).
|
||||
|
|
@ -2051,6 +2055,9 @@ type
|
|||
|
||||
/// Применяет стандартизацию к данным.
|
||||
function Transform(X: Matrix): Matrix;
|
||||
|
||||
/// Обратная операция к Transform.
|
||||
function InverseTransform(X: Matrix): Matrix;
|
||||
|
||||
/// Средние значения признаков, вычисленные при обучении.
|
||||
property Mean: Vector read fMean;
|
||||
|
|
@ -2106,6 +2113,9 @@ type
|
|||
|
||||
/// Применяет линейное масштабирование признаков к диапазону [0, 1].
|
||||
function Transform(X: Matrix): Matrix;
|
||||
|
||||
/// Преобразование, обратное Transform
|
||||
function InverseTransform(X: Matrix): Matrix;
|
||||
|
||||
/// Минимальные значения признаков, вычисленные при обучении.
|
||||
property Min: Vector read fMin;
|
||||
|
|
@ -2587,9 +2597,6 @@ const
|
|||
ER_INTERNAL_INVALID_MODEL_CLONE =
|
||||
'Внутренняя ошибка: Clone модели вернул несовместимый тип!!' +
|
||||
'Internal error: model Clone returned incompatible type';
|
||||
ER_PREDICT_NOT_SUPPORTED =
|
||||
'Модель не поддерживает Predict для данного типа алгоритма!!' +
|
||||
'Model does not support Predict for this type of algorithm';
|
||||
ER_NEED_AT_LEAST_TWO_CLASSES =
|
||||
'Необходимо как минимум два различных класса!!At least two distinct classes are required';
|
||||
ER_UNKNOWN_CLASS_LABEL =
|
||||
|
|
@ -3084,6 +3091,10 @@ begin
|
|||
Result := new LassoRegression(fAlpha, fMaxIter, fTol);
|
||||
end;
|
||||
|
||||
function LassoRegression.GetIsFitted: boolean;
|
||||
begin
|
||||
Result := (fModel <> nil) and fModel.IsFitted;
|
||||
end;
|
||||
//-----------------------------
|
||||
// LogisticRegression
|
||||
//-----------------------------
|
||||
|
|
@ -3576,6 +3587,14 @@ begin
|
|||
fRng := new System.Random(fRandomSeed);
|
||||
end;
|
||||
|
||||
function DecisionTreeCore.GetFeatureImportances: Vector;
|
||||
begin
|
||||
if fRoot = nil then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
Result := fFeatureImportances.Normalized;
|
||||
end;
|
||||
|
||||
procedure DecisionTreeCore.Fit(X: Matrix; y: Vector);
|
||||
begin
|
||||
// --- init importance
|
||||
|
|
@ -3673,7 +3692,7 @@ end;
|
|||
function DecisionTreeCore.BuildNode(X: Matrix; y: Vector; indices: array of integer; depth: integer): DecisionTreeNode;
|
||||
begin
|
||||
// 1. stop: depth
|
||||
if depth >= fMaxDepth then
|
||||
if (fMaxDepth > 0) and (depth >= fMaxDepth) then
|
||||
exit(CreateLeaf(y, indices));
|
||||
|
||||
// 2. stop: min samples
|
||||
|
|
@ -4061,7 +4080,7 @@ begin
|
|||
if minSamplesLeaf < 1 then
|
||||
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
|
||||
|
||||
if minSamplesLeaf >= minSamplesSplit then
|
||||
if 2 * minSamplesLeaf >= minSamplesSplit then
|
||||
ArgumentOutOfRangeError(
|
||||
ER_MIN_LEAF_GE_SPLIT,
|
||||
minSamplesLeaf, minSamplesSplit
|
||||
|
|
@ -5108,6 +5127,7 @@ begin
|
|||
fMinSamplesSplit,
|
||||
fMinSamplesLeaf,
|
||||
treeCriterion,
|
||||
fClassCount,
|
||||
ComputeMaxFeatures(p),
|
||||
treeSeed
|
||||
);
|
||||
|
|
@ -5309,6 +5329,9 @@ function RandomForestClassifier.FeatureImportances: Vector;
|
|||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if fTrees.Length = 0 then
|
||||
Error(ER_MODEL_NOT_INITIALIZED);
|
||||
|
||||
var p := fTrees[0].FeatureImportances.Length;
|
||||
var resultVec := new Vector(p);
|
||||
|
|
@ -5318,7 +5341,7 @@ begin
|
|||
|
||||
resultVec *= 1.0 / fTrees.Length;
|
||||
|
||||
Result := resultVec;
|
||||
Result := resultVec.Normalized;
|
||||
end;
|
||||
|
||||
function RandomForestClassifier.ToString: string;
|
||||
|
|
@ -5691,7 +5714,7 @@ begin
|
|||
ArgumentError(ER_EMPTY_DATASET);
|
||||
|
||||
if XTrain.RowCount <> yTrain.Length then
|
||||
DimensionError(ER_XY_SIZE_MISMATCH);
|
||||
DimensionError(ER_XY_SIZE_MISMATCH,XTrain.RowCount,yTrain.Length);
|
||||
|
||||
if useValidation then
|
||||
begin
|
||||
|
|
@ -7034,12 +7057,15 @@ begin
|
|||
var sum := 0.0;
|
||||
var d := fXTrain.ColCount;
|
||||
|
||||
var train := fXTrain; // локальная ссылка
|
||||
var test := XTest;
|
||||
|
||||
for var j := 0 to d - 1 do
|
||||
begin
|
||||
var diff := fXTrain[trainRow, j] - XTest[testRow, j];
|
||||
var diff := train.Data[trainRow, j] - test.Data[testRow, j];
|
||||
sum += diff * diff;
|
||||
end;
|
||||
|
||||
|
||||
exit(sum);
|
||||
end;
|
||||
|
||||
|
|
@ -7085,6 +7111,112 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// topK
|
||||
//-----------------------------
|
||||
|
||||
type TopK = class
|
||||
private
|
||||
fDist: array of real;
|
||||
fIdx: array of integer;
|
||||
fCount: integer;
|
||||
fK: integer;
|
||||
fWorstIdx: integer;
|
||||
|
||||
procedure UpdateWorst;
|
||||
|
||||
public
|
||||
constructor Create(k: integer);
|
||||
procedure Clear;
|
||||
|
||||
function Count: integer;
|
||||
function Worst: real;
|
||||
|
||||
procedure Add(d: real; id: integer);
|
||||
|
||||
function GetIndex(i: integer): integer;
|
||||
function GetDist(i: integer): real;
|
||||
end;
|
||||
|
||||
constructor TopK.Create(k: integer);
|
||||
begin
|
||||
fK := k;
|
||||
SetLength(fDist, k);
|
||||
SetLength(fIdx, k);
|
||||
fCount := 0;
|
||||
fWorstIdx := 0;
|
||||
end;
|
||||
|
||||
procedure TopK.Clear;
|
||||
begin
|
||||
fCount := 0;
|
||||
fWorstIdx := 0;
|
||||
end;
|
||||
|
||||
function TopK.Count: integer;
|
||||
begin
|
||||
Result := fCount;
|
||||
end;
|
||||
|
||||
function TopK.Worst: real;
|
||||
begin
|
||||
if fCount < fK then
|
||||
Result := real.PositiveInfinity
|
||||
else
|
||||
Result := fDist[fWorstIdx];
|
||||
end;
|
||||
|
||||
procedure TopK.UpdateWorst;
|
||||
begin
|
||||
var wi := 0;
|
||||
var wd := fDist[0];
|
||||
|
||||
for var i := 1 to fCount - 1 do
|
||||
if fDist[i] > wd then
|
||||
begin
|
||||
wd := fDist[i];
|
||||
wi := i;
|
||||
end;
|
||||
|
||||
fWorstIdx := wi;
|
||||
end;
|
||||
|
||||
procedure TopK.Add(d: real; id: integer);
|
||||
begin
|
||||
// ещё не заполнено
|
||||
if fCount < fK then
|
||||
begin
|
||||
fDist[fCount] := d;
|
||||
fIdx[fCount] := id;
|
||||
fCount += 1;
|
||||
|
||||
if fCount = fK then
|
||||
UpdateWorst;
|
||||
|
||||
exit;
|
||||
end;
|
||||
|
||||
// быстрый отсев
|
||||
if d >= fDist[fWorstIdx] then
|
||||
exit;
|
||||
|
||||
// замена худшего
|
||||
fDist[fWorstIdx] := d;
|
||||
fIdx[fWorstIdx] := id;
|
||||
|
||||
UpdateWorst;
|
||||
end;
|
||||
|
||||
function TopK.GetIndex(i: integer): integer;
|
||||
begin
|
||||
Result := fIdx[i];
|
||||
end;
|
||||
|
||||
function TopK.GetDist(i: integer): real;
|
||||
begin
|
||||
Result := fDist[i];
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// KNNClassifier
|
||||
//-----------------------------
|
||||
|
|
@ -7123,6 +7255,15 @@ begin
|
|||
|
||||
// --- copy train data
|
||||
fXTrain := X.Clone;
|
||||
|
||||
fSampleSize := Min(3000, fXTrain.RowCount); // попробуй 2000–4000
|
||||
|
||||
SetLength(fSampleIdx, fSampleSize);
|
||||
|
||||
var rnd := new System.Random(42);
|
||||
|
||||
for var i := 0 to fSampleSize - 1 do
|
||||
fSampleIdx[i] := rnd.Next(fXTrain.RowCount);
|
||||
|
||||
// =========================================================
|
||||
// ЕДИНЫЙ ENCODING
|
||||
|
|
@ -7197,15 +7338,36 @@ begin
|
|||
|
||||
var m := X.RowCount;
|
||||
var n := fXTrain.RowCount;
|
||||
var p := fXTrain.ColCount;
|
||||
|
||||
var trainRows := fXTrain.Data.Rows;
|
||||
var testRows := X.Data.Rows;
|
||||
|
||||
Result := new Vector(m);
|
||||
|
||||
for var i := 0 to m - 1 do
|
||||
begin
|
||||
// заполнить расстояния
|
||||
// for var t := 0 to n - 1 do
|
||||
// begin
|
||||
// fNeighbors[t].dist := SquaredL2(t, X, i);
|
||||
// fNeighbors[t].idx := t;
|
||||
// end;
|
||||
|
||||
var rowTest := testRows[i];
|
||||
for var t := 0 to n - 1 do
|
||||
begin
|
||||
fNeighbors[t].dist := SquaredL2(t, X, i);
|
||||
begin
|
||||
var rowTrain := trainRows[t];
|
||||
|
||||
var sum := 0.0;
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var diff := rowTrain[j] - rowTest[j];
|
||||
sum += diff * diff;
|
||||
end;
|
||||
|
||||
fNeighbors[t].dist := sum;
|
||||
fNeighbors[t].idx := t;
|
||||
end;
|
||||
|
||||
|
|
@ -7652,18 +7814,70 @@ begin
|
|||
var p := X.ColCount;
|
||||
var k := fNClusters;
|
||||
|
||||
// --- 1. Инициализация центроидов (случайные строки X)
|
||||
|
||||
var idx := Arr(0..n-1);
|
||||
idx.Shuffle(rnd);
|
||||
// --- 1. K-Means++ инициализация
|
||||
|
||||
var centers := new Matrix(k, p);
|
||||
|
||||
for var c := 0 to k - 1 do
|
||||
|
||||
// первый центр — случайный
|
||||
var first := rnd.Next(n);
|
||||
for var j := 0 to p - 1 do
|
||||
centers[0,j] := X[first,j];
|
||||
|
||||
// расстояния до ближайшего центра
|
||||
var dist1 := new real[n];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
dist1[i] := double.MaxValue;
|
||||
|
||||
// выбираем остальные центры
|
||||
for var c := 1 to k - 1 do
|
||||
begin
|
||||
var r := idx[c];
|
||||
// обновляем dist (минимальное расстояние до уже выбранных центров)
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var d := 0.0;
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var diff := X[i,j] - centers[c-1,j];
|
||||
d += diff * diff;
|
||||
end;
|
||||
|
||||
if d < dist1[i] then
|
||||
dist1[i] := d;
|
||||
end;
|
||||
|
||||
// сумма расстояний
|
||||
var sumDist := 0.0;
|
||||
for var i := 0 to n - 1 do
|
||||
sumDist += dist1[i];
|
||||
|
||||
// если всё совпало (редкий случай)
|
||||
if sumDist = 0 then
|
||||
begin
|
||||
var r := rnd.Next(n);
|
||||
for var j := 0 to p - 1 do
|
||||
centers[c,j] := X[r,j];
|
||||
continue;
|
||||
end;
|
||||
|
||||
// выбор по вероятности
|
||||
var target := rnd.NextDouble * sumDist;
|
||||
var acc := 0.0;
|
||||
var chosen := n - 1;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
acc += dist1[i];
|
||||
if acc >= target then
|
||||
begin
|
||||
chosen := i;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
centers[c,j] := X[r,j];
|
||||
centers[c,j] := X[chosen,j];
|
||||
end;
|
||||
|
||||
var inertia := 0.0;
|
||||
|
|
@ -8489,7 +8703,34 @@ begin
|
|||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to p - 1 do
|
||||
Result[i,j] := (X[i,j] - fMean[j]) / fStd[j];
|
||||
if fStd[j] <> 0 then
|
||||
Result[i,j] := (X[i,j] - fMean[j]) / fStd[j]
|
||||
else
|
||||
Result[i,j] := 0.0;
|
||||
end;
|
||||
|
||||
function StandardScaler.InverseTransform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if X = nil then
|
||||
ArgumentNullError(ER_X_NULL);
|
||||
|
||||
if X.ColCount <> fFeatureCount then
|
||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
||||
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
Result := new Matrix(n, p);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to p - 1 do
|
||||
if fStd[j] <> 0 then
|
||||
Result[i,j] := X[i,j] * fStd[j] + fMean[j]
|
||||
else
|
||||
Result[i,j] := fMean[j];
|
||||
end;
|
||||
|
||||
function StandardScaler.ToString: string;
|
||||
|
|
@ -8577,6 +8818,34 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.InverseTransform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
if X = nil then
|
||||
ArgumentNullError(ER_X_NULL);
|
||||
|
||||
if X.ColCount <> fFeatureCount then
|
||||
DimensionError(ER_FEATURE_COUNT_MISMATCH);
|
||||
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
Result := new Matrix(n, p);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var range := fMax[j] - fMin[j];
|
||||
|
||||
if range <> 0 then
|
||||
Result[i,j] := X[i,j] * range + fMin[j]
|
||||
else
|
||||
Result[i,j] := fMin[j];
|
||||
end;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.ToString: string;
|
||||
begin
|
||||
Result := 'MinMaxScaler(min=' + fRangeMin + ', max=' + fRangeMax + ')';
|
||||
|
|
|
|||
|
|
@ -137,15 +137,8 @@ type
|
|||
/// (Fit/Transform/FitTransform)
|
||||
UDataPipeline = class(PipelineBase, IModel)
|
||||
private
|
||||
//fDataSteps: List<IPreprocessor>;
|
||||
//fMatrixSteps: List<ITransformer>;
|
||||
fModel: IUnsupervisedModel;
|
||||
|
||||
//fFeatures: array of string;
|
||||
//fFinalFeatures: array of string;
|
||||
|
||||
//fFitted: boolean;
|
||||
|
||||
procedure ValidateNumericFeatures(df: DataFrame);
|
||||
function TransformToMatrix(df: DataFrame): Matrix;
|
||||
protected
|
||||
|
|
@ -226,9 +219,6 @@ const
|
|||
'Матричный шаг не может идти после модели!!Matrix step cannot appear after the model';
|
||||
ER_DATAPIPE_UNKNOWN_STEP_TYPE =
|
||||
'Неизвестный тип шага конвейера: {0}!!Unknown pipeline step type: {0}';
|
||||
|
||||
ER_TO_MATRIX_NO_COLUMNS =
|
||||
'ToMatrix: не указаны столбцы!!ToMatrix: no columns specified';
|
||||
ER_TO_VECTOR_NON_NUMERIC =
|
||||
'ToVector: столбец "{0}" содержит нечисловые или NA значения!!' +
|
||||
'ToVector: column "{0}" contains non-numeric or NA values';
|
||||
|
|
@ -267,9 +257,6 @@ const
|
|||
ER_MODEL_NOT_SUPERVISED =
|
||||
'Модель (тип: {0}) не поддерживает Fit(X, y)!!' +
|
||||
'Model (type: {0}) does not support Fit(X, y)';
|
||||
ER_XY_SIZE_MISMATCH =
|
||||
'Несовпадение размеров X и y: X имеет {0} строк, y имеет {1} элементов!!' +
|
||||
'X and y size mismatch: X has {0} rows, y has {1} elements';
|
||||
ER_PIPELINE_FEATURE_NOT_FOUND =
|
||||
'Признак "{0}" отсутствует во входных данных!!' +
|
||||
'Feature "{0}" not found in input data';
|
||||
|
|
@ -294,9 +281,6 @@ const
|
|||
ER_DATAPIPE_INVALID_TASK =
|
||||
'DataPipeline поддерживает только классификацию и регрессию!!' +
|
||||
'DataPipeline supports classification and regression only';
|
||||
ER_PREDICT_NOT_SUPPORTED =
|
||||
'Модель не поддерживает операцию Predict!!' +
|
||||
'Model does not support Predict operation';
|
||||
ER_PIPELINE_NO_STEPS =
|
||||
'Pipeline не содержит шагов!!' +
|
||||
'Pipeline must contain at least one step';
|
||||
|
|
@ -561,74 +545,31 @@ begin
|
|||
if fModel = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
||||
var current := df;
|
||||
var current: DataFrame;
|
||||
var X := PrepareMatrix(df, current);
|
||||
|
||||
// --- 0) Проверка входной схемы
|
||||
ValidateSchema(current);
|
||||
|
||||
// --- 1) DataFrame шаги
|
||||
for var i := 0 to fDataSteps.Count - 1 do
|
||||
begin
|
||||
fDataSteps[i] := fDataSteps[i].Fit(current);
|
||||
current := fDataSteps[i].Transform(current);
|
||||
end;
|
||||
|
||||
// --- target должен остаться
|
||||
if not current.HasColumn(fTarget) then
|
||||
ArgumentError(ER_PIPELINE_TARGET_REMOVED, fTarget);
|
||||
|
||||
// --- 2) вычислить финальные признаки
|
||||
var feats := new List<string>;
|
||||
|
||||
foreach var f in fFeatures do
|
||||
begin
|
||||
if current.HasColumn(f) then
|
||||
begin
|
||||
feats.Add(f);
|
||||
continue;
|
||||
end;
|
||||
|
||||
// искать производные признаки (OneHotEncoder)
|
||||
foreach var c in current.Schema.ColumnNames do
|
||||
if c.StartsWith(f + '_') then
|
||||
if not feats.Contains(c) then
|
||||
feats.Add(c);
|
||||
end;
|
||||
|
||||
if feats.Count = 0 then
|
||||
ArgumentError(ER_PIPELINE_NO_FEATURES);
|
||||
|
||||
// fFinalFeatures — признаки DataFrame (ДО matrix-transform)
|
||||
// После FitTransformMatrix пространство признаков меняется (PCA и т.п.),
|
||||
// но fFinalFeatures используется только для ToMatrix в Predict.
|
||||
|
||||
fFinalFeatures := feats.ToArray;
|
||||
|
||||
var X := current.ToMatrix(fFinalFeatures);
|
||||
|
||||
var classes: array of string;
|
||||
var y: Vector;
|
||||
|
||||
|
||||
case fTask of
|
||||
tkRegression:
|
||||
y := current.ToVector(fTarget);
|
||||
|
||||
|
||||
tkClassification:
|
||||
begin
|
||||
var labels := current.EncodeLabels(fTarget, classes);
|
||||
y := new Vector(labels);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
if X.RowCount <> y.Length then
|
||||
DimensionError(ER_XY_SIZE_MISMATCH, X.RowCount, y.Length);
|
||||
|
||||
// --- 3) Matrix transformers
|
||||
X := FitTransformMatrix(X, y);
|
||||
// После этого X уже не соответствует fFinalFeatures по размерности,
|
||||
// но это нормально: fFinalFeatures используется только до ToMatrix.
|
||||
|
||||
// --- 4) модель
|
||||
fModel := fModel.Fit(X, y);
|
||||
|
||||
if fTask = tkClassification then
|
||||
|
|
@ -949,47 +890,11 @@ begin
|
|||
if fModel = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
||||
var current := df;
|
||||
var current: DataFrame;
|
||||
var X := PrepareMatrix(df, current);
|
||||
|
||||
// --- 0) Проверка входной схемы
|
||||
ValidateSchema(current);
|
||||
|
||||
// --- 1) DataFrame шаги
|
||||
for var i := 0 to fDataSteps.Count - 1 do
|
||||
begin
|
||||
fDataSteps[i] := fDataSteps[i].Fit(current);
|
||||
current := fDataSteps[i].Transform(current);
|
||||
end;
|
||||
|
||||
// --- 2) вычислить финальные признаки
|
||||
var feats := new List<string>;
|
||||
|
||||
foreach var f in fFeatures do
|
||||
begin
|
||||
if current.HasColumn(f) then
|
||||
begin
|
||||
feats.Add(f);
|
||||
continue;
|
||||
end;
|
||||
|
||||
// искать производные признаки (OneHotEncoder)
|
||||
foreach var c in current.Schema.ColumnNames do
|
||||
if c.StartsWith(f + '_') then
|
||||
if not feats.Contains(c) then
|
||||
feats.Add(c);
|
||||
end;
|
||||
|
||||
if feats.Count = 0 then
|
||||
ArgumentError(ER_PIPELINE_NO_FEATURES);
|
||||
|
||||
fFinalFeatures := feats.ToArray;
|
||||
|
||||
var X := current.ToMatrix(fFinalFeatures);
|
||||
|
||||
// --- 3) Matrix transformers
|
||||
X := FitTransformMatrix(X);
|
||||
|
||||
// --- 4) модель
|
||||
fModel := fModel.Fit(X);
|
||||
|
||||
fFitted := true;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,13 @@ function EncodeLabelsInt(labels: array of integer; var classes: array of integer
|
|||
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||
|
||||
/// Преобразует целочисленные метки классов в индексы (0,1,2,...)
|
||||
/// с использованием заранее заданного массива classes (mapping: индекс → значение).
|
||||
/// classes должен быть получен из EncodeLabelsInt.
|
||||
/// Если встречается неизвестное значение — выбрасывается исключение.
|
||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||
function TransformLabelsInt(labels: array of integer; classes: array of integer): array of integer;
|
||||
|
||||
/// Преобразует целочисленные индексы классов обратно в строковые метки.
|
||||
/// Массив classes задаёт соответствие: classes[i] — имя класса с индексом i.
|
||||
/// Используется для получения текстовых предсказаний моделей.
|
||||
|
|
@ -73,6 +80,11 @@ function DecodeLabels(y: array of integer; classes: array of string): array of s
|
|||
/// Используется для определения множества классов в задаче классификации.
|
||||
function UniqueLabels(labels: array of string): array of string;
|
||||
|
||||
function CloneOrNil(v: Vector): Vector;
|
||||
|
||||
procedure CheckSameLength(a, b: Vector);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses MLExceptions;
|
||||
|
|
@ -173,6 +185,34 @@ begin
|
|||
Result := res;
|
||||
end;
|
||||
|
||||
function TransformLabelsInt(labels: array of integer; classes: array of integer): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
if classes = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'classes');
|
||||
|
||||
// mapping: значение → индекс
|
||||
var map := new Dictionary<integer, integer>;
|
||||
for var i := 0 to classes.Length - 1 do
|
||||
map[classes[i]] := i;
|
||||
|
||||
var res := new integer[labels.Length];
|
||||
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var v := labels[i];
|
||||
|
||||
if not map.ContainsKey(v) then
|
||||
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, v);
|
||||
|
||||
res[i] := map[v];
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
begin
|
||||
var classes: array of string;
|
||||
|
|
@ -229,4 +269,17 @@ begin
|
|||
Result := labels.Distinct.ToArray;
|
||||
end;
|
||||
|
||||
function CloneOrNil(v: Vector): Vector;
|
||||
begin
|
||||
if v = nil then
|
||||
exit(nil);
|
||||
Result := v.Clone;
|
||||
end;
|
||||
|
||||
procedure CheckSameLength(a, b: Vector);
|
||||
begin
|
||||
if a.Length <> b.Length then
|
||||
DimensionError(ER_DIM_MISMATCH, a.Length, b.Length);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -223,6 +223,10 @@ type
|
|||
/// Поддерживает как бинарную, так и многоклассовую классификацию.
|
||||
/// Для бинарной классификации дополнительно доступны
|
||||
/// агрегированные показатели: TP, TN, FP, FN.
|
||||
///
|
||||
/// Ожидается, что yTrue и yPred содержат закодированные метки классов
|
||||
/// (целые значения 0..K-1, допускается хранение в Vector типа real).
|
||||
/// Для строковых меток необходимо предварительно применить EncodeLabels
|
||||
ConfusionMatrix = class
|
||||
private
|
||||
fMatrix: array[,] of integer;
|
||||
|
|
@ -579,6 +583,7 @@ type
|
|||
implementation
|
||||
|
||||
uses MLExceptions;
|
||||
uses MLUtilsABC;
|
||||
|
||||
const
|
||||
ER_INVALID_VALUE =
|
||||
|
|
@ -1671,6 +1676,102 @@ end;
|
|||
/// Для больших выборок рекомендуется использовать альтернативную реализацию
|
||||
/// через таблицу сопряжённости (contingency table) с асимптотикой O(n · k)
|
||||
static function Metrics.AdjustedRandIndex(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue = nil then
|
||||
ArgumentNullError(ER_Y_NULL);
|
||||
|
||||
if yPred = nil then
|
||||
ArgumentNullError(ER_Y_NULL);
|
||||
|
||||
var n := yTrue.Length;
|
||||
if n <> yPred.Length then
|
||||
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
|
||||
|
||||
if n < 2 then
|
||||
begin
|
||||
Result := 1.0;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// --- Vector -> int[]
|
||||
var yTInt := new integer[n];
|
||||
var yPInt := new integer[n];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
yTInt[i] := Round(yTrue[i]);
|
||||
yPInt[i] := Round(yPred[i]);
|
||||
end;
|
||||
|
||||
// --- encoding
|
||||
var classesTrue: array of integer;
|
||||
var classesPred: array of integer;
|
||||
|
||||
var yT := EncodeLabelsInt(yTInt, classesTrue);
|
||||
var yP := EncodeLabelsInt(yPInt, classesPred);
|
||||
|
||||
var kT := classesTrue.Length;
|
||||
var kP := classesPred.Length;
|
||||
|
||||
// --- contingency
|
||||
var table := new integer[kT, kP];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
table[yT[i], yP[i]] += 1;
|
||||
|
||||
// --- sums
|
||||
var sumNij := 0.0;
|
||||
var sumAi := 0.0;
|
||||
var sumBj := 0.0;
|
||||
|
||||
// n_ij
|
||||
for var i := 0 to kT - 1 do
|
||||
for var j := 0 to kP - 1 do
|
||||
begin
|
||||
var x := table[i,j];
|
||||
if x >= 2 then
|
||||
sumNij += x * (x - 1) / 2.0;
|
||||
end;
|
||||
|
||||
// a_i
|
||||
for var i := 0 to kT - 1 do
|
||||
begin
|
||||
var s := 0;
|
||||
for var j := 0 to kP - 1 do
|
||||
s += table[i,j];
|
||||
|
||||
if s >= 2 then
|
||||
sumAi += s * (s - 1) / 2.0;
|
||||
end;
|
||||
|
||||
// b_j
|
||||
for var j := 0 to kP - 1 do
|
||||
begin
|
||||
var s := 0;
|
||||
for var i := 0 to kT - 1 do
|
||||
s += table[i,j];
|
||||
|
||||
if s >= 2 then
|
||||
sumBj += s * (s - 1) / 2.0;
|
||||
end;
|
||||
|
||||
var total := n * (n - 1) / 2.0;
|
||||
|
||||
var expected := (sumAi * sumBj) / total;
|
||||
var maxIndex := 0.5 * (sumAi + sumBj);
|
||||
|
||||
var denom := maxIndex - expected;
|
||||
|
||||
if denom = 0 then
|
||||
begin
|
||||
Result := 0.0;
|
||||
exit;
|
||||
end;
|
||||
|
||||
Result := (sumNij - expected) / denom;
|
||||
end;
|
||||
|
||||
{static function Metrics.AdjustedRandIndex(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue.Length <> yPred.Length then
|
||||
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
|
||||
|
|
@ -1718,7 +1819,7 @@ begin
|
|||
else
|
||||
Result := (ri - expected) / (1 - expected);
|
||||
end;
|
||||
end;
|
||||
end;}
|
||||
|
||||
function R(s: string; w: integer): string;
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ type
|
|||
/// metric — функция качества, принимающая (y_true, y_pred)
|
||||
/// и возвращающая значение метрики (например, Accuracy или MSE).
|
||||
/// Возвращает среднее значение метрики по всем частям.
|
||||
///
|
||||
/// Метод принимает только ISupervisedModel, работающие с матричными данными
|
||||
/// DataPipeline сюда передавать нельзя, так как он работает с DataFrame.
|
||||
static function CrossValidate(model: ISupervisedModel; X: Matrix; y: Vector;
|
||||
k: integer; metric: (Vector,Vector) -> real; seed: integer := -1): real;
|
||||
|
||||
|
|
@ -50,6 +53,9 @@ type
|
|||
/// с сохранением пропорций классов в каждой части.
|
||||
/// Рекомендуется для задач классификации, особенно при несбалансированных классах.
|
||||
/// Возвращает среднее значение метрики по k разбиениям.
|
||||
///
|
||||
/// Метод принимает только ISupervisedModel, работающие с матричными данными
|
||||
/// DataPipeline сюда передавать нельзя, так как он работает с DataFrame.
|
||||
static function StratifiedCrossValidate(model: ISupervisedModel; X: Matrix; y: Vector;
|
||||
k: integer; metric: (Vector,Vector) -> real; seed: integer := -1): real;
|
||||
end;
|
||||
|
|
@ -92,9 +98,6 @@ const
|
|||
ER_DIM_MISMATCH_TRAIN_TEST =
|
||||
'Несоответствие размерностей в TrainTestSplit: X.RowCount={0}, y.Length={1}!!' +
|
||||
'Dimension mismatch in TrainTestSplit: X.RowCount={0}, y.Length={1}';
|
||||
ER_TEST_RATIO_INVALID =
|
||||
'Параметр testRatio должен быть в интервале (0,1), получено {0}!!' +
|
||||
'Parameter testRatio must be in (0,1), got {0}';
|
||||
ER_K_INVALID =
|
||||
'Некорректное значение k в KFold: k={0}, n={1}!!' +
|
||||
'Invalid k in KFold: k={0}, n={1}';
|
||||
|
|
|
|||
Loading…
Reference in a new issue