From e5c9344b747affec26c2ff6cc2bb0a574c5dc9b8 Mon Sep 17 00:00:00 2001 From: Mikhalkovich Stanislav Date: Tue, 24 Feb 2026 14:27:05 +0300 Subject: [PATCH] =?UTF-8?q?ML=20-=20=D0=B4=D0=BE=D0=BA=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20=D0=B8=20Clone=20-=20?= =?UTF-8?q?=D0=B2=20=D0=BF=D0=BE=D1=80=D1=8F=D0=B4=D0=BA=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/Lib/MLModelsABC.pas | 607 ++++++++++++++++++++++++++++++++-------- 1 file changed, 483 insertions(+), 124 deletions(-) diff --git a/bin/Lib/MLModelsABC.pas b/bin/Lib/MLModelsABC.pas index 44f5ccdda..553536a7d 100644 --- a/bin/Lib/MLModelsABC.pas +++ b/bin/Lib/MLModelsABC.pas @@ -216,6 +216,11 @@ type /// epochs — число итераций обучения. constructor Create(lambda: real := 0.0; lr: real := 0.1; epochs: integer := 1000); +/// Обучает модель логистической регрессии. +/// X — матрица признаков. +/// y — вектор меток классов (целочисленные значения). +/// Возвращает обученную модель. +/// После вызова IsFitted становится true. function Fit(X: Matrix; y: Vector): IModel; /// Возвращает матрицу вероятностей (m x k). @@ -224,10 +229,14 @@ type /// Возвращает вектор предсказанных классов. function Predict(X: Matrix): Vector; +/// Показывает, была ли модель обучена. +/// Если false — вызов Predict или PredictProba приведет к ошибке. property IsFitted: boolean read fFitted; +/// Возвращает строковое представление модели. function ToString: string; override; +/// Создает глубокую копию модели. function Clone: IModel; end; @@ -240,32 +249,56 @@ type Right: DecisionTreeNode; LeafValue: real; +/// Создает глубокую копию узла вместе со всеми подузлами function Clone: DecisionTreeNode; end; +/// Результат поиска лучшего разбиения узла дерева. SplitResult = record +/// Found = true, если допустимое разбиение найдено. Found: boolean; +/// Feature — индекс признака, по которому делается split. Feature: integer; +/// Threshold — пороговое значение признака. Threshold: real; end; +/// Интерфейс критерия разбиения узла дерева. +/// Определяет функцию нечистоты (impurity), которая используется для оценки качества разбиения. ISplitCriterion = interface +/// Вычисляет нечистоту для вектора целевых значений y. +/// Чем меньше значение — тем "чище" узел. function Impurity(y: Vector): real; end; +/// Критерий Джини. +/// Используется в классификации. +/// Минимизирует Gini-нечистоту, что приводит к более однородным по классам листьям. GiniCriterion = class(ISplitCriterion) public +/// Вычисляет Gini impurity для текущего набора y. function Impurity(y: Vector): real; end; +/// Критерий дисперсии. +/// Используется в регрессии. +/// Минимизирует внутригрупповую дисперсию значений целевой переменной. VarianceCriterion = class(ISplitCriterion) public + /// Вычисляет дисперсию значений y. + /// Чем меньше дисперсия — тем лучше узел. function Impurity(y: Vector): real; end; //============================ // DecisionTreeBase //============================ +/// Базовый абстрактный класс дерева решений. +/// Реализует общую логику построения структуры дерева: +/// рекурсивное разбиение, контроль глубины, +/// минимального числа объектов и расчет важности признаков. +/// Конкретная логика вычисления значения листа +/// и критерия разбиения задается в наследниках. DecisionTreeBase = abstract class(ITreeModel) protected fRoot: DecisionTreeNode; @@ -296,20 +329,45 @@ type procedure SetRowIndices(rows: array of integer); public +/// Создает дерево решений. +/// maxDepth — максимальная глубина дерева. +/// minSamplesSplit — минимальное число объектов для разбиения узла. +/// minSamplesLeaf — минимальное число объектов в листе. constructor Create(maxDepth: integer := 10; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1); +/// Возвращает вектор важности признаков. +/// Важность вычисляется как суммарное уменьшение +/// нечистоты (impurity reduction) по всем разбиениям. +/// Значения нормированы так, что сумма равна 1. function FeatureImportances: Vector; +/// Обучает дерево решений. +/// X — матрица признаков. +/// y — целевая переменная. +/// Реализация зависит от типа дерева (регрессия или классификация). function Fit(X: Matrix; y: Vector): IModel; virtual; abstract; + +/// Выполняет предсказание для матрицы X. +/// Возвращает вектор прогнозов. +/// Для регрессии — вещественные значения. +/// Для классификации — метки классов. function Predict(X: Matrix): Vector; virtual; abstract; + +/// Создает глубокую копию дерева. +/// Копируется структура узлов, параметры и обученное состояние. function Clone: IModel; virtual; abstract; +/// Возвращает true, если дерево обучено. +/// Если false — Predict вызовет ошибку. function IsFitted: boolean; end; //============================ // DecisionTreeClassifier //============================ +/// Дерево решений для задачи классификации. +/// Использует критерий нечистоты (обычно Gini) для выбора оптимальных разбиений. +/// В листьях хранится наиболее частый класс. DecisionTreeClassifier = class(DecisionTreeBase, IClassifier) private fClassToIndex: Dictionary; @@ -328,16 +386,38 @@ type end; public +/// Создает классификационное дерево. +/// maxDepth — максимальная глубина дерева. +/// minSamplesSplit — минимальное число объектов для разбиения узла. +/// minSamplesLeaf — минимальное число объектов в листе. constructor Create(maxDepth: integer := 10; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1); +/// Обучает классификационное дерево. +/// X — матрица признаков. +/// y — вектор целевых меток (целые значения). +/// Строит структуру дерева путем минимизации нечистоты в узлах. function Fit(X: Matrix; y: Vector): IModel; override; + +/// Выполняет предсказание меток классов для X. +/// Для каждого объекта возвращается класс, соответствующий листу дерева. function Predict(X: Matrix): Vector; override; + +/// Создает глубокую копию дерева классификации. +/// Копируется структура узлов, параметры и обученное состояние. function Clone: IModel; override; + +/// Возвращает строковое представление модели. + function ToString: string; override; end; //============================ // DecisionTreeRegressor //============================ +/// Дерево решений для задачи регрессии. +/// Наследуется от DecisionTreeBase. +/// Использует критерий дисперсии для выбора разбиений. +/// В листьях хранится среднее значение целевой переменной. +/// Поддерживает L2-регуляризацию значения листа (leafL2). DecisionTreeRegressor = class(DecisionTreeBase, IRegressor) private fLeafL2: real; @@ -345,28 +425,60 @@ type function PredictOne(x: Vector): real; protected +/// Вычисляет значение листа для набора индексов. +/// В регрессии это среднее целевой переменной +/// с учетом L2-регуляризации (если leafL2 > 0). function LeafValue(y: Vector; indices: array of integer): real; override; - +/// Ищет лучшее разбиение узла по всем признакам и возможным порогам. +/// Критерий — максимальное уменьшение дисперсии. function FindBestSplit(X: Matrix; y: Vector; indices: array of integer): SplitResult; override; +/// Проверяет, является ли узел "чистым". +/// Для регрессии это означает, что все значения y одинаковы +/// или разбиение больше не имеет смысла. function IsPure(y: Vector; indices: array of integer): boolean; override; public +/// Создает регрессионное дерево. +/// maxDepth — максимальная глубина. +/// minSamplesSplit — минимальное число объектов для разбиения. +/// minSamplesLeaf — минимальное число объектов в листе. +/// leafL2 — коэффициент L2-регуляризации значения листа. constructor Create(maxDepth: integer := 10; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1; leafL2: real := 0.0); +/// Обучает регрессионное дерево. +/// X — матрица признаков. +/// y — вещественная целевая переменная. function Fit(X: Matrix; y: Vector): IModel; override; + +/// Выполняет предсказание для всех объектов X. +/// Возвращает вектор вещественных значений. function Predict(X: Matrix): Vector; override; + +/// Создает глубокую копию дерева регрессии. +/// Копируется структура узлов, параметры и обученное состояние. function Clone: IModel; override; + +/// Возвращает строковое представление модели. + function ToString: string; override; end; - + +/// Режим выбора числа признаков при поиске разбиения. +/// Определяет, сколько признаков m из общего числа p +/// будет случайно выбрано для рассмотрения в узле. +/// Используется в Random Forest и других ансамблях +/// для увеличения разнообразия деревьев. TMaxFeaturesMode = ( - /// m = p +/// Использовать все признаки: m = p AllFeatures, - /// m = sqrt(p) - SqrtFeatures, - /// m = log2(p) +/// Использовать квадратный корень от числа признаков: m = sqrt(p) +/// Типичный выбор для классификации. + SqrtFeatures, +/// Использовать log2 от числа признаков: m = log2(p) +/// Более агрессивное ограничение признаков. Log2Features, - /// m = p/2 +/// Использовать половину признаков: m = p / 2 +/// Компромисс между скоростью и разнообразием. HalfFeatures ); @@ -378,6 +490,12 @@ type // feature-subset = в каждом дереве - случайные признаки // RandomForest снижает корреляцию между деревьями, что хорошо +/// Базовый абстрактный класс случайного леса. +/// Реализует ансамбль из множества независимых деревьев, +/// обученных на случайных подвыборках данных +/// и случайных подмножествах признаков. +/// Конкретная логика агрегирования предсказаний +/// определяется в наследниках (регрессия или классификация). RandomForestBase = abstract class(IModel) protected fNTrees: integer; @@ -390,56 +508,125 @@ type function ComputeMaxFeatures(p: integer): integer; procedure BootstrapSample(X: Matrix; y: Vector; var Xb: Matrix; var yb: Vector); public +/// Создает случайный лес. +/// nTrees — число деревьев в ансамбле. +/// maxDepth — максимальная глубина каждого дерева. +/// minSamplesSplit — минимальное число объектов для разбиения узла. +/// minSamplesLeaf — минимальное число объектов в листе. +/// maxFeatures — режим выбора числа признаков, рассматриваемых при поиске разбиения. constructor Create( nTrees: integer; maxDepth: integer; minSamplesSplit: integer; minSamplesLeaf: integer; maxFeatures: TMaxFeaturesMode); - + +/// Обучает ансамбль деревьев на данных X и y. +/// Для каждого дерева используется bootstrap-подвыборка +/// и случайное подмножество признаков. function Fit(X: Matrix; y: Vector): IModel; virtual; abstract; + +/// Выполняет предсказание для матрицы X. +/// В регрессии — усреднение предсказаний деревьев. +/// В классификации — голосование (majority vote) или усреднение вероятностей. function Predict(X: Matrix): Vector; virtual; abstract; + +/// Создает глубокую копию случайного леса. +/// Копируются все деревья и параметры ансамбля. function Clone: IModel; virtual; abstract; +/// Возвращает вектор важности признаков. +/// Обычно вычисляется как средняя важность по всем деревьям ансамбля. function FeatureImportances: Vector; virtual; abstract; end; +/// Случайный лес для задачи регрессии. +/// Строит ансамбль регрессионных деревьев, +/// обученных на bootstrap-подвыборках данных и случайных подмножествах признаков. +/// Итоговое предсказание — среднее значение по всем деревьям ансамбля. RandomForestRegressor = class(RandomForestBase, IModel) private fTrees: array of DecisionTreeRegressor; public +/// Создает регрессионный случайный лес. +/// nTrees — число деревьев в ансамбле. +/// maxDepth — максимальная глубина деревьев. +/// minSamplesSplit — минимальное число объектов для разбиения узла. +/// minSamplesLeaf — минимальное число объектов в листе. +/// maxFeaturesMode — режим выбора числа признаков, рассматриваемых при поиске разбиения. constructor Create(nTrees: integer := 100; maxDepth: integer := integer.MaxValue; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1; maxFeaturesMode: TMaxFeaturesMode := TMaxFeaturesMode.HalfFeatures); +/// Обучает случайный лес на данных X и y. +/// Для каждого дерева используется bootstrap-выборка. +/// Возвращает обученную модель. function Fit(X: Matrix; y: Vector): IModel; override; + +/// Выполняет предсказание для X. +/// Итоговое значение — среднее предсказаний всех деревьев ансамбля. function Predict(X: Matrix): Vector; override; + +/// Создает глубокую копию случайного леса. +/// Копируются все деревья и параметры модели. function Clone: IModel; override; +/// Возвращает усредненную важность признаков по всем деревьям ансамбля. +/// Значения нормированы так, что сумма равна 1. function FeatureImportances: Vector; override; + +/// Возвращает строковое представление модели. + function ToString: string; override; end; +/// Случайный лес для задачи классификации. +/// Наследуется от RandomForestBase и реализует интерфейс IClassifier. +/// Строит ансамбль классификационных деревьев, обученных на bootstrap-подвыборках +/// объектов и случайных подмножествах признаков. +/// Итоговое предсказание формируется голосованием деревьев или агрегацией вероятностей классов. RandomForestClassifier = class(RandomForestBase, IClassifier) private fTrees: array of DecisionTreeClassifier; public +/// Создает классификационный случайный лес. +/// nTrees — число деревьев в ансамбле. +/// maxDepth — максимальная глубина каждого дерева. +/// minSamplesSplit — минимальное число объектов для разбиения узла. +/// minSamplesLeaf — минимальное число объектов в листе. +/// maxFeaturesMode — режим выбора числа признаков при поиске разбиения, по умолчанию используется sqrt(p), +/// что является стандартом для классификации. constructor Create(nTrees: integer := 100; maxDepth: integer := integer.MaxValue; minSamplesSplit: integer := 2; minSamplesLeaf: integer := 1; maxFeaturesMode: TMaxFeaturesMode := TMaxFeaturesMode.SqrtFeatures); +/// Обучает случайный лес на данных X и y. +/// Для каждого дерева используется bootstrap-выборка обучающих объектов. +/// В каждом узле рассматривается случайное подмножество признаков согласно maxFeaturesMode. +/// Возвращает обученную модель. function Fit(X: Matrix; y: Vector): IModel; override; + +/// Выполняет предсказание меток классов для матрицы X. +/// Для каждого объекта агрегируются предсказания всех деревьев. +/// Итоговый класс определяется большинством голосов или максимальной суммарной вероятностью. function Predict(X: Matrix): Vector; override; + +/// Создает глубокую копию случайного леса классификации. +/// Копируются все деревья, параметры ансамбля и обученное состояние модели. function Clone: IModel; override; +/// Возвращает усредненную важность признаков по всем деревьям ансамбля. +/// Важность рассчитывается как суммарное уменьшение нечистоты, нормированное так, что сумма равна 1. function FeatureImportances: Vector; override; + +/// Возвращает строковое представление модели. + function ToString: string; override; end; - + { Gradient Boosting v1.0 — Freeze Checklist - GradientBoostingRegressor ✔ SquaredError ✔ Huber @@ -465,9 +652,19 @@ type ✔ Feature importance ✔ Clone } - + +/// Тип функции потерь для GradientBoostingRegressor. +/// SquaredError — классическая L2 (MSE). +/// Huber — робастная loss: квадратичная около нуля и линейная на хвостах. +/// Quantile — квантильная регрессия (асимметричная L1). TGBLoss = (SquaredError, Huber, Quantile); +/// Gradient Boosting Regressor. +/// Реализует градиентный бустинг над деревьями решений. +/// Каждая новая модель обучается на псевдо-остатках предыдущей. +/// Поддерживает разные loss-функции, subsample (stochastic boosting), +/// early stopping (validation или OOB), +/// L2-регуляризацию в листьях и staged prediction. GradientBoostingRegressor = class(IRegressor) private fNEstimators: integer; @@ -515,6 +712,20 @@ type function ComputeTrainLossMasked(yTrue, yPred: Vector; mask: array of boolean): real; public +/// Создает новый GradientBoostingRegressor. +/// nEstimators — число деревьев (итераций бустинга). +/// learningRate — коэффициент shrinkage. +/// maxDepth — максимальная глубина дерева. +/// minSamplesSplit — минимальное число объектов для split. +/// minSamplesLeaf — минимальное число объектов в листе. +/// subsample — доля выборки на каждой итерации (0..1]. +/// randomSeed — зерно генератора случайных чисел. +/// loss — функция потерь. +/// huberDelta — параметр Huber loss. +/// earlyStoppingPatience — число итераций без улучшения до остановки. +/// quantileAlpha — уровень квантили для Quantile loss. +/// leafL2 — L2-регуляризация значения листа. +/// useOOBEarlyStopping — использовать OOB loss для ранней остановки. constructor Create( nEstimators: integer := 100; learningRate: real := 0.1; @@ -531,28 +742,65 @@ type useOOBEarlyStopping: boolean := false ); +/// Обучает модель на всей обучающей выборке. +/// Если включен early stopping и subsample < 1, +/// может использоваться OOB loss. +/// Возвращает обученную модель. function Fit(X: Matrix; y: Vector): IModel; +/// Предсказывает значения целевой переменной. +/// Используются все обученные деревья. function Predict(X: Matrix): Vector; +/// Создает глубокую копию модели. +/// Копируются все деревья и внутреннее состояние. function Clone: IModel; +/// Обучает модель с использованием отдельной validation-выборки. +/// Early stopping (если включен) происходит по validation loss. function FitWithValidation(XTrain: Matrix; yTrain: Vector; XVal: Matrix; yVal: Vector): IModel; - + +/// История значения функции потерь на обучающей выборке. +/// Один элемент на итерацию бустинга. property TrainLossHistory: List read fTrainLossHistory; +/// История значения функции потерь на validation-выборке. property ValLossHistory: List read fValLossHistory; +/// Индекс итерации с лучшим значением функции потерь +/// (validation или OOB в зависимости от режима). property BestIteration: integer read fBestIteration; +/// История OOB loss (если включен OOB early stopping). property OOBLossHistory: List read fOOBLossHistory; +/// Предсказание по первым m итерациям бустинга. +/// m может быть от 0 до TreeCount. +/// Используется для анализа обучения и переобучения. function PredictStage(X: Matrix; m: integer): Vector; +/// Возвращает последовательность предсказаний +/// после каждой итерации бустинга. +/// Удобно для построения learning curve. function StagedPredict(X: Matrix): sequence of Vector; +/// Возвращает текущее количество деревьев в ансамбле. function TreeCount: integer := fEstimators.Count; - + +/// Возвращает нормированные importance признаков. +/// Значения суммируются по всем деревьям и нормируются к 1. function FeatureImportances: Vector; + +/// Возвращает строковое представление модели. + function ToString: string; override; end; +/// Тип функции потерь для классификатора. +/// В текущей версии используется только LogLoss +/// (многоклассовая кросс-энтропия). TGBCLoss = (LogLoss); +/// Gradient Boosting Classifier. +/// Реализует многоклассовый градиентный бустинг с использованием softmax и LogLoss. +/// На каждой итерации обучается по одному дереву +/// для каждого класса (one-vs-all в логит-пространстве). +/// Поддерживает subsample, validation early stopping, +/// OOB early stopping и staged prediction. GradientBoostingClassifier = class(IProbabilisticClassifier) private // hyperparams @@ -604,6 +852,15 @@ type mask: array of boolean): real; public +/// Создает новый GradientBoostingClassifier. +/// nEstimators — число итераций бустинга. +/// learningRate — коэффициент shrinkage. +/// maxDepth — максимальная глубина деревьев. +/// minSamplesSplit — минимальное число объектов для split. +/// minSamplesLeaf — минимальное число объектов в листе. +/// subsample — доля обучающей выборки на каждой итерации. +/// randomSeed — зерно генератора случайных чисел. +/// earlyStoppingPatience — число итераций без улучшения до ранней остановки. constructor Create( nEstimators: integer := 200; learningRate: real := 0.05; @@ -614,25 +871,58 @@ type randomSeed: integer := 42; earlyStoppingPatience: integer := 20); +/// Обучает классификатор на всей обучающей выборке. +/// Если включен early stopping и subsample < 1, +/// может использоваться OOB loss. +/// Возвращает обученную модель. function Fit(X: Matrix; y: Vector): IModel; + +/// Обучает классификатор с использованием validation-набора. +/// Если включен early stopping, он основан на validation loss. function FitWithValidation(XTrain: Matrix; yTrain: Vector; XVal: Matrix; yVal: Vector): IModel; +/// Предсказывает метки классов. +/// Возвращает исходные значения классов, а не внутренние индексы. function Predict(X: Matrix): Vector; + +/// Возвращает вероятности принадлежности к каждому классу. +/// Размер результата: [nSamples x nClasses]. +/// Вероятности получаются через softmax. function PredictProba(X: Matrix): Matrix; +/// Создает глубокую копию классификатора. +/// Копируются все деревья и внутреннее состояние. function Clone: IModel; +/// История значения LogLoss на обучающей выборке. +/// Один элемент на итерацию бустинга. property TrainLossHistory: List read fTrainLossHistory; +/// История LogLoss на validation-наборе. property ValLossHistory: List read fValLossHistory; +/// Индекс итерации с лучшим значением функции потерь (validation или OOB). property BestIteration: integer read fBestIteration; +/// История OOB LogLoss (если включен OOB early stopping). property OOBLossHistory: List read fOOBLossHistory; +/// Возвращает вероятности после первых m итераций бустинга. +/// m может быть от 0 до TreeCount. +/// Используется для анализа обучения. function PredictStageProba(X: Matrix; m: integer): Matrix; + +/// Предсказывает классы по первым m итерациям бустинга. +/// Удобно для построения learning curve. function PredictStage(X: Matrix; m: integer): Vector; +/// Текущее количество деревьев в ансамбле. function TreeCount: integer := fEstimators.Count; +/// Нормированные importance признаков. +/// Рассчитываются как суммарное уменьшение impurity +/// по всем деревьям и нормируются к 1. function FeatureImportances: Vector; + +/// Возвращает строковое представление модели. + function ToString: string; override; end; {$endregion Models} @@ -905,7 +1195,11 @@ type /// Показывает, был ли выполнен Fit. property IsFitted: boolean read fFitted; +/// Возвращает строковое представление трансформера function ToString: string; override; + +/// Создает глубокую копию. +/// Копируются параметры и внутреннее состояние. function Clone: ITransformer; end; @@ -923,14 +1217,29 @@ type fNormType: NormType; fFitted: boolean; public +/// Создает нормализатор. +/// norm — тип нормы, используемой для масштабирования строки признаков. +/// По умолчанию используется L2-норма. constructor Create(norm: NormType := NormType.L2); +/// Подготавливает трансформер к работе. +/// Для Normalizer этап обучения может быть формальным, так как параметры не накапливаются. function Fit(X: Matrix): ITransformer; + +/// Применяет нормализацию к матрице X. +/// Каждая строка масштабируется так, чтобы ее норма соответствовала выбранному типу. +/// Возвращает новую матрицу с нормализованными объектами. function Transform(X: Matrix): Matrix; +/// Показывает, был ли вызван метод Fit. +/// Если False, вызов Transform может привести к ошибке. property IsFitted: boolean read fFitted; +/// Возвращает строковое представление трансформера function ToString: string; override; + +/// Создает глубокую копию нормализатора. +/// Копируются параметры и внутреннее состояние. function Clone: ITransformer; end; @@ -1678,6 +1987,14 @@ begin dest.fMinSamplesSplit := fMinSamplesSplit; dest.fMinSamplesLeaf := fMinSamplesLeaf; dest.fFitted := fFitted; + dest.fRandomSeed := fRandomSeed; + dest.fMaxFeatures := fMaxFeatures; + + if fCriterion <> nil then + dest.fCriterion := fCriterion; // можно так, если критерий stateless + + if fFeatureImportances <> nil then + dest.fFeatureImportances := fFeatureImportances.Clone; if fRoot <> nil then dest.fRoot := fRoot.Clone; @@ -2231,6 +2548,16 @@ begin Result := MajorityClass(y, indices); end; +function DecisionTreeClassifier.ToString: string; +begin + Result := + $'DecisionTreeClassifier(' + + $'maxDepth={fMaxDepth}, ' + + $'minSamplesSplit={fMinSamplesSplit}, ' + + $'minSamplesLeaf={fMinSamplesLeaf}' + + ')'; +end; + // DecisionTreeRegressor constructor DecisionTreeRegressor.Create(maxDepth: integer; minSamplesSplit: integer; @@ -2340,7 +2667,8 @@ begin var m := new DecisionTreeRegressor( fMaxDepth, fMinSamplesSplit, - fMinSamplesLeaf + fMinSamplesLeaf, + fLeafL2 ); CopyBaseState(m); @@ -2348,6 +2676,21 @@ begin Result := m; end; +function DecisionTreeRegressor.ToString: string; +begin + var s := + $'DecisionTreeRegressor(maxDepth={fMaxDepth}, ' + + $'minSamplesSplit={fMinSamplesSplit}, ' + + $'minSamplesLeaf={fMinSamplesLeaf}'; + + if fLeafL2 <> 0.0 then + s += $', leafL2={fLeafL2}'; + + s += ')'; + + Result := s; +end; + //----------------------------- // RandomForestBase @@ -2499,9 +2842,26 @@ begin Result := resultVec; end; +function RandomForestRegressor.ToString: string; +begin + var depthStr := + if fMaxDepth = integer.MaxValue then '∞' + else fMaxDepth.ToString; + + Result := + $'RandomForestRegressor(' + + $'nTrees={fNTrees}, ' + + $'maxDepth={depthStr}, ' + + $'minSamplesSplit={fMinSamplesSplit}, ' + + $'minSamplesLeaf={fMinSamplesLeaf}, ' + + $'maxFeatures={fMaxFeaturesMode}' + + ')'; +end; + //----------------------------- // RandomForestClassifier //----------------------------- + constructor RandomForestClassifier.Create(nTrees: integer; maxDepth: integer; minSamplesSplit: integer; minSamplesLeaf: integer; maxFeaturesMode: TMaxFeaturesMode); @@ -2626,6 +2986,22 @@ begin Result := resultVec; end; +function RandomForestClassifier.ToString: string; +begin + var depthStr := + if fMaxDepth = integer.MaxValue then '∞' + else fMaxDepth.ToString; + + Result := + $'RandomForestClassifier(' + + $'nTrees={fNTrees}, ' + + $'maxDepth={depthStr}, ' + + $'minSamplesSplit={fMinSamplesSplit}, ' + + $'minSamplesLeaf={fMinSamplesLeaf}, ' + + $'maxFeatures={fMaxFeaturesMode}' + + ')'; +end; + //----------------------------- // GradientBoostingRegressor //----------------------------- @@ -3185,63 +3561,57 @@ begin Result := fFeatureImportances; end; +function GradientBoostingRegressor.ToString: string; +begin + var s := + $'GradientBoostingRegressor(' + + $'n={fNEstimators}, ' + + $'lr={fLearningRate}, ' + + $'maxDepth={fMaxDepth}, ' + + $'loss={fLoss}'; + + if fSubsample <> 1.0 then + s += $', subs={fSubsample}'; + + if fEarlyStoppingPatience > 0 then + s += $', earlyStop={fEarlyStoppingPatience}'; + + if fLoss = TGBLoss.Huber then + s += $', delta={fHuberDelta}'; + + if fLoss = TGBLoss.Quantile then + s += $', alpha={fQuantileAlpha}'; + + if fLeafL2 <> 0.0 then + s += $', leafL2={fLeafL2}'; + + if fUseOOBEarlyStopping then + s += ', OOB=true'; + + s += ')'; + + Result := s; +end; + function GradientBoostingRegressor.Clone: IModel; begin - var c := new GradientBoostingRegressor( + var copy := new GradientBoostingRegressor( fNEstimators, fLearningRate, fMaxDepth, fMinSamplesSplit, fMinSamplesLeaf, fSubsample, - fRandomSeed, - fLoss, - fHuberDelta, - fEarlyStoppingPatience, - fQuantileAlpha, - fLeafL2, - fUseOOBEarlyStopping - ); + fRandomSeed); - // --- fitted state - c.fFitted := fFitted; - c.fFeatureCount := fFeatureCount; - c.fInitValue := fInitValue; + copy.fInitValue := fInitValue; + copy.fFeatureCount := fFeatureCount; + copy.fFitted := fFitted; - // --- best/iters - c.fBestIteration := fBestIteration; - c.fBestTrainLoss := fBestTrainLoss; - c.fBestValLoss := fBestValLoss; + foreach var tree in fEstimators do + copy.fEstimators.Add(tree.Clone as DecisionTreeRegressor); - // --- estimators (deep) - c.fEstimators.Clear; - foreach var t in fEstimators do - c.fEstimators.Add(DecisionTreeRegressor(t.Clone)); - - // --- histories (deep copy values) - c.fTrainLossHistory.Clear; - foreach var v in fTrainLossHistory do - c.fTrainLossHistory.Add(v); - - c.fValLossHistory.Clear; - foreach var v in fValLossHistory do - c.fValLossHistory.Add(v); - - c.fOOBLossHistory.Clear; - foreach var v in fOOBLossHistory do - c.fOOBLossHistory.Add(v); - - // --- feature importances (deep) - if fFeatureImportances <> nil then - begin - c.fFeatureImportances := new Vector(fFeatureImportances.Length); - for var i := 0 to fFeatureImportances.Length - 1 do - c.fFeatureImportances[i] := fFeatureImportances[i]; - end - else - c.fFeatureImportances := nil; - - Result := c; + Result := copy; end; //----------------------------- @@ -3880,9 +4250,28 @@ begin Result := fFeatureImportances; end; +function GradientBoostingClassifier.ToString: string; +begin + var s := + $'GradientBoostingClassifier(' + + $'n={fNEstimators}, ' + + $'lr={fLearningRate}, ' + + $'maxDepth={fMaxDepth}'; + + if fSubsample <> 1.0 then + s += $', subs={fSubsample}'; + + if fEarlyStoppingPatience > 0 then + s += $', earlyStop={fEarlyStoppingPatience}'; + + s += ')'; + + Result := s; +end; + function GradientBoostingClassifier.Clone: IModel; begin - var c := new GradientBoostingClassifier( + var model := new GradientBoostingClassifier( fNEstimators, fLearningRate, fMaxDepth, @@ -3894,77 +4283,47 @@ begin ); // --- fitted state - c.fFitted := fFitted; - c.fFeatureCount := fFeatureCount; - - // --- class mapping (deep) - c.fClassCount := fClassCount; + model.fFitted := fFitted; + model.fFeatureCount := fFeatureCount; + model.fClassCount := fClassCount; + // --- classes if fClasses <> nil then begin - SetLength(c.fClasses, Length(fClasses)); + SetLength(model.fClasses, Length(fClasses)); for var i := 0 to Length(fClasses) - 1 do - c.fClasses[i] := fClasses[i]; - end - else - c.fClasses := nil; - - c.fClassIndex := new Dictionary; - if fClassIndex <> nil then - foreach var kv in fClassIndex do - c.fClassIndex[kv.Key] := kv.Value; - - // --- init logits (deep) - if fInitLogits <> nil then - begin - SetLength(c.fInitLogits, Length(fInitLogits)); - for var i := 0 to Length(fInitLogits) - 1 do - c.fInitLogits[i] := fInitLogits[i]; - end - else - c.fInitLogits := nil; - - // --- best/iters - c.fBestIteration := fBestIteration; - c.fBestValLoss := fBestValLoss; - - // --- estimators (deep): List - c.fEstimators.Clear; - foreach var arr in fEstimators do - begin - var k := Length(arr); - var arr2 := new DecisionTreeRegressor[k]; - - for var cls := 0 to k - 1 do - arr2[cls] := DecisionTreeRegressor(arr[cls].Clone); - - c.fEstimators.Add(arr2); + model.fClasses[i] := fClasses[i]; end; - // --- histories - c.fTrainLossHistory.Clear; - foreach var v in fTrainLossHistory do - c.fTrainLossHistory.Add(v); - - c.fValLossHistory.Clear; - foreach var v in fValLossHistory do - c.fValLossHistory.Add(v); - - c.fOOBLossHistory.Clear; - foreach var v in fOOBLossHistory do - c.fOOBLossHistory.Add(v); - - // --- feature importances (deep) - if fFeatureImportances <> nil then + if fClassIndex <> nil then begin - c.fFeatureImportances := new Vector(fFeatureImportances.Length); - for var i := 0 to fFeatureImportances.Length - 1 do - c.fFeatureImportances[i] := fFeatureImportances[i]; - end - else - c.fFeatureImportances := nil; + model.fClassIndex := new Dictionary; + foreach var kv in fClassIndex do + model.fClassIndex.Add(kv.Key, kv.Value); + end; - Result := c; + // --- estimators (deep copy) + foreach var trees in fEstimators do + begin + var newTrees := new DecisionTreeRegressor[Length(trees)]; + + for var cls := 0 to Length(trees) - 1 do + newTrees[cls] := trees[cls].Clone as DecisionTreeRegressor; + + model.fEstimators.Add(newTrees); + end; + + // --- history + foreach var v in fTrainLossHistory do + model.fTrainLossHistory.Add(v); + + foreach var v in fValLossHistory do + model.fValLossHistory.Add(v); + + model.fBestIteration := fBestIteration; + model.fBestValLoss := fBestValLoss; + + Result := model; end; function GradientBoostingClassifier.Fit(X: Matrix; y: Vector): IModel;