ML -
Проверки на NaN Inf
This commit is contained in:
parent
398ea2440f
commit
03fa44dc8b
|
|
@ -51,14 +51,14 @@ begin
|
||||||
if scoreFunc = nil then
|
if scoreFunc = nil then
|
||||||
ArgumentNullError(ER_SCORE_FUNC_NULL);
|
ArgumentNullError(ER_SCORE_FUNC_NULL);
|
||||||
|
|
||||||
if X.Rows <> y.Length then
|
if X.RowCount <> y.Length then
|
||||||
DimensionError(ER_DIM_MISMATCH, X.Rows, y.Length);
|
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
|
||||||
|
|
||||||
var baselinePred := model.Predict(X);
|
var baselinePred := model.Predict(X);
|
||||||
var baselineScore := scoreFunc(y, baselinePred);
|
var baselineScore := scoreFunc(y, baselinePred);
|
||||||
|
|
||||||
var n := X.Rows;
|
var n := X.RowCount;
|
||||||
var p := X.Cols;
|
var p := X.ColCount;
|
||||||
|
|
||||||
var resultVec := new Vector(p);
|
var resultVec := new Vector(p);
|
||||||
var rng := new System.Random(seed);
|
var rng := new System.Random(seed);
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ type
|
||||||
procedure SetData(i, j: integer; value: real) := data[i, j] := value;
|
procedure SetData(i, j: integer; value: real) := data[i, j] := value;
|
||||||
public
|
public
|
||||||
data: array[,] of real;
|
data: array[,] of real;
|
||||||
property Rows: integer read data.GetLength(0);
|
property RowCount: integer read data.RowCount;
|
||||||
property Cols: integer read data.GetLength(1);
|
property ColCount: integer read data.ColCount;
|
||||||
|
|
||||||
property Item[i, j: integer]: real
|
property Item[i, j: integer]: real
|
||||||
read data[i, j] write SetData; default;
|
read data[i, j] write SetData; default;
|
||||||
|
|
@ -140,9 +140,6 @@ type
|
||||||
|
|
||||||
// ---------- Основные методы ----------
|
// ---------- Основные методы ----------
|
||||||
|
|
||||||
function RowCount: integer := data.RowCount;
|
|
||||||
function ColCount: integer := data.ColCount;
|
|
||||||
|
|
||||||
/// Возвращает транспонированную матрицу Aᵀ
|
/// Возвращает транспонированную матрицу Aᵀ
|
||||||
function Transpose: Matrix;
|
function Transpose: Matrix;
|
||||||
/// Проверяет, является ли матрица симметричной с заданным допуском.
|
/// Проверяет, является ли матрица симметричной с заданным допуском.
|
||||||
|
|
@ -241,6 +238,8 @@ function SolveAuto(A: Matrix; b: Vector): Vector;
|
||||||
/// Входные A и b изменяются. Возвращаемый вектор x имеет длину n.
|
/// Входные A и b изменяются. Возвращаемый вектор x имеет длину n.
|
||||||
function SolveRidge(A: Matrix; b: Vector; lambda: real := 0): Vector;
|
function SolveRidge(A: Matrix; b: Vector; lambda: real := 0): Vector;
|
||||||
|
|
||||||
|
function SolveLeastSquaresQR(A: Matrix; b: Vector): Vector;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses MLExceptions;
|
uses MLExceptions;
|
||||||
|
|
@ -291,7 +290,11 @@ const
|
||||||
'Матрица вырождена!!Matrix is singular';
|
'Матрица вырождена!!Matrix is singular';
|
||||||
ER_VECTOR_SIZE_MISMATCH =
|
ER_VECTOR_SIZE_MISMATCH =
|
||||||
'Несоответствие длины вектора: {0} и {1}!!Vector size mismatch: {0} and {1}';
|
'Несоответствие длины вектора: {0} и {1}!!Vector size mismatch: {0} and {1}';
|
||||||
|
ER_QR_REQUIRES_M_GE_N =
|
||||||
|
'Для QR-разложения требуется m >= n!!QR decomposition requires m >= n';
|
||||||
|
ER_SINGULAR_MATRIX =
|
||||||
|
'Матрица вырождена или плохо обусловлена!!Matrix is singular or ill-conditioned';
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// Vector
|
// Vector
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
@ -813,20 +816,20 @@ end;
|
||||||
|
|
||||||
static procedure Matrix.CheckSameSize(A, B: Matrix);
|
static procedure Matrix.CheckSameSize(A, B: Matrix);
|
||||||
begin
|
begin
|
||||||
if (A.Rows <> B.Rows) or (A.Cols <> B.Cols) then
|
if (A.RowCount <> B.RowCount) or (A.ColCount <> B.ColCount) then
|
||||||
DimensionError(ER_MATRIX_SIZE_MISMATCH, A.Rows, A.Cols, B.Rows, B.Cols);
|
DimensionError(ER_MATRIX_SIZE_MISMATCH, A.RowCount, A.ColCount, B.RowCount, B.ColCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
static procedure Matrix.CheckMulSize(A, B: Matrix);
|
static procedure Matrix.CheckMulSize(A, B: Matrix);
|
||||||
begin
|
begin
|
||||||
if A.Cols <> B.Rows then
|
if A.ColCount <> B.RowCount then
|
||||||
DimensionError(ER_MATRIX_MUL_SIZE_MISMATCH, A.Rows, A.Cols, B.Rows, B.Cols);
|
DimensionError(ER_MATRIX_MUL_SIZE_MISMATCH, A.RowCount, A.ColCount, B.RowCount, B.ColCount);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
static procedure Matrix.CheckVecSize(A: Matrix; x: Vector);
|
static procedure Matrix.CheckVecSize(A: Matrix; x: Vector);
|
||||||
begin
|
begin
|
||||||
if A.Cols <> x.Length then
|
if A.ColCount <> x.Length then
|
||||||
DimensionError(ER_MATRIX_VECTOR_SIZE_MISMATCH, A.Rows, A.Cols, x.Length);
|
DimensionError(ER_MATRIX_VECTOR_SIZE_MISMATCH, A.RowCount, A.ColCount, x.Length);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -834,26 +837,26 @@ end;
|
||||||
static function Matrix.operator +(A, B: Matrix): Matrix;
|
static function Matrix.operator +(A, B: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
CheckSameSize(A, B);
|
CheckSameSize(A, B);
|
||||||
Result := new Matrix(A.Rows, A.Cols);
|
Result := new Matrix(A.RowCount, A.ColCount);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
Result.data[i, j] := A.data[i, j] + B.data[i, j];
|
Result.data[i, j] := A.data[i, j] + B.data[i, j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
static function Matrix.operator -(A, B: Matrix): Matrix;
|
static function Matrix.operator -(A, B: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
CheckSameSize(A, B);
|
CheckSameSize(A, B);
|
||||||
Result := new Matrix(A.Rows, A.Cols);
|
Result := new Matrix(A.RowCount, A.ColCount);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
Result.data[i, j] := A.data[i, j] - B.data[i, j];
|
Result.data[i, j] := A.data[i, j] - B.data[i, j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function operator *(A: Matrix; alpha: real): Matrix; extensionmethod;
|
function operator *(A: Matrix; alpha: real): Matrix; extensionmethod;
|
||||||
begin
|
begin
|
||||||
Result := new Matrix(A.Rows, A.Cols);
|
Result := new Matrix(A.RowCount, A.ColCount);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
Result.data[i, j] := alpha * A.data[i, j];
|
Result.data[i, j] := alpha * A.data[i, j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -877,11 +880,11 @@ end;
|
||||||
static function Matrix.operator *(A: Matrix; x: Vector): Vector;
|
static function Matrix.operator *(A: Matrix; x: Vector): Vector;
|
||||||
begin
|
begin
|
||||||
CheckVecSize(A, x);
|
CheckVecSize(A, x);
|
||||||
Result := new Vector(A.Rows);
|
Result := new Vector(A.RowCount);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
begin
|
begin
|
||||||
var s := 0.0;
|
var s := 0.0;
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
s += A.data[i, j] * x[j];
|
s += A.data[i, j] * x[j];
|
||||||
Result[i] := s;
|
Result[i] := s;
|
||||||
end;
|
end;
|
||||||
|
|
@ -890,13 +893,13 @@ end;
|
||||||
static function Matrix.operator *(A, B: Matrix): Matrix;
|
static function Matrix.operator *(A, B: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
CheckMulSize(A, B);
|
CheckMulSize(A, B);
|
||||||
Result := new Matrix(A.Rows, B.Cols);
|
Result := new Matrix(A.RowCount, B.ColCount);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var k := 0 to A.Cols - 1 do
|
for var k := 0 to A.ColCount - 1 do
|
||||||
begin
|
begin
|
||||||
var aik := A.data[i, k];
|
var aik := A.data[i, k];
|
||||||
if aik <> 0.0 then
|
if aik <> 0.0 then
|
||||||
for var j := 0 to B.Cols - 1 do
|
for var j := 0 to B.ColCount - 1 do
|
||||||
Result.data[i, j] += aik * B.data[k, j];
|
Result.data[i, j] += aik * B.data[k, j];
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
@ -904,8 +907,8 @@ end;
|
||||||
static function Matrix.operator +=(A, B: Matrix): Matrix;
|
static function Matrix.operator +=(A, B: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
CheckSameSize(A, B);
|
CheckSameSize(A, B);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
A.data[i, j] += B.data[i, j];
|
A.data[i, j] += B.data[i, j];
|
||||||
Result := A;
|
Result := A;
|
||||||
end;
|
end;
|
||||||
|
|
@ -913,43 +916,43 @@ end;
|
||||||
static function Matrix.operator -=(A, B: Matrix): Matrix;
|
static function Matrix.operator -=(A, B: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
CheckSameSize(A, B);
|
CheckSameSize(A, B);
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
A.data[i, j] -= B.data[i, j];
|
A.data[i, j] -= B.data[i, j];
|
||||||
Result := A;
|
Result := A;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
static function Matrix.operator *=(A: Matrix; alpha: real): Matrix;
|
static function Matrix.operator *=(A: Matrix; alpha: real): Matrix;
|
||||||
begin
|
begin
|
||||||
for var i := 0 to A.Rows - 1 do
|
for var i := 0 to A.RowCount - 1 do
|
||||||
for var j := 0 to A.Cols - 1 do
|
for var j := 0 to A.ColCount - 1 do
|
||||||
A.data[i, j] *= alpha;
|
A.data[i, j] *= alpha;
|
||||||
Result := A;
|
Result := A;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Matrix.Transpose: Matrix;
|
function Matrix.Transpose: Matrix;
|
||||||
begin
|
begin
|
||||||
Result := new Matrix(Cols, Rows);
|
Result := new Matrix(ColCount, RowCount);
|
||||||
for var i := 0 to Rows - 1 do
|
for var i := 0 to RowCount - 1 do
|
||||||
for var j := 0 to Cols - 1 do
|
for var j := 0 to ColCount - 1 do
|
||||||
Result.data[j, i] := data[i, j];
|
Result.data[j, i] := data[i, j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Matrix.GetRow(i: integer): Vector;
|
function Matrix.GetRow(i: integer): Vector;
|
||||||
begin
|
begin
|
||||||
if (i < 0) or (i >= Rows) then
|
if (i < 0) or (i >= RowCount) then
|
||||||
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE, i, Rows);
|
ArgumentOutOfRangeError(ER_ROW_INDEX_OUT_OF_RANGE, i, RowCount);
|
||||||
Result := new Vector(Cols);
|
Result := new Vector(ColCount);
|
||||||
for var j := 0 to Cols - 1 do
|
for var j := 0 to ColCount - 1 do
|
||||||
Result[j] := data[i, j];
|
Result[j] := data[i, j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function Matrix.GetCol(j: integer): Vector;
|
function Matrix.GetCol(j: integer): Vector;
|
||||||
begin
|
begin
|
||||||
if (j < 0) or (j >= Cols) then
|
if (j < 0) or (j >= ColCount) then
|
||||||
ArgumentOutOfRangeError(ER_COL_INDEX_OUT_OF_RANGE, j, Cols);
|
ArgumentOutOfRangeError(ER_COL_INDEX_OUT_OF_RANGE, j, ColCount);
|
||||||
Result := new Vector(Rows);
|
Result := new Vector(RowCount);
|
||||||
for var i := 0 to Rows - 1 do
|
for var i := 0 to RowCount - 1 do
|
||||||
Result[i] := data[i, j];
|
Result[i] := data[i, j];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -976,14 +979,14 @@ end;
|
||||||
|
|
||||||
function Matrix.IsSymmetric(tol: real): boolean;
|
function Matrix.IsSymmetric(tol: real): boolean;
|
||||||
begin
|
begin
|
||||||
if Rows <> Cols then
|
if RowCount <> ColCount then
|
||||||
begin
|
begin
|
||||||
Result := false;
|
Result := false;
|
||||||
exit;
|
exit;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
for var i := 0 to Rows - 1 do
|
for var i := 0 to RowCount - 1 do
|
||||||
for var j := i + 1 to Cols - 1 do
|
for var j := i + 1 to ColCount - 1 do
|
||||||
if Abs(data[i, j] - data[j, i]) > tol then
|
if Abs(data[i, j] - data[j, i]) > tol then
|
||||||
begin
|
begin
|
||||||
Result := false;
|
Result := false;
|
||||||
|
|
@ -995,13 +998,13 @@ end;
|
||||||
|
|
||||||
function Matrix.EigenSymmetric(tol: real; maxIter: integer): (Vector, Matrix);
|
function Matrix.EigenSymmetric(tol: real; maxIter: integer): (Vector, Matrix);
|
||||||
begin
|
begin
|
||||||
if Rows <> Cols then
|
if RowCount <> ColCount then
|
||||||
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
||||||
|
|
||||||
if not IsSymmetric(tol) then
|
if not IsSymmetric(tol) then
|
||||||
ArgumentError(ER_MATRIX_NOT_SYMMETRIC);
|
ArgumentError(ER_MATRIX_NOT_SYMMETRIC);
|
||||||
|
|
||||||
var n := Rows;
|
var n := RowCount;
|
||||||
|
|
||||||
var M := Clone;
|
var M := Clone;
|
||||||
var V := Matrix.Identity(n);
|
var V := Matrix.Identity(n);
|
||||||
|
|
@ -1136,8 +1139,8 @@ end;
|
||||||
|
|
||||||
function Matrix.PCA(k: integer): (Matrix, Vector);
|
function Matrix.PCA(k: integer): (Matrix, Vector);
|
||||||
begin
|
begin
|
||||||
var m := Rows;
|
var m := RowCount;
|
||||||
var n := Cols;
|
var n := ColCount;
|
||||||
|
|
||||||
if k < 1 then
|
if k < 1 then
|
||||||
ArgumentError(ER_PCA_K_INVALID);
|
ArgumentError(ER_PCA_K_INVALID);
|
||||||
|
|
@ -1200,10 +1203,10 @@ end;
|
||||||
// Helper
|
// Helper
|
||||||
function Cholesky(A: Matrix): Matrix;
|
function Cholesky(A: Matrix): Matrix;
|
||||||
begin
|
begin
|
||||||
if A.Rows <> A.Cols then
|
if A.RowCount <> A.ColCount then
|
||||||
ArgumentError(ER_CHOLESKY_NOT_SQUARE);
|
ArgumentError(ER_CHOLESKY_NOT_SQUARE);
|
||||||
|
|
||||||
var n := A.Rows;
|
var n := A.RowCount;
|
||||||
var L := new Matrix(n, n);
|
var L := new Matrix(n, n);
|
||||||
|
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
|
|
@ -1229,10 +1232,10 @@ end;
|
||||||
// Helper
|
// Helper
|
||||||
function LUDecompose(A: Matrix): (Matrix, array of integer);
|
function LUDecompose(A: Matrix): (Matrix, array of integer);
|
||||||
begin
|
begin
|
||||||
if A.Rows <> A.Cols then
|
if A.RowCount <> A.ColCount then
|
||||||
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
||||||
|
|
||||||
var n := A.Rows;
|
var n := A.RowCount;
|
||||||
var LU := A.Clone;
|
var LU := A.Clone;
|
||||||
var p := new integer[n];
|
var p := new integer[n];
|
||||||
|
|
||||||
|
|
@ -1294,7 +1297,7 @@ end;
|
||||||
// Helper
|
// Helper
|
||||||
function SolveLowerTriangularUnit(LU: Matrix; b: Vector): Vector;
|
function SolveLowerTriangularUnit(LU: Matrix; b: Vector): Vector;
|
||||||
begin
|
begin
|
||||||
var n := LU.Rows;
|
var n := LU.RowCount;
|
||||||
var y := new Vector(n);
|
var y := new Vector(n);
|
||||||
|
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
|
|
@ -1310,7 +1313,7 @@ end;
|
||||||
|
|
||||||
function SolveLowerTriangular(L: Matrix; b: Vector): Vector;
|
function SolveLowerTriangular(L: Matrix; b: Vector): Vector;
|
||||||
begin
|
begin
|
||||||
var n := L.Rows;
|
var n := L.RowCount;
|
||||||
var y := new Vector(n);
|
var y := new Vector(n);
|
||||||
|
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
|
|
@ -1328,7 +1331,7 @@ end;
|
||||||
// Helper
|
// Helper
|
||||||
function SolveUpperTriangular(U: Matrix; y: Vector): Vector;
|
function SolveUpperTriangular(U: Matrix; y: Vector): Vector;
|
||||||
begin
|
begin
|
||||||
var n := U.Rows;
|
var n := U.RowCount;
|
||||||
var x := new Vector(n);
|
var x := new Vector(n);
|
||||||
|
|
||||||
for var i := n - 1 downto 0 do
|
for var i := n - 1 downto 0 do
|
||||||
|
|
@ -1344,11 +1347,11 @@ end;
|
||||||
|
|
||||||
function Solve(A: Matrix; b: Vector): Vector;
|
function Solve(A: Matrix; b: Vector): Vector;
|
||||||
begin
|
begin
|
||||||
if A.Rows <> A.Cols then
|
if A.RowCount <> A.ColCount then
|
||||||
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
||||||
|
|
||||||
if b.Length <> A.Rows then
|
if b.Length <> A.RowCount then
|
||||||
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, A.Rows);
|
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, A.RowCount);
|
||||||
|
|
||||||
var (LU, p) := LUDecompose(A);
|
var (LU, p) := LUDecompose(A);
|
||||||
var pb := Permute(b, p);
|
var pb := Permute(b, p);
|
||||||
|
|
@ -1358,11 +1361,11 @@ end;
|
||||||
|
|
||||||
function SolveSPD(A: Matrix; b: Vector): Vector;
|
function SolveSPD(A: Matrix; b: Vector): Vector;
|
||||||
begin
|
begin
|
||||||
if A.Rows <> A.Cols then
|
if A.RowCount <> A.ColCount then
|
||||||
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
ArgumentError(ER_MATRIX_NOT_SQUARE);
|
||||||
|
|
||||||
if b.Length <> A.Rows then
|
if b.Length <> A.RowCount then
|
||||||
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, A.Rows);
|
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, A.RowCount);
|
||||||
|
|
||||||
var L := Cholesky(A);
|
var L := Cholesky(A);
|
||||||
var y := SolveLowerTriangular(L, b);
|
var y := SolveLowerTriangular(L, b);
|
||||||
|
|
@ -1383,8 +1386,8 @@ begin
|
||||||
if lambda < 0.0 then
|
if lambda < 0.0 then
|
||||||
ArgumentError(ER_LAMBDA_NEGATIVE);
|
ArgumentError(ER_LAMBDA_NEGATIVE);
|
||||||
|
|
||||||
var m := A.Rows;
|
var m := A.RowCount;
|
||||||
var n := A.Cols;
|
var n := A.ColCount;
|
||||||
|
|
||||||
if b.Length <> m then
|
if b.Length <> m then
|
||||||
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, m);
|
DimensionError(ER_VECTOR_SIZE_MISMATCH, b.Length, m);
|
||||||
|
|
@ -1430,6 +1433,89 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function SolveLeastSquaresQR(A: Matrix; b: Vector): Vector;
|
||||||
|
begin
|
||||||
|
if A.RowCount <> b.Length then
|
||||||
|
DimensionError(ER_DIM_MISMATCH, A.RowCount, b.Length);
|
||||||
|
|
||||||
|
var m := A.RowCount;
|
||||||
|
var n := A.ColCount;
|
||||||
|
|
||||||
|
if m < n then
|
||||||
|
ArgumentError(ER_QR_REQUIRES_M_GE_N);
|
||||||
|
|
||||||
|
var R := A.Clone;
|
||||||
|
var y := b.Clone;
|
||||||
|
|
||||||
|
for var k := 0 to n - 1 do
|
||||||
|
begin
|
||||||
|
// ---- вычислить норму столбца k начиная с строки k
|
||||||
|
var normx := 0.0;
|
||||||
|
for var i := k to m - 1 do
|
||||||
|
normx += R[i,k] * R[i,k];
|
||||||
|
|
||||||
|
normx := Sqrt(normx);
|
||||||
|
|
||||||
|
if normx = 0 then
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ---- знак для устойчивости
|
||||||
|
if R[k,k] >= 0 then
|
||||||
|
normx := -normx;
|
||||||
|
|
||||||
|
// ---- построить Householder-вектор v
|
||||||
|
var beta := 1.0 / (normx * R[k,k] - normx * normx);
|
||||||
|
|
||||||
|
R[k,k] -= normx;
|
||||||
|
|
||||||
|
// ---- применить отражение к R
|
||||||
|
for var j := k + 1 to n - 1 do
|
||||||
|
begin
|
||||||
|
var s := 0.0;
|
||||||
|
for var i := k to m - 1 do
|
||||||
|
s += R[i,k] * R[i,j];
|
||||||
|
|
||||||
|
s *= beta;
|
||||||
|
|
||||||
|
for var i := k to m - 1 do
|
||||||
|
R[i,j] -= s * R[i,k];
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ---- применить отражение к y
|
||||||
|
var sy := 0.0;
|
||||||
|
for var i := k to m - 1 do
|
||||||
|
sy += R[i,k] * y[i];
|
||||||
|
|
||||||
|
sy *= beta;
|
||||||
|
|
||||||
|
for var i := k to m - 1 do
|
||||||
|
y[i] -= sy * R[i,k];
|
||||||
|
|
||||||
|
// ---- восстановить диагональный элемент
|
||||||
|
R[k,k] := normx;
|
||||||
|
|
||||||
|
for var i := k + 1 to m - 1 do
|
||||||
|
R[i,k] := 0.0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// ---- Back substitution (решаем R[0:n,0:n] * x = y[0:n])
|
||||||
|
var x := new Vector(n);
|
||||||
|
|
||||||
|
for var i := n - 1 downto 0 do
|
||||||
|
begin
|
||||||
|
var sum := y[i];
|
||||||
|
|
||||||
|
for var j := i + 1 to n - 1 do
|
||||||
|
sum -= R[i,j] * x[j];
|
||||||
|
|
||||||
|
if Abs(R[i,i]) < 1e-14 then
|
||||||
|
ArgumentError(ER_SINGULAR_MATRIX);
|
||||||
|
|
||||||
|
x[i] := sum / R[i,i];
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := x;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
@ -15,6 +15,8 @@ const
|
||||||
'Метод Fit() не вызван!!Fit() must be called first';
|
'Метод Fit() не вызван!!Fit() must be called first';
|
||||||
ER_MODEL_NULL =
|
ER_MODEL_NULL =
|
||||||
'Модель не может быть nil!!Model cannot be nil';
|
'Модель не может быть nil!!Model cannot be nil';
|
||||||
|
ER_SELECTKBEST_K_INVALID =
|
||||||
|
'Параметр k в SelectKBest должен быть >= 1. Передано: {0}.!!SelectKBest parameter k must be >= 1. Given: {0}.';
|
||||||
|
|
||||||
type
|
type
|
||||||
/// Базовое исключение ML-библиотеки
|
/// Базовое исключение ML-библиотеки
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -187,16 +187,16 @@ begin
|
||||||
if fFitted then
|
if fFitted then
|
||||||
Error(ER_PIPELINE_MODIFY_AFTER_FIT);
|
Error(ER_PIPELINE_MODIFY_AFTER_FIT);
|
||||||
|
|
||||||
if step is IDataStep then
|
if step is IPreprocessor then // Data Step
|
||||||
begin
|
begin
|
||||||
if (fMatrixSteps.Count > 0) or (fModel <> nil) then
|
if (fMatrixSteps.Count > 0) or (fModel <> nil) then
|
||||||
ArgumentError(ER_DATAPIPE_DF_AFTER_MATRIX);
|
ArgumentError(ER_DATAPIPE_DF_AFTER_MATRIX);
|
||||||
|
|
||||||
fDataSteps.Add(step as IPreprocessor);
|
fDataSteps.Add(step as IPreprocessor);
|
||||||
exit(Self);
|
exit(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if step is IMatrixStep then
|
if step is ITransformer then // Matrix Step
|
||||||
begin
|
begin
|
||||||
if step is IModel then
|
if step is IModel then
|
||||||
begin
|
begin
|
||||||
|
|
@ -270,7 +270,7 @@ begin
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// 5) Модель
|
// 5) Модель
|
||||||
fModel.Fit(X, y);
|
fModel := fModel.Fit(X, y);
|
||||||
|
|
||||||
fFitted := true;
|
fFitted := true;
|
||||||
Result := Self;
|
Result := Self;
|
||||||
|
|
|
||||||
|
|
@ -9,40 +9,44 @@ type
|
||||||
public
|
public
|
||||||
/// Делит данные на обучающую и тестовую выборки.
|
/// Делит данные на обучающую и тестовую выборки.
|
||||||
/// testRatio — доля объектов, попадающих в тестовую выборку (по умолчанию 0.2).
|
/// testRatio — доля объектов, попадающих в тестовую выборку (по умолчанию 0.2).
|
||||||
/// Перемешивает объекты перед разбиением.
|
/// Перед разбиением объекты перемешиваются.
|
||||||
/// Возвращает (X_train, X_test, y_train, y_test).
|
/// Возвращает кортеж (X_train, X_test, y_train, y_test).
|
||||||
static function TrainTestSplit(X: Matrix; y: Vector;
|
static function TrainTestSplit(X: Matrix; y: Vector;
|
||||||
testRatio: real := 0.2; seed: integer := 0): (Matrix, Matrix, Vector, Vector);
|
testRatio: real := 0.2; seed: integer := -1): (Matrix, Matrix, Vector, Vector);
|
||||||
|
|
||||||
/// Разбивает индексы объектов на k непересекающихся частей (fold).
|
/// Разбивает индексы объектов на k непересекающихся частей (fold).
|
||||||
/// На каждом шаге одна часть используется как тестовая, остальные — как обучающая.
|
/// На каждом шаге одна часть используется как тестовая, остальные — как обучающая выборка.
|
||||||
/// Применяется для k-fold кросс-валидации.
|
/// Используется для k-fold кросс-валидации.
|
||||||
/// Возвращает последовательность пар (trainIdx, testIdx).
|
/// Возвращает последовательность пар (trainIdx, testIdx).
|
||||||
static function KFold(n, k: integer; seed: integer := 0):
|
static function KFold(n, k: integer; seed: integer := -1):
|
||||||
sequence of (array of integer, array of integer);
|
sequence of (array of integer, array of integer);
|
||||||
|
|
||||||
/// Разбивает данные на k частей с сохранением пропорций классов.
|
/// Разбивает данные на k частей (k-fold) с сохранением пропорций классов
|
||||||
/// В каждой части сохраняется примерно то же соотношение
|
/// (стратифицированная k-fold кросс-валидация).
|
||||||
/// объектов разных классов, что и во всей выборке.
|
/// В каждой части доля объектов каждого класса
|
||||||
/// Рекомендуется для задач классификации,
|
/// максимально близка к их доле во всей выборке
|
||||||
/// особенно при неравномерном распределении классов.
|
/// (разница не превышает одного объекта на класс).
|
||||||
/// Возвращает последовательность пар (trainIdx, testIdx).
|
/// Рекомендуется для задач классификации,
|
||||||
|
/// особенно при несбалансированных классах.
|
||||||
|
/// Возвращает последовательность пар (trainIdx, testIdx)
|
||||||
static function StratifiedKFold(y: Vector; k: integer;
|
static function StratifiedKFold(y: Vector; k: integer;
|
||||||
seed: integer := 0): sequence of (array of integer, array of integer);
|
seed: integer := -1): sequence of (array of integer, array of integer);
|
||||||
|
|
||||||
/// Выполняет k-fold кросс-валидацию модели.
|
/// Выполняет k-fold кросс-валидацию модели.
|
||||||
/// На каждом шаге модель обучается на обучающей части и оценивается на тестовой.
|
/// На каждом шаге модель обучается на обучающей части и оценивается на соответствующей тестовой части.
|
||||||
/// metric — функция качества (например, Accuracy или MSE).
|
/// metric — функция качества, принимающая (y_true, y_pred)
|
||||||
|
/// и возвращающая значение метрики (например, Accuracy или MSE).
|
||||||
/// Возвращает среднее значение метрики по всем частям.
|
/// Возвращает среднее значение метрики по всем частям.
|
||||||
static function CrossValidate(model: IModel; X: Matrix; y: Vector;
|
static function CrossValidate(model: IModel; X: Matrix; y: Vector;
|
||||||
k: integer; metric: (Vector,Vector) -> real; seed: integer := 0): real;
|
k: integer; metric: (Vector,Vector) -> real; seed: integer := -1): real;
|
||||||
|
|
||||||
/// Выполняет k-fold кросс-валидацию с сохранением пропорций классов.
|
/// Выполняет стратифицированную k-fold кросс-валидацию модели.
|
||||||
/// Использует StratifiedKFold для разбиения данных.
|
/// Разбиение данных выполняется методом StratifiedKFold
|
||||||
/// Подходит для задач классификации.
|
/// с сохранением пропорций классов в каждой части.
|
||||||
/// Возвращает среднее значение метрики по всем частям.
|
/// Рекомендуется для задач классификации, особенно при несбалансированных классах.
|
||||||
|
/// Возвращает среднее значение метрики по k разбиениям.
|
||||||
static function StratifiedCrossValidate(model: IModel; X: Matrix; y: Vector;
|
static function StratifiedCrossValidate(model: IModel; X: Matrix; y: Vector;
|
||||||
k: integer; metric: (Vector,Vector) -> real; seed: integer := 0): real;
|
k: integer; metric: (Vector,Vector) -> real; seed: integer := -1): real;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
/// Класс для подбора гиперпараметров методом перебора по сетке (Grid Search).
|
/// Класс для подбора гиперпараметров методом перебора по сетке (Grid Search).
|
||||||
|
|
@ -105,13 +109,17 @@ begin
|
||||||
var n := X.RowCount;
|
var n := X.RowCount;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
||||||
var rnd := new System.Random(seed);
|
var rnd := if seed >= 0 then
|
||||||
|
new System.Random(seed)
|
||||||
|
else
|
||||||
|
new System.Random;
|
||||||
|
|
||||||
// Перемешанные индексы
|
// Перемешанные индексы
|
||||||
var idx := (0..n-1).ToArray;
|
var idx := (0..n-1).ToArray;
|
||||||
idx := idx.OrderBy(i -> rnd.Next).ToArray;
|
idx := idx.OrderBy(i -> rnd.Next).ToArray;
|
||||||
|
|
||||||
var testSize := Round(n * testRatio);
|
var rawSize := Round(n * testRatio);
|
||||||
|
var testSize := Max(1, Min(n - 1, rawSize));
|
||||||
var trainSize := n - testSize;
|
var trainSize := n - testSize;
|
||||||
|
|
||||||
var X_train := new Matrix(trainSize, p);
|
var X_train := new Matrix(trainSize, p);
|
||||||
|
|
@ -151,70 +159,144 @@ begin
|
||||||
if (k < 2) or (k > n) then
|
if (k < 2) or (k > n) then
|
||||||
ArgumentError(ER_K_INVALID, k, n);
|
ArgumentError(ER_K_INVALID, k, n);
|
||||||
|
|
||||||
var rnd := new System.Random(seed);
|
var rnd := if seed >= 0 then
|
||||||
var idx := (0..n-1).OrderBy(i -> rnd.Next).ToArray;
|
new System.Random(seed)
|
||||||
|
else
|
||||||
|
new System.Random;
|
||||||
|
|
||||||
|
// --- 1. Создаём массив индексов 0..n-1
|
||||||
|
var idx := new integer[n];
|
||||||
|
for var i := 0 to n - 1 do
|
||||||
|
idx[i] := i;
|
||||||
|
|
||||||
|
// --- 2. Fisher–Yates shuffle (in-place, O(n))
|
||||||
|
for var i := n - 1 downto 1 do
|
||||||
|
begin
|
||||||
|
var j := rnd.Next(i + 1);
|
||||||
|
var tmp := idx[i];
|
||||||
|
idx[i] := idx[j];
|
||||||
|
idx[j] := tmp;
|
||||||
|
end;
|
||||||
|
|
||||||
var baseSize := n div k;
|
var baseSize := n div k;
|
||||||
var extra := n mod k;
|
var extra := n mod k;
|
||||||
var start := 0;
|
var start := 0;
|
||||||
|
|
||||||
|
// --- 3. Формируем фолды
|
||||||
for var fold := 0 to k - 1 do
|
for var fold := 0 to k - 1 do
|
||||||
begin
|
begin
|
||||||
var size := baseSize + (if fold < extra then 1 else 0);
|
var size := baseSize + Ord(fold < extra);
|
||||||
|
|
||||||
var testIdx := idx.Skip(start).Take(size).ToArray;
|
// --- test indices
|
||||||
var trainIdx := idx.Take(start).Concat(idx.Skip(start + size)).ToArray;
|
var testIdx := new integer[size];
|
||||||
|
System.Array.Copy(idx, start, testIdx, 0, size);
|
||||||
|
|
||||||
|
// --- train indices
|
||||||
|
var trainSize := n - size;
|
||||||
|
var trainIdx := new integer[trainSize];
|
||||||
|
|
||||||
|
// левая часть
|
||||||
|
if start > 0 then
|
||||||
|
System.Array.Copy(idx, 0, trainIdx, 0, start);
|
||||||
|
|
||||||
|
// правая часть
|
||||||
|
var tailCount := n - (start + size);
|
||||||
|
if tailCount > 0 then
|
||||||
|
System.Array.Copy(idx, start + size, trainIdx, start, tailCount);
|
||||||
|
|
||||||
yield (trainIdx, testIdx);
|
yield (trainIdx, testIdx);
|
||||||
|
|
||||||
start += size;
|
start += size;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
static function Validation.StratifiedKFold(y: Vector; k: integer;
|
static function Validation.StratifiedKFold(y: Vector; k: integer; seed: integer):
|
||||||
seed: integer): sequence of (array of integer, array of integer);
|
sequence of (array of integer, array of integer);
|
||||||
begin
|
begin
|
||||||
var n := y.Length;
|
var n := y.Length;
|
||||||
if (k < 2) or (k > n) then
|
if (k < 2) or (k > n) then
|
||||||
ArgumentError(ER_K_INVALID_STRATIFIED, k, n);
|
ArgumentError(ER_K_INVALID_STRATIFIED, k, n);
|
||||||
|
|
||||||
var rnd := new System.Random(seed);
|
var rnd :=
|
||||||
|
if seed >= 0 then new System.Random(seed)
|
||||||
|
else new System.Random;
|
||||||
|
|
||||||
var idx0 := (0..n-1)
|
// 1) Собираем индексы по классам за один проход
|
||||||
.Where(i -> y[i] = 0)
|
var classMap := new Dictionary<integer, List<integer>>();
|
||||||
.OrderBy(i -> rnd.Next)
|
for var i := 0 to n - 1 do
|
||||||
.ToArray;
|
begin
|
||||||
|
var v := y[i];
|
||||||
|
var cls := integer(v);
|
||||||
|
|
||||||
var idx1 := (0..n-1)
|
// Валидация: метки должны быть целыми (хотя тип double)
|
||||||
.Where(i -> y[i] = 1)
|
if Abs(v - cls) > 1e-12 then
|
||||||
.OrderBy(i -> rnd.Next)
|
ArgumentError(ER_STRATIFIED_LABELS_INVALID);
|
||||||
.ToArray;
|
|
||||||
|
|
||||||
if idx0.Length + idx1.Length <> n then
|
var lst: List<integer>;
|
||||||
ArgumentError(ER_STRATIFIED_LABELS_INVALID);
|
if classMap.TryGetValue(cls, lst) then
|
||||||
|
lst.Add(i)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
lst := new List<integer>;
|
||||||
|
lst.Add(i);
|
||||||
|
classMap.Add(cls, lst);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
var base0 := idx0.Length div k;
|
// 2) Подготавливаем контейнеры фолдов
|
||||||
var extra0 := idx0.Length mod k;
|
var folds := new List<integer>[k];
|
||||||
|
for var f := 0 to k - 1 do
|
||||||
|
folds[f] := new List<integer>;
|
||||||
|
|
||||||
var base1 := idx1.Length div k;
|
// 3) Для каждого класса: shuffle + равномерно раскладываем по k фолдам
|
||||||
var extra1 := idx1.Length mod k;
|
foreach var pair in classMap do
|
||||||
|
begin
|
||||||
|
var indices := pair.Value;
|
||||||
|
var m := indices.Count;
|
||||||
|
|
||||||
|
// Fisher–Yates shuffle in-place
|
||||||
|
for var i := m - 1 downto 1 do
|
||||||
|
begin
|
||||||
|
var j := rnd.Next(i + 1);
|
||||||
|
var tmp := indices[i];
|
||||||
|
indices[i] := indices[j];
|
||||||
|
indices[j] := tmp;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Равномерное распределение (разница размеров ≤ 1)
|
||||||
|
var baseSize := m div k;
|
||||||
|
var extra := m mod k;
|
||||||
|
var start := 0;
|
||||||
|
|
||||||
|
for var fold := 0 to k - 1 do
|
||||||
|
begin
|
||||||
|
var size := baseSize + Ord(fold < extra);
|
||||||
|
for var t := 0 to size - 1 do
|
||||||
|
folds[fold].Add(indices[start + t]);
|
||||||
|
start += size;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// 4) Возвращаем (trainIdx, testIdx) для каждого фолда
|
||||||
for var fold := 0 to k - 1 do
|
for var fold := 0 to k - 1 do
|
||||||
begin
|
begin
|
||||||
var start0 := fold * base0 + Min(fold, extra0);
|
var testIdx := folds[fold].ToArray();
|
||||||
var size0 := base0 + (if fold < extra0 then 1 else 0);
|
|
||||||
|
|
||||||
var start1 := fold * base1 + Min(fold, extra1);
|
// boolean-mask вместо Contains -> O(n)
|
||||||
var size1 := base1 + (if fold < extra1 then 1 else 0);
|
var mask := new boolean[n];
|
||||||
|
foreach var id in testIdx do
|
||||||
|
mask[id] := true;
|
||||||
|
|
||||||
var testIdx :=
|
var trainSize := n - testIdx.Length;
|
||||||
idx0.Skip(start0).Take(size0)
|
var trainIdx := new integer[trainSize];
|
||||||
.Concat(idx1.Skip(start1).Take(size1))
|
var p := 0;
|
||||||
.ToArray;
|
|
||||||
|
|
||||||
var trainIdx :=
|
for var i := 0 to n - 1 do
|
||||||
(0..n-1)
|
if not mask[i] then
|
||||||
.Where(i -> not testIdx.Contains(i))
|
begin
|
||||||
.ToArray;
|
trainIdx[p] := i;
|
||||||
|
p += 1;
|
||||||
|
end;
|
||||||
|
|
||||||
yield (trainIdx, testIdx);
|
yield (trainIdx, testIdx);
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue