Перейти к содержимому

DecisionTreeClassifier

DecisionTreeClassifier — это модель классификации, которая принимает решение как последовательность простых вопросов.

Дерево решений похоже на обычную инструкцию:

  • если условие выполнено, идём по одной ветке;
  • если не выполнено, идём по другой;
  • в конце попадаем в лист дерева, где записан ответ.

Дерево решений не только выдаёт класс, но и показывает последовательность проверок, которые привели к ответу. Таким образом, дерево решений позволяет интерпретировать (объяснять) ответ модели, что важно в отдельных сценариях. Например, если клиент просит, почему алгоритм не одобрил ему кредит, можно привести путь в дереве решений, который привел к неодобрению кредита.

Дерево решений подбирает вопросы к признакам так, чтобы лучше разделить объекты разных классов.

Например, в задаче одобрения кредита модель может задавать вопросы:

  • возраст клиента меньше или равен некоторому порогу?
  • зарплата меньше или равна некоторому порогу?

Каждый вопрос делит данные на две части. После нескольких таких делений дерево приходит к одному из классов: Одобрить или Отказать.

В примере есть небольшая таблица клиентов. По возрасту и зарплате модель учится предсказывать, надо ли одобрить кредит.

uses MLABC;
begin
var df := DataFrame.FromCsvText('''
Возраст,Зарплата,Одобрить кредит
17,25,Отказать
64,80,Одобрить
18,22,Отказать
20,36,Одобрить
38,37,Отказать
49,59,Одобрить
55,74,Одобрить
25,70,Отказать
29,33,Отказать
31,102,Одобрить
33,88,Отказать
''');
var featureNames := ['Возраст', 'Зарплата'];
var X := df.ToMatrix(featureNames);
var target := df.EncodeTarget('Одобрить кредит');
var model := new DecisionTreeClassifier;
model.Fit(X, target.Labels);
var view := model.Tree(featureNames, target.ClassNames);
// Вывод дерева решений
Println(view);
// Новый клиент
var example := [20.0, 100.0];
var pred := model.PredictOne(example);
Println('Возраст:', example[0]);
Println('Зарплата:', example[1]);
Println('Одобрить кредит:', target.ClassNames[pred]);
end.
Возраст ≤ 43.5?
├── да: Зарплата ≤ 95?
│ ├── да: Возраст ≤ 22.5?
│ │ ├── да: Возраст ≤ 19?
│ │ │ ├── да: Отказать
│ │ │ └── нет: Одобрить
│ │ └── нет: Отказать
│ └── нет: Одобрить
└── нет: Одобрить

Дерево принимает решение, последовательно проверяя условия от верхней вершины к одной из конечных вершин.

Для нового клиента:

Возраст: 20
Зарплата: 100

путь по дереву будет таким:

Возраст ≤ 43.5? да, поскольку 20 ≤ 43.5
Зарплата ≤ 95? нет, поскольку 100 > 95

При ответе «нет» на второе условие мы попадаем в лист:

Одобрить

Это и есть ответ модели.

В большинстве учебных примеров можно использовать настройки по умолчанию:

var model := new DecisionTreeClassifier;

Иногда полезно ограничить сложность дерева:

  • maxDepth — максимальная глубина дерева;
  • minSamplesSplit — сколько объектов должно быть в узле, чтобы его можно было делить дальше;
  • minSamplesLeaf — минимальное число объектов в листе.

Например:

var model := new DecisionTreeClassifier(maxDepth := 3);

Если дерево слишком глубокое, оно может запомнить обучающие данные слишком подробно и хуже работать на новых примерах.

Дереву решений обычно не нужен StandardScaler.

Модель сравнивает признаки с порогами: например, Возраст ≤ 43.5 или Зарплата ≤ 95. Поэтому ей не так важно, что один признак измеряется годами, а другой — тысячами рублей.

  • Легко объяснить: модель выглядит как набор правил.
  • Можно вывести дерево и посмотреть, как принимается решение.
  • Обычно не требует масштабирования признаков.
  • Может строить нелинейные границы между классами.
  • Одно дерево легко переобучается.
  • Небольшое изменение данных может изменить структуру дерева.
  • Для сложных задач часто лучше работают ансамбли деревьев: RandomForestClassifier и GradientBoostingClassifier.