gridSearch RidgeRegression ElasticNet

This commit is contained in:
Mikhalkovich Stanislav 2026-02-15 08:43:44 +03:00
parent bc77b1564a
commit f0599232a9
8 changed files with 392 additions and 10 deletions

View file

@ -15,7 +15,7 @@ internal static class RevisionClass
public const string Major = "3";
public const string Minor = "11";
public const string Build = "1";
public const string Revision = "3749";
public const string Revision = "3751";
public const string MainVersion = Major + "." + Minor;
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;

View file

@ -1,4 +1,4 @@
%MINOR%=11
%REVISION%=3749
%REVISION%=3751
%COREVERSION%=1
%MAJOR%=3

View file

@ -1 +1 @@
3.11.1.3749
3.11.1.3751

View file

@ -1 +1 @@
!define VERSION '3.11.1.3749'
!define VERSION '3.11.1.3751'

View file

@ -114,6 +114,11 @@ copy bin\Lib\DataFrameABC.pcu Release\PascalABCNETLinux\Lib\DataFrameABC.pcu
copy bin\Lib\DataFrameABCCore.pcu Release\PascalABCNETLinux\Lib\DataFrameABCCore.pcu
copy bin\Lib\LinearAlgebraML.pcu Release\PascalABCNETLinux\Lib\LinearAlgebraML.pcu
copy bin\Lib\PreprocessorABC.pcu Release\PascalABCNETLinux\Lib\PreprocessorABC.pcu
copy bin\Lib\MetricsABC.pcu Release\PascalABCNETLinux\Lib\MetricsABC.pcu
copy bin\Lib\MLABC.pcu Release\PascalABCNETLinux\Lib\MLABC.pcu
copy bin\Lib\MLCoreABC.pcu Release\PascalABCNETLinux\Lib\MLCoreABC.pcu
copy bin\Lib\MLModelsABC.pcu Release\PascalABCNETLinux\Lib\MLModelsABC.pcu
copy bin\Lib\ValidationML.pcu Release\PascalABCNETLinux\Lib\ValidationML.pcu
copy bin\Lib\ABCDatabases.pas Release\PascalABCNETLinux\LibSource\ABCDatabases.pas
copy bin\Lib\BBCMicrobit.pas Release\PascalABCNETLinux\LibSource\BBCMicrobit.pas
@ -159,6 +164,11 @@ copy bin\Lib\DataFrameABC.pas Release\PascalABCNETLinux\LibSource\DataFrameABC.p
copy bin\Lib\DataFrameABCCore.pas Release\PascalABCNETLinux\LibSource\DataFrameABCCore.pas
copy bin\Lib\LinearAlgebraML.pas Release\PascalABCNETLinux\LibSource\LinearAlgebraML.pas
copy bin\Lib\PreprocessorABC.pas Release\PascalABCNETLinux\LibSource\PreprocessorABC.pas
copy bin\Lib\MetricsABC.pas Release\PascalABCNETLinux\LibSource\MetricsABC.pas
copy bin\Lib\MLABC.pas Release\PascalABCNETLinux\LibSource\MLABC.pas
copy bin\Lib\MLCoreABC.pas Release\PascalABCNETLinux\LibSource\MLCoreABC.pas
copy bin\Lib\MLModelsABC.pas Release\PascalABCNETLinux\LibSource\MLModelsABC.pas
copy bin\Lib\ValidationML.pas Release\PascalABCNETLinux\LibSource\ValidationML.pas
copy bin\Lng\Eng\.LanguageName Release\PascalABCNETLinux\Lng\Eng\.LanguageName
copy bin\Lng\Eng\AspectsTree.dat Release\PascalABCNETLinux\Lng\Eng\AspectsTree.dat

View file

@ -15,19 +15,23 @@ 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;
StandardScaler = MLModelsABC.StandardScaler;
Activations = MLModelsABC.Activations;
Pipeline = MLModelsABC.Pipeline;
LinearRegression = MLModelsABC.LinearRegression;
LogisticRegression = MLModelsABC.LogisticRegression;
RidgeRegression = MLModelsABC.RidgeRegression;
ElasticNet = ElasticNet;
implementation
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;

View file

@ -6,6 +6,7 @@ uses MLCoreABC;
uses LinearAlgebraML;
type
{$region Activations}
/// Активационные функции для моделей
Activations = static class
public
@ -40,10 +41,16 @@ type
/// Формула: softmax(x_i) = e^{x_i} / Σ e^{x_j}.
static function Softmax(v: Vector): Vector;
end;
{$endregion Activations}
{$region Models}
IModel = MLCoreABC.IModel;
/// Линейная регрессионная модель
/// Предсказывает числовое значение по линейной комбинации признаков
/// Используется в задачах регрессии при отсутствии сильной
/// мультиколлинеарности и когда число признаков существенно меньше числа объектов.
LinearRegression = class(IRegressor)
private
fCoef: Vector;
@ -82,6 +89,8 @@ type
/// Предсказывает вероятность принадлежности объекта к классу 1
/// на основе линейной комбинации признаков и сигмоидной функции.
/// Поддерживает L2-регуляризацию.
/// Используется в задачах бинарной классификации,
/// когда требуется вероятностный вывод и интерпретируемые коэффициенты.
LogisticRegression = class(IClassifier)
private
fCoef: Vector;
@ -129,7 +138,107 @@ type
/// После вызова Fit значение становится true.
property IsFitted: boolean read fFitted;
end;
/// Линейная регрессионная модель с L2-регуляризацией (Ridge).
/// Минимизирует функцию:
/// ||y - ( + b)||² + λ ||β||².
/// Устойчива к мультиколлинеарности и плохо обусловленным данным.
/// Используется при коррелированных признаках
/// и в задачах, где важна численная стабильность решения.
RidgeRegression = class(IRegressor)
private
fLambda: real;
fCoef: Vector;
fIntercept: real;
fFitted: boolean;
public
/// Создаёт модель Ridge-регрессии.
/// lambda коэффициент L2-регуляризации (0 обычная линейная регрессия).
constructor Create(lambda: real := 1.0);
/// Обучает модель на числовых данных.
/// X матрица m × n (m объектов, n признаков).
/// y вектор длины m с непрерывными значениями.
/// Выполняется центрирование признаков и целевой переменной.
function Fit(X: Matrix; y: Vector): IModel;
/// Предсказывает непрерывные значения для объектов X.
/// Результат вектор длины m.
function Predict(X: Matrix): Vector;
/// Вектор коэффициентов модели (веса признаков).
/// Длина равна числу признаков.
/// Доступен после обучения (Fit).
property Coefficients: Vector read fCoef;
/// Свободный член модели (смещение, bias).
/// Не подвергается регуляризации.
property Intercept: real read fIntercept;
/// Коэффициент L2-регуляризации.
property Lambda: real read fLambda;
/// Показывает, была ли модель обучена.
/// После вызова Fit значение становится true.
property IsFitted: boolean read fFitted;
end;
/// Линейная регрессионная модель ElasticNet.
/// Минимизирует функцию:
/// ||y - ( + b)||² + λ1 ||β|| + λ2 ||β||².
/// Объединяет L1-регуляризацию (разреженность, отбор признаков)
/// и L2-регуляризацию (численная устойчивость).
/// Используется при большом числе признаков, особенно если признаки коррелированы.
/// Обучение выполняется методом покоординатного спуска
ElasticNet = class(IRegressor)
private
fLambda1: real; // L1
fLambda2: real; // L2
fMaxIter: integer;
fTol: real;
fCoef: Vector;
fIntercept: real;
fFitted: boolean;
/// Применяет оператор мягкого порога:
/// soft(z, γ) = sign(z) * max(|z| - γ, 0).
/// Используется для реализации L1-регуляризации.
function SoftThreshold(z, gamma: real): real;
public
/// Создаёт модель ElasticNet.
/// lambda1 коэффициент L1-регуляризации (>= 0).
/// lambda2 коэффициент L2-регуляризации (>= 0).
/// maxIter максимальное число итераций coordinate descent.
/// tol критерий остановки по изменению коэффициентов.
constructor Create(lambda1, lambda2: real; maxIter: integer := 1000; tol: real := 1e-6);
/// Обучает модель на числовых данных.
/// X матрица m × n (m объектов, n признаков).
/// y вектор длины m с непрерывными значениями.
/// Выполняется центрирование признаков и целевой переменной.
function Fit(X: Matrix; y: Vector): IModel;
/// Предсказывает непрерывные значения для объектов X.
/// Результат вектор длины m.
function Predict(X: Matrix): Vector;
/// Вектор коэффициентов модели (веса признаков).
/// Длина равна числу признаков.
/// Доступен после обучения (Fit).
property Coefficients: Vector read fCoef;
/// Свободный член модели (смещение, bias).
/// Не подвергается регуляризации.
property Intercept: real read fIntercept;
/// Показывает, была ли модель обучена.
/// После вызова Fit значение становится true.
property IsFitted: boolean read fFitted;
end;
{$endregion Models}
{$region Pipeline}
/// Конвейер машинного обучения (Pipeline).
/// Объединяет несколько шагов подготовки данных и модель
/// в единую последовательность обработки.
@ -172,7 +281,9 @@ type
/// Показывает, был ли пайплайн обучен (вызван метод Fit).
property IsFitted: boolean read fFitted;
end;
{$endregion Pipeline}
{$region Transformers}
/// Стандартизирует признаки: вычитает среднее
/// и делит на стандартное отклонение по каждому столбцу.
/// Используется для приведения признаков к сопоставимому масштабу.
@ -231,10 +342,13 @@ type
/// Признак того, что преобразование обучено.
property IsFitted: boolean read fFitted;
end;
{$endregion Transformers}
implementation
uses System;
//-----------------------------
// LinearRegression
//-----------------------------
@ -270,7 +384,7 @@ begin
// 4. Ridge regularization
if flambda > 0 then
XtX := XtX + flambda * Matrix.Identity(p);
XtX := XtX + Matrix.Identity(p) * flambda;
// 5. Solve
fcoef := Solve(XtX, XtY);
@ -383,6 +497,160 @@ begin
Result := Predict(X, 0.5);
end;
//-----------------------------
// RidgeRegression
//-----------------------------
constructor RidgeRegression.Create(lambda: real);
begin
if lambda < 0 then
raise new ArgumentException('lambda must be >= 0');
fLambda := lambda;
fFitted := false;
end;
function RidgeRegression.Fit(X: Matrix; y: Vector): IModel;
begin
if X.RowCount <> y.Length then
raise new ArgumentException('X and y size mismatch');
var n := X.RowCount;
var p := X.ColCount;
// Means
var muX := X.ColumnMeans;
var muY := y.Mean;
// Centered copies
var Xc := X.Clone;
var yc := y.Clone;
for var j := 0 to p - 1 do
for var i := 0 to n - 1 do
Xc[i, j] -= muX[j];
for var i := 0 to n - 1 do
yc[i] -= muY;
// Ridge solution
fCoef := SolveRidge(Xc, yc, fLambda);
// Intercept (NOT regularized)
fIntercept := muY - muX.Dot(fCoef);
fFitted := true;
Result := Self;
end;
function RidgeRegression.Predict(X: Matrix): Vector;
begin
if not fFitted then
raise new InvalidOperationException('Model is not fitted');
Result := X * fCoef;
for var i := 0 to Result.Length - 1 do
Result[i] += fIntercept;
end;
constructor ElasticNet.Create(lambda1, lambda2: real; maxIter: integer; tol: real);
begin
if (lambda1 < 0) or (lambda2 < 0) then
raise new ArgumentException('lambda must be >= 0');
fLambda1 := lambda1;
fLambda2 := lambda2;
fMaxIter := maxIter;
fTol := tol;
fFitted := false;
end;
function ElasticNet.SoftThreshold(z, gamma: real): real;
begin
if z > gamma then
exit(z - gamma)
else if z < -gamma then
exit(z + gamma)
else
exit(0.0);
end;
function ElasticNet.Fit(X: Matrix; y: Vector): IModel;
begin
if X.RowCount <> y.Length then
raise new ArgumentException('X and y size mismatch');
// Loss(β) = ||y - ( + b)||² + λ1||β||1 + λ2||β||2²
var n := X.RowCount;
var p := X.ColCount;
var muX := X.ColumnMeans;
var muY := y.Mean;
var Xc := X.Clone;
var yc := y.Clone;
for var j := 0 to p - 1 do
for var i := 0 to n - 1 do
Xc[i,j] -= muX[j];
for var i := 0 to n - 1 do
yc[i] -= muY;
fCoef := new Vector(p);
var residual := yc.Clone; // initial residual = yc (since β=0)
for var iter := 0 to fMaxIter - 1 do
begin
var maxChange := 0.0;
for var j := 0 to p - 1 do
begin
var oldBeta := fCoef[j];
var rho := 0.0;
var zj := 0.0;
for var i := 0 to n - 1 do
begin
rho += Xc[i,j] * (residual[i] + Xc[i,j] * oldBeta);
zj += Xc[i,j] * Xc[i,j];
end;
var newBeta := SoftThreshold(rho, fLambda1) / (zj + fLambda2);
var delta := newBeta - oldBeta;
if Abs(delta) > 0 then
for var i := 0 to n - 1 do
residual[i] -= Xc[i,j] * delta;
if Abs(delta) > maxChange then
maxChange := Abs(delta);
fCoef[j] := newBeta;
end;
if maxChange < fTol then
break;
end;
fIntercept := muY - muX.Dot(fCoef);
fFitted := true;
Result := Self;
end;
function ElasticNet.Predict(X: Matrix): Vector;
begin
if not fFitted then
raise new InvalidOperationException('Model is not fitted');
Result := X * fCoef;
for var i := 0 to Result.Length - 1 do
Result[i] += fIntercept;
end;
//-----------------------------
// Pipeline
//-----------------------------

View file

@ -44,9 +44,35 @@ type
static function StratifiedCrossValidate(model: IModel; X: Matrix; y: Vector;
k: integer; metric: (Vector,Vector) -> real; seed: integer := 0): real;
end;
/// Класс для подбора гиперпараметров методом перебора по сетке (Grid Search).
/// Для каждого значения параметра выполняется k-кратная кросс-валидация.
/// Выбирается параметр, дающий наилучшее среднее значение метрики.
/// Используется для настройки регуляризации и других гиперпараметров моделей.
GridSearch = static class
public
/// Выполняет подбор гиперпараметра по заданной сетке значений.
/// modelFactory функция создания модели по значению параметра.
/// paramValues набор тестируемых значений гиперпараметра.
/// X, y обучающие данные.
/// k число фолдов в кросс-валидации.
/// metric функция оценки качества (yTrue, yPred) real.
/// Возвращает кортеж (лучший параметр, лучшее среднее значение метрики).
class function Search<T>(
modelFactory: real -> IModel;
paramValues: array of real;
X: Matrix; y: Vector;
k: integer;
metric: (Vector, Vector) -> real
): (real, real); where T: IModel;
end;
implementation
//-----------------------------
// Validation
//-----------------------------
static function Validation.TrainTestSplit(X: Matrix; y: Vector;
testRatio: real; seed: integer): (Matrix, Matrix, Vector, Vector);
begin
@ -265,6 +291,80 @@ begin
Result := total / folds;
end;
//-----------------------------
// GridSearch
//-----------------------------
class function GridSearch.Search<T>(
modelFactory: real -> IModel;
paramValues: array of real;
X: Matrix; y: Vector;
k: integer;
metric: (Vector, Vector) -> real
): (real, real); where T: IModel;
begin
var bestParam := 0.0;
var bestScore := -1e308;
var n := X.RowCount;
var foldSize := n div k;
foreach var param in paramValues do
begin
var totalScore := 0.0;
for var fold := 0 to k - 1 do
begin
var startIdx := fold * foldSize;
var endIdx := if fold = k - 1 then n - 1 else (startIdx + foldSize - 1);
var trainCount := n - (endIdx - startIdx + 1);
var testCount := endIdx - startIdx + 1;
var Xtrain := new Matrix(trainCount, X.ColCount);
var ytrain := new Vector(trainCount);
var Xtest := new Matrix(testCount, X.ColCount);
var ytest := new Vector(testCount);
var ti := 0;
var si := 0;
for var i := 0 to n - 1 do
begin
if (i >= startIdx) and (i <= endIdx) then
begin
for var j := 0 to X.ColCount - 1 do
Xtest[si,j] := X[i,j];
ytest[si] := y[i];
si += 1;
end
else
begin
for var j := 0 to X.ColCount - 1 do
Xtrain[ti,j] := X[i,j];
ytrain[ti] := y[i];
ti += 1;
end;
end;
var model := modelFactory(param);
model.Fit(Xtrain, ytrain);
var pred := model.Predict(Xtest);
totalScore += metric(ytest, pred);
end;
var avgScore := totalScore / k;
if avgScore > bestScore then
begin
bestScore := avgScore;
bestParam := param;
end;
end;
Result := (bestParam, bestScore);
end;
end.