ML - исправления очередного аудита

This commit is contained in:
Mikhalkovich Stanislav 2026-04-04 21:48:15 +03:00
parent e338acb18b
commit 5c8f2b0caa
13 changed files with 208 additions and 366 deletions

View file

@ -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 = "3790";
public const string Revision = "3793";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;

View file

@ -1,4 +1,4 @@
%COREVERSION%=1
%REVISION%=3790
%MINOR%=11
%REVISION%=3793
%COREVERSION%=1
%MAJOR%=3

View file

@ -1 +1 @@
3.11.1.3790
3.11.1.3793

View file

@ -1 +1 @@
!define VERSION '3.11.1.3790'
!define VERSION '3.11.1.3793'

View file

@ -10,7 +10,7 @@ interface
uses DataFrameABCCore;
const DataFrameVersion = '1.0';
const DataFrameVersion = '1.1';
// Реэкспортируем публичные типы для удобства
type
@ -93,6 +93,9 @@ type
function GetColumnIndex(name: string): integer;
function GetColumn(name: string): Column;
function CloneWithCopiedColumns: DataFrame;
static procedure EnsureValidArray(var valid: array of boolean; rowCount, filledCount: integer);
public
/// Создает пустой DataFrame
constructor Create;
@ -2475,7 +2478,7 @@ begin
cnt += 1;
end;
if cnt = 0 then
if cnt <= 1 then
exit(0.0);
var mean := sum / cnt;
@ -2491,7 +2494,7 @@ begin
acc += d * d;
end;
Result := acc / cnt;
Result := acc / (cnt - 1);
end;
function DataFrame.Variance(colName: string): real
@ -3182,14 +3185,10 @@ begin
Result := columns[idx];
end;
function DataFrame.WithColumnInt(name: string; f: DataFrameCursor -> integer): DataFrame;
function DataFrame.CloneWithCopiedColumns: DataFrame;
begin
if fSchema.HasColumn(name) then
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
Result := new DataFrame;
// 1. копируем столбцы (без categorical!)
for var i := 0 to columns.Count - 1 do
begin
var col := columns[i];
@ -3198,30 +3197,47 @@ begin
ctInt:
begin
var c := IntColumn(col);
res.AddIntColumn(c.Info.Name, c.Data, c.IsValid);
Result.AddIntColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctStr:
begin
var c := StrColumn(col);
res.AddStrColumn(c.Info.Name, c.Data, c.IsValid);
Result.AddStrColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctFloat:
begin
var c := FloatColumn(col);
res.AddFloatColumn(c.Info.Name, c.Data, c.IsValid);
Result.AddFloatColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctBool:
begin
var c := BoolColumn(col);
res.AddBoolColumn(c.Info.Name, c.Data, c.IsValid);
Result.AddBoolColumn(c.Info.Name, c.Data, c.IsValid);
end;
end;
end;
end;
static procedure DataFrame.EnsureValidArray(var valid: array of boolean; rowCount, filledCount: integer);
begin
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to filledCount - 1 do
valid[j] := true;
end;
end;
function DataFrame.WithColumnInt(name: string; f: DataFrameCursor -> integer): DataFrame;
begin
if fSchema.HasColumn(name) then
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := CloneWithCopiedColumns;
// 2. вычисляем новый столбец
var data := new integer[RowCount];
var valid: array of boolean := nil;
@ -3236,14 +3252,7 @@ begin
on e: Exception do
begin
data[i] := 0;
if valid = nil then
begin
valid := new boolean[RowCount];
for var j := 0 to i - 1 do
valid[j] := true;
end;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
i += 1;
continue;
@ -3256,13 +3265,10 @@ begin
i += 1;
end;
// 3. добавляем новый столбец
res.AddIntColumn(name, data, valid);
res.SetSchema(ExtendSchema(name, ctInt, false));
Result := res;
AssertSchemaConsistent;
end;
@ -3270,41 +3276,9 @@ function DataFrame.WithColumnFloat(name: string; f: DataFrameCursor -> real): Da
begin
if fSchema.HasColumn(name) then
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
// 1. копируем существующие колонки
for var i := 0 to columns.Count - 1 do
begin
var col := columns[i];
case col.Info.ColType of
ctInt:
begin
var c := IntColumn(col);
res.AddIntColumn(c.Info.Name, c.Data, c.IsValid);
end;
var res := CloneWithCopiedColumns;
ctStr:
begin
var c := StrColumn(col);
res.AddStrColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctFloat:
begin
var c := FloatColumn(col);
res.AddFloatColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctBool:
begin
var c := BoolColumn(col);
res.AddBoolColumn(c.Info.Name, c.Data, c.IsValid);
end;
end;
end;
// 2. вычисляем новый столбец
var data := new real[RowCount];
var valid: array of boolean := nil;
@ -3316,16 +3290,10 @@ begin
try
data[i] := f(cur);
except
on e: Exception do
begin
data[i] := 0.0;
if valid = nil then
begin
valid := new boolean[RowCount];
for var j := 0 to i - 1 do
valid[j] := true;
end;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
i += 1;
continue;
@ -3338,13 +3306,10 @@ begin
i += 1;
end;
// 3. добавляем новый столбец
res.AddFloatColumn(name, data, valid);
res.SetSchema(ExtendSchema(name, ctFloat, false));
Result := res;
AssertSchemaConsistent;
end;
@ -3352,41 +3317,9 @@ function DataFrame.WithColumnStr(name: string; f: DataFrameCursor -> string): Da
begin
if fSchema.HasColumn(name) then
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
// 1. копируем существующие колонки
for var i := 0 to columns.Count - 1 do
begin
var col := columns[i];
case col.Info.ColType of
ctInt:
begin
var c := IntColumn(col);
res.AddIntColumn(c.Info.Name, c.Data, c.IsValid);
end;
var res := CloneWithCopiedColumns;
ctStr:
begin
var c := StrColumn(col);
res.AddStrColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctFloat:
begin
var c := FloatColumn(col);
res.AddFloatColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctBool:
begin
var c := BoolColumn(col);
res.AddBoolColumn(c.Info.Name, c.Data, c.IsValid);
end;
end;
end;
// 2. вычисляем новый столбец
var data := new string[RowCount];
var valid: array of boolean := nil;
@ -3398,16 +3331,10 @@ begin
try
data[i] := f(cur);
except
on e: Exception do
begin
data[i] := '';
if valid = nil then
begin
valid := new boolean[RowCount];
for var j := 0 to i - 1 do
valid[j] := true;
end;
data[i] := nil;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
i += 1;
continue;
@ -3420,13 +3347,10 @@ begin
i += 1;
end;
// 3. добавляем новый столбец
res.AddStrColumn(name, data, valid);
res.SetSchema(ExtendSchema(name, ctStr, false));
Result := res;
AssertSchemaConsistent;
end;
@ -3434,41 +3358,9 @@ function DataFrame.WithColumnBool(name: string; f: DataFrameCursor -> boolean):
begin
if fSchema.HasColumn(name) then
ArgumentError(ER_COLUMN_ALREADY_EXISTS, name);
var res := new DataFrame;
// 1. копируем существующие колонки
for var i := 0 to columns.Count - 1 do
begin
var col := columns[i];
case col.Info.ColType of
ctInt:
begin
var c := IntColumn(col);
res.AddIntColumn(c.Info.Name, c.Data, c.IsValid);
end;
var res := CloneWithCopiedColumns;
ctStr:
begin
var c := StrColumn(col);
res.AddStrColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctFloat:
begin
var c := FloatColumn(col);
res.AddFloatColumn(c.Info.Name, c.Data, c.IsValid);
end;
ctBool:
begin
var c := BoolColumn(col);
res.AddBoolColumn(c.Info.Name, c.Data, c.IsValid);
end;
end;
end;
// 2. вычисляем новый столбец
var data := new boolean[RowCount];
var valid: array of boolean := nil;
@ -3480,16 +3372,10 @@ begin
try
data[i] := f(cur);
except
on e: Exception do
begin
data[i] := false;
if valid = nil then
begin
valid := new boolean[RowCount];
for var j := 0 to i - 1 do
valid[j] := true;
end;
EnsureValidArray(valid, RowCount, i);
valid[i] := false;
i += 1;
continue;
@ -3502,13 +3388,10 @@ begin
i += 1;
end;
// 3. добавляем новый столбец
res.AddBoolColumn(name, data, valid);
res.SetSchema(ExtendSchema(name, ctBool, false));
Result := res;
AssertSchemaConsistent;
end;
@ -3549,27 +3432,56 @@ begin
var cur := GetCursor;
var row := 0;
while cur.MoveNext do
begin
// --- NA: обрабатываем явно, без исключений
if not cur.IsValid(colIndex) then
begin
data[row] := 0.0;
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to row - 1 do
valid[j] := true;
end;
valid[row] := false;
row += 1;
continue;
end;
// --- вычисление
try
data[row] := f(cur);
if valid <> nil then valid[row] := true;
except
on e: Exception do
begin
data[row] := 0.0;
if valid = nil then
begin
valid := new boolean[rowCount];
for var j := 0 to row - 1 do valid[j] := true;
for var j := 0 to row - 1 do
valid[j] := true;
end;
valid[row] := false;
row += 1;
continue;
end;
end;
if valid <> nil then
valid[row] := true;
row += 1;
end;
// --- сборка результата
var res := new DataFrame;
for var i := 0 to columns.Count - 1 do
if i <> colIndex then
res.AddColumnView(columns[i])
@ -5452,11 +5364,8 @@ begin
Error(ER_ZERO_STD_STANDARDIZE);
Result := df.ReplaceColumnFloat(colName, cur ->
begin
if not cur.IsValid(idx) then
Error(ER_INVALID_VALUE_IN_COLUMN, colName);
Result := (cur.Float(idx) - mean) / std;
end);
(cur.Float(idx) - mean) / std
);
end;
static function Statistics.StandardizeAll(df: DataFrame): DataFrame;
@ -5653,37 +5562,6 @@ end;
// CSVLoader
//-----------------------------
{procedure ScanFields(line: string; delimiter: char;
starts, lens: array of integer; var actualCount: integer);
begin
var j := 1;
var col := 0;
var n := line.Length;
starts[0] := 1;
while j <= n do
begin
if line[j] = delimiter then
begin
if col < starts.Length then
lens[col] := j - starts[col];
col += 1;
if col < starts.Length then
starts[col] := j + 1;
end;
j += 1;
end;
if col < starts.Length then
lens[col] := n - starts[col] + 1;
for var k := col + 1 to starts.Length - 1 do
lens[k] := 0;
actualCount := col + 1;
end;}
procedure ScanFieldsQuoted(
line: string; delimiter: char;
starts, lens: array of integer;
@ -6466,13 +6344,34 @@ begin
System.IO.FileShare.ReadWrite
);
try
// --- BOM check
var bom := new byte[3];
var n := fs.Read(bom, 0, 3);
// UTF-8 BOM
if (n >= 3) and (bom[0] = $EF) and (bom[1] = $BB) and (bom[2] = $BF) then
Result := Encoding.UTF8
else
Result := Encoding.UTF8;
exit(Encoding.UTF8);
// --- reset position
fs.Position := 0;
// --- read small chunk
var bufferSize := 4096;
var buffer := new byte[bufferSize];
var readBytes := fs.Read(buffer, 0, bufferSize);
// --- strict UTF-8 check
try
var utf8 := new System.Text.UTF8Encoding(false, true); // strict
utf8.GetString(buffer, 0, readBytes);
exit(Encoding.UTF8);
except
// not UTF-8
end;
// --- fallback
Result := Encoding.Default;
finally
fs.Close;
end;

View file

@ -789,7 +789,14 @@ end;}
function JoinKey.Equals(oth: object): boolean;
begin
var other: JoinKey := JoinKey(oth);
if oth = nil then
exit(false);
if not (oth is JoinKey) then
exit(false);
var other := JoinKey(oth);
if Ints.Length <> other.Ints.Length then exit(false);
if Floats.Length <> other.Floats.Length then exit(false);
if Strs.Length <> other.Strs.Length then exit(false);

View file

@ -327,6 +327,9 @@ const
ER_SINGULAR_MATRIX =
'Матрица вырождена или плохо обусловлена!!Matrix is singular or ill-conditioned';
type
MLNotSPDException = class(MLException);
//-----------------------------
// Vector
//-----------------------------
@ -1254,7 +1257,6 @@ begin
end;
// Helper
function Cholesky(A: Matrix): Matrix;
begin
@ -1274,7 +1276,7 @@ begin
if i = j then
begin
if s <= 0.0 then
Error(ER_MATRIX_NOT_SPD);
raise new MLNotSPDException('Матрица не является положительно определённой (SPD)');
L[i, i] := Sqrt(s);
end
else
@ -1423,6 +1425,7 @@ begin
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, A.RowCount);
var L := Cholesky(A);
var y := SolveLowerTriangular(L, b);
Result := SolveUpperTriangular(L.Transpose, y);
end;
@ -1432,7 +1435,8 @@ begin
try
Result := SolveSPD(A, b);
except
Result := Solve(A, b);
on e: MLNotSPDException do
Result := Solve(A, b);
end;
end;

View file

@ -92,7 +92,7 @@ type
IModel = MLCoreABC.IModel;
ISupervisedModel = MLCoreABC.ISupervisedModel;
IUnSupervisedModel = MLCoreABC.IUnSupervisedModel;
IUnsupervisedModel = MLCoreABC.IUnsupervisedModel;
UPipeline = MLModelsABC.UPipeline;
UDataPipeline = MLPipelineABC.UDataPipeline;
TaskKind = MLPipelineABC.TaskKind;

View file

@ -1303,26 +1303,31 @@ end;
static function Datasets.StudentExam: Dataset;
begin
NotImplementedError(ER_NOT_IMPLEMENTED, 'Datasets.StudentExam');
Result := nil;
end;
static function Datasets.BankClients: Dataset;
begin
NotImplementedError(ER_NOT_IMPLEMENTED, 'Datasets.BankClients');
Result := nil;
end;
static function Datasets.TaxiTrips: Dataset;
begin
NotImplementedError(ER_NOT_IMPLEMENTED, 'Datasets.TaxiTrips');
Result := nil;
end;
static function Datasets.MoscowTransport: Dataset;
begin
NotImplementedError(ER_NOT_IMPLEMENTED, 'Datasets.MoscowTransport');
Result := nil;
end;
static function Datasets.OnlineShopping: Dataset;
begin
NotImplementedError(ER_NOT_IMPLEMENTED, 'Datasets.OnlineShopping');
Result := nil;
end;

View file

@ -23,7 +23,9 @@ const
'Пустой набор данных для {0}!!Empty dataset for {0}';
ER_COLUMN_NOT_FOUND =
'В DataFrame отсутствует столбец "{0}"!!DataFrame does not contain column "{0}"';
ER_NOT_IMPLEMENTED =
'Метод {0} не реализован!!Method {0} is not implemented';
type
/// Базовое исключение ML-библиотеки
MLException = class(Exception);
@ -33,6 +35,8 @@ type
/// Несоответствие размерностей
MLDimensionException = class(MLException);
MLNotImplementedException = class(MLException);
procedure Error(msg: string; params args: array of object);
procedure ArgumentError(msg: string; params args: array of object);
@ -40,6 +44,7 @@ procedure NotFittedError(msg: string; params args: array of object);
procedure DimensionError(msg: string; params args: array of object);
procedure ArgumentNullError(msg: string; params args: array of object);
procedure ArgumentOutOfRangeError(msg: string; params args: array of object);
procedure NotImplementedError(msg: string; params args: array of object);
implementation
@ -83,4 +88,10 @@ begin
raise new System.ArgumentOutOfRangeException(FormatSafe(msg, args));
end;
procedure NotImplementedError(msg: string; params args: array of object);
begin
raise new MLNotImplementedException(FormatSafe(msg, args));
end;
end.

View file

@ -1013,7 +1013,7 @@ type
: ISupervisedModel;
procedure BuildClassMapping(y: Vector);
function EncodeLabels(y: Vector): array of integer;
function ApplyLabelEncoding(y: Vector): array of integer;
procedure SoftmaxRow(var logits: array of real; var probs: array of real);
procedure SoftmaxMatrix(logits: Matrix; probs: Matrix);
@ -1339,7 +1339,6 @@ type
private
fEps: real;
fMinSamples: integer;
fSeed: integer;
fFitted: boolean;
fFeatureCount: integer;
@ -1356,8 +1355,7 @@ type
/// seed — параметр для совместимости API.
constructor Create(
eps: real;
minSamples: integer := 5;
seed: integer := -1
minSamples: integer := 5
);
/// Обучает модель на матрице признаков.
@ -3506,18 +3504,6 @@ begin
if X.RowCount <> y.Length then
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
if fMinSamplesSplit < 2 then
ArgumentOutOfRangeError(ER_MINSAMPLESSPLIT_INVALID, fMinSamplesSplit);
if fMinSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MINSAMPLESLEAF_INVALID, fMinSamplesLeaf);
if fMaxDepth < -1 then
ArgumentOutOfRangeError(ER_MAX_DEPTH_INVALID, fMaxDepth);
if fMaxDepth > 10000 then
ArgumentError(ER_MAX_DEPTH_TOO_LARGE, fMaxDepth);
fFeatureImportances := new Vector(X.ColCount);
@ -3758,21 +3744,9 @@ begin
if X.RowCount <> y.Length then
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
if fMinSamplesSplit < 2 then
ArgumentOutOfRangeError(ER_MINSAMPLESSPLIT_INVALID, fMinSamplesSplit);
if fMinSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MINSAMPLESLEAF_INVALID, fMinSamplesLeaf);
if fLeafL2 < 0 then
ArgumentOutOfRangeError(ER_L2_NEGATIVE, fLeafL2);
if fMaxDepth < -1 then
ArgumentOutOfRangeError(ER_MAX_DEPTH_INVALID, fMaxDepth);
if fMaxDepth > 10000 then
ArgumentError(ER_MAX_DEPTH_TOO_LARGE, fMaxDepth);
fFeatureImportances := new Vector(X.ColCount);
var indices: array of integer;
@ -3986,24 +3960,6 @@ begin
if X.RowCount <> y.Length then
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
if fNTrees <= 0 then
ArgumentOutOfRangeError(ER_NTREES_INVALID, fNTrees);
if fMinSamplesSplit < 2 then
ArgumentOutOfRangeError(ER_MINSAMPLESSPLIT_INVALID, fMinSamplesSplit);
if fMinSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MINSAMPLESLEAF_INVALID, fMinSamplesLeaf);
if fMinSamplesLeaf >= fMinSamplesSplit then
ArgumentError(ER_MIN_LEAF_GE_SPLIT, fMinSamplesLeaf, fMinSamplesSplit);
if fMaxDepth < -1 then
ArgumentOutOfRangeError(ER_MAX_DEPTH_INVALID, fMaxDepth);
if fMaxDepth > MAX_ALLOWED_TREE_DEPTH then
ArgumentError(ER_MAX_DEPTH_TOO_LARGE, fMaxDepth);
var n := X.RowCount;
var p := X.ColCount;
@ -4231,24 +4187,6 @@ begin
if X.RowCount <> y.Length then
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
if fNTrees <= 0 then
ArgumentOutOfRangeError(ER_NTREES_INVALID, fNTrees);
if fMinSamplesSplit < 2 then
ArgumentOutOfRangeError(ER_MINSAMPLESSPLIT_INVALID, fMinSamplesSplit);
if fMinSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MINSAMPLESLEAF_INVALID, fMinSamplesLeaf);
if fMinSamplesLeaf >= fMinSamplesSplit then
ArgumentError(ER_MIN_LEAF_GE_SPLIT, fMinSamplesLeaf, fMinSamplesSplit);
if fMaxDepth < -1 then
ArgumentOutOfRangeError(ER_MAX_DEPTH_INVALID, fMaxDepth);
if fMaxDepth > MAX_ALLOWED_TREE_DEPTH then
ArgumentError(ER_MAX_DEPTH_TOO_LARGE, fMaxDepth);
var n := X.RowCount;
var p := X.ColCount;
@ -4594,6 +4532,51 @@ begin
Result := fClassLabels;
end;
//-----------------------------
// Helper GBC GBR
//-----------------------------
function BuildSubsampleIndices(nTrain: integer; subsample: real; rng: System.Random): array of integer;
begin
if nTrain <= 0 then
ArgumentOutOfRangeError(ER_EMPTY_DATASET);
if (subsample <= 0.0) or (subsample > 1.0) then
ArgumentOutOfRangeError(ER_SUBSAMPLE_INVALID, subsample);
var k := Floor(nTrain * subsample);
if k < 1 then
k := 1;
if k > nTrain then
k := nTrain;
// если берём всё — можно быстро вернуть [0..nTrain-1]
if k = nTrain then
begin
Result := new integer[nTrain];
for var i := 0 to nTrain - 1 do
Result[i] := i;
exit;
end;
var all := new integer[nTrain];
for var i := 0 to nTrain - 1 do
all[i] := i;
// partial Fisher–Yates (без повторений)
for var i := 0 to k - 1 do
begin
var j := i + rng.Next(nTrain - i);
var tmp := all[i];
all[i] := all[j];
all[j] := tmp;
end;
Result := new integer[k];
for var i := 0 to k - 1 do
Result[i] := all[i];
end;
//-----------------------------
// GradientBoostingRegressor
//-----------------------------
@ -4629,6 +4612,9 @@ begin
if minSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MIN_SAMPLES_LEAF_INVALID, minSamplesLeaf);
if maxDepth > MAX_ALLOWED_TREE_DEPTH then
ArgumentOutOfRangeError(ER_MAX_DEPTH_TOO_LARGE, maxDepth);
fNEstimators := nEstimators;
fLearningRate := learningRate;
@ -4905,28 +4891,6 @@ begin
DimensionError(ER_FEATURE_COUNT_MISMATCH);
end;
// --- hyperparameter checks ---
if fNEstimators <= 0 then
ArgumentOutOfRangeError(ER_NESTIMATORS_INVALID, fNEstimators);
if fLearningRate <= 0 then
ArgumentOutOfRangeError(ER_LEARNING_RATE_INVALID, fLearningRate);
if (fSubsample <= 0.0) or (fSubsample > 1.0) then
ArgumentOutOfRangeError(ER_SUBSAMPLE_INVALID, fSubsample);
if fMinSamplesSplit < 2 then
ArgumentOutOfRangeError(ER_MINSAMPLESSPLIT_INVALID, fMinSamplesSplit);
if fMinSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MINSAMPLESLEAF_INVALID, fMinSamplesLeaf);
if fMaxDepth < 0 then
ArgumentOutOfRangeError(ER_MAX_DEPTH_INVALID, fMaxDepth);
if fMaxDepth > MAX_ALLOWED_TREE_DEPTH then
ArgumentError(ER_MAX_DEPTH_TOO_LARGE, fMaxDepth);
// --- init state ---
fOOBLossHistory.Clear;
fEstimators.Clear;
@ -4994,40 +4958,20 @@ begin
// --- subsample ---
var rows: array of integer := nil;
var used: array of boolean := nil;
if useSubsample then
begin
var k := Floor(nTrain * fSubsample);
if k < 1 then k := 1;
if k > nTrain then k := nTrain;
var all := new integer[nTrain];
for var i := 0 to nTrain - 1 do
all[i] := i;
// partial Fisher–Yates
for var i := 0 to k - 1 do
begin
var j := i + fRng.Next(nTrain - i);
var tmp := all[i];
all[i] := all[j];
all[j] := tmp;
end;
rows := new integer[k];
for var i := 0 to k - 1 do
rows[i] := all[i];
rows := BuildSubsampleIndices(nTrain, fSubsample, fRng);
tree.SetRowIndices(rows);
if useOOB then
begin
used := new boolean[nTrain];
for var i := 0 to k - 1 do
for var i := 0 to rows.Length - 1 do
used[rows[i]] := true;
end;
end;
tree.Fit(XTrain, r);
fEstimators.Add(tree);
@ -5399,6 +5343,7 @@ begin
Result := copy;
end;
//-----------------------------
// GradientBoostingClassifier
//-----------------------------
@ -5450,28 +5395,6 @@ begin
DimensionError(ER_FEATURE_COUNT_MISMATCH);
end;
// --- hyperparameter checks ---
if fNEstimators <= 0 then
ArgumentOutOfRangeError(ER_NESTIMATORS_INVALID, fNEstimators);
if fLearningRate <= 0 then
ArgumentOutOfRangeError(ER_LEARNING_RATE_INVALID, fLearningRate);
if (fSubsample <= 0.0) or (fSubsample > 1.0) then
ArgumentOutOfRangeError(ER_SUBSAMPLE_INVALID, fSubsample);
if fMinSamplesSplit < 2 then
ArgumentOutOfRangeError(ER_MINSAMPLESSPLIT_INVALID, fMinSamplesSplit);
if fMinSamplesLeaf < 1 then
ArgumentOutOfRangeError(ER_MINSAMPLESLEAF_INVALID, fMinSamplesLeaf);
if fMaxDepth < 0 then
ArgumentOutOfRangeError(ER_MAX_DEPTH_INVALID, fMaxDepth);
if fMaxDepth > MAX_ALLOWED_TREE_DEPTH then
ArgumentError(ER_MAX_DEPTH_TOO_LARGE, fMaxDepth);
// --- reset state
fOOBLossHistory.Clear;
fEstimators.Clear;
@ -5485,7 +5408,7 @@ begin
// --- mapping
BuildClassMapping(yTrain);
var yEncoded := EncodeLabels(yTrain);
var yEncoded := ApplyLabelEncoding(yTrain);
var nTrain := XTrain.RowCount;
var classCount := fClassCount;
@ -5540,7 +5463,7 @@ begin
if useValidation then
begin
logitsVal := new Matrix(XVal.RowCount, classCount);
yValEncoded := EncodeLabels(yVal);
yValEncoded := ApplyLabelEncoding(yVal);
// --- инициализация log-prior
for var i := 0 to XVal.RowCount - 1 do
@ -5578,6 +5501,11 @@ begin
// inBag
var inBag: array of boolean := nil;
if useSubsample then
begin
subIndices := BuildSubsampleIndices(nTrain, fSubsample, fRng);
end;
if useOOB then
begin
SetLength(inBag, nTrain);
@ -5741,7 +5669,7 @@ begin
fClassIndex[fClasses[cls]] := cls;
end;
function GradientBoostingClassifier.EncodeLabels(y: Vector): array of integer;
function GradientBoostingClassifier.ApplyLabelEncoding(y: Vector): array of integer;
begin
var n := y.Length;
Result := new integer[n];
@ -7297,8 +7225,7 @@ end;
constructor DBSCAN.Create(
eps: real;
minSamples: integer;
seed: integer
minSamples: integer
);
begin
if eps <= 0 then
@ -7309,7 +7236,6 @@ begin
fEps := eps;
fMinSamples := minSamples;
fSeed := seed;
fFitted := False;
end;
@ -7454,7 +7380,7 @@ end;
function DBSCAN.Clone: IModel;
begin
var m := new DBSCAN(fEps, fMinSamples, fSeed);
var m := new DBSCAN(fEps, fMinSamples);
m.fFitted := fFitted;
m.fFeatureCount := fFeatureCount;

View file

@ -101,8 +101,6 @@ type
procedure Hist(x: Vector; bins: integer := 0;
color: ColorWPF := DefaultColor; alpha: real := 0.7; legend: string := nil);
//procedure Surface(labels: array of integer; nx, ny: integer; xmin, xmax, ymin, ymax: real; pal: PlotML.Palette := nil);
procedure Surface(x1, x2: array of real; nx, ny: integer; f: Matrix -> array of integer; pal: PlotML.Palette := nil);
procedure Heatmap(m: Matrix);
@ -1319,16 +1317,6 @@ begin
end;
end;
{procedure Cell.Surface(labels: array of integer; nx, ny: integer; xmin, xmax, ymin, ymax: real; pal: PlotML.Palette);
begin
Plot.RunUI(() ->
begin
EnsureChart;
var usePal := if pal <> nil then pal else palette;
Plot.DrawSurface(chart, labels, nx, ny, xmin, xmax, ymin, ymax, usePal);
end);
end;}
procedure Cell.Surface(x1, x2: array of real; nx, ny: integer; f: Matrix -> array of integer; pal: PlotML.Palette);
begin
Plot.RunUI(() ->

View file

@ -69,7 +69,8 @@ type
X: Matrix; y: Vector;
k: integer;
metric: (Vector, Vector) -> real;
maximize: boolean := True
maximize: boolean := True;
seed: integer := -1
): (real, real, T); where T: class,ISupervisedModel;
end;
@ -447,7 +448,8 @@ class function GridSearch.Search<T>(
y: Vector;
k: integer;
metric: (Vector, Vector) -> real;
maximize: boolean
maximize: boolean;
seed: integer
): (real, real, T); where T: class,ISupervisedModel;
begin
if modelFactory = nil then
@ -480,7 +482,7 @@ begin
var model := modelFactory(param);
if model = nil then
ArgumentError(ER_MODEL_NULL);
var avgScore := Validation.CrossValidate(model, X, y, k, metric);
var avgScore := Validation.CrossValidate(model, X, y, k, metric, seed);
if double.IsNaN(avgScore) or double.IsInfinity(avgScore) then
ArgumentError(ER_INVALID_VALUE, 'avgScore');