pascalabcnet/bin/Lib/InspectionML.pas

222 lines
7.5 KiB
ObjectPascal
Raw Permalink Normal View History

2026-02-21 13:34:18 +03:00
/// InspectionML — инструменты анализа поведения обученных моделей.
///
/// Модуль предназначен для исследования и интерпретации уже обученных
/// моделей машинного обучения.
///
/// Содержит алгоритмы:
/// • оценки важности признаков
/// • анализа чувствительности модели
/// • построения частичных зависимостей
/// • диагностических процедур
///
/// Область ответственности:
/// • работает только с обученными моделями (IModel)
/// • не участвует в обучении
/// • не вычисляет метрики напрямую
/// • не изменяет состояние модели
///
/// Архитектурный принцип:
/// • модуль зависит от абстракции IModel
/// • модели не зависят от данного модуля
unit InspectionML;
interface
uses MLCoreABC, LinearAlgebraML;
type
Inspection = static class
public
2026-04-04 14:33:26 +03:00
/// PermutationImportance — оценка важности признаков методом перестановок.
///
/// Для каждого признака случайно перемешивает соответствующий столбец
/// и измеряет снижение качества модели по заданной функции scoreFunc.
///
/// Текущая версия предназначена для регрессионных моделей.
2026-04-04 14:33:26 +03:00
///
/// Параметры:
/// • model — обученная регрессионная модель;
2026-04-04 14:33:26 +03:00
/// • X — матрица признаков (nSamples × nFeatures);
/// • y — вектор истинных значений;
/// • scoreFunc — функция оценки качества (например, MSE, Accuracy);
/// • seed — начальное значение генератора случайных чисел.
///
/// Возвращает:
/// Вектор важностей признаков длины nFeatures.
/// Чем больше значение, тем сильнее признак влияет на качество модели
static function PermutationImportance(model: IRegressor; X: Matrix; y: Vector;
scoreFunc: (Vector, Vector) -> real;
nRepeats: integer := 5;
higherIsBetter: boolean := True;
seed: integer := -1): Vector;
/// PermutationImportance — оценка важности признаков методом перестановок.
///
/// Текущая версия предназначена для классификационных моделей.
/// Для каждого признака случайно перемешивает соответствующий столбец
/// и измеряет снижение качества модели по заданной функции scoreFunc.
///
/// Параметры:
/// • model — обученная классификационная модель;
/// • X — матрица признаков (nSamples × nFeatures);
/// • y — массив истинных меток классов;
/// • scoreFunc — функция оценки качества (например, Accuracy);
/// • seed — начальное значение генератора случайных чисел.
///
/// Возвращает:
/// Вектор важностей признаков длины nFeatures.
/// Чем больше значение, тем сильнее признак влияет на качество модели
static function PermutationImportance(model: IClassifier; X: Matrix; y: array of integer;
scoreFunc: (array of integer, array of integer) -> real;
nRepeats: integer := 5;
higherIsBetter: boolean := True;
seed: integer := -1): Vector;
2026-02-21 13:34:18 +03:00
end;
implementation
uses MLExceptions;
2026-04-23 16:30:27 +03:00
uses MLUtilsABC;
const
2026-04-05 00:00:36 +03:00
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: IRegressor;
X: Matrix; y: Vector;
2026-04-05 00:00:36 +03:00
scoreFunc: (Vector, Vector) -> real;
nRepeats: integer;
higherIsBetter: boolean;
seed: integer): Vector;
2026-02-21 13:34:18 +03:00
begin
if model = nil then
ArgumentNullError(ER_MODEL_NULL);
if scoreFunc = nil then
ArgumentNullError(ER_SCORE_FUNC_NULL);
2026-03-02 00:22:35 +03:00
if X.RowCount <> y.Length then
DimensionError(ER_DIM_MISMATCH, X.RowCount, y.Length);
2026-02-21 13:34:18 +03:00
2026-04-05 00:00:36 +03:00
if nRepeats < 1 then
ArgumentOutOfRangeError(ER_ARG_OUT_OF_RANGE, 'nRepeats', nRepeats);
2026-02-21 13:34:18 +03:00
var baselinePred := model.Predict(X);
var baselineScore := scoreFunc(y, baselinePred);
2026-03-02 00:22:35 +03:00
var n := X.RowCount;
var p := X.ColCount;
2026-02-21 13:34:18 +03:00
var resultVec := new Vector(p);
2026-04-23 16:30:27 +03:00
var userProvidedSeed: boolean;
var baseSeed := ResolveRandomSeed(seed, userProvidedSeed);
2026-04-05 00:00:36 +03:00
for var j := 0 to p - 1 do
2026-02-21 13:34:18 +03:00
begin
2026-04-05 00:00:36 +03:00
var acc := 0.0;
2026-02-21 13:34:18 +03:00
2026-04-05 00:00:36 +03:00
for var r := 0 to nRepeats - 1 do
2026-02-21 13:34:18 +03:00
begin
2026-04-05 00:00:36 +03:00
var Xperm := X.Clone;
// --- детерминированный seed для (j, r)
var runSeed := baseSeed + j * 100000 + r;
2026-02-21 13:34:18 +03:00
2026-04-05 00:00:36 +03:00
var rnd := new System.Random(runSeed);
// --- shuffle столбца j (FisherYates)
2026-04-05 00:00:36 +03:00
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);
if higherIsBetter then
acc += (baselineScore - permScore)
else
acc += (permScore - baselineScore);
2026-04-05 00:00:36 +03:00
end;
2026-02-21 13:34:18 +03:00
2026-04-05 00:00:36 +03:00
resultVec[j] := acc / nRepeats;
2026-02-21 13:34:18 +03:00
end;
Result := resultVec;
end;
static function Inspection.PermutationImportance(
model: IClassifier;
X: Matrix; y: array of integer;
scoreFunc: (array of integer, array of integer) -> real;
nRepeats: integer;
higherIsBetter: boolean;
seed: integer): Vector;
begin
if model = nil then
ArgumentNullError(ER_MODEL_NULL);
if scoreFunc = nil then
ArgumentNullError(ER_SCORE_FUNC_NULL);
if X.RowCount <> y.Length then
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 baselineScore := scoreFunc(y, baselinePred);
var n := X.RowCount;
var p := X.ColCount;
var resultVec := new Vector(p);
var userProvidedSeed: boolean;
var baseSeed := ResolveRandomSeed(seed, userProvidedSeed);
for var j := 0 to p - 1 do
begin
var acc := 0.0;
for var r := 0 to nRepeats - 1 do
begin
var Xperm := X.Clone;
var runSeed := baseSeed + j * 100000 + r;
var rnd := new System.Random(runSeed);
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);
if higherIsBetter then
acc += (baselineScore - permScore)
else
acc += (permScore - baselineScore);
end;
resultVec[j] := acc / nRepeats;
end;
Result := resultVec;
end;
end.