ML - исправления очередного аудита
This commit is contained in:
parent
e338acb18b
commit
5c8f2b0caa
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%COREVERSION%=1
|
||||
%REVISION%=3790
|
||||
%MINOR%=11
|
||||
%REVISION%=3793
|
||||
%COREVERSION%=1
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.1.3790
|
||||
3.11.1.3793
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.1.3790'
|
||||
!define VERSION '3.11.1.3793'
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(() ->
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Reference in a new issue