ML - правки аудита 2
This commit is contained in:
parent
5c8f2b0caa
commit
f61cd7770c
|
|
@ -62,6 +62,8 @@ const
|
||||||
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
||||||
ER_UNKNOWN_CLASS_IN_TRANSFORM =
|
ER_UNKNOWN_CLASS_IN_TRANSFORM =
|
||||||
'Неизвестное значение класса "{0}" при преобразовании меток!!Unknown class value "{0}" in TransformLabels';
|
'Неизвестное значение класса "{0}" при преобразовании меток!!Unknown class value "{0}" in TransformLabels';
|
||||||
|
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;
|
function LabelsToInts(y: Vector): array of integer;
|
||||||
begin
|
begin
|
||||||
|
|
@ -115,7 +117,7 @@ begin
|
||||||
var lbl := labels[i];
|
var lbl := labels[i];
|
||||||
|
|
||||||
if not map.ContainsKey(lbl) then
|
if not map.ContainsKey(lbl) then
|
||||||
Error('Unknown class in test');
|
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
|
||||||
|
|
||||||
res[i] := map[lbl];
|
res[i] := map[lbl];
|
||||||
end;
|
end;
|
||||||
|
|
@ -363,7 +365,7 @@ begin
|
||||||
var lbl := data[i].ToString;
|
var lbl := data[i].ToString;
|
||||||
|
|
||||||
if not map.ContainsKey(lbl) then
|
if not map.ContainsKey(lbl) then
|
||||||
Error('Unknown class in TransformLabels');
|
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
|
||||||
|
|
||||||
res[i] := map[lbl];
|
res[i] := map[lbl];
|
||||||
end;
|
end;
|
||||||
|
|
@ -453,8 +455,15 @@ function DecodeLabels(y: array of integer; classes: array of string): array of s
|
||||||
begin
|
begin
|
||||||
var res := new string[y.Length];
|
var res := new string[y.Length];
|
||||||
|
|
||||||
for var i := 0 to y.Length-1 do
|
for var i := 0 to y.Length - 1 do
|
||||||
res[i] := classes[y[i]];
|
begin
|
||||||
|
var idx := y[i];
|
||||||
|
|
||||||
|
if (idx < 0) or (idx >= classes.Length) then
|
||||||
|
Error(ER_LABEL_INDEX_OUT_OF_RANGE, idx, classes.Length);
|
||||||
|
|
||||||
|
res[i] := classes[idx];
|
||||||
|
end;
|
||||||
|
|
||||||
Result := res;
|
Result := res;
|
||||||
end;
|
end;
|
||||||
|
|
|
||||||
|
|
@ -504,8 +504,6 @@ const
|
||||||
'Тип соединения не реализован!!Join kind not implemented';
|
'Тип соединения не реализован!!Join kind not implemented';
|
||||||
ER_JOIN_KEYS_LENGTH_MISMATCH =
|
ER_JOIN_KEYS_LENGTH_MISMATCH =
|
||||||
'leftKeys и rightKeys должны иметь одинаковую длину!!leftKeys and rightKeys must have the same length';
|
'leftKeys и rightKeys должны иметь одинаковую длину!!leftKeys and rightKeys must have the same length';
|
||||||
ER_COLUMN_NOT_FOUND =
|
|
||||||
'Столбец "{0}" не найден!!Column not found: {0}';
|
|
||||||
ER_ADD_COLUMN_ROW_MISMATCH =
|
ER_ADD_COLUMN_ROW_MISMATCH =
|
||||||
'Несоответствие числа строк при добавлении столбца!!Row count mismatch when adding column';
|
'Несоответствие числа строк при добавлении столбца!!Row count mismatch when adding column';
|
||||||
ER_COLUMN_VALID_LENGTH_MISMATCH =
|
ER_COLUMN_VALID_LENGTH_MISMATCH =
|
||||||
|
|
|
||||||
|
|
@ -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; seed: integer := 0): Vector;
|
scoreFunc: (Vector, Vector) -> real; nRepeats: integer := 5; seed: integer := 0): Vector;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
@ -53,10 +53,20 @@ implementation
|
||||||
uses MLExceptions;
|
uses MLExceptions;
|
||||||
|
|
||||||
const
|
const
|
||||||
ER_SCORE_FUNC_NULL = 'scoreFunc не может быть nil!!scoreFunc cannot be nil';
|
ER_SCORE_FUNC_NULL =
|
||||||
|
'scoreFunc не может быть nil!!scoreFunc cannot be nil';
|
||||||
|
ER_ARG_OUT_OF_RANGE =
|
||||||
|
'Аргумент {0} имеет недопустимое значение {1}!!Argument {0} has invalid value {1}';
|
||||||
|
|
||||||
static function Inspection.PermutationImportance(model: IPredictiveModel; X: Matrix; y: Vector;
|
|
||||||
scoreFunc: (Vector, Vector) -> real; seed: integer): Vector;
|
static function Inspection.PermutationImportance(
|
||||||
|
model: IPredictiveModel;
|
||||||
|
X: Matrix;
|
||||||
|
y: Vector;
|
||||||
|
scoreFunc: (Vector, Vector) -> real;
|
||||||
|
nRepeats: integer;
|
||||||
|
seed: integer
|
||||||
|
): Vector;
|
||||||
begin
|
begin
|
||||||
if model = nil then
|
if model = nil then
|
||||||
ArgumentNullError(ER_MODEL_NULL);
|
ArgumentNullError(ER_MODEL_NULL);
|
||||||
|
|
@ -67,6 +77,9 @@ begin
|
||||||
if X.RowCount <> y.Length then
|
if X.RowCount <> y.Length then
|
||||||
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
|
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
|
||||||
|
|
||||||
|
if nRepeats < 1 then
|
||||||
|
ArgumentOutOfRangeError(ER_ARG_OUT_OF_RANGE, 'nRepeats', nRepeats);
|
||||||
|
|
||||||
var baselinePred := model.Predict(X);
|
var baselinePred := model.Predict(X);
|
||||||
var baselineScore := scoreFunc(y, baselinePred);
|
var baselineScore := scoreFunc(y, baselinePred);
|
||||||
|
|
||||||
|
|
@ -74,25 +87,38 @@ begin
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
||||||
var resultVec := new Vector(p);
|
var resultVec := new Vector(p);
|
||||||
var rng := new System.Random(seed);
|
|
||||||
|
|
||||||
for var j := 0 to p-1 do
|
for var j := 0 to p - 1 do
|
||||||
begin
|
begin
|
||||||
var Xperm := X.Clone;
|
var acc := 0.0;
|
||||||
|
|
||||||
// Fisher–Yates shuffle столбца j (детерминированно через seed)
|
for var r := 0 to nRepeats - 1 do
|
||||||
for var i := n-1 downto 1 do
|
|
||||||
begin
|
begin
|
||||||
var k := rng.Next(i+1);
|
var Xperm := X.Clone;
|
||||||
var tmp := Xperm[i,j];
|
|
||||||
Xperm[i,j] := Xperm[k,j];
|
// --- seed для каждого (признак, повтор)
|
||||||
Xperm[k,j] := tmp;
|
var runSeed :=
|
||||||
|
if seed >= 0 then seed + j * 100000 + r
|
||||||
|
else System.Environment.TickCount and integer.MaxValue;
|
||||||
|
|
||||||
|
var rnd := new System.Random(runSeed);
|
||||||
|
|
||||||
|
// --- shuffle столбца j
|
||||||
|
for var i := n - 1 downto 1 do
|
||||||
|
begin
|
||||||
|
var k := rnd.Next(i + 1);
|
||||||
|
var tmp := Xperm[i,j];
|
||||||
|
Xperm[i,j] := Xperm[k,j];
|
||||||
|
Xperm[k,j] := tmp;
|
||||||
|
end;
|
||||||
|
|
||||||
|
var permPred := model.Predict(Xperm);
|
||||||
|
var permScore := scoreFunc(y, permPred);
|
||||||
|
|
||||||
|
acc += (baselineScore - permScore);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var permPred := model.Predict(Xperm);
|
resultVec[j] := acc / nRepeats;
|
||||||
var permScore := scoreFunc(y, permPred);
|
|
||||||
|
|
||||||
resultVec[j] := baselineScore - permScore;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := resultVec;
|
Result := resultVec;
|
||||||
|
|
|
||||||
|
|
@ -152,9 +152,6 @@ type
|
||||||
static function operator *(A: Matrix; x: Vector): Vector;
|
static function operator *(A: Matrix; x: Vector): Vector;
|
||||||
static function operator *(A, B: Matrix): Matrix;
|
static function operator *(A, B: Matrix): Matrix;
|
||||||
|
|
||||||
//static function operator *(alpha: real; A: Matrix): Matrix;
|
|
||||||
//static function operator *(A: Matrix; alpha: real): Matrix;
|
|
||||||
|
|
||||||
// ---------- in-place operators ----------
|
// ---------- in-place operators ----------
|
||||||
static function operator +=(A, B: Matrix): Matrix;
|
static function operator +=(A, B: Matrix): Matrix;
|
||||||
static function operator -=(A, B: Matrix): Matrix;
|
static function operator -=(A, B: Matrix): Matrix;
|
||||||
|
|
|
||||||
|
|
@ -18,20 +18,20 @@ type
|
||||||
/// Предоставляет удобные методы для получения матриц признаков
|
/// Предоставляет удобные методы для получения матриц признаков
|
||||||
/// и целевых значений для обучения моделей
|
/// и целевых значений для обучения моделей
|
||||||
Dataset = class
|
Dataset = class
|
||||||
private
|
private
|
||||||
function ValueLabel(feature, value: string): string;
|
function ValueLabel(feature, value: string): string;
|
||||||
function CloneMeta(df: DataFrame): Dataset;
|
function CloneMeta(df: DataFrame): Dataset;
|
||||||
public
|
public
|
||||||
Name: string;
|
|
||||||
Data: DataFrame;
|
|
||||||
Features: array of string;
|
|
||||||
Target: string;
|
|
||||||
Task: TaskType;
|
|
||||||
|
|
||||||
FeatureLabels: Dictionary<string,string>;
|
FeatureLabels: Dictionary<string,string>;
|
||||||
ValueLabels: Dictionary<string,Dictionary<string,string>>;
|
ValueLabels: Dictionary<string,Dictionary<string,string>>;
|
||||||
Description: string;
|
Description: string;
|
||||||
|
|
||||||
|
auto property Name: string;
|
||||||
|
auto property Data: DataFrame;
|
||||||
|
auto property Features: array of string;
|
||||||
|
auto property Target: string;
|
||||||
|
auto property Task: TaskType;
|
||||||
|
|
||||||
/// Возвращает true, если датасет относится к задаче с учителем
|
/// Возвращает true, если датасет относится к задаче с учителем
|
||||||
/// (classification или regression).
|
/// (classification или regression).
|
||||||
function IsSupervised: boolean;
|
function IsSupervised: boolean;
|
||||||
|
|
|
||||||
|
|
@ -1271,6 +1271,10 @@ type
|
||||||
fIterations: integer;
|
fIterations: integer;
|
||||||
fHasConverged: boolean;
|
fHasConverged: boolean;
|
||||||
|
|
||||||
|
fRandomSeed: integer;
|
||||||
|
fUserProvidedSeed: boolean;
|
||||||
|
fRng: System.Random;
|
||||||
|
|
||||||
function RunSingle(X: Matrix; rnd: System.Random): (Matrix, real, integer, boolean);
|
function RunSingle(X: Matrix; rnd: System.Random): (Matrix, real, integer, boolean);
|
||||||
public
|
public
|
||||||
/// Создаёт модель KMeans.
|
/// Создаёт модель KMeans.
|
||||||
|
|
@ -1361,9 +1365,6 @@ type
|
||||||
/// Обучает модель на матрице признаков.
|
/// Обучает модель на матрице признаков.
|
||||||
function Fit(X: Matrix): IUnsupervisedModel;
|
function Fit(X: Matrix): IUnsupervisedModel;
|
||||||
|
|
||||||
/// Возвращает метки кластеров.
|
|
||||||
function Predict(X: Matrix): Vector;
|
|
||||||
|
|
||||||
/// Возвращает метки кластеров.
|
/// Возвращает метки кластеров.
|
||||||
function PredictLabels(X: Matrix): array of integer;
|
function PredictLabels(X: Matrix): array of integer;
|
||||||
|
|
||||||
|
|
@ -3042,16 +3043,24 @@ begin
|
||||||
dest.fMaxFeatures := fMaxFeatures;
|
dest.fMaxFeatures := fMaxFeatures;
|
||||||
|
|
||||||
dest.fUserProvidedSeed := fUserProvidedSeed;
|
dest.fUserProvidedSeed := fUserProvidedSeed;
|
||||||
dest.fRng := new System.Random(fRandomSeed);
|
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
dest.fRng := new System.Random(fRandomSeed)
|
||||||
|
else
|
||||||
|
dest.fRng := new System.Random;
|
||||||
|
|
||||||
|
// MUST be stateless
|
||||||
if fCriterion <> nil then
|
if fCriterion <> nil then
|
||||||
dest.fCriterion := fCriterion; // можно так, если критерий stateless
|
dest.fCriterion := fCriterion;
|
||||||
|
|
||||||
if fFeatureImportances <> nil then
|
if fFeatureImportances <> nil then
|
||||||
dest.fFeatureImportances := fFeatureImportances.Clone;
|
dest.fFeatureImportances := fFeatureImportances.Clone;
|
||||||
|
|
||||||
if fRoot <> nil then
|
if fRoot <> nil then
|
||||||
dest.fRoot := fRoot.Clone;
|
dest.fRoot := fRoot.Clone;
|
||||||
|
|
||||||
|
if fRowIndices <> nil then
|
||||||
|
dest.fRowIndices := Copy(fRowIndices);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DecisionTreeBase.GetFeatureSubset(nFeatures: integer): array of integer;
|
function DecisionTreeBase.GetFeatureSubset(nFeatures: integer): array of integer;
|
||||||
|
|
@ -3613,11 +3622,8 @@ begin
|
||||||
fRandomSeed
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- базовое состояние (глубокое копирование дерева!)
|
|
||||||
CopyBaseState(m);
|
CopyBaseState(m);
|
||||||
|
|
||||||
m.fMaxFeatures := fMaxFeatures;
|
|
||||||
|
|
||||||
// --- классы
|
// --- классы
|
||||||
m.fClassCount := fClassCount;
|
m.fClassCount := fClassCount;
|
||||||
|
|
||||||
|
|
@ -3825,8 +3831,6 @@ begin
|
||||||
|
|
||||||
CopyBaseState(m);
|
CopyBaseState(m);
|
||||||
|
|
||||||
m.fLeafL2 := fLeafL2;
|
|
||||||
|
|
||||||
Result := m;
|
Result := m;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -4099,7 +4103,15 @@ begin
|
||||||
fRandomSeed
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// --- seed ---
|
||||||
|
rf.fRandomSeed := fRandomSeed;
|
||||||
rf.fUserProvidedSeed := fUserProvidedSeed;
|
rf.fUserProvidedSeed := fUserProvidedSeed;
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
rf.fRng := new System.Random(fRandomSeed)
|
||||||
|
else
|
||||||
|
rf.fRng := new System.Random;
|
||||||
|
|
||||||
rf.fFitted := fFitted;
|
rf.fFitted := fFitted;
|
||||||
rf.fFeatureCount := fFeatureCount;
|
rf.fFeatureCount := fFeatureCount;
|
||||||
|
|
||||||
|
|
@ -4439,9 +4451,16 @@ begin
|
||||||
fRandomSeed
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// --- seed ---
|
||||||
|
rf.fRandomSeed := fRandomSeed;
|
||||||
rf.fUserProvidedSeed := fUserProvidedSeed;
|
rf.fUserProvidedSeed := fUserProvidedSeed;
|
||||||
rf.fFitted := fFitted;
|
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
rf.fRng := new System.Random(fRandomSeed)
|
||||||
|
else
|
||||||
|
rf.fRng := new System.Random;
|
||||||
|
|
||||||
|
rf.fFitted := fFitted;
|
||||||
rf.fFeatureCount := fFeatureCount;
|
rf.fFeatureCount := fFeatureCount;
|
||||||
|
|
||||||
// --- classes ---
|
// --- classes ---
|
||||||
|
|
@ -4467,7 +4486,7 @@ begin
|
||||||
SetLength(rf.fTrees, fTrees.Length);
|
SetLength(rf.fTrees, fTrees.Length);
|
||||||
for var i := 0 to fTrees.Length - 1 do
|
for var i := 0 to fTrees.Length - 1 do
|
||||||
rf.fTrees[i] := DecisionTreeClassifier(fTrees[i].Clone);
|
rf.fTrees[i] := DecisionTreeClassifier(fTrees[i].Clone);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := rf;
|
Result := rf;
|
||||||
end;
|
end;
|
||||||
|
|
@ -5301,31 +5320,35 @@ begin
|
||||||
fMinSamplesSplit,
|
fMinSamplesSplit,
|
||||||
fMinSamplesLeaf,
|
fMinSamplesLeaf,
|
||||||
fSubsample,
|
fSubsample,
|
||||||
seed := fRandomSeed
|
fLoss,
|
||||||
|
fHuberDelta,
|
||||||
|
fEarlyStoppingPatience,
|
||||||
|
fQuantileAlpha,
|
||||||
|
fLeafL2,
|
||||||
|
fUseOOBEarlyStopping,
|
||||||
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- seed policy ---
|
// --- seed ---
|
||||||
|
copy.fRandomSeed := fRandomSeed;
|
||||||
copy.fUserProvidedSeed := fUserProvidedSeed;
|
copy.fUserProvidedSeed := fUserProvidedSeed;
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
copy.fRng := new System.Random(fRandomSeed)
|
||||||
|
else
|
||||||
|
copy.fRng := new System.Random;
|
||||||
|
|
||||||
// --- basic state ---
|
// --- basic state ---
|
||||||
copy.fInitValue := fInitValue;
|
copy.fInitValue := fInitValue;
|
||||||
copy.fFeatureCount := fFeatureCount;
|
copy.fFeatureCount := fFeatureCount;
|
||||||
copy.fFitted := fFitted;
|
copy.fFitted := fFitted;
|
||||||
|
|
||||||
// --- best iteration state ---
|
// --- best iteration ---
|
||||||
copy.fBestIteration := fBestIteration;
|
copy.fBestIteration := fBestIteration;
|
||||||
copy.fBestTrainLoss := fBestTrainLoss;
|
copy.fBestTrainLoss := fBestTrainLoss;
|
||||||
copy.fBestScoreLoss := fBestScoreLoss;
|
copy.fBestScoreLoss := fBestScoreLoss;
|
||||||
|
|
||||||
// --- loss configuration ---
|
// --- histories ---
|
||||||
copy.fLoss := fLoss;
|
|
||||||
copy.fHuberDelta := fHuberDelta;
|
|
||||||
copy.fQuantileAlpha := fQuantileAlpha;
|
|
||||||
copy.fLeafL2 := fLeafL2;
|
|
||||||
copy.fEarlyStoppingPatience := fEarlyStoppingPatience;
|
|
||||||
copy.fUseOOBEarlyStopping := fUseOOBEarlyStopping;
|
|
||||||
|
|
||||||
// --- deep copy histories ---
|
|
||||||
copy.fTrainLossHistory.Clear;
|
copy.fTrainLossHistory.Clear;
|
||||||
copy.fTrainLossHistory.AddRange(fTrainLossHistory);
|
copy.fTrainLossHistory.AddRange(fTrainLossHistory);
|
||||||
|
|
||||||
|
|
@ -5335,7 +5358,7 @@ begin
|
||||||
copy.fOOBLossHistory.Clear;
|
copy.fOOBLossHistory.Clear;
|
||||||
copy.fOOBLossHistory.AddRange(fOOBLossHistory);
|
copy.fOOBLossHistory.AddRange(fOOBLossHistory);
|
||||||
|
|
||||||
// --- deep copy trees ---
|
// --- estimators ---
|
||||||
copy.fEstimators.Clear;
|
copy.fEstimators.Clear;
|
||||||
foreach var tree in fEstimators do
|
foreach var tree in fEstimators do
|
||||||
copy.fEstimators.Add(tree.Clone as DecisionTreeRegressor);
|
copy.fEstimators.Add(tree.Clone as DecisionTreeRegressor);
|
||||||
|
|
@ -6174,9 +6197,15 @@ begin
|
||||||
fRandomSeed
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- seed policy ---
|
// --- seed ---
|
||||||
|
model.fRandomSeed := fRandomSeed;
|
||||||
model.fUserProvidedSeed := fUserProvidedSeed;
|
model.fUserProvidedSeed := fUserProvidedSeed;
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
model.fRng := new System.Random(fRandomSeed)
|
||||||
|
else
|
||||||
|
model.fRng := new System.Random;
|
||||||
|
|
||||||
// --- fitted state ---
|
// --- fitted state ---
|
||||||
model.fFitted := fFitted;
|
model.fFitted := fFitted;
|
||||||
model.fFeatureCount := fFeatureCount;
|
model.fFeatureCount := fFeatureCount;
|
||||||
|
|
@ -6197,26 +6226,19 @@ begin
|
||||||
model.fClassIndex.Add(kv.Key, kv.Value);
|
model.fClassIndex.Add(kv.Key, kv.Value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- estimators (deep copy) ---
|
// --- estimators ---
|
||||||
foreach var trees in fEstimators do
|
foreach var trees in fEstimators do
|
||||||
begin
|
begin
|
||||||
var newTrees := new DecisionTreeRegressor[Length(trees)];
|
var newTrees := new DecisionTreeRegressor[Length(trees)];
|
||||||
|
|
||||||
for var cls := 0 to Length(trees) - 1 do
|
for var cls := 0 to Length(trees) - 1 do
|
||||||
newTrees[cls] := trees[cls].Clone as DecisionTreeRegressor;
|
newTrees[cls] := trees[cls].Clone as DecisionTreeRegressor;
|
||||||
|
|
||||||
model.fEstimators.Add(newTrees);
|
model.fEstimators.Add(newTrees);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// --- histories ---
|
// --- histories ---
|
||||||
foreach var v in fTrainLossHistory do
|
foreach var v in fTrainLossHistory do model.fTrainLossHistory.Add(v);
|
||||||
model.fTrainLossHistory.Add(v);
|
foreach var v in fValLossHistory do model.fValLossHistory.Add(v);
|
||||||
|
foreach var v in fOOBLossHistory do model.fOOBLossHistory.Add(v);
|
||||||
foreach var v in fValLossHistory do
|
|
||||||
model.fValLossHistory.Add(v);
|
|
||||||
|
|
||||||
foreach var v in fOOBLossHistory do
|
|
||||||
model.fOOBLossHistory.Add(v);
|
|
||||||
|
|
||||||
model.fBestIteration := fBestIteration;
|
model.fBestIteration := fBestIteration;
|
||||||
model.fBestScoreLoss := fBestScoreLoss;
|
model.fBestScoreLoss := fBestScoreLoss;
|
||||||
|
|
@ -6938,8 +6960,17 @@ begin
|
||||||
fMaxIter := maxIter;
|
fMaxIter := maxIter;
|
||||||
fTol := tol;
|
fTol := tol;
|
||||||
fNInit := nInit;
|
fNInit := nInit;
|
||||||
fSeed := seed;
|
|
||||||
|
|
||||||
|
// --- seed (единый стиль)
|
||||||
|
fRandomSeed := seed;
|
||||||
|
fUserProvidedSeed := seed >= 0;
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
fRng := new System.Random(seed)
|
||||||
|
else
|
||||||
|
fRng := new System.Random;
|
||||||
|
|
||||||
|
// --- state
|
||||||
fFitted := False;
|
fFitted := False;
|
||||||
fFeatureCount := 0;
|
fFeatureCount := 0;
|
||||||
|
|
||||||
|
|
@ -7077,11 +7108,8 @@ begin
|
||||||
|
|
||||||
fFeatureCount := p;
|
fFeatureCount := p;
|
||||||
|
|
||||||
var actualSeed :=
|
// --- базовый RNG (уже создан в конструкторе)
|
||||||
if fSeed >= 0 then fSeed
|
var rndBase := fRng;
|
||||||
else System.Environment.TickCount and integer.MaxValue;
|
|
||||||
|
|
||||||
var rndBase := new System.Random(actualSeed);
|
|
||||||
|
|
||||||
var bestInertia := 1e308;
|
var bestInertia := 1e308;
|
||||||
var bestCenters: Matrix := nil;
|
var bestCenters: Matrix := nil;
|
||||||
|
|
@ -7090,7 +7118,8 @@ begin
|
||||||
|
|
||||||
for var run := 1 to fNInit do
|
for var run := 1 to fNInit do
|
||||||
begin
|
begin
|
||||||
var runSeed := rndBase.Next;
|
// --- независимый RNG для каждого запуска
|
||||||
|
var runSeed := rndBase.Next(integer.MaxValue);
|
||||||
var rnd := new System.Random(runSeed);
|
var rnd := new System.Random(runSeed);
|
||||||
|
|
||||||
var (centers, inertia, iters, converged) := RunSingle(X, rnd);
|
var (centers, inertia, iters, converged) := RunSingle(X, rnd);
|
||||||
|
|
@ -7109,7 +7138,7 @@ begin
|
||||||
fIterations := bestIterations;
|
fIterations := bestIterations;
|
||||||
fHasConverged := bestConverged;
|
fHasConverged := bestConverged;
|
||||||
fFitted := True;
|
fFitted := True;
|
||||||
|
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -7188,25 +7217,28 @@ begin
|
||||||
fMaxIter,
|
fMaxIter,
|
||||||
fTol,
|
fTol,
|
||||||
fNInit,
|
fNInit,
|
||||||
fSeed
|
fRandomSeed
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// --- seed ---
|
||||||
|
km.fRandomSeed := fRandomSeed;
|
||||||
|
km.fUserProvidedSeed := fUserProvidedSeed;
|
||||||
|
|
||||||
|
if fUserProvidedSeed then
|
||||||
|
km.fRng := new System.Random(fRandomSeed)
|
||||||
|
else
|
||||||
|
km.fRng := new System.Random;
|
||||||
|
|
||||||
|
// --- state ---
|
||||||
km.fFitted := fFitted;
|
km.fFitted := fFitted;
|
||||||
km.fFeatureCount := fFeatureCount;
|
km.fFeatureCount := fFeatureCount;
|
||||||
km.fInertia := fInertia;
|
km.fInertia := fInertia;
|
||||||
km.fIterations := fIterations;
|
km.fIterations := fIterations;
|
||||||
km.fHasConverged := fHasConverged;
|
km.fHasConverged := fHasConverged;
|
||||||
|
|
||||||
|
// --- centers ---
|
||||||
if fCenters <> nil then
|
if fCenters <> nil then
|
||||||
begin
|
km.fCenters := fCenters.Clone;
|
||||||
var k := fCenters.RowCount;
|
|
||||||
var p := fCenters.ColCount;
|
|
||||||
km.fCenters := new Matrix(k, p);
|
|
||||||
|
|
||||||
for var i := 0 to k - 1 do
|
|
||||||
for var j := 0 to p - 1 do
|
|
||||||
km.fCenters[i,j] := fCenters[i,j];
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := km;
|
Result := km;
|
||||||
end;
|
end;
|
||||||
|
|
@ -7339,25 +7371,6 @@ begin
|
||||||
Result := Self;
|
Result := Self;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function DBSCAN.Predict(X: Matrix): Vector;
|
|
||||||
begin
|
|
||||||
if not fFitted then
|
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
|
||||||
|
|
||||||
if X = nil then
|
|
||||||
ArgumentNullError(ER_ARG_NULL, 'X');
|
|
||||||
|
|
||||||
// честная защита: Predict работает только для той же выборки
|
|
||||||
if X.RowCount <> Length(fLabels) then
|
|
||||||
ArgumentError(ER_DBSCAN_PREDICT_ONLY_TRAIN_DATA);
|
|
||||||
|
|
||||||
var n := Length(fLabels);
|
|
||||||
Result := new Vector(n);
|
|
||||||
|
|
||||||
for var i := 0 to n - 1 do
|
|
||||||
Result[i] := fLabels[i];
|
|
||||||
end;
|
|
||||||
|
|
||||||
function DBSCAN.PredictLabels(X: Matrix): array of integer;
|
function DBSCAN.PredictLabels(X: Matrix): array of integer;
|
||||||
begin
|
begin
|
||||||
if X = nil then
|
if X = nil then
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
unit PlotML;
|
/// Модуль визуализации на базе InteractiveDataDisplay (WPF).
|
||||||
|
/// ВАЖНО:
|
||||||
|
/// • Требует Windows и WPF.
|
||||||
|
/// • Использует GAC-сборку InteractiveDataDisplay.WPF.
|
||||||
|
/// • Не поддерживается на Linux/macOS и в .NET Core без дополнительной настройки.
|
||||||
|
/// Рекомендуется использовать только в desktop-сценариях.
|
||||||
|
unit PlotML;
|
||||||
|
|
||||||
{$reference %GAC%\InteractiveDataDisplay.WPF.dll}
|
{$reference %GAC%\InteractiveDataDisplay.WPF.dll}
|
||||||
{$reference 'PresentationFramework.dll'}
|
{$reference 'PresentationFramework.dll'}
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ type
|
||||||
property ColumnName: string read col;
|
property ColumnName: string read col;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ImputeStrategy = (isMean, isConstant);
|
ImputeStrategy = (isMean, isConstant, isMedian);
|
||||||
|
|
||||||
/// Заполняет пропущенные значения (NA) в числовых столбцах
|
/// Заполняет пропущенные значения (NA) в числовых столбцах
|
||||||
/// Поддерживает стратегии isMean и isConstant
|
/// Поддерживает стратегии isMean и isConstant
|
||||||
|
|
@ -102,6 +102,7 @@ type
|
||||||
strategy: ImputeStrategy;
|
strategy: ImputeStrategy;
|
||||||
constants: array of object;
|
constants: array of object;
|
||||||
means: array of real;
|
means: array of real;
|
||||||
|
medians: array of real;
|
||||||
fitted: boolean;
|
fitted: boolean;
|
||||||
public
|
public
|
||||||
/// Создаёт Imputer с заполнением средним значением
|
/// Создаёт Imputer с заполнением средним значением
|
||||||
|
|
@ -189,8 +190,10 @@ const
|
||||||
'Imputer(constant): value type mismatch for column "{0}"';
|
'Imputer(constant): value type mismatch for column "{0}"';
|
||||||
ER_ONEHOT_EMPTY_COLUMN =
|
ER_ONEHOT_EMPTY_COLUMN =
|
||||||
'Столбец "{0}" не содержит категориальных значений!!Column "{0}" contains no categorical values';
|
'Столбец "{0}" не содержит категориальных значений!!Column "{0}" contains no categorical values';
|
||||||
ER_COLUMN_NOT_FOUND =
|
ER_IMPUTER_CONSTANTS_INVALID =
|
||||||
'Столбец "{0}" не найден!!Column "{0}" not found';
|
'Массив констант не задан или имеет неверный размер!!Constants array is null or has invalid length';
|
||||||
|
ER_IMPUTER_STRATEGY_NOT_SUPPORTED =
|
||||||
|
'Стратегия импутации {0} не поддерживается!!Imputation strategy {0} is not supported';
|
||||||
|
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
// LabelEncoder
|
// LabelEncoder
|
||||||
|
|
@ -288,7 +291,13 @@ end;
|
||||||
|
|
||||||
function OneHotEncoder.Fit(df: DataFrame): IPreprocessor;
|
function OneHotEncoder.Fit(df: DataFrame): IPreprocessor;
|
||||||
begin
|
begin
|
||||||
|
if df = nil then
|
||||||
|
ArgumentNullError(ER_ARG_NULL, 'df');
|
||||||
|
|
||||||
var idx := df.Schema.IndexOf(col);
|
var idx := df.Schema.IndexOf(col);
|
||||||
|
if idx < 0 then
|
||||||
|
Error(ER_COLUMN_NOT_FOUND, col);
|
||||||
|
|
||||||
if df.Schema.ColumnTypeAt(idx) <> ColumnType.ctStr then
|
if df.Schema.ColumnTypeAt(idx) <> ColumnType.ctStr then
|
||||||
Error(ER_ONEHOT_NOT_STRING, col);
|
Error(ER_ONEHOT_NOT_STRING, col);
|
||||||
|
|
||||||
|
|
@ -299,8 +308,11 @@ begin
|
||||||
while cur.MoveNext do
|
while cur.MoveNext do
|
||||||
begin
|
begin
|
||||||
if not cur.IsValid(idx) then continue;
|
if not cur.IsValid(idx) then continue;
|
||||||
|
|
||||||
var s := cur.Str(idx);
|
var s := cur.Str(idx);
|
||||||
if not indexByValue.ContainsKey(s) then
|
|
||||||
|
var dummy: integer;
|
||||||
|
if not indexByValue.TryGetValue(s, dummy) then
|
||||||
begin
|
begin
|
||||||
indexByValue[s] := values.Count;
|
indexByValue[s] := values.Count;
|
||||||
values.Add(s);
|
values.Add(s);
|
||||||
|
|
@ -380,9 +392,6 @@ end;
|
||||||
|
|
||||||
constructor Imputer.Create(strategy: ImputeStrategy; params columns: array of string);
|
constructor Imputer.Create(strategy: ImputeStrategy; params columns: array of string);
|
||||||
begin
|
begin
|
||||||
if strategy <> isMean then
|
|
||||||
ArgumentError(ER_IMPUTER_INVALID_STRATEGY_MEAN);
|
|
||||||
|
|
||||||
if (columns = nil) or (columns.Length = 0) then
|
if (columns = nil) or (columns.Length = 0) then
|
||||||
ArgumentError(ER_IMPUTER_NO_COLUMNS);
|
ArgumentError(ER_IMPUTER_NO_COLUMNS);
|
||||||
|
|
||||||
|
|
@ -415,35 +424,79 @@ end;
|
||||||
|
|
||||||
function Imputer.Fit(df: DataFrame): IPreprocessor;
|
function Imputer.Fit(df: DataFrame): IPreprocessor;
|
||||||
begin
|
begin
|
||||||
if strategy = isMean then
|
case strategy of
|
||||||
begin
|
isMean:
|
||||||
SetLength(means, cols.Length);
|
|
||||||
|
|
||||||
for var i := 0 to cols.Length - 1 do
|
|
||||||
begin
|
begin
|
||||||
var name := cols[i];
|
SetLength(means, cols.Length);
|
||||||
var idx := df.Schema.IndexOf(name);
|
|
||||||
var ct := df.Schema.ColumnTypeAt(idx);
|
|
||||||
|
|
||||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
for var i := 0 to cols.Length - 1 do
|
||||||
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
begin
|
||||||
|
var name := cols[i];
|
||||||
|
var idx := df.Schema.IndexOf(name);
|
||||||
|
var ct := df.Schema.ColumnTypeAt(idx);
|
||||||
|
|
||||||
var sum := 0.0;
|
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||||
var cnt := 0;
|
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
||||||
|
|
||||||
var cur := df.GetCursor;
|
var sum := 0.0;
|
||||||
while cur.MoveNext do
|
var cnt := 0;
|
||||||
if cur.IsValid(idx) then
|
|
||||||
begin
|
|
||||||
sum += cur.Float(idx);
|
|
||||||
cnt += 1;
|
|
||||||
end;
|
|
||||||
|
|
||||||
if cnt = 0 then
|
var cur := df.GetCursor;
|
||||||
Error(ER_IMPUTER_NO_VALID_VALUES, name);
|
while cur.MoveNext do
|
||||||
|
if cur.IsValid(idx) then
|
||||||
|
begin
|
||||||
|
sum += cur.Float(idx);
|
||||||
|
cnt += 1;
|
||||||
|
end;
|
||||||
|
|
||||||
means[i] := sum / cnt;
|
if cnt = 0 then
|
||||||
|
Error(ER_IMPUTER_NO_VALID_VALUES, name);
|
||||||
|
|
||||||
|
means[i] := sum / cnt;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
isConstant:
|
||||||
|
begin
|
||||||
|
// ничего делать не нужно
|
||||||
|
end;
|
||||||
|
|
||||||
|
isMedian:
|
||||||
|
begin
|
||||||
|
SetLength(medians, cols.Length);
|
||||||
|
|
||||||
|
for var i := 0 to cols.Length - 1 do
|
||||||
|
begin
|
||||||
|
var name := cols[i];
|
||||||
|
var idx := df.Schema.IndexOf(name);
|
||||||
|
var ct := df.Schema.ColumnTypeAt(idx);
|
||||||
|
|
||||||
|
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||||
|
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
||||||
|
|
||||||
|
// --- собираем значения
|
||||||
|
var values := new List<real>;
|
||||||
|
|
||||||
|
var cur := df.GetCursor;
|
||||||
|
while cur.MoveNext do
|
||||||
|
if cur.IsValid(idx) then
|
||||||
|
values.Add(cur.Float(idx));
|
||||||
|
|
||||||
|
if values.Count = 0 then
|
||||||
|
Error(ER_IMPUTER_NO_VALID_VALUES, name);
|
||||||
|
|
||||||
|
// --- сортируем
|
||||||
|
values.Sort;
|
||||||
|
|
||||||
|
// --- медиана
|
||||||
|
var n := values.Count;
|
||||||
|
if n mod 2 = 1 then
|
||||||
|
medians[i] := values[n div 2]
|
||||||
|
else
|
||||||
|
medians[i] := (values[n div 2 - 1] + values[n div 2]) / 2.0;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
fitted := true;
|
fitted := true;
|
||||||
|
|
@ -455,6 +508,11 @@ begin
|
||||||
if not fitted then
|
if not fitted then
|
||||||
NotFittedError(ER_FIT_NOT_CALLED);
|
NotFittedError(ER_FIT_NOT_CALLED);
|
||||||
|
|
||||||
|
// --- проверка constants
|
||||||
|
if (strategy = isConstant) and
|
||||||
|
((constants = nil) or (constants.Length <> cols.Length)) then
|
||||||
|
Error(ER_IMPUTER_CONSTANTS_INVALID);
|
||||||
|
|
||||||
var res := df;
|
var res := df;
|
||||||
|
|
||||||
for var i := 0 to cols.Length - 1 do
|
for var i := 0 to cols.Length - 1 do
|
||||||
|
|
@ -466,50 +524,65 @@ begin
|
||||||
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
if not (ct in [ColumnType.ctInt, ColumnType.ctFloat]) then
|
||||||
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
Error(ER_IMPUTER_COLUMN_NOT_NUMERIC, name);
|
||||||
|
|
||||||
if strategy = isMean then
|
case strategy of
|
||||||
begin
|
isMean:
|
||||||
var m := means[i];
|
|
||||||
res := res.ReplaceColumnFloat(
|
|
||||||
name,
|
|
||||||
c -> (if c.IsValid(idx) then c.Float(idx) else m)
|
|
||||||
);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
var v := constants[i];
|
|
||||||
if v = nil then
|
|
||||||
Error(ER_IMPUTER_CONSTANT_VALUE_NULL, name);
|
|
||||||
|
|
||||||
if ct = ColumnType.ctInt then
|
|
||||||
begin
|
begin
|
||||||
var k: integer;
|
var m := means[i];
|
||||||
try
|
|
||||||
k := integer(v);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
Error(ER_IMPUTER_CONSTANT_TYPE_MISMATCH, name);
|
|
||||||
end;
|
|
||||||
|
|
||||||
res := res.ReplaceColumnInt(
|
|
||||||
name,
|
|
||||||
c -> (if c.IsValid(idx) then c.Int(idx) else k)
|
|
||||||
);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
var r: real;
|
|
||||||
try
|
|
||||||
r := real(v);
|
|
||||||
except
|
|
||||||
on e: Exception do
|
|
||||||
Error(ER_IMPUTER_CONSTANT_TYPE_MISMATCH, name);
|
|
||||||
end;
|
|
||||||
|
|
||||||
res := res.ReplaceColumnFloat(
|
res := res.ReplaceColumnFloat(
|
||||||
name,
|
name,
|
||||||
c -> (if c.IsValid(idx) then c.Float(idx) else r)
|
c -> (if c.IsValid(idx) then c.Float(idx) else m)
|
||||||
);
|
);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
isConstant:
|
||||||
|
begin
|
||||||
|
var v := constants[i];
|
||||||
|
if v = nil then
|
||||||
|
Error(ER_IMPUTER_CONSTANT_VALUE_NULL, name);
|
||||||
|
|
||||||
|
if ct = ColumnType.ctInt then
|
||||||
|
begin
|
||||||
|
var k: integer;
|
||||||
|
try
|
||||||
|
k := integer(v);
|
||||||
|
except
|
||||||
|
on e: Exception do
|
||||||
|
Error(ER_IMPUTER_CONSTANT_TYPE_MISMATCH, name);
|
||||||
|
end;
|
||||||
|
|
||||||
|
res := res.ReplaceColumnInt(
|
||||||
|
name,
|
||||||
|
c -> (if c.IsValid(idx) then c.Int(idx) else k)
|
||||||
|
);
|
||||||
|
end
|
||||||
|
else // ctFloat
|
||||||
|
begin
|
||||||
|
var r: real;
|
||||||
|
try
|
||||||
|
r := real(v);
|
||||||
|
except
|
||||||
|
on e: Exception do
|
||||||
|
Error(ER_IMPUTER_CONSTANT_TYPE_MISMATCH, name);
|
||||||
|
end;
|
||||||
|
|
||||||
|
res := res.ReplaceColumnFloat(
|
||||||
|
name,
|
||||||
|
c -> (if c.IsValid(idx) then c.Float(idx) else r)
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
isMedian:
|
||||||
|
begin
|
||||||
|
var m := medians[i];
|
||||||
|
res := res.ReplaceColumnFloat(
|
||||||
|
name,
|
||||||
|
c -> (if c.IsValid(idx) then c.Float(idx) else m)
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
|
||||||
|
else
|
||||||
|
Error(ER_IMPUTER_STRATEGY_NOT_SUPPORTED, strategy);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -530,9 +603,23 @@ begin
|
||||||
isMean:
|
isMean:
|
||||||
Result := 'Imputer(strategy=mean, columns=' + colsStr + ')';
|
Result := 'Imputer(strategy=mean, columns=' + colsStr + ')';
|
||||||
|
|
||||||
|
isMedian:
|
||||||
|
Result := 'Imputer(strategy=median, columns=' + colsStr + ')';
|
||||||
|
|
||||||
isConstant:
|
isConstant:
|
||||||
|
begin
|
||||||
|
var valStr :=
|
||||||
|
if (constants <> nil) and (constants.Length > 0) and (constants[0] <> nil) then
|
||||||
|
constants[0].ToString
|
||||||
|
else
|
||||||
|
'null';
|
||||||
|
|
||||||
Result := 'Imputer(strategy=constant, value=' +
|
Result := 'Imputer(strategy=constant, value=' +
|
||||||
constants[0].ToString + ', columns=' + colsStr + ')';
|
valStr + ', columns=' + colsStr + ')';
|
||||||
|
end;
|
||||||
|
|
||||||
|
else
|
||||||
|
Result := 'Imputer(strategy=unknown, columns=' + colsStr + ')';
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,22 +56,26 @@ type
|
||||||
GridSearch = static class
|
GridSearch = static class
|
||||||
public
|
public
|
||||||
/// Выполняет подбор гиперпараметра по заданной сетке значений.
|
/// Выполняет подбор гиперпараметра по заданной сетке значений.
|
||||||
/// modelFactory — функция создания модели по значению параметра.
|
/// • modelFactory — функция создания модели по значению параметра (P -> T).
|
||||||
/// paramValues — набор тестируемых значений гиперпараметра.
|
/// • paramValues — набор тестируемых значений гиперпараметра типа P.
|
||||||
/// X, y — обучающие данные.
|
/// • X, y — обучающие данные.
|
||||||
/// k — число фолдов в кросс-валидации.
|
/// • k — число фолдов в кросс-валидации.
|
||||||
/// metric — функция оценки качества (yTrue, yPred) → real.
|
/// • metric — функция оценки качества (yTrue, yPred) → real.
|
||||||
/// Возвращает кортеж (лучший параметр, лучшее среднее значение метрики,
|
/// • maximize — если true, максимизируется метрика; иначе минимизируется.
|
||||||
/// модель, обученная на всём датасете с лучшим параметром).
|
/// • seed — seed для разбиения на фолды (для воспроизводимости).
|
||||||
class function Search<T>(
|
/// Возвращает кортеж:
|
||||||
modelFactory: real -> T;
|
/// • лучший параметр,
|
||||||
paramValues: array of real;
|
/// • лучшее среднее значение метрики,
|
||||||
X: Matrix; y: Vector;
|
/// • модель, обученная на всём датасете с лучшим параметром
|
||||||
k: integer;
|
class function Search<T, P>(
|
||||||
metric: (Vector, Vector) -> real;
|
modelFactory: P -> T;
|
||||||
maximize: boolean := True;
|
paramValues: array of P;
|
||||||
seed: integer := -1
|
X: Matrix; y: Vector;
|
||||||
): (real, real, T); where T: class,ISupervisedModel;
|
k: integer;
|
||||||
|
metric: (Vector, Vector) -> real;
|
||||||
|
maximize: boolean := True;
|
||||||
|
seed: integer := -1
|
||||||
|
): (P, real, T); where T: class, ISupervisedModel;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
@ -171,15 +175,15 @@ begin
|
||||||
if (k < 2) or (k > n) then
|
if (k < 2) or (k > n) then
|
||||||
ArgumentError(ER_K_INVALID, k, n);
|
ArgumentError(ER_K_INVALID, k, n);
|
||||||
|
|
||||||
var actualSeed := if seed >= 0 then seed
|
// --- RNG (без дублирования логики seed)
|
||||||
else System.Environment.TickCount and integer.MaxValue;
|
var rnd :=
|
||||||
|
if seed >= 0 then new System.Random(seed)
|
||||||
var rnd := new System.Random(actualSeed);
|
else new System.Random;
|
||||||
|
|
||||||
// --- 1. Индексы 0..n-1
|
// --- 1. Индексы 0..n-1
|
||||||
var idx := Arr(0..n-1);
|
var idx := Arr(0..n-1);
|
||||||
|
|
||||||
// --- 2. Перемешивание через стандартный Shuffle
|
// --- 2. Перемешивание
|
||||||
idx.Shuffle(rnd);
|
idx.Shuffle(rnd);
|
||||||
|
|
||||||
var baseSize := n div k;
|
var baseSize := n div k;
|
||||||
|
|
@ -224,10 +228,9 @@ begin
|
||||||
if (k < 2) or (k > n) then
|
if (k < 2) or (k > n) then
|
||||||
ArgumentError(ER_K_INVALID_STRATIFIED, k, n);
|
ArgumentError(ER_K_INVALID_STRATIFIED, k, n);
|
||||||
|
|
||||||
var actualSeed := if seed >= 0 then seed
|
var rnd :=
|
||||||
else System.Environment.TickCount and integer.MaxValue;
|
if seed >= 0 then new System.Random(seed)
|
||||||
|
else new System.Random;
|
||||||
var rnd := new System.Random(actualSeed);
|
|
||||||
|
|
||||||
// --- 1. Индексы по классам
|
// --- 1. Индексы по классам
|
||||||
var classMap := new Dictionary<integer, List<integer>>();
|
var classMap := new Dictionary<integer, List<integer>>();
|
||||||
|
|
@ -235,7 +238,7 @@ begin
|
||||||
for var i := 0 to n - 1 do
|
for var i := 0 to n - 1 do
|
||||||
begin
|
begin
|
||||||
var v := y[i];
|
var v := y[i];
|
||||||
var cls := integer(v);
|
var cls := Round(v);
|
||||||
|
|
||||||
if Abs(v - cls) > 1e-12 then
|
if Abs(v - cls) > 1e-12 then
|
||||||
ArgumentError(ER_STRATIFIED_LABELS_INVALID);
|
ArgumentError(ER_STRATIFIED_LABELS_INVALID);
|
||||||
|
|
@ -328,8 +331,12 @@ begin
|
||||||
var total := 0.0;
|
var total := 0.0;
|
||||||
var folds := 0;
|
var folds := 0;
|
||||||
var p := X.ColCount;
|
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, seed) do
|
foreach var (trainIdx, testIdx) in KFold(X.RowCount, k, baseSeed) do
|
||||||
begin
|
begin
|
||||||
var Xtr := new Matrix(trainIdx.Length, p);
|
var Xtr := new Matrix(trainIdx.Length, p);
|
||||||
var ytr := new Vector(trainIdx.Length);
|
var ytr := new Vector(trainIdx.Length);
|
||||||
|
|
@ -398,7 +405,11 @@ begin
|
||||||
var folds := 0;
|
var folds := 0;
|
||||||
var p := X.ColCount;
|
var p := X.ColCount;
|
||||||
|
|
||||||
foreach var (trainIdx, testIdx) in StratifiedKFold(y, k, seed) do
|
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
|
begin
|
||||||
var Xtr := new Matrix(trainIdx.Length, p);
|
var Xtr := new Matrix(trainIdx.Length, p);
|
||||||
var ytr := new Vector(trainIdx.Length);
|
var ytr := new Vector(trainIdx.Length);
|
||||||
|
|
@ -441,16 +452,16 @@ end;
|
||||||
// GridSearch
|
// GridSearch
|
||||||
//-----------------------------
|
//-----------------------------
|
||||||
|
|
||||||
class function GridSearch.Search<T>(
|
class function GridSearch.Search<T, P>(
|
||||||
modelFactory: real -> T;
|
modelFactory: P -> T;
|
||||||
paramValues: array of real;
|
paramValues: array of P;
|
||||||
X: Matrix;
|
X: Matrix;
|
||||||
y: Vector;
|
y: Vector;
|
||||||
k: integer;
|
k: integer;
|
||||||
metric: (Vector, Vector) -> real;
|
metric: (Vector, Vector) -> real;
|
||||||
maximize: boolean;
|
maximize: boolean;
|
||||||
seed: integer
|
seed: integer
|
||||||
): (real, real, T); where T: class,ISupervisedModel;
|
): (P, real, T); where T: class, ISupervisedModel;
|
||||||
begin
|
begin
|
||||||
if modelFactory = nil then
|
if modelFactory = nil then
|
||||||
ArgumentNullError(ER_ARG_NULL, 'modelFactory');
|
ArgumentNullError(ER_ARG_NULL, 'modelFactory');
|
||||||
|
|
@ -474,15 +485,20 @@ begin
|
||||||
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
|
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
|
||||||
|
|
||||||
var bestParam := paramValues[0];
|
var bestParam := paramValues[0];
|
||||||
var bestScore :=
|
var bestScore :=
|
||||||
if maximize then -1e308 else 1e308;
|
if maximize then -1e308 else 1e308;
|
||||||
|
|
||||||
|
var baseSeed :=
|
||||||
|
if seed >= 0 then seed
|
||||||
|
else System.Environment.TickCount and integer.MaxValue;
|
||||||
|
|
||||||
foreach var param in paramValues do
|
foreach var param in paramValues do
|
||||||
begin
|
begin
|
||||||
var model := modelFactory(param);
|
var model := modelFactory(param);
|
||||||
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, seed);
|
|
||||||
|
var avgScore := 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');
|
||||||
|
|
@ -501,7 +517,7 @@ begin
|
||||||
var bestModel := modelFactory(bestParam);
|
var bestModel := modelFactory(bestParam);
|
||||||
if bestModel = nil then
|
if bestModel = nil then
|
||||||
ArgumentError(ER_MODEL_NULL);
|
ArgumentError(ER_MODEL_NULL);
|
||||||
|
|
||||||
bestModel := bestModel.Fit(X, y) as T;
|
bestModel := bestModel.Fit(X, y) as T;
|
||||||
|
|
||||||
Result := (bestParam, bestScore, bestModel);
|
Result := (bestParam, bestScore, bestModel);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue