ML - рефактринг, исправление неточностей
This commit is contained in:
parent
0f0de28a00
commit
b9aa6ae65d
|
|
@ -15,7 +15,7 @@ internal static class RevisionClass
|
|||
public const string Major = "3";
|
||||
public const string Minor = "11";
|
||||
public const string Build = "1";
|
||||
public const string Revision = "3801";
|
||||
public const string Revision = "3804";
|
||||
|
||||
public const string MainVersion = Major + "." + Minor;
|
||||
public const string FullVersion = Major + "." + Minor + "." + Build + "." + Revision;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
%MINOR%=11
|
||||
%REVISION%=3801
|
||||
%COREVERSION%=1
|
||||
%REVISION%=3804
|
||||
%MINOR%=11
|
||||
%MAJOR%=3
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.1.3801
|
||||
3.11.1.3804
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
!define VERSION '3.11.1.3801'
|
||||
!define VERSION '3.11.1.3804'
|
||||
|
|
|
|||
|
|
@ -2,42 +2,14 @@
|
|||
|
||||
interface
|
||||
|
||||
uses DataFrameABC;
|
||||
uses LinearAlgebraML;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// Используется при обучении моделей и визуализации.
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Используется при обучении моделей и визуализации
|
||||
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
||||
|
||||
/// Преобразует строковые метки классов в целочисленные индексы
|
||||
/// с использованием заранее заданного массива classes (mapping).
|
||||
/// classes должен быть получен из EncodeLabels.
|
||||
/// Если встречается неизвестная метка — выбрасывается исключение.
|
||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||
|
||||
/// Преобразует целочисленные индексы классов обратно в строковые метки.
|
||||
/// Массив classes задаёт соответствие: classes[i] — имя класса с индексом i.
|
||||
/// Используется для получения текстовых предсказаний моделей.
|
||||
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
||||
|
||||
/// Возвращает список уникальных меток классов.
|
||||
/// Порядок соответствует первому появлению значений во входном массиве.
|
||||
/// Используется для определения множества классов в задаче классификации.
|
||||
function UniqueLabels(labels: array of string): array of string;
|
||||
// Здесь пока только методы расширения поэтому секция interface - пуста
|
||||
|
||||
implementation
|
||||
|
||||
uses MLUtilsABC;
|
||||
uses DataFrameABC;
|
||||
uses MLExceptions;
|
||||
uses LinearAlgebraML;
|
||||
|
||||
const
|
||||
ER_TO_MATRIX_NO_COLUMNS =
|
||||
|
|
@ -55,66 +27,7 @@ const
|
|||
'Неподдерживаемый тип столбца "{0}" для EncodeLabels!!Unsupported column type "{0}" for EncodeLabels';
|
||||
ER_UNKNOWN_CLASS_IN_TRANSFORM =
|
||||
'Неизвестное значение класса "{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 EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
var classList := new List<string>;
|
||||
var map := new Dictionary<string, integer>;
|
||||
|
||||
// собираем классы в порядке первого появления
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
if not map.ContainsKey(lbl) then
|
||||
begin
|
||||
map[lbl] := classList.Count;
|
||||
classList.Add(lbl);
|
||||
end;
|
||||
end;
|
||||
|
||||
// кодируем
|
||||
var res := new integer[labels.Length];
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
res[i] := map[labels[i]];
|
||||
|
||||
classes := classList.ToArray;
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
var map := new Dictionary<string, integer>;
|
||||
for var i := 0 to classes.Length - 1 do
|
||||
map[classes[i]] := i;
|
||||
|
||||
var res := new integer[labels.Length];
|
||||
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
|
||||
if not map.ContainsKey(lbl) then
|
||||
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
|
||||
|
||||
res[i] := map[lbl];
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
begin
|
||||
var classes: array of string;
|
||||
Result := EncodeLabels(labels, classes);
|
||||
end;
|
||||
|
||||
function ToMatrix(Self: DataFrame; colNames: array of string): Matrix; extensionmethod;
|
||||
begin
|
||||
|
|
@ -162,29 +75,6 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// Helper
|
||||
function EncodeLabelsIntHelper(labels: array of integer): array of integer;
|
||||
begin
|
||||
var classList := new List<integer>;
|
||||
var map := new Dictionary<integer, integer>;
|
||||
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
if not map.ContainsKey(lbl) then
|
||||
begin
|
||||
map[lbl] := classList.Count;
|
||||
classList.Add(lbl);
|
||||
end;
|
||||
end;
|
||||
|
||||
var y := new integer[labels.Length];
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
y[i] := map[labels[i]];
|
||||
|
||||
Result := y;
|
||||
end;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
|
|
@ -213,40 +103,15 @@ begin
|
|||
if Self.GetColumnType(target) = ColumnType.ctInt then
|
||||
begin
|
||||
var labels := Self.GetIntColumn(target).ToArray;
|
||||
Result := EncodeLabelsIntHelper(labels);
|
||||
var classes: array of integer;
|
||||
Result := EncodeLabelsInt(labels,classes);
|
||||
exit;
|
||||
end;
|
||||
|
||||
ArgumentError(ER_ENCODELABELS_UNSUPPORTED_TYPE, target);
|
||||
end;
|
||||
|
||||
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
var classList := new List<integer>;
|
||||
var map := new Dictionary<integer, integer>;
|
||||
|
||||
// собираем уникальные значения в порядке первого появления
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
if not map.ContainsKey(lbl) then
|
||||
begin
|
||||
map[lbl] := classList.Count;
|
||||
classList.Add(lbl);
|
||||
end;
|
||||
end;
|
||||
|
||||
// кодируем
|
||||
var res := new integer[labels.Length];
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
res[i] := map[labels[i]];
|
||||
|
||||
classes := classList.ToArray;
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
/// Кодирует значения категориального столбца DataFrame в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
|
|
@ -436,26 +301,6 @@ begin
|
|||
Result := EncodeLabelsInt(labels, classes);
|
||||
end;
|
||||
|
||||
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
||||
begin
|
||||
var res := new string[y.Length];
|
||||
|
||||
for var i := 0 to y.Length - 1 do
|
||||
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;
|
||||
end;
|
||||
|
||||
function UniqueLabels(labels: array of string): array of string;
|
||||
begin
|
||||
Result := labels.Distinct.ToArray;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
@ -2073,7 +2073,7 @@ begin
|
|||
names[i] := info.Name;
|
||||
types[i] := info.ColType;
|
||||
|
||||
// 🔥 берем из старой schema
|
||||
// берем из старой schema
|
||||
if (fSchema <> nil) and (i < fSchema.ColumnCount) then
|
||||
cats[i] := fSchema.CategoricalFlags[i]
|
||||
else
|
||||
|
|
@ -2970,7 +2970,7 @@ begin
|
|||
Error(ER_UNKNOWN_COLUMN_TYPE);
|
||||
end;
|
||||
|
||||
// 🔥 КЛЮЧЕВОЕ: перенос schema
|
||||
// КЛЮЧЕВОЕ: перенос schema
|
||||
var cats := new boolean[ColumnCount];
|
||||
for var i := 0 to ColumnCount - 1 do
|
||||
cats[i] := fSchema.CategoricalFlags[i];
|
||||
|
|
@ -3070,7 +3070,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// 🔥 КЛЮЧЕВОЕ: пересобираем schema
|
||||
// КЛЮЧЕВОЕ: пересобираем schema
|
||||
var n := ColumnCount;
|
||||
var names := new string[n];
|
||||
var types := new ColumnType[n];
|
||||
|
|
@ -3622,7 +3622,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// 🔥 КЛЮЧЕВОЕ: schema НЕ меняется
|
||||
// КЛЮЧЕВОЕ: schema НЕ меняется
|
||||
res.SetSchema(fSchema);
|
||||
|
||||
Result := res;
|
||||
|
|
@ -3677,7 +3677,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// 🔥 schema просто копируется
|
||||
// schema просто копируется
|
||||
res.SetSchema(fSchema);
|
||||
|
||||
Result := res;
|
||||
|
|
@ -3732,7 +3732,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// 🔥 schema НЕ меняется
|
||||
// schema НЕ меняется
|
||||
res.SetSchema(fSchema);
|
||||
|
||||
Result := res;
|
||||
|
|
@ -3787,7 +3787,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// 🔥 schema просто копируется
|
||||
// schema просто копируется
|
||||
res.SetSchema(fSchema);
|
||||
|
||||
Result := res;
|
||||
|
|
@ -4312,7 +4312,7 @@ begin
|
|||
res.AddColumnView(col);
|
||||
end;
|
||||
|
||||
// 🔥 пересобираем schema (меняются ТИПЫ)
|
||||
// пересобираем schema (меняются ТИПЫ)
|
||||
var n := fSchema.ColumnCount;
|
||||
|
||||
var namesArr := new string[n];
|
||||
|
|
@ -4743,7 +4743,7 @@ begin
|
|||
end;
|
||||
|
||||
names.Add(colName);
|
||||
cats.Add(true); // 🔥 ключи — categorical
|
||||
cats.Add(true); // ключи — categorical
|
||||
end;
|
||||
|
||||
res.AddIntColumn('count', counts, nil);
|
||||
|
|
@ -4752,7 +4752,7 @@ begin
|
|||
cats.Add(false);
|
||||
end;
|
||||
|
||||
// 🔥 устанавливаем schema
|
||||
// устанавливаем schema
|
||||
res.SetSchema(new DataFrameSchema(
|
||||
names.ToArray,
|
||||
types.ToArray,
|
||||
|
|
@ -5322,7 +5322,7 @@ begin
|
|||
|
||||
schemaNames.Add('Feature');
|
||||
schemaTypes.Add(ctStr);
|
||||
schemaCats.Add(true); // 🔥 categorical
|
||||
schemaCats.Add(true); // categorical
|
||||
|
||||
// 2️⃣ корреляции
|
||||
for var j := 0 to n - 1 do
|
||||
|
|
@ -5342,7 +5342,7 @@ begin
|
|||
schemaCats.Add(false);
|
||||
end;
|
||||
|
||||
// 🔥 schema
|
||||
// schema
|
||||
res.SetSchema(new DataFrameSchema(
|
||||
schemaNames.ToArray,
|
||||
schemaTypes.ToArray,
|
||||
|
|
@ -6324,7 +6324,7 @@ begin
|
|||
else
|
||||
types[j] := ctStr;
|
||||
|
||||
// 🔥 вот сюда переносим логику categorical
|
||||
// вот сюда переносим логику categorical
|
||||
cats[j] := ((catSet <> nil) and (headers[j] in catSet)) or autoCat[j];
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ type
|
|||
ColumnInfo = auto class
|
||||
Name: string;
|
||||
ColType: ColumnType;
|
||||
//IsCategorical: boolean; - мы убрали это отсюда - только Schema - источник истины!
|
||||
//IsCategorical - только в Schema!
|
||||
end;
|
||||
|
||||
DataFrameCursor = class;
|
||||
|
|
@ -84,15 +84,11 @@ type
|
|||
function TryGetNumericValue(i: integer; var value: real): boolean; virtual; abstract;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; virtual; abstract;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
//procedure AppendInvalid; virtual; abstract;
|
||||
/// Добавляет значение из курсора в указанной позиции
|
||||
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); virtual; abstract;
|
||||
end;
|
||||
|
||||
/// Столбец целых чисел
|
||||
IntColumn = class(Column)
|
||||
// ⚠️ Data и IsValid считаются immutable после создания
|
||||
// Data и IsValid считаются immutable после создания
|
||||
Data: array of integer; // Данные столбца
|
||||
IsValid: array of boolean; // Флаги валидности (может быть nil)
|
||||
public
|
||||
|
|
@ -103,17 +99,6 @@ type
|
|||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
/// ⚠ УСТАРЕВШИЙ МЕТОД.
|
||||
/// Не должен использоваться в новом коде.
|
||||
/// Сохраняется только для обратной совместимости.
|
||||
//procedure AppendInvalid; override;
|
||||
/// Добавляет значение из курсора в указанной позиции
|
||||
/// ⚠ УСТАРЕВШИЙ МЕТОД.
|
||||
/// Использует неэффективное поэлементное добавление (O(n²)).
|
||||
/// Не должен использоваться в новом коде.
|
||||
/// Сохраняется только для обратной совместимости.
|
||||
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); override;
|
||||
end;
|
||||
|
||||
/// Столбец вещественных чисел
|
||||
|
|
@ -128,10 +113,6 @@ type
|
|||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
//procedure AppendInvalid; override;
|
||||
/// Добавляет значение из курсора в указанной позиции
|
||||
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); override;
|
||||
end;
|
||||
|
||||
/// Столбец строк
|
||||
|
|
@ -164,10 +145,6 @@ type
|
|||
function TryGetNumericValue(i: integer; var value: real): boolean; override;
|
||||
/// Возвращает количество строк в столбце
|
||||
function RowCount: integer; override := Data.Length;
|
||||
/// Добавляет невалидное (NA) значение в конец столбца
|
||||
//procedure AppendInvalid; override;
|
||||
/// Добавляет значение из курсора в указанной позиции
|
||||
//procedure AppendFromCursor(cur: DataFrameCursor; colIndex: integer); override;
|
||||
end;
|
||||
|
||||
// Accessor типы для курсора
|
||||
|
|
|
|||
|
|
@ -195,6 +195,8 @@ type
|
|||
|
||||
function GetRow(i: integer): Vector;
|
||||
function GetCol(j: integer): Vector;
|
||||
|
||||
function TakeRows(indices: array of integer): Matrix;
|
||||
|
||||
// ---------- Статические методы ----------
|
||||
/// Возвращает единичную матрицу размера n
|
||||
|
|
@ -931,7 +933,6 @@ begin
|
|||
end;
|
||||
|
||||
|
||||
|
||||
static function Matrix.operator *(A: Matrix; x: Vector): Vector;
|
||||
begin
|
||||
CheckVecSize(A, x);
|
||||
|
|
@ -945,7 +946,7 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
static function Matrix.operator *(A, B: Matrix): Matrix;
|
||||
{static function Matrix.operator *(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckMulSize(A, B);
|
||||
Result := new Matrix(A.RowCount, B.ColCount);
|
||||
|
|
@ -957,6 +958,30 @@ begin
|
|||
for var j := 0 to B.ColCount - 1 do
|
||||
Result.fdata[i, j] += aik * B.fdata[k, j];
|
||||
end;
|
||||
end;}
|
||||
|
||||
static function Matrix.operator *(A, B: Matrix): Matrix;
|
||||
begin
|
||||
CheckMulSize(A, B);
|
||||
|
||||
var m := A.RowCount;
|
||||
var p := A.ColCount;
|
||||
var n := B.ColCount;
|
||||
|
||||
var BT := B.Transpose;
|
||||
|
||||
Result := new Matrix(m, n);
|
||||
|
||||
for var i := 0 to m - 1 do
|
||||
for var j := 0 to n - 1 do
|
||||
begin
|
||||
var sum := 0.0;
|
||||
|
||||
for var k := 0 to p - 1 do
|
||||
sum += A.fdata[i, k] * BT.fdata[j, k];
|
||||
|
||||
Result.fdata[i, j] := sum;
|
||||
end;
|
||||
end;
|
||||
|
||||
static function Matrix.operator +=(A, B: Matrix): Matrix;
|
||||
|
|
@ -1002,6 +1027,61 @@ begin
|
|||
Result[j] := fdata[i, j];
|
||||
end;
|
||||
|
||||
function Matrix.TakeRows(indices: array of integer): Matrix;
|
||||
begin
|
||||
var n := indices.Length;
|
||||
var p := ColCount;
|
||||
|
||||
var res := new Matrix(n, p);
|
||||
|
||||
var src := Data;
|
||||
var dst := res.Data;
|
||||
|
||||
// --- fast path: полный срез [0..n-1]
|
||||
var isFull := n = RowCount;
|
||||
|
||||
if isFull then
|
||||
begin
|
||||
for var i := 0 to n - 1 do
|
||||
if indices[i] <> i then
|
||||
begin
|
||||
isFull := False;
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
|
||||
if isFull then
|
||||
begin
|
||||
System.Array.Copy(src, 0, dst, 0, n * p);
|
||||
Result := res;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// --- обычный block-copy
|
||||
var i := 0;
|
||||
var dstOffset := 0;
|
||||
|
||||
while i < n do
|
||||
begin
|
||||
var start := indices[i];
|
||||
var len := 1;
|
||||
|
||||
while (i + len < n) and (indices[i + len] = start + len) do
|
||||
len += 1;
|
||||
|
||||
System.Array.Copy(
|
||||
src, start * p,
|
||||
dst, dstOffset,
|
||||
len * p
|
||||
);
|
||||
|
||||
dstOffset += len * p;
|
||||
i += len;
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function Matrix.GetCol(j: integer): Vector;
|
||||
begin
|
||||
if (j < 0) or (j >= ColCount) then
|
||||
|
|
|
|||
|
|
@ -125,6 +125,6 @@ begin
|
|||
Result := MLUtilsABC.LabelsToInts(y);
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string): array of integer := DataAdapters.EncodeLabels(labels);
|
||||
function EncodeLabels(labels: array of string): array of integer := MLUtilsABC.EncodeLabels(labels);
|
||||
|
||||
end.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -56,11 +56,6 @@ type
|
|||
fTask: TaskKind;
|
||||
fTarget: string;
|
||||
|
||||
// fDataSteps: List<IPreprocessor>;
|
||||
// fMatrixSteps: List<ITransformer>;
|
||||
// fFeatures: array of string;
|
||||
// fFinalFeatures: array of string;
|
||||
// fFitted: boolean;
|
||||
protected
|
||||
procedure ValidateSchema(df: DataFrame); override;
|
||||
public
|
||||
|
|
@ -75,88 +70,6 @@ type
|
|||
/// Запрещено добавлять шаги после вызова Fit/FitTransform.
|
||||
function Add(step: IPipelineStep): DataPipeline;
|
||||
|
||||
{ // Примеры использования препроцессоров и моделей в DataPipeline.
|
||||
// Препроцессоры работают на уровне DataFrame.
|
||||
// После них Pipeline автоматически преобразует данные в Matrix/Vector.
|
||||
// Далее выполняются матричные трансформеры и модель.
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Пример 1. Классификация с категориальным целевым признаком
|
||||
var pipe :=
|
||||
DataPipeline.Build(
|
||||
'species',
|
||||
['length','width'],
|
||||
new LabelEncoder('species'), // DataFrame-препроцессор (строки → числа)
|
||||
new StandardScaler, // матричный трансформер
|
||||
new LogisticRegression // модель
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Пример 2. Регрессия с пропущенными значениями и категориальным признаком
|
||||
var pipe :=
|
||||
DataPipeline.Build(
|
||||
'price',
|
||||
['area','floor','district'],
|
||||
new Imputer('area'), // DataFrame-препроцессор (заполнение NA)
|
||||
new OneHotEncoder('district'), // DataFrame-препроцессор
|
||||
new RandomForestRegressor // модель
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Пример 3. Сложный pipeline
|
||||
var pipe :=
|
||||
DataPipeline.Build(
|
||||
'target',
|
||||
['f1','f2','f3','category'],
|
||||
new Imputer('f2'), // DataFrame-препроцессор
|
||||
new OneHotEncoder('category'), // DataFrame-препроцессор
|
||||
new StandardScaler, // матричный трансформер
|
||||
new PCATransformer(2), // матричный трансформер
|
||||
new GradientBoostingRegressor // модель
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Пример 4. Классификация без Pipeline
|
||||
var df := Datasets.Flowers;
|
||||
|
||||
// --- Encode target (DataFrame уровень)
|
||||
df := df.SetCategorical(['species']);
|
||||
|
||||
var labels := df.EncodeLabels('species');
|
||||
|
||||
// --- X, y
|
||||
var X := df.ToMatrix(['length','width']);
|
||||
var y := new Vector(labels);
|
||||
|
||||
// --- Matrix уровень
|
||||
var scaler := new StandardScaler;
|
||||
X := scaler.FitTransform(X);
|
||||
|
||||
// --- Модель
|
||||
var model := new LogisticRegression;
|
||||
model.Fit(X, y);
|
||||
|
||||
// Pipeline.Build используется, когда данные уже представлены
|
||||
// в виде числовой матрицы признаков X и вектора целевой переменной y.
|
||||
// В этом случае DataFrame и препроцессоры уровня таблицы не требуются.
|
||||
//
|
||||
// Типичные ситуации:
|
||||
// • экспериментирование с ML-алгоритмами
|
||||
// • сравнение моделей
|
||||
// • кросс-валидация
|
||||
// • подбор гиперпараметров
|
||||
// • тестирование моделей
|
||||
|
||||
// Пример 5. Pipeline на матричном уровне
|
||||
var pipe :=
|
||||
Pipeline.Build(
|
||||
new StandardScaler,
|
||||
new PCATransformer(2),
|
||||
new LogisticRegression
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// Строит конвейер из шагов обработки данных и модели.
|
||||
///
|
||||
/// Используется в задачах с учителем (с target).
|
||||
|
|
|
|||
|
|
@ -18,6 +18,43 @@ function LabelsToInts(y: Vector): array of integer;
|
|||
/// Преобразует массив целых меток в Vector.
|
||||
function IntsToLabels(a: array of integer): Vector;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// Используется при обучении моделей и визуализации.
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
|
||||
/// Кодирует строковые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Используется при обучении моделей и визуализации
|
||||
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
||||
|
||||
/// Кодирует целые метки классов в целочисленные индексы.
|
||||
/// Каждому уникальному значению присваивается номер 0,1,2,...
|
||||
/// Порядок кодирования соответствует порядку первого появления меток.
|
||||
/// В параметр classes возвращается массив уникальных значений в порядке кодирования.
|
||||
/// Используется при обучении моделей и визуализации
|
||||
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
|
||||
|
||||
/// Преобразует строковые метки классов в целочисленные индексы
|
||||
/// с использованием заранее заданного массива classes (mapping).
|
||||
/// classes должен быть получен из EncodeLabels.
|
||||
/// Если встречается неизвестная метка — выбрасывается исключение.
|
||||
/// Используется для применения кодирования к тестовым данным (Transform).
|
||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||
|
||||
/// Преобразует целочисленные индексы классов обратно в строковые метки.
|
||||
/// Массив classes задаёт соответствие: classes[i] — имя класса с индексом i.
|
||||
/// Используется для получения текстовых предсказаний моделей.
|
||||
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
||||
|
||||
/// Возвращает список уникальных меток классов.
|
||||
/// Порядок соответствует первому появлению значений во входном массиве.
|
||||
/// Используется для определения множества классов в задаче классификации.
|
||||
function UniqueLabels(labels: array of string): array of string;
|
||||
|
||||
implementation
|
||||
|
||||
uses MLExceptions;
|
||||
|
|
@ -27,6 +64,11 @@ const
|
|||
'y не может быть nil!!y cannot be nil';
|
||||
ER_LABELS_ARRAY_NULL =
|
||||
'labels не может быть nil!!labels cannot be nil';
|
||||
ER_UNKNOWN_CLASS_IN_TRANSFORM =
|
||||
'Неизвестное значение класса "{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;
|
||||
begin
|
||||
|
|
@ -47,4 +89,112 @@ begin
|
|||
Result := new Vector(a);
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string; var classes: array of string): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
var classList := new List<string>;
|
||||
var map := new Dictionary<string, integer>;
|
||||
|
||||
// собираем классы в порядке первого появления
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
if not map.ContainsKey(lbl) then
|
||||
begin
|
||||
map[lbl] := classList.Count;
|
||||
classList.Add(lbl);
|
||||
end;
|
||||
end;
|
||||
|
||||
// кодируем
|
||||
var res := new integer[labels.Length];
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
res[i] := map[labels[i]];
|
||||
|
||||
classes := classList.ToArray;
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function TransformLabels(labels: array of string; classes: array of string): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
var map := new Dictionary<string, integer>;
|
||||
for var i := 0 to classes.Length - 1 do
|
||||
map[classes[i]] := i;
|
||||
|
||||
var res := new integer[labels.Length];
|
||||
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
|
||||
if not map.ContainsKey(lbl) then
|
||||
Error(ER_UNKNOWN_CLASS_IN_TRANSFORM, lbl);
|
||||
|
||||
res[i] := map[lbl];
|
||||
end;
|
||||
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function EncodeLabels(labels: array of string): array of integer;
|
||||
begin
|
||||
var classes: array of string;
|
||||
Result := EncodeLabels(labels, classes);
|
||||
end;
|
||||
|
||||
function EncodeLabelsInt(labels: array of integer; var classes: array of integer): array of integer;
|
||||
begin
|
||||
if labels = nil then
|
||||
ArgumentNullError(ER_ARG_NULL, 'labels');
|
||||
|
||||
var classList := new List<integer>;
|
||||
var map := new Dictionary<integer, integer>;
|
||||
|
||||
// собираем уникальные значения в порядке первого появления
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
begin
|
||||
var lbl := labels[i];
|
||||
if not map.ContainsKey(lbl) then
|
||||
begin
|
||||
map[lbl] := classList.Count;
|
||||
classList.Add(lbl);
|
||||
end;
|
||||
end;
|
||||
|
||||
// кодируем
|
||||
var res := new integer[labels.Length];
|
||||
for var i := 0 to labels.Length - 1 do
|
||||
res[i] := map[labels[i]];
|
||||
|
||||
classes := classList.ToArray;
|
||||
Result := res;
|
||||
end;
|
||||
|
||||
function DecodeLabels(y: array of integer; classes: array of string): array of string;
|
||||
begin
|
||||
var res := new string[y.Length];
|
||||
|
||||
for var i := 0 to y.Length - 1 do
|
||||
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;
|
||||
end;
|
||||
|
||||
function UniqueLabels(labels: array of string): array of string;
|
||||
begin
|
||||
Result := labels.Distinct.ToArray;
|
||||
end;
|
||||
|
||||
end.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -248,22 +248,50 @@ begin
|
|||
NotFittedError(ER_FIT_NOT_CALLED);
|
||||
|
||||
var idx := df.Schema.IndexOf(col);
|
||||
var n := df.RowCount;
|
||||
|
||||
Result := df.ReplaceColumnInt(
|
||||
col,
|
||||
c ->
|
||||
if not c.IsValid(idx) then
|
||||
Error(ER_LABELENCODER_NA) // будет поймано как NA
|
||||
else
|
||||
var data := new integer[n];
|
||||
var valid: array of boolean := nil;
|
||||
|
||||
var cur := df.GetCursor;
|
||||
var row := 0;
|
||||
while cur.MoveNext do
|
||||
begin
|
||||
if not cur.IsValid(idx) then
|
||||
begin
|
||||
if valid = nil then
|
||||
begin
|
||||
var s := c.Str(idx);
|
||||
if not mapping.ContainsKey(s) then
|
||||
Error(ER_LABELENCODER_UNSEEN_CATEGORY, s);
|
||||
Result := mapping[s];
|
||||
end
|
||||
);
|
||||
|
||||
Result := Result.SetCategorical([col]);
|
||||
valid := new boolean[n];
|
||||
for var j := 0 to row - 1 do
|
||||
valid[j] := true;
|
||||
end;
|
||||
valid[row] := false;
|
||||
data[row] := 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
var s := cur.Str(idx);
|
||||
|
||||
if not mapping.ContainsKey(s) then
|
||||
Error(ER_LABELENCODER_UNSEEN_CATEGORY, s);
|
||||
|
||||
data[row] := mapping[s];
|
||||
if valid <> nil then
|
||||
valid[row] := true;
|
||||
end;
|
||||
|
||||
row += 1;
|
||||
end;
|
||||
|
||||
var res := new DataFrame;
|
||||
|
||||
foreach var src in df.GetColumns do
|
||||
if src.Info.Name <> col then
|
||||
res.AddColumnView(src)
|
||||
else
|
||||
res.AddIntColumn(col, data, valid);
|
||||
|
||||
Result := res.SetCategorical([col]);
|
||||
end;
|
||||
|
||||
function LabelEncoder.FitTransform(df: DataFrame): DataFrame;
|
||||
|
|
|
|||
|
|
@ -311,6 +311,19 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
// --- 1.1 ПРОВЕРКА НА МИНИМАЛЬНЫЙ РАЗМЕР КЛАССА
|
||||
foreach var pair in classMap do
|
||||
begin
|
||||
var cls := pair.Key;
|
||||
var cnt := pair.Value.Count;
|
||||
|
||||
if cnt < k then
|
||||
ArgumentError(
|
||||
'StratifiedKFold: класс %d содержит %d объектов, что меньше числа фолдов (%d)',
|
||||
cls, cnt, k
|
||||
);
|
||||
end;
|
||||
|
||||
// --- 2. Контейнеры фолдов
|
||||
var folds := new List<integer>[k];
|
||||
for var f := 0 to k - 1 do
|
||||
|
|
|
|||
Loading…
Reference in a new issue