MLUtilsABC.pas
MAPE MedianAE Правки в ML
This commit is contained in:
parent
c69daac622
commit
d41a519d53
|
|
@ -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 = "3793";
|
||||
public const string Revision = "3799";
|
||||
|
||||
public const string MainVersion = Major + "." + Minor;
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%MINOR%=11
|
||||
%REVISION%=3793
|
||||
%REVISION%=3799
|
||||
%COREVERSION%=1
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.1.3793
|
||||
3.11.1.3799
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.1.3793'
|
||||
!define VERSION '3.11.1.3799'
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ uses
|
|||
WPF,
|
||||
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC,
|
||||
MetricsABC, MLABC, MLCoreABC, MLModelsABC, ValidationML, MLExceptions, InspectionML, MLPipelineABC,
|
||||
PlotML, MLDatasets, DataAdapters
|
||||
PlotML, MLDatasets, DataAdapters, MLUtilsABC
|
||||
;
|
||||
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
School, SF, TurtleABC,
|
||||
DataFrameABC, DataFrameABCCore, LinearAlgebraML, PreprocessorABC,
|
||||
MetricsABC, MLABC, MLCoreABC, MLModelsABC, ValidationML, MLExceptions, InspectionML, MLPipelineABC,
|
||||
PlotML, MLDatasets, DataAdapters
|
||||
MLDatasets, DataAdapters, MLUtilsABC
|
||||
;
|
||||
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@
|
|||
File ..\bin\Lib\PlotML.pcu
|
||||
File ..\bin\Lib\MLDatasets.pcu
|
||||
File ..\bin\Lib\DataAdapters.pcu
|
||||
File ..\bin\Lib\MLUtilsABC.pcu
|
||||
|
||||
|
||||
File ..\bin\Lib\PABCRtl.dll
|
||||
|
|
@ -303,6 +304,7 @@
|
|||
${AddFile} "PlotML.pcu"
|
||||
${AddFile} "MLDatasets.pcu"
|
||||
${AddFile} "DataAdapters.pcu"
|
||||
${AddFile} "MLUtilsABC.pcu"
|
||||
|
||||
|
||||
${AddFile} "turtle.png"
|
||||
|
|
@ -448,6 +450,7 @@
|
|||
File ..\bin\Lib\PlotML.pas
|
||||
File ..\bin\Lib\MLDatasets.pas
|
||||
File ..\bin\Lib\DataAdapters.pas
|
||||
File ..\bin\Lib\MLUtilsABC.pas
|
||||
|
||||
|
||||
File ..\bin\Lib\__RedirectIOMode.vb
|
||||
|
|
@ -544,6 +547,7 @@
|
|||
${AddFile} "PlotML.pas"
|
||||
${AddFile} "MLDatasets.pas"
|
||||
${AddFile} "DataAdapters.pas"
|
||||
${AddFile} "MLUtilsABC.pas"
|
||||
|
||||
|
||||
${AddFile} "__RedirectIOMode.vb"
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@ interface
|
|||
uses DataFrameABC;
|
||||
uses LinearAlgebraML;
|
||||
|
||||
/// Преобразует вектор меток классов в массив целых чисел.
|
||||
/// Используется при визуализации и других задачах,
|
||||
/// где метки должны быть представлены как 0,1,2,...
|
||||
/// Значения округляются функцией Round, чтобы устранить
|
||||
/// возможные небольшие численные ошибки
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
|
|
@ -65,14 +58,6 @@ const
|
|||
ER_LABEL_INDEX_OUT_OF_RANGE =
|
||||
'Индекс метки {0} вне диапазона [0, {1})!!Label index {0} is out of range [0, {1})';
|
||||
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
begin
|
||||
if y = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'y');
|
||||
|
||||
Result := ArrGen(y.Length, i -> Round(y[i]));
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ type
|
|||
/// Вектор важностей признаков длины nFeatures.
|
||||
/// Чем больше значение, тем сильнее признак влияет на качество модели
|
||||
static function PermutationImportance(model: IPredictiveModel; X: Matrix; y: Vector;
|
||||
scoreFunc: (Vector, Vector) -> real; nRepeats: integer := 5; seed: integer := 0): Vector;
|
||||
scoreFunc: (Vector, Vector) -> real; nRepeats: integer := 5; seed: integer := -1): Vector;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -65,7 +65,7 @@ static function Inspection.PermutationImportance(
|
|||
y: Vector;
|
||||
scoreFunc: (Vector, Vector) -> real;
|
||||
nRepeats: integer;
|
||||
seed: integer
|
||||
seed: integer
|
||||
): Vector;
|
||||
begin
|
||||
if model = nil then
|
||||
|
|
@ -88,6 +88,15 @@ begin
|
|||
|
||||
var resultVec := new Vector(p);
|
||||
|
||||
// Базовый seed для всех параметров.
|
||||
// Все модели оцениваются на одинаковых фолдах,
|
||||
// что обеспечивает корректное сравнение.
|
||||
// При seed = -1 разбиение случайное, но фиксируется
|
||||
// один раз для всего GridSearch.
|
||||
var baseSeed :=
|
||||
if seed >= 0 then seed
|
||||
else System.Environment.TickCount and integer.MaxValue;
|
||||
|
||||
for var j := 0 to p - 1 do
|
||||
begin
|
||||
var acc := 0.0;
|
||||
|
|
@ -96,14 +105,12 @@ begin
|
|||
begin
|
||||
var Xperm := X.Clone;
|
||||
|
||||
// --- seed для каждого (признак, повтор)
|
||||
var runSeed :=
|
||||
if seed >= 0 then seed + j * 100000 + r
|
||||
else System.Environment.TickCount and integer.MaxValue;
|
||||
// --- детерминированный seed для (j, r)
|
||||
var runSeed := baseSeed + j * 100000 + r;
|
||||
|
||||
var rnd := new System.Random(runSeed);
|
||||
|
||||
// --- shuffle столбца j
|
||||
// --- shuffle столбца j (Fisher–Yates)
|
||||
for var i := n - 1 downto 1 do
|
||||
begin
|
||||
var k := rnd.Next(i + 1);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ uses InspectionML;
|
|||
uses MLPipelineABC;
|
||||
uses MLDatasets;
|
||||
uses DataAdapters;
|
||||
uses MLUtilsABC;
|
||||
|
||||
type
|
||||
Vector = LinearAlgebraML.Vector;
|
||||
|
|
@ -121,7 +122,7 @@ implementation
|
|||
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
begin
|
||||
Result := DataAdapters.LabelsToInts(y);
|
||||
Result := MLUtilsABC.LabelsToInts(y);
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string): array of integer := DataAdapters.EncodeLabels(labels);
|
||||
|
|
|
|||
|
|
@ -416,7 +416,8 @@ type
|
|||
/// • maxDepth — максимальная глубина дерева.
|
||||
/// • minSamplesSplit — минимальное число объектов для разбиения узла.
|
||||
/// • minSamplesLeaf — минимальное число объектов в листе
|
||||
constructor Create(maxDepth: integer := 10; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1; seed: integer := -1);
|
||||
constructor Create(maxDepth: integer; minSamplesSplit: integer; minSamplesLeaf: integer;
|
||||
criterion: ISplitCriterion; seed: integer);
|
||||
|
||||
/// Возвращает вектор важности признаков.
|
||||
/// Важность вычисляется как суммарное уменьшение
|
||||
|
|
@ -474,7 +475,8 @@ type
|
|||
/// • maxDepth — максимальная глубина дерева (-1 означает без ограничения).
|
||||
/// • minSamplesSplit — минимальное число объектов для разбиения узла.
|
||||
/// • minSamplesLeaf — минимальное число объектов в листе
|
||||
constructor Create(maxDepth: integer := 10; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1; seed: integer := -1);
|
||||
constructor Create(maxDepth: integer := 10; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1;
|
||||
criterion: ISplitCriterion := nil; seed: integer := -1);
|
||||
|
||||
/// Обучает классификационное дерево.
|
||||
/// X — матрица признаков.
|
||||
|
|
@ -903,8 +905,11 @@ type
|
|||
seed: integer := -1
|
||||
);
|
||||
|
||||
/// Обучает модель на всей обучающей выборке.
|
||||
/// Если включен early stopping и subsample < 1, может использоваться OOB loss.
|
||||
/// Обучает модель градиентного бустинга на обучающей выборке.
|
||||
/// Обучение выполняется без early stopping.
|
||||
/// Для предотвращения переобучения используйте FitWithValidation.
|
||||
/// Early stopping работает только при наличии валидационной выборки
|
||||
/// и параметре earlyStoppingPatience > 0.
|
||||
/// Возвращает обученную модель.
|
||||
function Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||
/// Предсказывает значения целевой переменной.
|
||||
|
|
@ -914,10 +919,12 @@ type
|
|||
/// Копируются все деревья и внутреннее состояние.
|
||||
function Clone: IModel;
|
||||
|
||||
/// Обучает модель с использованием отдельной validation-выборки.
|
||||
/// Early stopping (если включен) происходит по validation loss.
|
||||
function FitWithValidation(XTrain: Matrix; yTrain: Vector;
|
||||
XVal: Matrix; yVal: Vector): IModel;
|
||||
/// Обучает модель градиентного бустинга с использованием валидационной выборки.
|
||||
/// Поддерживает early stopping:
|
||||
/// обучение останавливается, если метрика не улучшается
|
||||
/// в течение earlyStoppingPatience итераций.
|
||||
/// Возвращает обученную модель.
|
||||
function FitWithValidation(XTrain: Matrix; yTrain: Vector; XVal: Matrix; yVal: Vector): ISupervisedModel;
|
||||
|
||||
/// История значения функции потерь на обучающей выборке.
|
||||
/// Один элемент на итерацию бустинга.
|
||||
|
|
@ -1043,14 +1050,19 @@ type
|
|||
earlyStoppingPatience: integer := 20;
|
||||
seed: integer := -1);
|
||||
|
||||
/// Обучает классификатор на всей обучающей выборке.
|
||||
/// Если включен early stopping и subsample < 1, то может использоваться OOB loss.
|
||||
/// Обучает модель градиентного бустинга на обучающей выборке.
|
||||
/// Обучение выполняется без early stopping.
|
||||
/// Для предотвращения переобучения используйте FitWithValidation.
|
||||
/// Early stopping работает только при наличии валидационной выборки
|
||||
/// и параметре earlyStoppingPatience > 0.
|
||||
/// Возвращает обученную модель.
|
||||
function Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||
|
||||
/// Обучает классификатор с использованием validation-набора.
|
||||
/// Если включен early stopping, он основан на validation loss.
|
||||
function FitWithValidation(XTrain: Matrix; yTrain: Vector; XVal: Matrix; yVal: Vector): IModel;
|
||||
/// Обучает модель градиентного бустинга с использованием валидационной выборки.
|
||||
/// Поддерживает early stopping:
|
||||
/// обучение останавливается, если метрика не улучшается в течение earlyStoppingPatience итераций.
|
||||
/// Возвращает обученную модель.
|
||||
function FitWithValidation(XTrain: Matrix; yTrain: Vector; XVal: Matrix; yVal: Vector): ISupervisedModel;
|
||||
|
||||
/// Предсказывает метки классов.
|
||||
/// Возвращает исходные значения классов, а не внутренние индексы.
|
||||
|
|
@ -1799,11 +1811,7 @@ type
|
|||
|
||||
|
||||
{$region Utility functions}
|
||||
/// Преобразует вектор меток классов в массив целых.
|
||||
/// Предполагается, что значения y являются целыми
|
||||
/// (0,1,2,...) и могут содержать небольшие
|
||||
/// численные ошибки, поэтому используется Round.
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
|
||||
|
||||
{$endregion Utility functions}
|
||||
|
||||
|
|
@ -2993,6 +3001,7 @@ constructor DecisionTreeBase.Create(
|
|||
maxDepth: integer;
|
||||
minSamplesSplit: integer;
|
||||
minSamplesLeaf: integer;
|
||||
criterion: ISplitCriterion;
|
||||
seed: integer);
|
||||
begin
|
||||
if maxDepth < -1 then
|
||||
|
|
@ -3018,6 +3027,7 @@ begin
|
|||
fMaxDepth := maxDepth;
|
||||
fMinSamplesSplit := minSamplesSplit;
|
||||
fMinSamplesLeaf := minSamplesLeaf;
|
||||
fCriterion := criterion;
|
||||
|
||||
if seed < 0 then
|
||||
begin
|
||||
|
|
@ -3488,9 +3498,12 @@ begin
|
|||
Result := bestClass;
|
||||
end;
|
||||
|
||||
constructor DecisionTreeClassifier.Create(maxDepth: integer; minSamplesSplit: integer; minSamplesLeaf: integer; seed: integer);
|
||||
constructor DecisionTreeClassifier.Create(maxDepth: integer; minSamplesSplit: integer; minSamplesLeaf: integer;
|
||||
criterion: ISplitCriterion; seed: integer);
|
||||
begin
|
||||
inherited Create(maxDepth, minSamplesSplit, minSamplesLeaf, seed);
|
||||
inherited Create(maxDepth, minSamplesSplit, minSamplesLeaf,
|
||||
(if criterion = nil then new GiniCriterion else criterion),
|
||||
seed);
|
||||
end;
|
||||
|
||||
function DecisionTreeClassifier.Fit(X: Matrix; y: Vector): ISupervisedModel;
|
||||
|
|
@ -3619,6 +3632,7 @@ begin
|
|||
fMaxDepth,
|
||||
fMinSamplesSplit,
|
||||
fMinSamplesLeaf,
|
||||
fCriterion,
|
||||
fRandomSeed
|
||||
);
|
||||
|
||||
|
|
@ -3674,15 +3688,25 @@ end;
|
|||
|
||||
// DecisionTreeRegressor
|
||||
|
||||
constructor DecisionTreeRegressor.Create(maxDepth: integer; minSamplesSplit: integer;
|
||||
minSamplesLeaf: integer; leafL2: real; seed: integer);
|
||||
constructor DecisionTreeRegressor.Create(
|
||||
maxDepth: integer;
|
||||
minSamplesSplit: integer;
|
||||
minSamplesLeaf: integer;
|
||||
leafL2: real;
|
||||
seed: integer
|
||||
);
|
||||
begin
|
||||
inherited Create(maxDepth, minSamplesSplit, minSamplesLeaf, seed);
|
||||
|
||||
inherited Create(
|
||||
maxDepth,
|
||||
minSamplesSplit,
|
||||
minSamplesLeaf,
|
||||
new VarianceCriterion,
|
||||
seed
|
||||
);
|
||||
|
||||
if leafL2 < 0 then
|
||||
ArgumentOutOfRangeError(ER_LEAFL2_INVALID, leafL2);
|
||||
|
||||
fCriterion := new VarianceCriterion;
|
||||
|
||||
fLeafL2 := leafL2;
|
||||
end;
|
||||
|
||||
|
|
@ -4224,6 +4248,7 @@ begin
|
|||
fMaxDepth,
|
||||
fMinSamplesSplit,
|
||||
fMinSamplesLeaf,
|
||||
nil,
|
||||
treeSeed
|
||||
);
|
||||
|
||||
|
|
@ -4858,7 +4883,7 @@ end;
|
|||
|
||||
function GradientBoostingRegressor.FitWithValidation(
|
||||
XTrain: Matrix; yTrain: Vector;
|
||||
XVal: Matrix; yVal: Vector): IModel;
|
||||
XVal: Matrix; yVal: Vector): ISupervisedModel;
|
||||
begin
|
||||
Result := FitInternal(XTrain, yTrain, XVal, yVal, true);
|
||||
end;
|
||||
|
|
@ -6253,7 +6278,7 @@ end;
|
|||
|
||||
function GradientBoostingClassifier.FitWithValidation(
|
||||
XTrain: Matrix; yTrain: Vector;
|
||||
XVal: Matrix; yVal: Vector): IModel;
|
||||
XVal: Matrix; yVal: Vector): ISupervisedModel;
|
||||
begin
|
||||
Result := FitInternal(XTrain, yTrain, XVal, yVal, true);
|
||||
end;
|
||||
|
|
@ -8558,10 +8583,5 @@ begin
|
|||
Result := t;
|
||||
end;
|
||||
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
begin
|
||||
Result := ArrGen(y.Length, i -> Round(y[i]));
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
50
bin/Lib/MLUtilsABC.pas
Normal file
50
bin/Lib/MLUtilsABC.pas
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/// Вспомогательные функции для ML.
|
||||
///
|
||||
/// Содержит утилиты, используемые в нескольких модулях,
|
||||
/// не привязанные к DataFrame или конкретным моделям.
|
||||
unit MLUtilsABC;
|
||||
|
||||
interface
|
||||
|
||||
uses LinearAlgebraML;
|
||||
|
||||
/// Преобразует вектор меток классов в массив целых чисел.
|
||||
/// Используется при визуализации и других задачах,
|
||||
/// где метки должны быть представлены как 0,1,2,...
|
||||
/// Значения округляются функцией Round, чтобы устранить
|
||||
/// возможные небольшие численные ошибки
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
|
||||
/// Преобразует массив целых меток в Vector.
|
||||
function IntsToLabels(a: array of integer): Vector;
|
||||
|
||||
implementation
|
||||
|
||||
uses MLExceptions;
|
||||
|
||||
const
|
||||
ER_LABELS_NULL =
|
||||
'y не может быть nil!!y cannot be nil';
|
||||
ER_LABELS_ARRAY_NULL =
|
||||
'labels не может быть nil!!labels cannot be nil';
|
||||
|
||||
function LabelsToInts(y: Vector): array of integer;
|
||||
begin
|
||||
if y = nil then
|
||||
ArgumentNullError(ER_LABELS_NULL, 'y');
|
||||
|
||||
Result := new integer[y.Length];
|
||||
|
||||
for var i := 0 to y.Length - 1 do
|
||||
Result[i] := Round(y[i]);
|
||||
end;
|
||||
|
||||
function IntsToLabels(a: array of integer): Vector;
|
||||
begin
|
||||
if a = nil then
|
||||
ArgumentNullError(ER_LABELS_ARRAY_NULL, 'a');
|
||||
|
||||
Result := new Vector(a);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -34,6 +34,17 @@ type
|
|||
/// Менее чувствительна к выбросам, чем MSE.
|
||||
static function MAE(yTrue, yPred: Vector): real;
|
||||
|
||||
/// Средняя абсолютная процентная ошибка (MAPE).
|
||||
/// MAPE = (1/n) * Σ |(yTrue - yPred) / yTrue|.
|
||||
/// Значения yTrue = 0 игнорируются.
|
||||
/// Удобна для интерпретации в процентах.
|
||||
static function MAPE(yTrue, yPred: Vector): real;
|
||||
|
||||
/// Медианная абсолютная ошибка (MedianAE).
|
||||
/// MedianAE = median(|yTrue - yPred|).
|
||||
/// Устойчива к выбросам.
|
||||
static function MedianAE(yTrue, yPred: Vector): real;
|
||||
|
||||
/// Коэффициент детерминации (R²).
|
||||
/// R2 = 1 - SS_res / SS_tot.
|
||||
/// Показывает долю объяснённой дисперсии.
|
||||
|
|
@ -312,6 +323,17 @@ type
|
|||
/// Чем меньше значение, тем лучше модель
|
||||
static function MAE(yTrue, yPred: Vector): real := Metrics.MAE(yTrue, yPred);
|
||||
|
||||
/// Средняя абсолютная процентная ошибка (MAPE).
|
||||
/// MAPE = (1/n) * Σ |(yTrue - yPred) / yTrue|.
|
||||
/// Значения yTrue = 0 игнорируются.
|
||||
/// Удобна для интерпретации в процентах.
|
||||
static function MAPE(yTrue, yPred: Vector): real := Metrics.MAPE(yTrue, yPred);
|
||||
|
||||
/// Медианная абсолютная ошибка (MedianAE).
|
||||
/// MedianAE = median(|yTrue - yPred|).
|
||||
/// Устойчива к выбросам.
|
||||
static function MedianAE(yTrue, yPred: Vector): real := Metrics.MedianAE(yTrue, yPred);
|
||||
|
||||
/// Коэффициент детерминации (R²)
|
||||
/// R² = 1 - SS_res / SS_tot
|
||||
/// Показывает долю дисперсии, объяснённую моделью
|
||||
|
|
@ -398,6 +420,10 @@ const
|
|||
'Для Calinski–Harabasz требуется n > k!!Calinski–Harabasz requires n > k';
|
||||
ER_LABELS_MUST_BE_INTEGERS =
|
||||
'Метки кластеров должны быть целыми числами!!Cluster labels must be integers';
|
||||
ER_MAPE_ALL_ZERO_TARGET =
|
||||
'Все значения целевой переменной равны нулю — MAPE не определена!!All target values are zero — MAPE is undefined';
|
||||
|
||||
|
||||
//-----------------------------
|
||||
// Metrics
|
||||
//-----------------------------
|
||||
|
|
@ -478,6 +504,66 @@ begin
|
|||
Result := s / n;
|
||||
end;
|
||||
|
||||
static function Metrics.MAPE(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'yTrue');
|
||||
|
||||
if yPred = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'yPred');
|
||||
|
||||
if yTrue.Length <> yPred.Length then
|
||||
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
|
||||
|
||||
var sum := 0.0;
|
||||
var cnt := 0;
|
||||
|
||||
for var i := 0 to yTrue.Length - 1 do
|
||||
begin
|
||||
var y := yTrue[i];
|
||||
var yp := yPred[i];
|
||||
|
||||
if y = 0.0 then continue;
|
||||
|
||||
sum += Abs((y - yp) / y);
|
||||
cnt += 1;
|
||||
end;
|
||||
|
||||
if cnt = 0 then
|
||||
Error(ER_MAPE_ALL_ZERO_TARGET);
|
||||
|
||||
Result := sum / cnt;
|
||||
end;
|
||||
|
||||
static function Metrics.MedianAE(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'yTrue');
|
||||
|
||||
if yPred = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'yPred');
|
||||
|
||||
if yTrue.Length <> yPred.Length then
|
||||
DimensionError(ER_DIM_MISMATCH, yTrue.Length, yPred.Length);
|
||||
|
||||
var n := yTrue.Length;
|
||||
if n = 0 then
|
||||
ArgumentError(ER_EMPTY_DATA, 'MedianAbsoluteError');
|
||||
|
||||
var errors := new real[n];
|
||||
|
||||
for var i := 0 to n - 1 do
|
||||
errors[i] := Abs(yTrue[i] - yPred[i]);
|
||||
|
||||
// сортировка
|
||||
&Array.Sort(errors);
|
||||
|
||||
if n mod 2 = 1 then
|
||||
Result := errors[n div 2]
|
||||
else
|
||||
Result := (errors[n div 2 - 1] + errors[n div 2]) / 2.0;
|
||||
end;
|
||||
|
||||
static function Metrics.R2(yTrue, yPred: Vector): real;
|
||||
begin
|
||||
if yTrue = nil then
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ uses LinearAlgebraML, MLCoreABC;
|
|||
|
||||
type
|
||||
Validation = static class
|
||||
private
|
||||
static function CrossValidateCore(model: ISupervisedModel;
|
||||
X: Matrix; y: Vector;
|
||||
folds: sequence of (array of integer, array of integer);
|
||||
metric: (Vector, Vector) -> real): real;
|
||||
public
|
||||
/// Делит данные на обучающую и тестовую выборки.
|
||||
/// testRatio — доля объектов, попадающих в тестовую выборку (по умолчанию 0.2).
|
||||
|
|
@ -74,6 +79,7 @@ type
|
|||
k: integer;
|
||||
metric: (Vector, Vector) -> real;
|
||||
maximize: boolean := True;
|
||||
stratified: boolean := False;
|
||||
seed: integer := -1
|
||||
): (P, real, T); where T: class, ISupervisedModel;
|
||||
end;
|
||||
|
|
@ -108,6 +114,57 @@ const
|
|||
// Validation
|
||||
//-----------------------------
|
||||
|
||||
static function Validation.CrossValidateCore(
|
||||
model: ISupervisedModel;
|
||||
X: Matrix;
|
||||
y: Vector;
|
||||
folds: sequence of (array of integer, array of integer);
|
||||
metric: (Vector, Vector) -> real
|
||||
): real;
|
||||
begin
|
||||
var total := 0.0;
|
||||
var foldsCount := 0;
|
||||
var p := X.ColCount;
|
||||
|
||||
foreach var (trainIdx, testIdx) in folds 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.Clone() as ISupervisedModel;
|
||||
m := m.Fit(Xtr, ytr);
|
||||
|
||||
var pred := m.Predict(Xte);
|
||||
|
||||
total += metric(yte, pred);
|
||||
foldsCount += 1;
|
||||
end;
|
||||
|
||||
if foldsCount = 0 then
|
||||
ArgumentError(ER_EMPTY_DATA, 'CrossValidate');
|
||||
|
||||
Result := total / foldsCount;
|
||||
end;
|
||||
|
||||
static function Validation.TrainTestSplit(X: Matrix; y: Vector;
|
||||
testRatio: real; seed: integer): (Matrix, Matrix, Vector, Vector);
|
||||
begin
|
||||
|
|
@ -328,51 +385,17 @@ begin
|
|||
if (k < 2) or (k > X.RowCount) then
|
||||
ArgumentError(ER_K_INVALID, k, X.RowCount);
|
||||
|
||||
var total := 0.0;
|
||||
var folds := 0;
|
||||
var p := X.ColCount;
|
||||
|
||||
var baseSeed :=
|
||||
if seed >= 0 then seed
|
||||
else System.Environment.TickCount and integer.MaxValue;
|
||||
|
||||
foreach var (trainIdx, testIdx) in KFold(X.RowCount, k, baseSeed) 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.Clone() as ISupervisedModel;
|
||||
m := m.Fit(Xtr, ytr);
|
||||
|
||||
var pred := m.Predict(Xte);
|
||||
|
||||
total += metric(yte, pred);
|
||||
folds += 1;
|
||||
end;
|
||||
|
||||
if folds = 0 then
|
||||
ArgumentError(ER_EMPTY_DATA, 'CrossValidate');
|
||||
|
||||
Result := total / folds;
|
||||
Result := CrossValidateCore(
|
||||
model,
|
||||
X,
|
||||
y,
|
||||
KFold(X.RowCount, k, baseSeed),
|
||||
metric
|
||||
);
|
||||
end;
|
||||
|
||||
static function Validation.StratifiedCrossValidate(
|
||||
|
|
@ -401,51 +424,17 @@ begin
|
|||
if (k < 2) or (k > X.RowCount) then
|
||||
ArgumentError(ER_K_INVALID_STRATIFIED, k, X.RowCount);
|
||||
|
||||
var total := 0.0;
|
||||
var folds := 0;
|
||||
var p := X.ColCount;
|
||||
|
||||
var baseSeed :=
|
||||
if seed >= 0 then seed
|
||||
else System.Environment.TickCount and integer.MaxValue;
|
||||
|
||||
foreach var (trainIdx, testIdx) in StratifiedKFold(y, k, baseSeed) 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.Clone() as ISupervisedModel;
|
||||
m := m.Fit(Xtr, ytr);
|
||||
|
||||
var pred := m.Predict(Xte);
|
||||
|
||||
total += metric(yte, pred);
|
||||
folds += 1;
|
||||
end;
|
||||
|
||||
if folds = 0 then
|
||||
ArgumentError(ER_EMPTY_DATA, 'StratifiedCrossValidate');
|
||||
|
||||
Result := total / folds;
|
||||
Result := CrossValidateCore(
|
||||
model,
|
||||
X,
|
||||
y,
|
||||
StratifiedKFold(y, k, baseSeed),
|
||||
metric
|
||||
);
|
||||
end;
|
||||
|
||||
//-----------------------------
|
||||
|
|
@ -460,6 +449,7 @@ class function GridSearch.Search<T, P>(
|
|||
k: integer;
|
||||
metric: (Vector, Vector) -> real;
|
||||
maximize: boolean;
|
||||
stratified: boolean;
|
||||
seed: integer
|
||||
): (P, real, T); where T: class, ISupervisedModel;
|
||||
begin
|
||||
|
|
@ -498,7 +488,11 @@ begin
|
|||
if model = nil then
|
||||
ArgumentError(ER_MODEL_NULL);
|
||||
|
||||
var avgScore := Validation.CrossValidate(model, X, y, k, metric, baseSeed);
|
||||
var avgScore :=
|
||||
if stratified then
|
||||
Validation.StratifiedCrossValidate(model, X, y, k, metric, baseSeed)
|
||||
else
|
||||
Validation.CrossValidate(model, X, y, k, metric, baseSeed);
|
||||
|
||||
if double.IsNaN(avgScore) or double.IsInfinity(avgScore) then
|
||||
ArgumentError(ER_INVALID_VALUE, 'avgScore');
|
||||
|
|
|
|||
Loading…
Reference in a new issue