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