Новые ML-модули и исправление старых
This commit is contained in:
parent
4dc3470dbb
commit
bc77b1564a
|
|
@ -20,7 +20,8 @@ uses
|
|||
BBCMicroBit, School, SF, TwoPanelsWindow, NUnitABC, PlotWPF, XLSX, LightPT, Tasks, Мозаика,
|
||||
TasksArr, TasksMatr, TasksStr, Tasks1Begin, Tasks1BoolIfCase, Tasks1Loops, Tasks1Arr,
|
||||
WPF,
|
||||
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC
|
||||
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC,
|
||||
MetricsABC, MLABC, MLCoreABC, MLModelsABC, ValidationML
|
||||
;
|
||||
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -183,6 +183,11 @@
|
|||
File ..\bin\Lib\DataFrameABCCore.pcu
|
||||
File ..\bin\Lib\LinearAlgebraML.pcu
|
||||
File ..\bin\Lib\PreprocessorABC.pcu
|
||||
File ..\bin\Lib\MetricsABC.pcu
|
||||
File ..\bin\Lib\MLABC.pcu
|
||||
File ..\bin\Lib\MLCoreABC.pcu
|
||||
File ..\bin\Lib\MLModelsABC.pcu
|
||||
File ..\bin\Lib\ValidationML.pcu
|
||||
|
||||
File ..\bin\Lib\PABCRtl.dll
|
||||
File ..\bin\Lib\HelixToolkit.Wpf.dll
|
||||
|
|
@ -280,6 +285,12 @@
|
|||
${AddFile} "DataFrameABCCore.pcu"
|
||||
${AddFile} "LinearAlgebraML.pcu"
|
||||
${AddFile} "PreprocessorABC.pcu"
|
||||
${AddFile} "MetricsABC.pcu"
|
||||
${AddFile} "MLABC.pcu"
|
||||
${AddFile} "MLCoreABC.pcu"
|
||||
${AddFile} "MLModelsABC.pcu"
|
||||
${AddFile} "ValidationML.pcu"
|
||||
|
||||
|
||||
${AddFile} "turtle.png"
|
||||
|
||||
|
|
@ -413,6 +424,11 @@
|
|||
File ..\bin\Lib\DataFrameABCCore.pas
|
||||
File ..\bin\Lib\LinearAlgebraML.pas
|
||||
File ..\bin\Lib\PreprocessorABC.pas
|
||||
File ..\bin\Lib\MetricsABC.pas
|
||||
File ..\bin\Lib\MLABC.pas
|
||||
File ..\bin\Lib\MLCoreABC.pas
|
||||
File ..\bin\Lib\MLModelsABC.pas
|
||||
File ..\bin\Lib\ValidationML.pas
|
||||
|
||||
|
||||
File ..\bin\Lib\__RedirectIOMode.vb
|
||||
|
|
@ -498,6 +514,11 @@
|
|||
${AddFile} "DataFrameABCCore.pas"
|
||||
${AddFile} "LinearAlgebraML.pas"
|
||||
${AddFile} "PreprocessorABC.pas"
|
||||
${AddFile} "MetricsABC.pas"
|
||||
${AddFile} "MLABC.pas"
|
||||
${AddFile} "MLCoreABC.pas"
|
||||
${AddFile} "MLModelsABC.pas"
|
||||
${AddFile} "ValidationML.pas"
|
||||
|
||||
${AddFile} "__RedirectIOMode.vb"
|
||||
${AddFile} "VBSystem.vb"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,11 @@ type
|
|||
/// Статистическая информация о числовом столбце
|
||||
DescribeStats = DataFrameABCCore.DescribeStats;
|
||||
|
||||
IntColumn = DataFrameABCCore.IntColumn;
|
||||
FloatColumn = DataFrameABCCore.FloatColumn;
|
||||
BoolColumn = DataFrameABCCore.BoolColumn;
|
||||
StrColumn = DataFrameABCCore.StrColumn;
|
||||
|
||||
IGroupByContext = interface;
|
||||
|
||||
/// Класс для работы с табличными данными (датасетами).
|
||||
|
|
@ -85,10 +90,14 @@ type
|
|||
|
||||
function CreateEmptyBySchema(schema: DataFrameSchema): DataFrame;
|
||||
|
||||
function GetColumnIndex(name: string): integer;
|
||||
function GetColumn(name: string): Column;
|
||||
public
|
||||
/// Схема DataFrame: имена, типы и признаки категориальности
|
||||
property Schema: DataFrameSchema read fschema;
|
||||
|
||||
property Item[name: string]: Column read GetColumn; default;
|
||||
|
||||
/// Добавляет в DataFrame столбец-представление (view),
|
||||
/// использующий те же данные, что и исходный столбец
|
||||
procedure AddColumnView(src: Column);
|
||||
|
|
@ -109,13 +118,13 @@ type
|
|||
function GetCursor: DataFrameCursor;
|
||||
|
||||
/// Добавляет столбец целых чисел
|
||||
procedure AddIntColumn(name: string; data: array of integer; valid: array of boolean; isCategorical: boolean := false);
|
||||
procedure AddIntColumn(name: string; data: array of integer; valid: array of boolean := nil; isCategorical: boolean := false);
|
||||
/// Добавляет столбец вещественных чисел
|
||||
procedure AddFloatColumn(name: string; data: array of real; valid: array of boolean);
|
||||
procedure AddFloatColumn(name: string; data: array of real; valid: array of boolean := nil);
|
||||
/// Добавляет строковый столбец
|
||||
procedure AddStrColumn(name: string; data: array of string; valid: array of boolean; isCategorical: boolean := true);
|
||||
procedure AddStrColumn(name: string; data: array of string; valid: array of boolean := nil; isCategorical: boolean := true);
|
||||
/// Добавляет столбец логических значений
|
||||
procedure AddBoolColumn(name: string; data: array of boolean; valid: array of boolean);
|
||||
procedure AddBoolColumn(name: string; data: array of boolean; valid: array of boolean := nil);
|
||||
|
||||
/// Возвращает данные целочисленного столбца по имени
|
||||
function GetIntColumn(name: string): array of integer;
|
||||
|
|
@ -1429,10 +1438,22 @@ end;
|
|||
|
||||
procedure DataFrame.AddIntColumn(name: string; data: array of integer; valid: array of boolean; isCategorical: boolean);
|
||||
begin
|
||||
if (columns.Count > 0) and (data.Length <> RowCount) then
|
||||
raise new Exception('Row count mismatch');
|
||||
|
||||
var c := new IntColumn;
|
||||
c.Info := new ColumnInfo(name, ctInt, isCategorical);
|
||||
c.Data := data;
|
||||
|
||||
if valid = nil then
|
||||
c.IsValid := [True] * data.Length
|
||||
else
|
||||
begin
|
||||
if valid.Length <> data.Length then
|
||||
raise new Exception('AddIntColumn: data and valid length mismatch');
|
||||
c.IsValid := valid;
|
||||
end;
|
||||
|
||||
columns.Add(c);
|
||||
RebuildSchema;
|
||||
end;
|
||||
|
|
@ -1443,9 +1464,17 @@ begin
|
|||
raise new Exception('Row count mismatch');
|
||||
|
||||
var c := new FloatColumn;
|
||||
c.Info := new ColumnInfo(name, ctFloat, false);
|
||||
c.Info := new ColumnInfo(name, ctFloat, False);
|
||||
c.Data := data;
|
||||
|
||||
if valid = nil then
|
||||
c.IsValid := [True] * data.Length
|
||||
else
|
||||
begin
|
||||
if valid.Length <> data.Length then
|
||||
raise new Exception('AddFloatColumn: data and valid length mismatch');
|
||||
c.IsValid := valid;
|
||||
end;
|
||||
|
||||
columns.Add(c);
|
||||
RebuildSchema;
|
||||
|
|
@ -1459,7 +1488,15 @@ begin
|
|||
var c := new StrColumn;
|
||||
c.Info := new ColumnInfo(name, ctStr, isCategorical);
|
||||
c.Data := data;
|
||||
|
||||
if valid = nil then
|
||||
c.IsValid := [True] * data.Length
|
||||
else
|
||||
begin
|
||||
if valid.Length <> data.Length then
|
||||
raise new Exception('AddStrColumn: data and valid length mismatch');
|
||||
c.IsValid := valid;
|
||||
end;
|
||||
|
||||
columns.Add(c);
|
||||
RebuildSchema;
|
||||
|
|
@ -1471,9 +1508,17 @@ begin
|
|||
raise new Exception('Row count mismatch');
|
||||
|
||||
var c := new BoolColumn;
|
||||
c.Info := new ColumnInfo(name, ctBool, false);
|
||||
c.Info := new ColumnInfo(name, ctBool, False);
|
||||
c.Data := data;
|
||||
|
||||
if valid = nil then
|
||||
c.IsValid := [True] * data.Length
|
||||
else
|
||||
begin
|
||||
if valid.Length <> data.Length then
|
||||
raise new Exception('AddBoolColumn: data and valid length mismatch');
|
||||
c.IsValid := valid;
|
||||
end;
|
||||
|
||||
columns.Add(c);
|
||||
RebuildSchema;
|
||||
|
|
@ -2363,6 +2408,17 @@ begin
|
|||
Result := new DataFrame(cols, schema);
|
||||
end;
|
||||
|
||||
function DataFrame.GetColumnIndex(name: string): integer;
|
||||
begin
|
||||
Result := fschema.IndexOf(name); // если у вас есть такой метод
|
||||
end;
|
||||
|
||||
function DataFrame.GetColumn(name: string): Column;
|
||||
begin
|
||||
var idx := GetColumnIndex(name);
|
||||
Result := columns[idx];
|
||||
end;
|
||||
|
||||
function DataFrame.WithColumnInt(name: string; f: DataFrameCursor -> integer): DataFrame;
|
||||
begin
|
||||
if fSchema.HasColumn(name) then
|
||||
|
|
@ -2966,7 +3022,7 @@ end;
|
|||
|
||||
procedure DataFrame.AssertSchemaConsistent;
|
||||
begin
|
||||
{$IFDEF Test}
|
||||
{$IFNDEF Test}
|
||||
|
||||
// --- 1. одинаковая RowCount у всех столбцов ---
|
||||
var rc := columns[0].RowCount;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ type
|
|||
Column = abstract class
|
||||
Info: ColumnInfo;
|
||||
public
|
||||
/// Пытается извлечь i-тое данное из столбца как числовое если это возможно
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; virtual; abstract;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
|
|
@ -80,6 +82,8 @@ type
|
|||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string; isCategorical: boolean);
|
||||
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
|
|
@ -95,6 +99,8 @@ type
|
|||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string);
|
||||
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
|
|
@ -110,6 +116,7 @@ type
|
|||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string; isCategorical: boolean);
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
|
|
@ -125,6 +132,7 @@ type
|
|||
public
|
||||
constructor Create; begin end;
|
||||
constructor Create(name: string);
|
||||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
|
|
@ -468,6 +476,15 @@ begin
|
|||
else AppendInvalid;
|
||||
end;
|
||||
|
||||
function IntColumn.TryGetNumericValue(i: integer; var value: real): boolean;
|
||||
begin
|
||||
if not IsValid[i] then
|
||||
exit(false);
|
||||
|
||||
value := Data[i];
|
||||
exit(true);
|
||||
end;
|
||||
|
||||
constructor FloatColumn.Create(name: string);
|
||||
begin
|
||||
inherited Create;
|
||||
|
|
@ -500,6 +517,15 @@ begin
|
|||
IsValid := IsValid + [false];
|
||||
end;
|
||||
|
||||
function FloatColumn.TryGetNumericValue(i: integer; var value: real): boolean;
|
||||
begin
|
||||
if not IsValid[i] then
|
||||
exit(false);
|
||||
|
||||
value := Data[i];
|
||||
exit(true);
|
||||
end;
|
||||
|
||||
constructor StrColumn.Create(name: string; isCategorical: boolean);
|
||||
begin
|
||||
inherited Create;
|
||||
|
|
@ -533,6 +559,24 @@ begin
|
|||
IsValid := IsValid + [false];
|
||||
end;
|
||||
|
||||
function StrColumn.TryGetNumericValue(i: integer; var value: real): boolean;
|
||||
begin
|
||||
exit(false);
|
||||
end;
|
||||
|
||||
function BoolColumn.TryGetNumericValue(i: integer; var value: real): boolean;
|
||||
begin
|
||||
if not IsValid[i] then
|
||||
exit(false);
|
||||
|
||||
if Data[i] then
|
||||
value := 1.0
|
||||
else
|
||||
value := 0.0;
|
||||
|
||||
exit(true);
|
||||
end;
|
||||
|
||||
constructor BoolColumn.Create(name: string);
|
||||
begin
|
||||
inherited Create;
|
||||
|
|
|
|||
|
|
@ -27,24 +27,37 @@ type
|
|||
static function operator *(v: Vector; alpha: real): Vector;
|
||||
static function operator /(v: Vector; alpha: real): Vector;
|
||||
|
||||
// Dot product
|
||||
static function operator *(a, b: Vector): real;
|
||||
|
||||
static function operator +=(a: Vector; b: Vector): Vector;
|
||||
static function operator -=(a: Vector; b: Vector): Vector;
|
||||
static function operator *=(a: Vector; alpha: real): Vector;
|
||||
|
||||
static function operator +(v: Vector; c: real): Vector;
|
||||
static function operator +(c: real; v: Vector): Vector;
|
||||
|
||||
static function operator implicit(a: array of real): Vector := new Vector(a);
|
||||
static function operator implicit(a: array of integer): Vector := new Vector(a);
|
||||
|
||||
// ---------- Векторные функции ----------
|
||||
/// Применить функцию ко всем элементам вектора
|
||||
function Apply(f: real -> real): Vector;
|
||||
function Sqrt: Vector;
|
||||
function Exp: Vector := Apply(PABCSystem.Exp);
|
||||
function Ln: Vector := Apply(PABCSystem.Ln);
|
||||
function Abs: Vector := Apply(PABCSystem.Abs);
|
||||
|
||||
// ---------- Основные методы ----------
|
||||
function Sum: real;
|
||||
function Mean: real;
|
||||
function Norm2: real;
|
||||
function Norm: real;
|
||||
function Max: real;
|
||||
function Min: real;
|
||||
/// Скалярное произведение
|
||||
function Dot(b: Vector): real;
|
||||
|
||||
// ---------- Сервисные методы ----------
|
||||
function ToString: string; override := $'{data}';
|
||||
function ToString: string; override := $'{data.Select(x -> x.ToString(''G3''))}';
|
||||
function ToString(digits: integer): string := $'{data.Select(x -> x.ToString(''G''+digits))}';
|
||||
procedure Print := data.Print;
|
||||
procedure Println := data.Println;
|
||||
end;
|
||||
|
|
@ -69,6 +82,23 @@ type
|
|||
|
||||
function Clone: Matrix;
|
||||
|
||||
function ColumnSums: Vector;
|
||||
function RowSums: Vector;
|
||||
function ColumnMeans: Vector;
|
||||
function RowMeans: Vector;
|
||||
function ColumnVariances: Vector;
|
||||
function RowVariances: Vector;
|
||||
function ColumnStd: Vector;
|
||||
function RowStd: Vector;
|
||||
function ColumnMins: Vector;
|
||||
function ColumnMaxs: Vector;
|
||||
function RowMins: Vector;
|
||||
function RowMaxs: Vector;
|
||||
|
||||
function FrobeniusNorm: real;
|
||||
procedure AddScaledIdentity(lambda: real);
|
||||
|
||||
|
||||
static function operator implicit(a: array [,] of real): Matrix := new Matrix(a);
|
||||
static function operator implicit(a: array of array of real): Matrix := new Matrix(Matr(a));
|
||||
static function operator implicit(a: array of array of integer): Matrix := new Matrix(Matr(a.ConvertAll(x -> x.ConvertAll(y -> real(y)))));
|
||||
|
|
@ -89,6 +119,10 @@ type
|
|||
static function operator *=(A: Matrix; alpha: real): Matrix;
|
||||
|
||||
// ---------- Основные методы ----------
|
||||
|
||||
function RowCount: integer := data.RowCount;
|
||||
function ColCount: integer := data.ColCount;
|
||||
|
||||
/// Возвращает транспонированную матрицу Aᵀ
|
||||
function Transpose: Matrix;
|
||||
/// Проверяет, является ли матрица симметричной с заданным допуском.
|
||||
|
|
@ -129,6 +163,9 @@ type
|
|||
// ---------- Статические методы ----------
|
||||
/// Возвращает единичную матрицу размера n
|
||||
static function Identity(n: integer): Matrix;
|
||||
/// Возвращает внешнее произведение двух векторов
|
||||
static function OuterProduct(a, b: Vector): Matrix;
|
||||
|
||||
end;
|
||||
|
||||
/// Решает систему линейных уравнений A * x = b с помощью LU-разложения с частичным выбором главного элемента.
|
||||
|
|
@ -247,11 +284,6 @@ begin
|
|||
Result.data[i] := a.data[i] - b.data[i];
|
||||
end;
|
||||
|
||||
static function Vector.operator *(alpha: real; v: Vector): Vector;
|
||||
begin
|
||||
Result := v * alpha;
|
||||
end;
|
||||
|
||||
static function Vector.operator *(v: Vector; alpha: real): Vector;
|
||||
begin
|
||||
Result := new Vector(v.Length);
|
||||
|
|
@ -259,6 +291,11 @@ begin
|
|||
Result.data[i] := alpha * v.data[i];
|
||||
end;
|
||||
|
||||
static function Vector.operator *(alpha: real; v: Vector): Vector;
|
||||
begin
|
||||
Result := v * alpha;
|
||||
end;
|
||||
|
||||
static function Vector.operator /(v: Vector; alpha: real): Vector;
|
||||
begin
|
||||
if alpha = 0.0 then
|
||||
|
|
@ -269,15 +306,6 @@ begin
|
|||
Result.data[i] := v.data[i] * inv;
|
||||
end;
|
||||
|
||||
static function Vector.operator *(a, b: Vector): real;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
var s := 0.0;
|
||||
for var i := 0 to a.Length - 1 do
|
||||
s += a.data[i] * b.data[i];
|
||||
Result := s;
|
||||
end;
|
||||
|
||||
static function Vector.operator +=(a, b: Vector): Vector;
|
||||
begin
|
||||
CheckSameLength(a, b);
|
||||
|
|
@ -301,6 +329,20 @@ begin
|
|||
Result := a;
|
||||
end;
|
||||
|
||||
static function Vector.operator +(v: Vector; c: real): Vector;
|
||||
begin
|
||||
Result := new Vector(v.Length);
|
||||
for var i := 0 to v.Length - 1 do
|
||||
Result[i] := v[i] + c;
|
||||
end;
|
||||
|
||||
static function Vector.operator +(c: real; v: Vector): Vector;
|
||||
begin
|
||||
Result := v + c;
|
||||
end;
|
||||
|
||||
function Vector.Sqrt: Vector := Apply(PABCSystem.Sqrt);
|
||||
|
||||
function Vector.Sum: real;
|
||||
begin
|
||||
var s := 0.0;
|
||||
|
|
@ -317,14 +359,34 @@ end;
|
|||
|
||||
function Vector.Norm2: real;
|
||||
begin
|
||||
Result := Self * Self;
|
||||
Result := Self.Dot(Self);
|
||||
end;
|
||||
|
||||
function Vector.Norm: real;
|
||||
function Vector.Norm: real := PABCSystem.Sqrt(Norm2);
|
||||
|
||||
function Vector.Max: real := data.Max;
|
||||
|
||||
function Vector.Min: real := data.Min;
|
||||
|
||||
function Vector.Dot(b: Vector): real;
|
||||
begin
|
||||
Result := Sqrt(Norm2);
|
||||
if Length <> b.Length then
|
||||
raise new Exception('Dimension mismatch in Dot');
|
||||
|
||||
var s := 0.0;
|
||||
for var i := 0 to Length - 1 do
|
||||
s += self[i] * b[i];
|
||||
Result := s;
|
||||
end;
|
||||
|
||||
function Vector.Apply(f: real -> real): Vector;
|
||||
begin
|
||||
Result := new Vector(Length);
|
||||
for var i := 0 to Length - 1 do
|
||||
Result[i] := f(self[i]);
|
||||
end;
|
||||
|
||||
|
||||
//-----------------------------
|
||||
// Matrix
|
||||
//-----------------------------
|
||||
|
|
@ -347,6 +409,175 @@ begin
|
|||
Result := new Matrix(data);
|
||||
end;
|
||||
|
||||
function Matrix.ColumnSums: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
for var i := 0 to n - 1 do
|
||||
Result[j] += data[i, j];
|
||||
end;
|
||||
|
||||
function Matrix.ColumnMeans: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
if n = 0 then
|
||||
exit( new Vector(ColCount) );
|
||||
Result := ColumnSums / n;
|
||||
end;
|
||||
|
||||
function Matrix.RowSums: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to p - 1 do
|
||||
Result[i] += data[i, j];
|
||||
end;
|
||||
|
||||
function Matrix.RowMeans: Vector;
|
||||
begin
|
||||
var p := ColCount;
|
||||
if p = 0 then
|
||||
exit (new Vector(RowCount));
|
||||
Result := RowSums / p;
|
||||
end;
|
||||
|
||||
function Matrix.ColumnVariances: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
if n = 0 then
|
||||
exit (new Vector(ColCount));
|
||||
|
||||
var means := ColumnMeans;
|
||||
var p := ColCount;
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var d := data[i, j] - means[j];
|
||||
Result[j] += d * d;
|
||||
end;
|
||||
|
||||
Result := Result / n;
|
||||
end;
|
||||
|
||||
function Matrix.ColumnStd: Vector;
|
||||
begin
|
||||
Result := ColumnVariances.Sqrt;
|
||||
end;
|
||||
|
||||
function Matrix.RowVariances: Vector;
|
||||
begin
|
||||
var p := ColCount;
|
||||
if p = 0 then
|
||||
exit (new Vector(RowCount));
|
||||
|
||||
var means := RowMeans;
|
||||
var n := RowCount;
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var d := data[i, j] - means[i];
|
||||
Result[i] += d * d;
|
||||
end;
|
||||
|
||||
Result := Result / p;
|
||||
end;
|
||||
|
||||
function Matrix.RowStd: Vector;
|
||||
begin
|
||||
Result := RowVariances.Sqrt;
|
||||
end;
|
||||
|
||||
function Matrix.ColumnMins: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
Result[j] := data[0, j];
|
||||
|
||||
for var i := 1 to n - 1 do
|
||||
if data[i, j] < Result[j] then
|
||||
Result[j] := data[i, j];
|
||||
end;
|
||||
end;
|
||||
|
||||
function Matrix.ColumnMaxs: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
Result := new Vector(p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
Result[j] := data[0, j];
|
||||
|
||||
for var i := 1 to n - 1 do
|
||||
if data[i, j] > Result[j] then
|
||||
Result[j] := data[i, j];
|
||||
end;
|
||||
end;
|
||||
|
||||
function Matrix.RowMins: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
Result[i] := data[i, 0];
|
||||
|
||||
for var j := 1 to p - 1 do
|
||||
if data[i, j] < Result[i] then
|
||||
Result[i] := data[i, j];
|
||||
end;
|
||||
end;
|
||||
|
||||
function Matrix.RowMaxs: Vector;
|
||||
begin
|
||||
var n := RowCount;
|
||||
var p := ColCount;
|
||||
Result := new Vector(n);
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
Result[i] := data[i, 0];
|
||||
|
||||
for var j := 1 to p - 1 do
|
||||
if data[i, j] > Result[i] then
|
||||
Result[i] := data[i, j];
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function Matrix.FrobeniusNorm: real;
|
||||
begin
|
||||
var s := 0.0;
|
||||
for var i := 0 to RowCount - 1 do
|
||||
for var j := 0 to ColCount - 1 do
|
||||
s += data[i, j] * data[i, j];
|
||||
Result := PABCSystem.Sqrt(s);
|
||||
end;
|
||||
|
||||
procedure Matrix.AddScaledIdentity(lambda: real);
|
||||
begin
|
||||
var n := RowCount;
|
||||
for var i := 0 to n - 1 do
|
||||
data[i, i] += lambda;
|
||||
end;
|
||||
|
||||
static procedure Matrix.CheckSameSize(A, B: Matrix);
|
||||
begin
|
||||
if (A.Rows <> B.Rows) or (A.Cols <> B.Cols) then
|
||||
|
|
@ -368,6 +599,8 @@ begin
|
|||
$'Matrix-vector size mismatch: {A.Rows}x{A.Cols} * {x.Length}');
|
||||
end;
|
||||
|
||||
|
||||
|
||||
static function Matrix.operator +(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckSameSize(A, B);
|
||||
|
|
@ -488,6 +721,17 @@ begin
|
|||
Result[i, i] := 1.0;
|
||||
end;
|
||||
|
||||
static function Matrix.OuterProduct(a, b: Vector): Matrix;
|
||||
begin
|
||||
var m := a.Length;
|
||||
var n := b.Length;
|
||||
Result := new Matrix(m, n);
|
||||
|
||||
for var i := 0 to m - 1 do
|
||||
for var j := 0 to n - 1 do
|
||||
Result[i, j] := a[i] * b[j];
|
||||
end;
|
||||
|
||||
function Matrix.IsSymmetric(tol: real): boolean;
|
||||
begin
|
||||
if Rows <> Cols then
|
||||
|
|
|
|||
84
bin/Lib/MLABC.pas
Normal file
84
bin/Lib/MLABC.pas
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/// Основной модуль библиотеки машинного обучения.
|
||||
/// Объединяет модели, метрики, валидацию и вспомогательные компоненты.
|
||||
unit MLABC;
|
||||
|
||||
interface
|
||||
|
||||
uses LinearAlgebraML;
|
||||
uses ValidationML;
|
||||
uses MLModelsABC;
|
||||
uses MetricsABC;
|
||||
uses PreprocessorABC;
|
||||
USES DataFrameABC;
|
||||
|
||||
type
|
||||
Vector = LinearAlgebraML.Vector;
|
||||
Matrix = LinearAlgebraML.Matrix;
|
||||
Validation = ValidationML.Validation;
|
||||
LinearRegression = MLModelsABC.LinearRegression;
|
||||
LogisticRegression = MLModelsABC.LogisticRegression;
|
||||
ConfusionMatrix = MetricsABC.ConfusionMatrix;
|
||||
Metrics = MetricsABC.Metrics;
|
||||
Activations = MLModelsABC.Activations;
|
||||
Pipeline = MLModelsABC.Pipeline;
|
||||
DataPipeline = PreprocessorABC.DataPipeline;
|
||||
DataStandardScaler = PreprocessorABC.DataStandardScaler;
|
||||
StandardScaler = MLModelsABC.StandardScaler;
|
||||
DataFrame = DataFrameABC.DataFrame;
|
||||
Statistics = DataFrameABC.Statistics;
|
||||
CsvLoader = DataFrameABC.CsvLoader;
|
||||
|
||||
implementation
|
||||
|
||||
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
|
||||
begin
|
||||
var df := Self;
|
||||
var n := df.RowCount;
|
||||
var p := colNames.Length;
|
||||
|
||||
if p = 0 then
|
||||
raise new Exception('ToMatrix: no columns specified');
|
||||
|
||||
Result := new Matrix(n, p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var col := df[colNames[j]];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var value: real;
|
||||
|
||||
if not col.TryGetNumericValue(i, value) then
|
||||
raise new Exception(
|
||||
'ToMatrix: column "' + colNames[j] +
|
||||
'" contains non-numeric or NA values');
|
||||
|
||||
Result[i,j] := value;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function ToVector(Self: DataFrame; colName: string): Vector; extensionmethod;
|
||||
begin
|
||||
var df := Self;
|
||||
var n := df.RowCount;
|
||||
Result := new Vector(n);
|
||||
|
||||
var col := df[colName];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var value: real;
|
||||
|
||||
if not col.TryGetNumericValue(i, value) then
|
||||
raise new Exception(
|
||||
'ToVector: column "' + colName +
|
||||
'" contains non-numeric or NA values');
|
||||
|
||||
Result[i] := value;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
53
bin/Lib/MLCoreABC.pas
Normal file
53
bin/Lib/MLCoreABC.pas
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
unit MLCoreABC;
|
||||
|
||||
interface
|
||||
|
||||
uses LinearAlgebraML;
|
||||
|
||||
type
|
||||
/// Базовый интерфейс модели машинного обучения
|
||||
IModel = interface
|
||||
function Fit(X: Matrix; y: Vector): IModel;
|
||||
function Predict(X: Matrix): Vector;
|
||||
end;
|
||||
|
||||
/// Интерфейс классификатора.
|
||||
/// Наследуется от IModel.
|
||||
/// Предназначен для моделей, выполняющих классификацию
|
||||
/// (предсказание меток классов).
|
||||
IClassifier = interface(IModel)
|
||||
end;
|
||||
|
||||
/// Интерфейс классификатора, возвращающего вероятности.
|
||||
/// Расширяет IClassifier.
|
||||
/// Используется для моделей, которые умеют оценивать
|
||||
/// вероятность принадлежности к классу.
|
||||
/// Позволяет получать значения в диапазоне (0, 1)
|
||||
/// вместо только итогового решения.
|
||||
IProbabilisticClassifier = interface(IClassifier)
|
||||
/// Возвращает вероятность принадлежности к положительному классу для каждого объекта.
|
||||
function PredictProba(X: Matrix): Vector;
|
||||
end;
|
||||
|
||||
/// Интерфейс регрессионной модели.
|
||||
/// Наследуется от IModel.
|
||||
/// Предназначен для моделей, предсказывающих числовые значения.
|
||||
IRegressor = interface(IModel)
|
||||
end;
|
||||
|
||||
/// Базовый интерфейс преобразования признаков.
|
||||
/// Используется для масштабирования, отбора,
|
||||
/// уменьшения размерности и других преобразований данных.
|
||||
ITransformer = interface
|
||||
/// Обучает преобразование на данных.
|
||||
/// Запоминает необходимые параметры,
|
||||
/// которые будут использоваться при Transform.
|
||||
function Fit(X: Matrix): ITransformer;
|
||||
/// Применяет обученное преобразование к данным.
|
||||
/// Возвращает новую матрицу признаков.
|
||||
function Transform(X: Matrix): Matrix;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
end.
|
||||
560
bin/Lib/MLModelsABC.pas
Normal file
560
bin/Lib/MLModelsABC.pas
Normal file
|
|
@ -0,0 +1,560 @@
|
|||
unit MLModelsABC;
|
||||
|
||||
interface
|
||||
|
||||
uses MLCoreABC;
|
||||
uses LinearAlgebraML;
|
||||
|
||||
type
|
||||
/// Активационные функции для моделей
|
||||
Activations = static class
|
||||
public
|
||||
/// Функция активации Sigmoid (логистическая функция).
|
||||
/// Преобразует любое число в диапазон (0, 1).
|
||||
/// Используется в логистической регрессии
|
||||
/// и других моделях для получения вероятности.
|
||||
/// Формула: σ(x) = 1 / (1 + e^(−x)).
|
||||
static function Sigmoid(v: Vector): Vector;
|
||||
|
||||
/// Функция активации Tanh (гиперболический тангенс).
|
||||
/// Похожа на Sigmoid, но значения лежат в диапазоне (−1, 1).
|
||||
/// Применяется в моделях, где требуется
|
||||
/// симметричная нелинейность относительно нуля.
|
||||
/// Формула: tanh(x) = (e^x − e^(−x)) / (e^x + e^(−x)).
|
||||
static function Tanh(v: Vector): Vector;
|
||||
|
||||
/// Функция активации ReLU (Rectified Linear Unit).
|
||||
/// Отрицательные числа заменяет на 0,
|
||||
/// положительные оставляет без изменений.
|
||||
/// Используется как простое нелинейное преобразование
|
||||
/// в моделях машинного обучения.
|
||||
/// Формула: ReLU(x) = max(0, x).
|
||||
static function ReLU(v: Vector): Vector;
|
||||
|
||||
/// Функция активации Softmax.
|
||||
/// Преобразует набор значений в вероятностное распределение:
|
||||
/// все элементы неотрицательны,
|
||||
/// их сумма равна 1.
|
||||
/// Используется в задачах многоклассовой классификации.
|
||||
/// Каждый элемент интерпретируется как вероятность класса.
|
||||
/// Формула: softmax(x_i) = e^{x_i} / Σ e^{x_j}.
|
||||
static function Softmax(v: Vector): Vector;
|
||||
end;
|
||||
|
||||
IModel = MLCoreABC.IModel;
|
||||
/// Линейная регрессионная модель
|
||||
/// Предсказывает числовое значение по линейной комбинации признаков
|
||||
LinearRegression = class(IRegressor)
|
||||
private
|
||||
fCoef: Vector;
|
||||
fIntercept: real;
|
||||
fLambda: real;
|
||||
fFitted: boolean;
|
||||
public
|
||||
constructor Create(lambda: real := 0.0);
|
||||
|
||||
/// Обучает модель на числовых данных
|
||||
/// X — матрица m × n (m объектов, n признаков)
|
||||
/// y — вектор длины m
|
||||
function Fit(X: Matrix; y: Vector): IModel;
|
||||
|
||||
/// Предсказывает значения для матрицы признаков
|
||||
/// Возвращает вектор длины m
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
||||
/// Вектор коэффициентов модели (веса признаков).
|
||||
/// Каждый элемент показывает вклад соответствующего признака в линейную комбинацию.
|
||||
/// Доступен после обучения (Fit).
|
||||
property Coefficients: Vector read fCoef;
|
||||
|
||||
/// Свободный член модели (смещение, bias).
|
||||
/// Добавляется к линейной комбинации признаков.
|
||||
/// Интерпретируется как значение предсказания, когда все признаки равны нулю.
|
||||
property Intercept: real read fIntercept;
|
||||
|
||||
/// Показывает, была ли модель обучена.
|
||||
/// После вызова Fit значение становится true.
|
||||
/// Используется для проверки корректности вызова Predict.
|
||||
property IsFitted: boolean read fFitted;
|
||||
end;
|
||||
|
||||
/// Логистическая регрессионная модель для бинарной классификации.
|
||||
/// Предсказывает вероятность принадлежности объекта к классу 1
|
||||
/// на основе линейной комбинации признаков и сигмоидной функции.
|
||||
/// Поддерживает L2-регуляризацию.
|
||||
LogisticRegression = class(IClassifier)
|
||||
private
|
||||
fCoef: Vector;
|
||||
fIntercept: real;
|
||||
fLambda: real;
|
||||
fLearningRate: real;
|
||||
fEpochs: integer;
|
||||
fFitted: boolean;
|
||||
public
|
||||
/// Создаёт модель логистической регрессии.
|
||||
/// lambda — коэффициент L2-регуляризации (0 — без регуляризации).
|
||||
/// lr — шаг градиентного спуска.
|
||||
/// epochs — число итераций обучения.
|
||||
constructor Create(lambda: real := 0.0; lr: real := 0.1; epochs: integer := 1000);
|
||||
|
||||
/// Обучает модель на числовых данных.
|
||||
/// X — матрица m × n (m объектов, n признаков).
|
||||
/// y — вектор длины m, содержащий метки классов (0 или 1).
|
||||
function Fit(X: Matrix; y: Vector): IModel;
|
||||
|
||||
/// Возвращает вероятность принадлежности к положительному классу (1)
|
||||
/// для каждого объекта.
|
||||
/// Результат — вектор длины m со значениями в диапазоне (0, 1).
|
||||
function PredictProba(X: Matrix): Vector;
|
||||
|
||||
/// Выполняет бинарную классификацию с порогом 0.5.
|
||||
/// Возвращает вектор из 0 и 1.
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
||||
/// Выполняет бинарную классификацию с заданным порогом.
|
||||
/// threshold — значение в диапазоне (0, 1).
|
||||
/// Если вероятность ≥ threshold, возвращается 1, иначе 0.
|
||||
function Predict(X: Matrix; threshold: real): Vector;
|
||||
|
||||
/// Вектор коэффициентов модели (веса признаков).
|
||||
/// Длина равна числу признаков.
|
||||
/// Доступен после обучения (Fit).
|
||||
property Coefficients: Vector read fCoef;
|
||||
|
||||
/// Свободный член модели (смещение, bias).
|
||||
/// Добавляется к линейной комбинации признаков.
|
||||
property Intercept: real read fIntercept;
|
||||
|
||||
/// Показывает, была ли модель обучена.
|
||||
/// После вызова Fit значение становится true.
|
||||
property IsFitted: boolean read fFitted;
|
||||
end;
|
||||
|
||||
/// Конвейер машинного обучения (Pipeline).
|
||||
/// Объединяет несколько шагов подготовки данных и модель
|
||||
/// в единую последовательность обработки.
|
||||
/// Сначала данные проходят через все преобразования,
|
||||
/// затем итоговые признаки передаются в модель.
|
||||
/// Обеспечивает единый интерфейс Fit / Predict.
|
||||
Pipeline = class(IModel)
|
||||
private
|
||||
fTransformers: List<ITransformer>;
|
||||
fModel: IModel;
|
||||
fFitted: boolean;
|
||||
public
|
||||
/// Создаёт пайплайн (конвейер машинного обучения) для заданной модели.
|
||||
/// model — финальная модель (регрессор или классификатор),
|
||||
/// которая будет обучаться после всех преобразований.
|
||||
constructor Create(model: IModel);
|
||||
|
||||
/// Строит пайплайн (конвейер машинного обучения).
|
||||
/// model — финальная модель, которая будет обучаться.
|
||||
/// transformers — последовательность преобразований признаков,
|
||||
/// применяемых к данным перед обучением модели.
|
||||
/// Возвращает настроенный конвейер.
|
||||
static function Build(model: IModel; params transformers: array of ITransformer): Pipeline;
|
||||
|
||||
/// Добавляет преобразование в конец пайплайна
|
||||
function Add(t: ITransformer): Pipeline;
|
||||
|
||||
/// Обучает преобразования и модель
|
||||
function Fit(X: Matrix; y: Vector): IModel;
|
||||
|
||||
/// Применяет только преобразования (без модели)
|
||||
function Transform(X: Matrix): Matrix;
|
||||
|
||||
/// Делает предсказание
|
||||
function Predict(X: Matrix): Vector;
|
||||
|
||||
/// Возвращает вероятности (если модель поддерживает)
|
||||
function PredictProba(X: Matrix): Vector;
|
||||
|
||||
/// Показывает, был ли пайплайн обучен (вызван метод Fit).
|
||||
property IsFitted: boolean read fFitted;
|
||||
end;
|
||||
|
||||
/// Стандартизирует признаки: вычитает среднее
|
||||
/// и делит на стандартное отклонение по каждому столбцу.
|
||||
/// Используется для приведения признаков к сопоставимому масштабу.
|
||||
StandardScaler = class(ITransformer)
|
||||
private
|
||||
fMean: Vector;
|
||||
fStd: Vector;
|
||||
fFitted: boolean;
|
||||
public
|
||||
/// Вычисляет среднее и стандартное отклонение по каждому признаку.
|
||||
function Fit(X: Matrix): ITransformer;
|
||||
|
||||
/// Применяет стандартизацию к данным.
|
||||
function Transform(X: Matrix): Matrix;
|
||||
|
||||
/// Средние значения признаков, вычисленные при обучении.
|
||||
property Mean: Vector read fMean;
|
||||
|
||||
/// Стандартные отклонения признаков, вычисленные при обучении.
|
||||
property Std: Vector read fStd;
|
||||
|
||||
/// Признак того, что преобразование обучено.
|
||||
property IsFitted: boolean read fFitted;
|
||||
end;
|
||||
|
||||
/// Масштабирует признаки в заданный диапазон
|
||||
/// (по умолчанию [0, 1]) на основе минимального и максимального значения каждого столбца.
|
||||
/// Используется для приведения признаков к единому масштабу без центрирования.
|
||||
MinMaxScaler = class(ITransformer)
|
||||
private
|
||||
fMin: Vector;
|
||||
fMax: Vector;
|
||||
fFitted: boolean;
|
||||
fRangeMin: real;
|
||||
fRangeMax: real;
|
||||
public
|
||||
/// Создаёт MinMaxScaler с диапазоном [rangeMin, rangeMax].
|
||||
/// По умолчанию масштабирование выполняется к [0, 1].
|
||||
constructor Create(rangeMin: real := 0.0; rangeMax: real := 1.0);
|
||||
/// Вычисляет минимальные и максимальные значения
|
||||
/// по каждому признаку.
|
||||
function Fit(X: Matrix): ITransformer;
|
||||
|
||||
/// Применяет линейное масштабирование признаков
|
||||
/// к диапазону [0, 1].
|
||||
function Transform(X: Matrix): Matrix;
|
||||
|
||||
/// Минимальные значения признаков,
|
||||
/// вычисленные при обучении.
|
||||
property Min: Vector read fMin;
|
||||
|
||||
/// Максимальные значения признаков,
|
||||
/// вычисленные при обучении.
|
||||
property Max: Vector read fMax;
|
||||
|
||||
/// Признак того, что преобразование обучено.
|
||||
property IsFitted: boolean read fFitted;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
//-----------------------------
|
||||
// LinearRegression
|
||||
//-----------------------------
|
||||
|
||||
constructor LinearRegression.Create(lambda: real);
|
||||
begin
|
||||
self.flambda := lambda;
|
||||
ffitted := false;
|
||||
end;
|
||||
|
||||
function LinearRegression.Fit(X: Matrix; y: Vector): IModel;
|
||||
begin
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
if n = 0 then
|
||||
raise new Exception('Empty dataset');
|
||||
if y.Length <> n then
|
||||
raise new Exception('Dimension mismatch in Fit');
|
||||
|
||||
// 1. Means
|
||||
var meanX := X.ColumnMeans;
|
||||
var meanY := y.Mean;
|
||||
|
||||
// 2. XtX and XtY
|
||||
var Xt := X.Transpose;
|
||||
var XtX := Xt * X;
|
||||
var XtY := Xt * y;
|
||||
|
||||
// 3. Centering correction
|
||||
XtX := XtX - n * Matrix.OuterProduct(meanX, meanX);
|
||||
XtY := XtY - n * meanX * meanY;
|
||||
|
||||
// 4. Ridge regularization
|
||||
if flambda > 0 then
|
||||
XtX := XtX + flambda * Matrix.Identity(p);
|
||||
|
||||
// 5. Solve
|
||||
fcoef := Solve(XtX, XtY);
|
||||
|
||||
// 6. Intercept
|
||||
fintercept := meanY - meanX.Dot(fcoef);
|
||||
|
||||
ffitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function LinearRegression.Predict(X: Matrix): Vector;
|
||||
begin
|
||||
if not ffitted then
|
||||
raise new Exception('Model not fitted');
|
||||
|
||||
if X.ColCount <> fcoef.Length then
|
||||
raise new Exception('Dimension mismatch in Predict');
|
||||
|
||||
Result := X * fcoef + intercept;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// Activations
|
||||
//-----------------------------
|
||||
|
||||
static function Activations.Sigmoid(v: Vector): Vector := v.Apply(x -> 1.0 / (1.0 + Exp(-x)));
|
||||
|
||||
static function Activations.Tanh(v: Vector): Vector := v.Apply(System.Math.Tanh);
|
||||
|
||||
static function Activations.ReLU(v: Vector): Vector := v.Apply(x -> (if x > 0 then x else 0.0));
|
||||
|
||||
static function Activations.Softmax(v: Vector): Vector;
|
||||
begin
|
||||
var maxVal := v.Max;
|
||||
var shifted := v.Apply(x -> Exp(x - maxVal));
|
||||
var sumExp := shifted.Sum;
|
||||
if sumExp = 0 then
|
||||
exit(new Vector(v.Length));
|
||||
Result := shifted / sumExp;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// LogisticRegression
|
||||
//-----------------------------
|
||||
|
||||
constructor LogisticRegression.Create(lambda: real; lr: real; epochs: integer);
|
||||
begin
|
||||
fLambda := lambda;
|
||||
fLearningRate := lr;
|
||||
fEpochs := epochs;
|
||||
fFitted := false;
|
||||
end;
|
||||
|
||||
function LogisticRegression.Fit(X: Matrix; y: Vector): IModel;
|
||||
begin
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
if y.Length <> n then
|
||||
raise new Exception('Dimension mismatch in Fit');
|
||||
|
||||
fCoef := new Vector(p);
|
||||
fIntercept := 0.0;
|
||||
|
||||
for var epoch := 1 to fEpochs do
|
||||
begin
|
||||
var z := X * fCoef + fIntercept;
|
||||
var yHat := Activations.Sigmoid(z);
|
||||
|
||||
var error := yHat - y;
|
||||
|
||||
var gradW := (X.Transpose * error) / n;
|
||||
|
||||
if fLambda > 0 then
|
||||
gradW := gradW + fLambda * fCoef;
|
||||
|
||||
var gradB := error.Sum / n;
|
||||
|
||||
fCoef := fCoef - fLearningRate * gradW;
|
||||
fIntercept -= fLearningRate * gradB;
|
||||
end;
|
||||
|
||||
fFitted := true;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function LogisticRegression.PredictProba(X: Matrix): Vector;
|
||||
begin
|
||||
if not fFitted then
|
||||
raise new Exception('Model not fitted');
|
||||
|
||||
Result := Activations.Sigmoid(X * fCoef + fIntercept);
|
||||
end;
|
||||
|
||||
function LogisticRegression.Predict(X: Matrix; threshold: real): Vector;
|
||||
begin
|
||||
var p := PredictProba(X);
|
||||
Result := new Vector(p.Length);
|
||||
|
||||
for var i := 0 to p.Length - 1 do
|
||||
if p[i] >= threshold then
|
||||
Result[i] := 1.0
|
||||
else
|
||||
Result[i] := 0.0;
|
||||
end;
|
||||
|
||||
function LogisticRegression.Predict(X: Matrix): Vector;
|
||||
begin
|
||||
Result := Predict(X, 0.5);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// Pipeline
|
||||
//-----------------------------
|
||||
|
||||
constructor Pipeline.Create(model: IModel);
|
||||
begin
|
||||
fModel := model;
|
||||
fTransformers := new List<ITransformer>;
|
||||
fFitted := false;
|
||||
end;
|
||||
|
||||
static function Pipeline.Build(model: IModel; transformers: array of ITransformer): Pipeline;
|
||||
begin
|
||||
var pipe := new Pipeline(model);
|
||||
|
||||
foreach var t in transformers do
|
||||
pipe.Add(t);
|
||||
|
||||
Result := pipe;
|
||||
end;
|
||||
|
||||
function Pipeline.Add(t: ITransformer): Pipeline;
|
||||
begin
|
||||
fTransformers.Add(t);
|
||||
Result := self;
|
||||
end;
|
||||
|
||||
function Pipeline.Fit(X: Matrix; y: Vector): IModel;
|
||||
begin
|
||||
var Xcur := X;
|
||||
|
||||
foreach var t in fTransformers do
|
||||
begin
|
||||
t.Fit(Xcur);
|
||||
Xcur := t.Transform(Xcur);
|
||||
end;
|
||||
|
||||
fModel.Fit(Xcur, y);
|
||||
fFitted := true;
|
||||
|
||||
Result := self;
|
||||
end;
|
||||
|
||||
function Pipeline.Transform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
raise new Exception('Pipeline is not fitted');
|
||||
|
||||
var Xt := X;
|
||||
|
||||
foreach var t in fTransformers do
|
||||
Xt := t.Transform(Xt);
|
||||
|
||||
Result := Xt;
|
||||
end;
|
||||
|
||||
function Pipeline.Predict(X: Matrix): Vector;
|
||||
begin
|
||||
if not fFitted then
|
||||
raise new Exception('Pipeline not fitted');
|
||||
|
||||
var Xcur := X;
|
||||
|
||||
foreach var t in fTransformers do
|
||||
Xcur := t.Transform(Xcur);
|
||||
|
||||
Result := fModel.Predict(Xcur);
|
||||
end;
|
||||
|
||||
function Pipeline.PredictProba(X: Matrix): Vector;
|
||||
begin
|
||||
if not fFitted then
|
||||
raise new Exception('Pipeline is not fitted');
|
||||
|
||||
var Xt := X;
|
||||
|
||||
foreach var t in fTransformers do
|
||||
Xt := t.Transform(Xt);
|
||||
|
||||
if fModel is IProbabilisticClassifier then
|
||||
exit(IProbabilisticClassifier(fModel).PredictProba(Xt));
|
||||
|
||||
raise new Exception('Model does not support probability prediction');
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// StandardScaler
|
||||
//-----------------------------
|
||||
|
||||
function StandardScaler.Fit(X: Matrix): ITransformer;
|
||||
begin
|
||||
fMean := X.ColumnMeans;
|
||||
fStd := X.ColumnStd;
|
||||
|
||||
fFitted := true;
|
||||
Result := self;
|
||||
end;
|
||||
|
||||
function StandardScaler.Transform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
raise new Exception('StandardScaler not fitted');
|
||||
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
Result := new Matrix(n, p);
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var mean := fMean[j];
|
||||
var std := fStd[j];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
if std <> 0 then
|
||||
Result[i,j] := (X[i,j] - mean) / std
|
||||
else Result[i,j] := 0.0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// MinMaxScaler
|
||||
//-----------------------------
|
||||
|
||||
constructor MinMaxScaler.Create(rangeMin: real; rangeMax: real);
|
||||
begin
|
||||
if rangeMax <= rangeMin then
|
||||
raise new Exception('rangeMax must be greater than rangeMin');
|
||||
|
||||
fRangeMin := rangeMin;
|
||||
fRangeMax := rangeMax;
|
||||
fFitted := false;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.Fit(X: Matrix): ITransformer;
|
||||
begin
|
||||
fMin := X.ColumnMins;
|
||||
fMax := X.ColumnMaxs;
|
||||
|
||||
fFitted := true;
|
||||
Result := self;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.Transform(X: Matrix): Matrix;
|
||||
begin
|
||||
if not fFitted then
|
||||
raise new Exception('MinMaxScaler is not fitted');
|
||||
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
Result := new Matrix(n, p);
|
||||
|
||||
var scale := fRangeMax - fRangeMin;
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var minVal := fMin[j];
|
||||
var maxVal := fMax[j];
|
||||
var denom := maxVal - minVal;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
if denom <> 0 then
|
||||
Result[i,j] := fRangeMin + (X[i,j] - minVal) / denom * scale
|
||||
else Result[i,j] := fRangeMin; // если столбец константный
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
end.
|
||||
329
bin/Lib/MetricsABC.pas
Normal file
329
bin/Lib/MetricsABC.pas
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
unit MetricsABC;
|
||||
|
||||
interface
|
||||
|
||||
uses LinearAlgebraML;
|
||||
|
||||
type
|
||||
/// Метрики оценки качества моделей машинного обучения
|
||||
/// для задач регрессии и классификации.
|
||||
Metrics = static class
|
||||
public
|
||||
/// Среднеквадратичная ошибка (MSE).
|
||||
/// MSE = (1/n) * Σ (yTrue - yPred)^2.
|
||||
/// Чувствительна к большим отклонениям.
|
||||
static function MSE(yTrue, yPred: Vector): real;
|
||||
|
||||
/// Средняя абсолютная ошибка (MAE).
|
||||
/// MAE = (1/n) * Σ |yTrue - yPred|.
|
||||
/// Менее чувствительна к выбросам, чем MSE.
|
||||
static function MAE(yTrue, yPred: Vector): real;
|
||||
|
||||
/// Коэффициент детерминации (R²).
|
||||
/// R2 = 1 - SS_res / SS_tot.
|
||||
/// Показывает долю объяснённой дисперсии.
|
||||
/// R2 = 1 — идеальная модель,
|
||||
/// R2 = 0 — не лучше среднего,
|
||||
/// R2 < 0 — хуже константной модели.
|
||||
static function R2(yTrue, yPred: Vector): real;
|
||||
|
||||
// ---- Вероятностные метрики классификации ----
|
||||
|
||||
/// Логарифмическая функция потерь (LogLoss).
|
||||
/// Оценивает качество вероятностных предсказаний.
|
||||
/// Сильно штрафует уверенные, но ошибочные прогнозы.
|
||||
/// Используется для моделей, возвращающих вероятности
|
||||
/// (например, LogisticRegression).
|
||||
static function LogLoss(yTrue, yProb: Vector): real;
|
||||
|
||||
// ---- ROC / AUC ----
|
||||
|
||||
/// Строит точки ROC-кривой по вероятностным предсказаниям.
|
||||
/// Возвращает кортеж из двух векторов:
|
||||
/// FPR (доля ложных положительных)
|
||||
/// и TPR (доля истинно положительных).
|
||||
static function ROC(yTrue, yProb: Vector): (Vector, Vector);
|
||||
|
||||
/// Площадь под ROC-кривой (AUC).
|
||||
/// Значение от 0 до 1.
|
||||
/// 0.5 — случайная модель,
|
||||
/// 1 — идеальная модель.
|
||||
static function AUC(yTrue, yProb: Vector): real;
|
||||
end;
|
||||
|
||||
type
|
||||
/// Матрица ошибок (Confusion Matrix) для бинарной классификации.
|
||||
/// Хранит количество истинно положительных (TP),
|
||||
/// истинно отрицательных (TN),
|
||||
/// ложно положительных (FP)
|
||||
/// и ложно отрицательных (FN) предсказаний.
|
||||
/// Используется для анализа качества классификатора
|
||||
/// и вычисления метрик Accuracy, Precision, Recall и F1
|
||||
ConfusionMatrix = class
|
||||
private
|
||||
fTP, fTN, fFP, fFN: integer;
|
||||
public
|
||||
/// Создаёт матрицу ошибок по истинным меткам yTrue
|
||||
/// и предсказаниям модели yPred.
|
||||
/// Ожидается, что векторы содержат значения 0 и 1
|
||||
/// и имеют одинаковую длину.
|
||||
/// Подсчитывает TP, TN, FP и FN.
|
||||
constructor Create(yTrue, yPred: Vector);
|
||||
|
||||
/// Истинно положительные (True Positives).
|
||||
/// Количество объектов класса 1,
|
||||
/// которые модель правильно предсказала как 1.
|
||||
property TP: integer read fTP;
|
||||
|
||||
/// Истинно отрицательные (True Negatives).
|
||||
/// Количество объектов класса 0,
|
||||
/// которые модель правильно предсказала как 0.
|
||||
property TN: integer read fTN;
|
||||
|
||||
/// Ложно положительные (False Positives).
|
||||
/// Количество объектов класса 0,
|
||||
/// которые модель ошибочно предсказала как 1.
|
||||
property FP: integer read fFP;
|
||||
|
||||
/// Ложно отрицательные (False Negatives).
|
||||
/// Количество объектов класса 1,
|
||||
/// которые модель ошибочно предсказала как 0.
|
||||
property FN: integer read fFN;
|
||||
|
||||
/// Доля правильных предсказаний.
|
||||
/// Показывает, какая часть объектов классифицирована верно.
|
||||
/// Удобна, когда классы примерно сбалансированы.
|
||||
/// Пример: Accuracy = 0.9 означает 90% правильных ответов.
|
||||
function Accuracy: real;
|
||||
/// Точность (Precision) для положительного класса (1).
|
||||
/// Среди всех объектов, которые модель предсказала как 1,
|
||||
/// показывает долю действительно принадлежащих этому классу.
|
||||
/// Важна, когда критичны ложные положительные результаты («ложные тревоги»).
|
||||
function Precision: real;
|
||||
/// Полнота (Recall) для положительного класса (1).
|
||||
/// Среди всех объектов, которые в действительности равны 1,
|
||||
/// показывает долю правильно найденных моделью.
|
||||
/// Важна, когда нежелательны пропуски положительных случаев (например, заболеваний).
|
||||
function Recall: real;
|
||||
/// F1-мера.
|
||||
/// Объединённая метрика, учитывающая и Precision, и Recall одновременно.
|
||||
/// Высока только тогда, когда и Precision, и Recall высоки.
|
||||
/// Используется, когда классы сильно различаются по количеству элементов
|
||||
function F1: real;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
//-----------------------------
|
||||
// Metrics
|
||||
//-----------------------------
|
||||
|
||||
static function Metrics.MSE(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue.Length <> yPred.Length then
|
||||
raise new Exception('Dimension mismatch in MSE');
|
||||
|
||||
var n := yTrue.Length;
|
||||
var s := 0.0;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var d := yTrue[i] - yPred[i];
|
||||
s += d * d;
|
||||
end;
|
||||
|
||||
Result := s / n;
|
||||
end;
|
||||
|
||||
static function Metrics.MAE(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue.Length <> yPred.Length then
|
||||
raise new Exception('Dimension mismatch in MAE');
|
||||
|
||||
var n := yTrue.Length;
|
||||
var s := 0.0;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
s += Abs(yTrue[i] - yPred[i]);
|
||||
|
||||
Result := s / n;
|
||||
end;
|
||||
|
||||
static function Metrics.R2(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue.Length <> yPred.Length then
|
||||
raise new Exception('Dimension mismatch in R2');
|
||||
|
||||
var n := yTrue.Length;
|
||||
var meanY := yTrue.Mean;
|
||||
|
||||
var ssRes := 0.0;
|
||||
var ssTot := 0.0;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var d1 := yTrue[i] - yPred[i];
|
||||
ssRes += d1 * d1;
|
||||
|
||||
var d2 := yTrue[i] - meanY;
|
||||
ssTot += d2 * d2;
|
||||
end;
|
||||
|
||||
if ssTot = 0 then
|
||||
exit(0.0);
|
||||
|
||||
Result := 1 - ssRes / ssTot;
|
||||
end;
|
||||
|
||||
static function Metrics.LogLoss(yTrue, yProb: Vector): real;
|
||||
begin
|
||||
if yTrue.Length <> yProb.Length then
|
||||
raise new Exception('Dimension mismatch in LogLoss');
|
||||
|
||||
var n := yTrue.Length;
|
||||
var eps := 1e-15;
|
||||
var s := 0.0;
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
begin
|
||||
var p := yProb[i];
|
||||
|
||||
if p < eps then p := eps;
|
||||
if p > 1 - eps then p := 1 - eps;
|
||||
|
||||
s += yTrue[i] * Ln(p) + (1 - yTrue[i]) * Ln(1 - p);
|
||||
end;
|
||||
|
||||
Result := -s / n;
|
||||
end;
|
||||
|
||||
static function Metrics.ROC(yTrue, yProb: Vector): (Vector, Vector);
|
||||
begin
|
||||
if yTrue.Length <> yProb.Length then
|
||||
raise new Exception('Dimension mismatch in ROC');
|
||||
|
||||
var n := yTrue.Length;
|
||||
|
||||
// Индексы, отсортированные по убыванию вероятностей
|
||||
var idx :=
|
||||
(0..n-1)
|
||||
.OrderByDescending(i -> yProb[i])
|
||||
.ToArray;
|
||||
|
||||
var tp := 0;
|
||||
var fp := 0;
|
||||
|
||||
var totalPos := 0;
|
||||
var totalNeg := 0;
|
||||
|
||||
for var i := 0 to n-1 do
|
||||
if yTrue[i] = 1 then
|
||||
totalPos += 1
|
||||
else
|
||||
totalNeg += 1;
|
||||
|
||||
var fprList := new List<real>;
|
||||
var tprList := new List<real>;
|
||||
|
||||
for var k := 0 to n-1 do
|
||||
begin
|
||||
var i := idx[k];
|
||||
|
||||
if yTrue[i] = 1 then
|
||||
tp += 1
|
||||
else
|
||||
fp += 1;
|
||||
|
||||
var tpr := 0.0;
|
||||
var fpr := 0.0;
|
||||
|
||||
if totalPos > 0 then
|
||||
tpr := tp / totalPos;
|
||||
|
||||
if totalNeg > 0 then
|
||||
fpr := fp / totalNeg;
|
||||
|
||||
tprList.Add(tpr);
|
||||
fprList.Add(fpr);
|
||||
end;
|
||||
|
||||
Result := (new Vector(fprList.ToArray),
|
||||
new Vector(tprList.ToArray));
|
||||
end;
|
||||
|
||||
static function Metrics.AUC(yTrue, yProb: Vector): real;
|
||||
begin
|
||||
var (fpr, tpr) := ROC(yTrue, yProb);
|
||||
|
||||
var area := 0.0;
|
||||
|
||||
for var i := 1 to fpr.Length - 1 do
|
||||
begin
|
||||
var dx := fpr[i] - fpr[i - 1];
|
||||
var avgY := (tpr[i] + tpr[i - 1]) / 2;
|
||||
area += dx * avgY;
|
||||
end;
|
||||
|
||||
Result := area;
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
// ConfusionMatrix
|
||||
//-----------------------------
|
||||
|
||||
constructor ConfusionMatrix.Create(yTrue, yPred: Vector);
|
||||
begin
|
||||
if yTrue.Length <> yPred.Length then
|
||||
raise new Exception('Dimension mismatch in ConfusionMatrix');
|
||||
|
||||
for var i := 0 to yTrue.Length - 1 do
|
||||
begin
|
||||
if yTrue[i] = 1 then
|
||||
if yPred[i] = 1 then
|
||||
fTP += 1
|
||||
else
|
||||
fFN += 1
|
||||
else
|
||||
if yPred[i] = 1 then
|
||||
fFP += 1
|
||||
else
|
||||
fTN += 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.Accuracy: real;
|
||||
begin
|
||||
var total := fTP + fTN + fFP + fFN;
|
||||
if total = 0 then
|
||||
exit(0.0);
|
||||
Result := (fTP + fTN) / total;
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.Precision: real;
|
||||
begin
|
||||
if fTP + fFP = 0 then
|
||||
exit(0.0);
|
||||
Result := fTP / (fTP + fFP);
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.Recall: real;
|
||||
begin
|
||||
if fTP + fFN = 0 then
|
||||
exit(0.0);
|
||||
Result := fTP / (fTP + fFN);
|
||||
end;
|
||||
|
||||
function ConfusionMatrix.F1: real;
|
||||
begin
|
||||
var p := Precision;
|
||||
var r := Recall;
|
||||
|
||||
if p + r = 0 then
|
||||
exit(0.0);
|
||||
|
||||
Result := 2 * p * r / (p + r);
|
||||
end;
|
||||
|
||||
|
||||
|
||||
end.
|
||||
|
|
@ -40,7 +40,7 @@ type
|
|||
/// При Fit вычисляет среднее значение и стандартное отклонение столбцов.
|
||||
/// Применяет преобразование: x' = (x - mean) / std.
|
||||
/// Пропущенные значения (NA) сохраняются.
|
||||
StandardScaler = class(IPreprocessor)
|
||||
DataStandardScaler = class(IPreprocessor)
|
||||
private
|
||||
cols: array of string;
|
||||
means: array of real;
|
||||
|
|
@ -61,7 +61,7 @@ type
|
|||
/// При Fit вычисляет минимальное и максимальное значения столбцов
|
||||
/// Применяет преобразование: x' = (x - min) / (max - min)
|
||||
/// Пропущенные значения (NA) сохраняются
|
||||
MinMaxScaler = class(IPreprocessor)
|
||||
DataMinMaxScaler = class(IPreprocessor)
|
||||
private
|
||||
cols: array of string;
|
||||
mins: array of real;
|
||||
|
|
@ -153,18 +153,18 @@ type
|
|||
|
||||
|
||||
type
|
||||
/// Pipeline (конвейер) шагов подготовки данных.
|
||||
/// Выполняет шаги последовательно с семантикой Fit / Transform. Pipeline = class
|
||||
Pipeline = class
|
||||
/// DataPipeline (конвейер) шагов подготовки данных.
|
||||
/// Выполняет шаги последовательно с семантикой Fit / Transform.
|
||||
DataPipeline = class
|
||||
private
|
||||
steps: List<IPreprocessor>;
|
||||
fitted: boolean;
|
||||
public
|
||||
constructor;
|
||||
/// Добавляет шаг в конец pipeline
|
||||
function Add(p: IPreprocessor): Pipeline;
|
||||
function Add(p: IPreprocessor): DataPipeline;
|
||||
/// Обучает все шаги pipeline на DataFrame
|
||||
function Fit(df: DataFrame): Pipeline;
|
||||
function Fit(df: DataFrame): DataPipeline;
|
||||
/// Применяет обученный pipeline к DataFrame
|
||||
function Transform(df: DataFrame): DataFrame;
|
||||
/// Выполняет Fit и Transform последовательно
|
||||
|
|
@ -177,7 +177,7 @@ implementation
|
|||
// StandardScaler
|
||||
//-----------------------------
|
||||
|
||||
constructor StandardScaler.Create(params columns: array of string);
|
||||
constructor DataStandardScaler.Create(params columns: array of string);
|
||||
begin
|
||||
if (columns = nil) or (columns.Length = 0) then
|
||||
raise new ArgumentException('StandardScaler: columns not specified');
|
||||
|
|
@ -186,7 +186,7 @@ begin
|
|||
fitted := false;
|
||||
end;
|
||||
|
||||
function StandardScaler.Fit(df: DataFrame): IPreprocessor;
|
||||
function DataStandardScaler.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
var n := cols.Length;
|
||||
SetLength(means, n);
|
||||
|
|
@ -230,7 +230,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function StandardScaler.Transform(df: DataFrame): DataFrame;
|
||||
function DataStandardScaler.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('StandardScaler: not fitted');
|
||||
|
|
@ -256,7 +256,7 @@ begin
|
|||
Result := res;
|
||||
end;
|
||||
|
||||
function StandardScaler.FitTransform(df: DataFrame): DataFrame;
|
||||
function DataStandardScaler.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
|
|
@ -266,7 +266,7 @@ end;
|
|||
// MinMaxScaler
|
||||
//-----------------------------
|
||||
|
||||
constructor MinMaxScaler.Create(params columns: array of string);
|
||||
constructor DataMinMaxScaler.Create(params columns: array of string);
|
||||
begin
|
||||
if (columns = nil) or (columns.Length = 0) then
|
||||
raise new ArgumentException('MinMaxScaler: columns not specified');
|
||||
|
|
@ -275,7 +275,7 @@ begin
|
|||
fitted := false;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.Fit(df: DataFrame): IPreprocessor;
|
||||
function DataMinMaxScaler.Fit(df: DataFrame): IPreprocessor;
|
||||
begin
|
||||
var n := cols.Length;
|
||||
SetLength(mins, n);
|
||||
|
|
@ -326,7 +326,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.Transform(df: DataFrame): DataFrame;
|
||||
function DataMinMaxScaler.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('MinMaxScaler: not fitted');
|
||||
|
|
@ -353,7 +353,7 @@ begin
|
|||
Result := res;
|
||||
end;
|
||||
|
||||
function MinMaxScaler.FitTransform(df: DataFrame): DataFrame;
|
||||
function DataMinMaxScaler.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
|
|
@ -678,13 +678,13 @@ end;
|
|||
// Pipeline
|
||||
//-----------------------------
|
||||
|
||||
constructor Pipeline.Create;
|
||||
constructor DataPipeline.Create;
|
||||
begin
|
||||
steps := new List<IPreprocessor>;
|
||||
fitted := false;
|
||||
end;
|
||||
|
||||
function Pipeline.Add(p: IPreprocessor): Pipeline;
|
||||
function DataPipeline.Add(p: IPreprocessor): DataPipeline;
|
||||
begin
|
||||
if fitted then
|
||||
raise new Exception('Cannot add step after Fit');
|
||||
|
|
@ -693,7 +693,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.Fit(df: DataFrame): Pipeline;
|
||||
function DataPipeline.Fit(df: DataFrame): DataPipeline;
|
||||
begin
|
||||
var current := df;
|
||||
|
||||
|
|
@ -707,7 +707,7 @@ begin
|
|||
Result := Self;
|
||||
end;
|
||||
|
||||
function Pipeline.Transform(df: DataFrame): DataFrame;
|
||||
function DataPipeline.Transform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
if not fitted then
|
||||
raise new Exception('Pipeline is not fitted');
|
||||
|
|
@ -719,7 +719,7 @@ begin
|
|||
Result := current;
|
||||
end;
|
||||
|
||||
function Pipeline.FitTransform(df: DataFrame): DataFrame;
|
||||
function DataPipeline.FitTransform(df: DataFrame): DataFrame;
|
||||
begin
|
||||
Fit(df);
|
||||
Result := Transform(df);
|
||||
|
|
|
|||
270
bin/Lib/ValidationML.pas
Normal file
270
bin/Lib/ValidationML.pas
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
unit ValidationML;
|
||||
|
||||
interface
|
||||
|
||||
uses LinearAlgebraML, MLModelsABC;
|
||||
|
||||
type
|
||||
Validation = static class
|
||||
public
|
||||
/// Делит данные на обучающую и тестовую выборки.
|
||||
/// testRatio — доля объектов, попадающих в тестовую выборку (по умолчанию 0.2).
|
||||
/// Перемешивает объекты перед разбиением.
|
||||
/// Возвращает (X_train, X_test, y_train, y_test).
|
||||
static function TrainTestSplit(X: Matrix; y: Vector;
|
||||
testRatio: real := 0.2; seed: integer := 0): (Matrix, Matrix, Vector, Vector);
|
||||
|
||||
/// Разбивает индексы объектов на k непересекающихся частей (fold).
|
||||
/// На каждом шаге одна часть используется как тестовая, остальные — как обучающая.
|
||||
/// Применяется для k-fold кросс-валидации.
|
||||
/// Возвращает последовательность пар (trainIdx, testIdx).
|
||||
static function KFold(n, k: integer; seed: integer := 0):
|
||||
sequence of (array of integer, array of integer);
|
||||
|
||||
/// Разбивает данные на k частей с сохранением пропорций классов.
|
||||
/// В каждой части сохраняется примерно то же соотношение
|
||||
/// объектов разных классов, что и во всей выборке.
|
||||
/// Рекомендуется для задач классификации,
|
||||
/// особенно при неравномерном распределении классов.
|
||||
/// Возвращает последовательность пар (trainIdx, testIdx).
|
||||
static function StratifiedKFold(y: Vector; k: integer;
|
||||
seed: integer := 0): sequence of (array of integer, array of integer);
|
||||
|
||||
/// Выполняет k-fold кросс-валидацию модели.
|
||||
/// На каждом шаге модель обучается на обучающей части и оценивается на тестовой.
|
||||
/// metric — функция качества (например, Accuracy или MSE).
|
||||
/// Возвращает среднее значение метрики по всем частям.
|
||||
static function CrossValidate(model: IModel; X: Matrix; y: Vector;
|
||||
k: integer; metric: (Vector,Vector) -> real; seed: integer := 0): real;
|
||||
|
||||
/// Выполняет k-fold кросс-валидацию с сохранением пропорций классов.
|
||||
/// Использует StratifiedKFold для разбиения данных.
|
||||
/// Подходит для задач классификации.
|
||||
/// Возвращает среднее значение метрики по всем частям.
|
||||
static function StratifiedCrossValidate(model: IModel; X: Matrix; y: Vector;
|
||||
k: integer; metric: (Vector,Vector) -> real; seed: integer := 0): real;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
static function Validation.TrainTestSplit(X: Matrix; y: Vector;
|
||||
testRatio: real; seed: integer): (Matrix, Matrix, Vector, Vector);
|
||||
begin
|
||||
if X.RowCount <> y.Length then
|
||||
raise new Exception('Dimension mismatch in TrainTestSplit');
|
||||
|
||||
if (testRatio <= 0) or (testRatio >= 1) then
|
||||
raise new Exception('testRatio must be in (0,1)');
|
||||
|
||||
var n := X.RowCount;
|
||||
var p := X.ColCount;
|
||||
|
||||
var rnd := new System.Random(seed);
|
||||
|
||||
// Перемешанные индексы
|
||||
var idx := (0..n-1).ToArray;
|
||||
idx := idx.OrderBy(i -> rnd.Next).ToArray;
|
||||
|
||||
var testSize := Round(n * testRatio);
|
||||
var trainSize := n - testSize;
|
||||
|
||||
var X_train := new Matrix(trainSize, p);
|
||||
var X_test := new Matrix(testSize, p);
|
||||
|
||||
var y_train := new Vector(trainSize);
|
||||
var y_test := new Vector(testSize);
|
||||
|
||||
// Заполняем train
|
||||
for var i := 0 to trainSize - 1 do
|
||||
begin
|
||||
var row := idx[i];
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
X_train[i,j] := X[row,j];
|
||||
|
||||
y_train[i] := y[row];
|
||||
end;
|
||||
|
||||
// Заполняем test
|
||||
for var i := 0 to testSize - 1 do
|
||||
begin
|
||||
var row := idx[trainSize + i];
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
X_test[i,j] := X[row,j];
|
||||
|
||||
y_test[i] := y[row];
|
||||
end;
|
||||
|
||||
Result := (X_train, X_test, y_train, y_test);
|
||||
end;
|
||||
|
||||
static function Validation.KFold(n, k: integer; seed: integer):
|
||||
sequence of (array of integer, array of integer);
|
||||
begin
|
||||
if (k < 2) or (k > n) then
|
||||
raise new Exception('Invalid k in KFold');
|
||||
|
||||
var rnd := new System.Random(seed);
|
||||
var idx := (0..n-1).OrderBy(i -> rnd.Next).ToArray;
|
||||
|
||||
var baseSize := n div k;
|
||||
var extra := n mod k;
|
||||
var start := 0;
|
||||
|
||||
for var fold := 0 to k - 1 do
|
||||
begin
|
||||
var size := baseSize + (if fold < extra then 1 else 0);
|
||||
|
||||
var testIdx := idx.Skip(start).Take(size).ToArray;
|
||||
var trainIdx := idx.Take(start).Concat(idx.Skip(start + size)).ToArray;
|
||||
|
||||
yield (trainIdx, testIdx);
|
||||
start += size;
|
||||
end;
|
||||
end;
|
||||
|
||||
static function Validation.StratifiedKFold(y: Vector; k: integer;
|
||||
seed: integer): sequence of (array of integer, array of integer);
|
||||
begin
|
||||
var n := y.Length;
|
||||
if (k < 2) or (k > n) then
|
||||
raise new Exception('Invalid k in StratifiedKFold');
|
||||
|
||||
var rnd := new System.Random(seed);
|
||||
|
||||
var idx0 := (0..n-1)
|
||||
.Where(i -> y[i] = 0)
|
||||
.OrderBy(i -> rnd.Next)
|
||||
.ToArray;
|
||||
|
||||
var idx1 := (0..n-1)
|
||||
.Where(i -> y[i] = 1)
|
||||
.OrderBy(i -> rnd.Next)
|
||||
.ToArray;
|
||||
|
||||
if idx0.Length + idx1.Length <> n then
|
||||
raise new Exception('StratifiedKFold supports only 0/1 labels');
|
||||
|
||||
var base0 := idx0.Length div k;
|
||||
var extra0 := idx0.Length mod k;
|
||||
|
||||
var base1 := idx1.Length div k;
|
||||
var extra1 := idx1.Length mod k;
|
||||
|
||||
for var fold := 0 to k - 1 do
|
||||
begin
|
||||
var start0 := fold * base0 + Min(fold, extra0);
|
||||
var size0 := base0 + (if fold < extra0 then 1 else 0);
|
||||
|
||||
var start1 := fold * base1 + Min(fold, extra1);
|
||||
var size1 := base1 + (if fold < extra1 then 1 else 0);
|
||||
|
||||
var testIdx :=
|
||||
idx0.Skip(start0).Take(size0)
|
||||
.Concat(idx1.Skip(start1).Take(size1))
|
||||
.ToArray;
|
||||
|
||||
var trainIdx :=
|
||||
(0..n-1)
|
||||
.Where(i -> not testIdx.Contains(i))
|
||||
.ToArray;
|
||||
|
||||
yield (trainIdx, testIdx);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
static function Validation.CrossValidate(model: IModel; X: Matrix; y: Vector;
|
||||
k: integer; metric: (Vector,Vector) -> real; seed: integer): real;
|
||||
begin
|
||||
if X.RowCount <> y.Length then
|
||||
raise new Exception('Dimension mismatch in CrossValidate');
|
||||
|
||||
var total := 0.0;
|
||||
var folds := 0;
|
||||
var p := X.ColCount;
|
||||
|
||||
foreach var (trainIdx, testIdx) in KFold(X.RowCount, k, seed) do
|
||||
begin
|
||||
var Xtr := new Matrix(trainIdx.Length, p);
|
||||
var ytr := new Vector(trainIdx.Length);
|
||||
|
||||
for var i := 0 to trainIdx.Length - 1 do
|
||||
begin
|
||||
var r := trainIdx[i];
|
||||
for var j := 0 to p - 1 do
|
||||
Xtr[i,j] := X[r,j];
|
||||
ytr[i] := y[r];
|
||||
end;
|
||||
|
||||
var Xte := new Matrix(testIdx.Length, p);
|
||||
var yte := new Vector(testIdx.Length);
|
||||
|
||||
for var i := 0 to testIdx.Length - 1 do
|
||||
begin
|
||||
var r := testIdx[i];
|
||||
for var j := 0 to p - 1 do
|
||||
Xte[i,j] := X[r,j];
|
||||
yte[i] := y[r];
|
||||
end;
|
||||
|
||||
var m := model.Fit(Xtr, ytr);
|
||||
var pred := m.Predict(Xte);
|
||||
|
||||
total += metric(yte, pred);
|
||||
folds += 1;
|
||||
end;
|
||||
|
||||
Result := total / folds;
|
||||
end;
|
||||
|
||||
static function Validation.StratifiedCrossValidate(
|
||||
model: IModel; X: Matrix; y: Vector;
|
||||
k: integer; metric: (Vector,Vector) -> real; seed: integer
|
||||
): real;
|
||||
begin
|
||||
if X.RowCount <> y.Length then
|
||||
raise new Exception('Dimension mismatch in StratifiedCrossValidate');
|
||||
|
||||
var total := 0.0;
|
||||
var folds := 0;
|
||||
var p := X.ColCount;
|
||||
|
||||
foreach var (trainIdx, testIdx) in StratifiedKFold(y, k, seed) do
|
||||
begin
|
||||
var Xtr := new Matrix(trainIdx.Length, p);
|
||||
var ytr := new Vector(trainIdx.Length);
|
||||
|
||||
for var i := 0 to trainIdx.Length - 1 do
|
||||
begin
|
||||
var r := trainIdx[i];
|
||||
for var j := 0 to p - 1 do
|
||||
Xtr[i,j] := X[r,j];
|
||||
ytr[i] := y[r];
|
||||
end;
|
||||
|
||||
var Xte := new Matrix(testIdx.Length, p);
|
||||
var yte := new Vector(testIdx.Length);
|
||||
|
||||
for var i := 0 to testIdx.Length - 1 do
|
||||
begin
|
||||
var r := testIdx[i];
|
||||
for var j := 0 to p - 1 do
|
||||
Xte[i,j] := X[r,j];
|
||||
yte[i] := y[r];
|
||||
end;
|
||||
|
||||
var m := model.Fit(Xtr, ytr);
|
||||
var pred := m.Predict(Xte);
|
||||
|
||||
total += metric(yte, pred);
|
||||
folds += 1;
|
||||
end;
|
||||
|
||||
Result := total / folds;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
end.
|
||||
Loading…
Reference in a new issue