Много добавок в ML

This commit is contained in:
Mikhalkovich Stanislav 2026-02-16 14:12:07 +03:00
parent d4e56ead6c
commit 95a1e58952
4 changed files with 949 additions and 45 deletions

View file

@ -5,7 +5,6 @@ interface
type
Vector = class
private
static procedure CheckSameLength(const a, b: Vector);
static procedure CheckNonEmpty(const v: Vector);
procedure SetData(i: integer; value: real) := data[i] := value;
@ -93,7 +92,25 @@ type
function ColumnMins: Vector;
function ColumnMaxs: Vector;
function RowMins: Vector;
function RowMaxs: Vector;
function RowMaxs: Vector;
function RowSum(i: integer): real;
function RowMean(i: integer): real;
function RowVariance(i: integer): real;
function RowStd(i: integer): real;
function RowMin(i: integer): real;
function RowMax(i: integer): real;
function RowArgMin(i: integer): integer;
function RowArgMax(i: integer): integer;
function ColumnSum(j: integer): real;
function ColumnMean(j: integer): real;
function ColumnVariance(j: integer): real;
function ColumnStd(j: integer): real;
function ColumnMin(j: integer): real;
function ColumnMax(j: integer): real;
function ColumnArgMin(j: integer): integer;
function ColumnArgMax(j: integer): integer;
function FrobeniusNorm: real;
procedure AddScaledIdentity(lambda: real);
@ -561,6 +578,154 @@ begin
end;
end;
function Matrix.RowArgMin(i: integer): integer;
begin
var minVal := data[i,0];
var arg := 0;
for var j := 1 to ColCount - 1 do
if data[i,j] < minVal then
begin
minVal := data[i,j];
arg := j;
end;
Result := arg;
end;
function Matrix.RowMin(i: integer): real;
begin
Result := data[i, RowArgMin(i)];
end;
function Matrix.RowArgMax(i: integer): integer;
begin
var maxVal := data[i,0];
var arg := 0;
for var j := 1 to ColCount - 1 do
if data[i,j] > maxVal then
begin
maxVal := data[i,j];
arg := j;
end;
Result := arg;
end;
function Matrix.RowMax(i: integer): real;
begin
Result := data[i, RowArgMax(i)];
end;
function Matrix.RowSum(i: integer): real;
begin
var sum := 0.0;
for var j := 0 to ColCount - 1 do
sum += data[i,j];
Result := sum;
end;
function Matrix.RowMean(i: integer): real;
begin
Result := RowSum(i) / ColCount;
end;
function Matrix.RowVariance(i: integer): real;
begin
var mean := RowMean(i);
var sum := 0.0;
for var j := 0 to ColCount - 1 do
begin
var d := data[i,j] - mean;
sum += d * d;
end;
Result := sum / ColCount;
end;
function Matrix.RowStd(i: integer): real;
begin
Result := Sqrt(RowVariance(i));
end;
function Matrix.ColumnArgMin(j: integer): integer;
begin
var minVal := data[0,j];
var arg := 0;
for var i := 1 to RowCount - 1 do
if data[i,j] < minVal then
begin
minVal := data[i,j];
arg := i;
end;
Result := arg;
end;
function Matrix.ColumnMin(j: integer): real;
begin
Result := data[ColumnArgMin(j), j];
end;
function Matrix.ColumnArgMax(j: integer): integer;
begin
var maxVal := data[0,j];
var arg := 0;
for var i := 1 to RowCount - 1 do
if data[i,j] > maxVal then
begin
maxVal := data[i,j];
arg := i;
end;
Result := arg;
end;
function Matrix.ColumnMax(j: integer): real;
begin
Result := data[ColumnArgMax(j), j];
end;
function Matrix.ColumnSum(j: integer): real;
begin
var sum := 0.0;
for var i := 0 to RowCount - 1 do
sum += data[i,j];
Result := sum;
end;
function Matrix.ColumnMean(j: integer): real;
begin
Result := ColumnSum(j) / RowCount;
end;
function Matrix.ColumnVariance(j: integer): real;
begin
var mean := ColumnMean(j);
var sum := 0.0;
for var i := 0 to RowCount - 1 do
begin
var d := data[i,j] - mean;
sum += d * d;
end;
Result := sum / RowCount;
end;
function Matrix.ColumnStd(j: integer): real;
begin
Result := Sqrt(ColumnVariance(j));
end;
function Matrix.FrobeniusNorm: real;
begin

View file

@ -24,13 +24,20 @@ type
CsvLoader = DataFrameABC.CsvLoader;
StandardScaler = MLModelsABC.StandardScaler;
PCATransformer = MLModelsABC.PCATransformer;
MinMaxScaler = MLModelsABC.MinMaxScaler;
VarianceThreshold = MLModelsABC.VarianceThreshold;
SelectKBest = MLModelsABC.SelectKBest;
FeatureScore = MLModelsABC.FeatureScore;
Activations = MLModelsABC.Activations;
Pipeline = MLModelsABC.Pipeline;
LinearRegression = MLModelsABC.LinearRegression;
LogisticRegression = MLModelsABC.LogisticRegression;
RidgeRegression = MLModelsABC.RidgeRegression;
ElasticNet = ElasticNet;
ElasticNet = MLModelsABC.ElasticNet;
MulticlassLogisticRegression = MLModelsABC.MulticlassLogisticRegression;
implementation

View file

@ -48,6 +48,17 @@ type
function Transform(X: Matrix): Matrix;
end;
/// Интерфейс преобразования признаков с учётом целевой переменной.
/// Используется для методов отбора признаков и других процедур,
/// в которых при обучении требуется вектор целевых значений.
ISupervisedTransformer = interface(ITransformer)
/// Обучает преобразование на данных с использованием
/// как признаков X, так и целевой переменной y.
/// Запоминает необходимые параметры,
/// которые будут использоваться при Transform.
function Fit(X: Matrix; y: Vector): ISupervisedTransformer;
end;
implementation
end.

View file

@ -234,34 +234,80 @@ type
/// Показывает, была ли модель обучена.
/// После вызова Fit значение становится true.
property IsFitted: boolean read fFitted;
end;
/// Многоклассовая логистическая регрессия (Softmax).
/// Предсказывает вероятности принадлежности к каждому классу
/// на основе линейной комбинации признаков и softmax-функции.
/// Использует кросс-энтропийную функцию потерь.
/// Поддерживает L2-регуляризацию.
MulticlassLogisticRegression = class(IProbabilisticClassifier)
private
fW: Matrix; // p x k
fIntercept: Vector; // k
fLambda: real;
fLearningRate: real;
fEpochs: integer;
fFitted: boolean;
fClassCount: integer;
public
/// lambda коэффициент L2-регуляризации.
/// lr шаг градиентного спуска.
/// epochs число итераций обучения.
constructor Create(lambda: real := 0.0; lr: real := 0.1; epochs: integer := 1000);
function Fit(X: Matrix; y: Vector): IModel;
function PredictProba(X: Matrix): Vector;
/// Возвращает матрицу вероятностей (m x k).
function PredictProbaMatrix(X: Matrix): Matrix;
/// Возвращает вектор предсказанных классов.
function Predict(X: Matrix): Vector;
property IsFitted: boolean read fFitted;
end;
{$endregion Models}
{$region Pipeline}
/// Конвейер машинного обучения (Pipeline).
/// Объединяет несколько шагов подготовки данных и модель
/// в единую последовательность обработки.
/// Сначала данные проходят через все преобразования,
/// затем итоговые признаки передаются в модель.
/// Обеспечивает единый интерфейс Fit / Predict.
/// Последовательный конвейер машинного обучения.
/// Гарантирует строгий порядок выполнения шагов:
/// [преобразователи] [модель].
///
/// Поддерживает:
/// - преобразователи без учёта целевой переменной;
/// - преобразователи с учётом целевой переменной;
/// - одну финальную модель.
///
/// Обеспечивает единый интерфейс Fit / Predict
/// и воспроизводимость полного процесса обучения.
Pipeline = class(IModel)
private
fTransformers: List<ITransformer>;
fModel: IModel;
fFitted: boolean;
public
/// Создаёт пайплайн (конвейер машинного обучения) для заданной модели.
/// model финальная модель (регрессор или классификатор),
/// которая будет обучаться после всех преобразований.
/// Создаёт конвейер машинного обучения для заданной модели.
/// model модель, которая будет обучена
/// после последовательного применения всех преобразователей.
constructor Create(model: IModel);
/// Создаёт пустой пайплайн (конвейер машинного обучения).
/// Модель должна быть установлена через SetModel.
constructor Create;
/// Строит пайплайн (конвейер машинного обучения).
/// model финальная модель, которая будет обучаться.
/// transformers последовательность преобразований признаков,
/// применяемых к данным перед обучением модели.
/// Возвращает настроенный конвейер.
/// Строит конвейер машинного обучения.
/// model модель, обучаемая после применения всех преобразователей.
/// transformers последовательность преобразователей признаков,
/// применяемых к данным перед обучением модели.
/// Возвращает сконструированный конвейер.
static function Build(model: IModel; params transformers: array of ITransformer): Pipeline;
/// Устанавливает или заменяет модель.
function SetModel(m: IModel): Pipeline;
/// Добавляет преобразование в конец пайплайна
function Add(t: ITransformer): Pipeline;
@ -342,6 +388,163 @@ type
/// Признак того, что преобразование обучено.
property IsFitted: boolean read fFitted;
end;
/// Трансформер главных компонент (PCA).
/// Выполняет уменьшение размерности путём проекции данных
/// на первые k главных компонент.
/// На этапе Fit вычисляет главные компоненты ковариационной матрицы.
/// На этапе Transform проецирует данные:
/// Z = (X - μ) · W
PCATransformer = class(ITransformer)
private
fK: integer;
fComponents: Matrix; // W
fMean: Vector; // μ
fFitted: boolean;
public
/// Создаёт PCA-трансформер.
/// k число главных компонент (k > 0).
constructor Create(k: integer);
/// Обучает трансформер на матрице признаков X.
/// X матрица m × n.
function Fit(X: Matrix): ITransformer;
/// Преобразует матрицу X в пространство главных компонент.
/// Возвращает матрицу m × k.
function Transform(X: Matrix): Matrix;
property Components: Matrix read fComponents;
property Mean: Vector read fMean;
property IsFitted: boolean read fFitted;
end;
/// Трансформер, удаляющий признаки с малой дисперсией.
/// Удаляет столбцы X_j, для которых Var(X_j) < threshold.
/// Не использует целевую переменную (unsupervised).
VarianceThreshold = class(ITransformer)
private
fThreshold: real;
fSelected: array of integer;
fFitted: boolean;
public
/// threshold минимальная допустимая дисперсия (>= 0).
constructor Create(threshold: real := 0.0);
/// Вычисляет дисперсии признаков и запоминает индексы
/// признаков, удовлетворяющих порогу.
function Fit(X: Matrix): ITransformer;
/// Возвращает матрицу, содержащую только отобранные признаки.
function Transform(X: Matrix): Matrix;
/// Индексы выбранных признаков.
property SelectedFeatures: array of integer read fSelected;
/// Показывает, был ли выполнен Fit.
property IsFitted: boolean read fFitted;
end;
/// Тип критерия оценки признаков для SelectKBest.
/// Определяет способ вычисления значимости признака
/// относительно целевой переменной.
FeatureScore = (
/// Абсолютное значение коэффициента корреляции Пирсона
/// между признаком и целевой переменной.
/// Подходит для задач регрессии и бинарной классификации при линейной зависимости.
Correlation,
/// F-статистика линейной регрессии (FRegression).
/// Оценивает статистическую значимость линейной связи
/// между признаком и целевой переменной.
/// Основан на коэффициенте детерминации () и F-статистике.
/// Более строгий критерий, чем простая корреляция.
FRegression,
/// ANOVA F-критерий.
/// Используется в задачах классификации.
/// Оценивает различие средних значений признака между различными классами.
AnovaF,
/// Хи-квадрат (Chi-Square) критерий независимости.
/// Используется в задачах классификации.
/// Оценивает зависимость между признаком и классом
/// на основе различия наблюдаемых и ожидаемых частот.
/// Предполагает, что значения признака неотрицательны.
/// Часто применяется для текстовых данных и частотных представлений (bag-of-words).
ChiSquare
);
/// Преобразователь с учётом целевой переменной
/// Для каждого признака вычисляет score(X_j, y)
/// и оставляет k признаков с наибольшим значением score.
/// Может использовать встроенные критерии
/// или пользовательскую функцию оценки.
SelectKBest = class(ITransformer)
private
fK: integer;
fScoreType: FeatureScore;
fScoreFunc: (Vector, Vector) -> real;
fSelected: array of integer;
fFitted: boolean;
function ComputeScore(feature: Vector; y: Vector): real;
function ComputeCorrelation(x: Vector; y: Vector): real;
function ComputeFRegression(feature: Vector; y: Vector): real;
function ComputeAnovaF(feature: Vector; y: Vector): real;
function ComputeChiSquare(feature: Vector; y: Vector): real;
public
/// Создаёт трансформер с использованием встроенного критерия.
/// k число отбираемых признаков.
/// score тип критерия (например, Correlation).
constructor Create(k: integer; score: FeatureScore := FeatureScore.Correlation);
/// Создаёт трансформер с пользовательской функцией оценки.
/// scoreFunc функция (feature, y) real.
constructor Create(k: integer; scoreFunc: (Vector, Vector) -> real);
/// Выбрасывает исключение
/// Данный преобразователь требует целевую переменную.
/// Используйте перегруженный метод Fit(X, y).
function Fit(X: Matrix): ITransformer;
/// Вычисляет оценки признаков и выбирает k лучших.
function Fit(X: Matrix; y: Vector): ITransformer;
/// Возвращает матрицу, содержащую только выбранные признаки.
function Transform(X: Matrix): Matrix;
/// Индексы выбранных признаков.
property SelectedFeatures: array of integer read fSelected;
/// Показывает, был ли выполнен Fit.
property IsFitted: boolean read fFitted;
end;
/// Тип нормы для нормализации строк.
NormType = (L1, L2);
/// Преобразователь нормализации по строкам.
/// Для каждой строки X_i выполняет нормализацию:
/// L1: x := x / ||x||
/// L2: x := x / ||x||
/// Используется перед моделями, чувствительными к масштабу
/// (LogisticRegression, SVM, L1-регуляризация).
Normalizer = class(ITransformer)
private
fNormType: NormType;
fFitted: boolean;
public
constructor Create(norm: NormType := NormType.L2);
function Fit(X: Matrix): ITransformer;
function Transform(X: Matrix): Matrix;
property IsFitted: boolean read fFitted;
end;
{$endregion Transformers}
@ -650,54 +853,209 @@ begin
Result[i] += fIntercept;
end;
constructor MulticlassLogisticRegression.Create(lambda: real; lr: real; epochs: integer);
begin
fLambda := lambda;
fLearningRate := lr;
fEpochs := epochs;
fFitted := false;
end;
function MulticlassLogisticRegression.Fit(X: Matrix; y: Vector): IModel;
begin
var m := X.RowCount;
var p := X.ColCount;
// число классов
fClassCount := Round(y.Max) + 1;
// инициализация параметров
fW := new Matrix(p, fClassCount);
fIntercept := new Vector(fClassCount);
// one-hot матрица
var YoneHot := new Matrix(m, fClassCount);
for var i := 0 to m - 1 do
YoneHot[i, Round(y[i])] := 1.0;
for var epoch := 1 to fEpochs do
begin
// Z = XW + b
var Z := X * fW;
for var i := 0 to m - 1 do
for var k := 0 to fClassCount - 1 do
Z[i,k] += fIntercept[k];
// softmax
for var i := 0 to m - 1 do
begin
var maxVal := Z.RowMax(i);
var sumExp := 0.0;
for var k := 0 to fClassCount - 1 do
begin
Z[i,k] := Exp(Z[i,k] - maxVal);
sumExp += Z[i,k];
end;
for var k := 0 to fClassCount - 1 do
Z[i,k] /= sumExp;
end;
var PP := Z;
// градиенты
var diff := PP - YoneHot; // m x k
var gradW := X.Transpose * diff; // p x k
// L2 регуляризация (только веса)
if fLambda <> 0 then
gradW += fLambda * fW;
// обновление
fW -= fLearningRate * gradW;
// градиент по intercept
var gradB := diff.ColumnSums;
fIntercept -= fLearningRate * gradB;
end;
fFitted := true;
Result := Self;
end;
function MulticlassLogisticRegression.PredictProba(X: Matrix): Vector;
begin
var P := PredictProbaMatrix(X);
var m := P.RowCount;
Result := new Vector(m);
for var i := 0 to m - 1 do
begin
var k := P.RowArgMax(i);
Result[i] := P[i, k];
end;
end;
function MulticlassLogisticRegression.PredictProbaMatrix(X: Matrix): Matrix;
begin
if not fFitted then
raise new Exception('Model not fitted');
var m := X.RowCount;
var Z := X * fW;
for var i := 0 to m - 1 do
for var k := 0 to fClassCount - 1 do
Z[i,k] += fIntercept[k];
for var i := 0 to m - 1 do
begin
var maxVal := Z.RowMax(i);
var sumExp := 0.0;
for var k := 0 to fClassCount - 1 do
begin
Z[i,k] := Exp(Z[i,k] - maxVal);
sumExp += Z[i,k];
end;
for var k := 0 to fClassCount - 1 do
Z[i,k] /= sumExp;
end;
Result := Z;
end;
function MulticlassLogisticRegression.Predict(X: Matrix): Vector;
begin
var P := PredictProbaMatrix(X);
var m := P.RowCount;
Result := new Vector(m);
for var i := 0 to m - 1 do
Result[i] := P.RowArgMax(i);
end;
//-----------------------------
// Pipeline
//-----------------------------
constructor Pipeline.Create;
begin
fTransformers := new List<ITransformer>;
fModel := nil;
fFitted := false;
end;
constructor Pipeline.Create(model: IModel);
begin
Create;
if model = nil then
raise new ArgumentException('Model cannot be nil');
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);
Result := new Pipeline(model);
foreach var t in transformers do
pipe.Add(t);
Result := pipe;
Result.Add(t);
end;
function Pipeline.Add(t: ITransformer): Pipeline;
begin
if t = nil then
raise new ArgumentException('Transformer cannot be nil');
fTransformers.Add(t);
Result := self;
Result := Self;
end;
function Pipeline.SetModel(m: IModel): Pipeline;
begin
if m = nil then
raise new ArgumentException('Model cannot be nil');
fModel := m;
Result := Self;
end;
function Pipeline.Fit(X: Matrix; y: Vector): IModel;
begin
var Xcur := X;
if fModel = nil then
raise new Exception('Model is not set');
var Xt := X;
foreach var t in fTransformers do
begin
t.Fit(Xcur);
Xcur := t.Transform(Xcur);
if t is ISupervisedTransformer (var sup) then
sup.Fit(Xt, y)
else t.Fit(Xt);
Xt := t.Transform(Xt);
end;
fModel.Fit(Xcur, y);
fFitted := true;
fModel.Fit(Xt, y);
Result := self;
fFitted := true;
Result := Self;
end;
function Pipeline.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new Exception('Pipeline is not fitted');
raise new Exception('Pipeline not fitted');
var Xt := X;
@ -712,28 +1070,21 @@ 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);
var Xt := Transform(X);
Result := fModel.Predict(Xt);
end;
function Pipeline.PredictProba(X: Matrix): Vector;
begin
if not fFitted then
raise new Exception('Pipeline is not fitted');
raise new Exception('Pipeline not fitted');
var Xt := X;
if not (fModel is IProbabilisticClassifier) then
raise new Exception('Model does not support probability prediction');
foreach var t in fTransformers do
Xt := t.Transform(Xt);
var Xt := Transform(X);
if fModel is IProbabilisticClassifier then
exit(IProbabilisticClassifier(fModel).PredictProba(Xt));
raise new Exception('Model does not support probability prediction');
Result := (fModel as IProbabilisticClassifier).PredictProba(Xt);
end;
//-----------------------------
@ -823,6 +1174,376 @@ begin
end;
end;
//-----------------------------
// PCATransformer
//-----------------------------
constructor PCATransformer.Create(k: integer);
begin
if k <= 0 then
raise new ArgumentException('k must be > 0');
fK := k;
fFitted := false;
end;
function PCATransformer.Fit(X: Matrix): ITransformer;
begin
if fK > X.ColCount then
raise new ArgumentException('k exceeds feature count');
fMean := X.ColumnMeans;
var Xc := X.Clone;
for var j := 0 to X.ColCount - 1 do
for var i := 0 to X.RowCount - 1 do
Xc[i,j] -= fMean[j];
var (W, xxx) := Xc.PCA(fK);
fComponents := W;
fFitted := true;
Result := Self;
end;
function PCATransformer.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('PCA not fitted');
var Xc := X.Clone;
for var j := 0 to X.ColCount - 1 do
for var i := 0 to X.RowCount - 1 do
Xc[i,j] -= fMean[j];
Result := Xc * fComponents;
end;
//-----------------------------
// VarianceThreshold
//-----------------------------
constructor VarianceThreshold.Create(threshold: real);
begin
if threshold < 0 then
raise new ArgumentException('threshold must be >= 0');
fThreshold := threshold;
fFitted := false;
end;
function VarianceThreshold.Fit(X: Matrix): ITransformer;
begin
var vars := X.ColumnVariances;
var tmp := new List<integer>;
for var j := 0 to X.ColCount - 1 do
if vars[j] >= fThreshold then
tmp.Add(j);
fSelected := tmp.ToArray;
fFitted := true;
Result := Self;
end;
function VarianceThreshold.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('VarianceThreshold not fitted');
var n := X.RowCount;
var k := fSelected.Length;
var R := new Matrix(n, k);
for var i := 0 to n - 1 do
for var c := 0 to k - 1 do
R[i,c] := X[i, fSelected[c]];
Result := R;
end;
//-----------------------------
// SelectKBest
//-----------------------------
constructor SelectKBest.Create(k: integer; score: FeatureScore);
begin
if k <= 0 then
raise new ArgumentException('k must be > 0');
fK := k;
fScoreType := score;
fScoreFunc := nil;
fFitted := false;
end;
constructor SelectKBest.Create(k: integer; scoreFunc: (Vector, Vector) -> real);
begin
if k <= 0 then
raise new ArgumentException('k must be > 0');
fK := k;
fScoreFunc := scoreFunc;
fFitted := false;
end;
function SelectKBest.ComputeCorrelation(x: Vector; y: Vector): real;
begin
var mx := x.Mean;
var my := y.Mean;
var num := 0.0;
var dx := 0.0;
var dy := 0.0;
for var i := 0 to x.Length - 1 do
begin
var vx := x[i] - mx;
var vy := y[i] - my;
num += vx * vy;
dx += vx * vx;
dy += vy * vy;
end;
if (dx = 0) or (dy = 0) then
exit(0.0);
Result := Abs(num / Sqrt(dx * dy));
end;
function SelectKBest.ComputeFRegression(feature: Vector; y: Vector): real;
begin
var r := ComputeCorrelation(feature, y);
var n := feature.Length;
if Abs(r) >= 1 then
exit(1e308);
Result := (r*r / (1 - r*r)) * (n - 2);
end;
function SelectKBest.ComputeAnovaF(feature: Vector; y: Vector): real;
begin
var mean0 := 0.0;
var mean1 := 0.0;
var n0 := 0;
var n1 := 0;
for var i := 0 to y.Length - 1 do
if y[i] = 0 then
begin
mean0 += feature[i];
n0 += 1;
end
else
begin
mean1 += feature[i];
n1 += 1;
end;
if (n0 = 0) or (n1 = 0) then
exit(0.0);
mean0 /= n0;
mean1 /= n1;
var grandMean := feature.Mean;
var ssBetween :=
n0 * Sqr(mean0 - grandMean) +
n1 * Sqr(mean1 - grandMean);
var ssWithin := 0.0;
for var i := 0 to y.Length - 1 do
if y[i] = 0 then
ssWithin += Sqr(feature[i] - mean0)
else
ssWithin += Sqr(feature[i] - mean1);
if ssWithin = 0 then
exit(1e308);
Result := (ssBetween) / (ssWithin / (y.Length - 2));
end;
function SelectKBest.ComputeChiSquare(feature: Vector; y: Vector): real;
begin
var n := y.Length;
var sum0 := 0.0;
var sum1 := 0.0;
var total := 0.0;
var n0 := 0;
var n1 := 0;
for var i := 0 to n - 1 do
begin
var v := feature[i];
if v < 0 then
raise new ArgumentException('ChiSquare requires non-negative features');
total += v;
if y[i] = 0 then
begin
sum0 += v;
n0 += 1;
end
else
begin
sum1 += v;
n1 += 1;
end;
end;
if (n0 = 0) or (n1 = 0) or (total = 0) then
exit(0.0);
var expected0 := total * n0 / n;
var expected1 := total * n1 / n;
var chi := 0.0;
if expected0 > 0 then
chi += Sqr(sum0 - expected0) / expected0;
if expected1 > 0 then
chi += Sqr(sum1 - expected1) / expected1;
Result := chi;
end;
function SelectKBest.ComputeScore(feature: Vector; y: Vector): real;
begin
case fScoreType of
FeatureScore.Correlation: Result := ComputeCorrelation(feature, y);
FeatureScore.FRegression: Result := ComputeFRegression(feature, y);
FeatureScore.AnovaF: Result := ComputeAnovaF(feature, y);
FeatureScore.ChiSquare: Result := ComputeChiSquare(feature, y);
else
raise new InvalidOperationException('Unknown FeatureScore type');
end;
end;
function SelectKBest.Fit(X: Matrix; y: Vector): ITransformer;
begin
var p := X.ColCount;
var scores: array of (real, integer);
SetLength(scores, p);
for var j := 0 to p - 1 do
begin
var col := new Vector(X.RowCount);
for var i := 0 to X.RowCount - 1 do
col[i] := X[i,j];
var s :=
if fScoreFunc <> nil then
fScoreFunc(col, y)
else
ComputeCorrelation(col, y);
scores[j] := (s, j);
end;
scores := scores.OrderByDescending(t -> t.Item1).ToArray;
var k := Min(fK, p);
SetLength(fSelected, k);
for var i := 0 to k - 1 do
fSelected[i] := scores[i].Item2;
fFitted := true;
Result := Self;
end;
function SelectKBest.Fit(X: Matrix): ITransformer;
begin
Result := nil;
raise new InvalidOperationException('Target variable is required');
end;
function SelectKBest.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('SelectKBest not fitted');
var n := X.RowCount;
var k := fSelected.Length;
var R := new Matrix(n, k);
for var i := 0 to n - 1 do
for var c := 0 to k - 1 do
R[i,c] := X[i, fSelected[c]];
Result := R;
end;
//-----------------------------
// Normalizer
//-----------------------------
constructor Normalizer.Create(norm: NormType);
begin
fNormType := norm;
fFitted := false;
end;
function Normalizer.Fit(X: Matrix): ITransformer;
begin
// Нормализация не требует обучения
fFitted := true;
Result := Self;
end;
function Normalizer.Transform(X: Matrix): Matrix;
begin
if not fFitted then
raise new InvalidOperationException('Normalizer not fitted');
var n := X.RowCount;
var p := X.ColCount;
var R := new Matrix(n, p);
for var i := 0 to n - 1 do
begin
var norm := 0.0;
case fNormType of
NormType.L1:
for var j := 0 to p - 1 do
norm += Abs(X[i,j]);
NormType.L2:
for var j := 0 to p - 1 do
norm += Sqr(X[i,j]);
end;
if fNormType = NormType.L2 then
norm := Sqrt(norm);
if norm = 0 then
continue;
for var j := 0 to p - 1 do
R[i,j] := X[i,j] / norm;
end;
Result := R;
end;
end.