Нахожу в тесте вопросы, которые в принципе не освещаются в лекции. Нужно гуглить на других ресурсах, чтобы решить тест, или же он всё же должен испытывать знания, полученные в ходе лекции? |
Машинное обучение
2.3. Дерево решений
Идея, лежащая в основе алгоритма обучения дерева решений, состоит в
рекурсивном разбиении пространства признаков с помощью простых
правил на непересекающиеся области. Полученная в результате обучения модель представляет собой дерево, где каждому внутреннему узлу
соответствует некоторая область пространства признаков и правило ее
разбиения на подобласти (, если -й признак количественный и
,
если признак категориальный). Каждой листовой вершине также
соответствует область пространства
и приписано константное значение,
которому принимается равной функция
в данной области.
Алгоритм обучения дерева решений, осуществления с его помощью предсказаний, сохранения/загрузки модели, а также дополнительный функционал по работе с деревьями решений реализованы в библиотеке OpenCV в классе CvDTree. Остановимся подробнее на описании данного класса.
Для обучения дерева решений служит метод train.
bool train( const Mat< trainData, int tflag, const Mat< responses, const Mat< varIdx=Mat(), const Mat< sampleIdx=Mat(), const Mat< varType=Mat(), const Mat< missingDataMask=Mat(), CvDTreeParams params=CvDTreeParams() );
Рассмотрим параметры метода.
-
trainData – матрица, содержащая векторы
из обучающей выборки. Матрица должна иметь тип CV_32F и размеры
или
. В отличие от метода CvSVM::train здесь возможен вариант как с построчным хранением векторов
, так и с их хранением по столбцам.
- tflag – флаг, определяющий по строкам (tflag=CV_ROW_SAMPLE) хранятся вектора выборки в матрице trainData или по столбцам (tflag=CV_COL_SAMPLE).
-
responses – матрица-вектор, содержащая значения целевой
переменной
для прецедентов обучающей выборки. Данная матрица должна иметь тип CV_32S или CV_32Fи размеры
или
.
-
varIdx – матрица-вектор, содержащая либо номера признаков
(тип матрицы CV_32S), которые необходимо использовать при
обучении, либо маску (тип матрицы CV_8U) размера
, где единицами отмечены используемые признаки, нулями – игнорируемые. По умолчанию (varIdx=Mat()) используются все признаки.
- sampleIdx – матрица-вектор, имеющая такой же формат, как и varIdx, но отвечающая за прецеденты выборки, которые необходимо использовать для обучения. По умолчанию используются все имеющиеся прецеденты.
-
varType – матрица-вектор, содержащая информацию о типах
переменных. Деревья решений поддерживают использование как
количественных (CV_VAR_ORDERED), так и категориальных
(CV_VAR_CATEGORICAL) признаков. Данная матрица должна
иметь тип CV_8U и размеры
или
, где последний элемент определяет тип целевой переменной. По умолчанию (varType=Mat()) целевая переменная считается категориальной, т.е. решается задача классификации, а остальные количественными.
-
missingDataMask – матрица, содержащая маску пропущенных
значений, где единицами отмечены неизвестные значения
признаков, нулями – известные. В отличие от машины опорных
векторов, деревья решений позволяют работать с пропущенными
значениями в признаковых описаниях объектов
. Матрица missingDataMask должна иметь тип CV_8U и по размеру совпадать с матрицей trainData. По умолчанию считается, что пропущенных значений нет.
- params – параметры алгоритма обучения.
Для представления параметров алгоритма обучения дерева решений используется структура CvDTreeParams:
struct CvDTreeParams { int max_categories; int max_depth; int min_sample_count; int cv_folds; bool use_surrogates; bool use_1se_rule; bool truncate_pruned_tree; float regression_accuracy; const float* priors; CvDTreeParams(); CvDTreeParams( int max_depth, int min_sample_count, float regression_accuracy, bool use_surrogates, int max_categories, int cv_folds, bool use_1se_rule, bool truncate_pruned_tree, const float* priors ); };
Остановимся подробнее на описании данной структуры.
- max_depth – ограничение на высоту обучаемого дерева.
- min_sample_count – минимальное количество объектов обучающей выборки, которое должно быть в области пространства признаков, определяемой некоторым узлом дерева, чтобы разбиение этой области продолжилось.
- regression_accuracy – разбиение узла регрессионного дерева прекращается, если модуль разности значения, приписанного этому узлу, и истинного значения целевого признака для всех прецедентов, попавших в данный узел, не превышает данного значения.
- max_categories – максимальное количество возможных значений категориального признака, для которого точно вычисляется наилучшее правило разбиения. Данный параметр актуален лишь для задач классификации с числом классов больше двух, т.к. в данном случае выбор подмножества L множества возможных значений Q категориального признака происходит с помощью алгоритма с экспоненциальной от мощности Q трудоемкостью [2]. В данном случае для снижения времени работы алгоритма производится предварительная кластеризация множества Q так, чтобы количество кластеров не превышало max_categories. В случае если решается задача регрессии или бинарной классификации, то точное отыскания оптимального разбиения по данному признаку выполняется за линейное время от |Q| [2] и не требует предварительной кластеризации. Следует отметить, что максимальная поддерживаемая в OpenCV мощность равна 64.
- use_surrogates – флаг, определяющий необходимость построения суррогатных разбиений. Каждому узлу дерева решений может быть приписано не только одно (основное) правило разбиения, а также несколько второстепенных (суррогатных) правил. В качестве суррогатных выбираются разбиения наиболее похожие на основное [1]. Построение суррогатных разбиений (use_surrogates=true) ведет к росту времени обучения модели, однако, может повысить качество оценки значимости переменных с помощью построенного дерева и улучшить качество предсказания для объектов с пропущенными значениями.
- cv_folds – количество частей, на которые разделяется обучающая выборка, для выполнения перекрестного контроля при выполнении процедуры отсечений (pruning) [1]. Типичная стратегия построения дерева решений заключается в обучении большого дерева, к которому затем применяются отсечения для предотвращения переобучения. Если cv_folds=0, то отсечения не выполняются.
- use_1se_rule – использование более строгого критерия отсечения. Если use_1se_rule=true, то после процедуры отсечения получается меньшее дерево.
- truncate_pruned_tree – указывает, следует ли физически удалять из памяти отсеченные узлы дерева.
- priors – для задачи классификации, задает априорные вероятности появления точек различных классов. Данный параметр можно использовать, например, в случае несбалансированной обучающей выборки, т.е. неравного количества прецедентов разных классов.
Предсказания с помощью предварительно обученной модели выполняются с помощью метода predict:
CvDTreeNode* predict( const Mat< sample, const Mat< missingDataMask=Mat(), bool preprocessedInput=false ) const;
Рассмотрим параметры данного метода:
-
sample – матрица-вектор типа CV_32F и размера
, содержащая координаты одной точки в пространстве признаков.
-
missingDataMask – матрица-вектор типа CV_8U и размера
, представляющая собой маску пропущенных значений в sample.
-
preprocessedInput – определяет порядок работы с
категориальными признаками в ходе предсказания. В матрице
признаковых описаний объектов обучающей выборки,
используемой методом CvDTree::train, значения
категориальных признаков могут быть обозначены произвольными
целыми числами. Например, множеством различных значений
признака может быть {3,7,9}. Однако внутренним представлением
обученной модели является множество, состоящее из чисел от 0 до
. Таким образом, на этапе предсказания требуется выполнять пересчет значений категориальных признаков, что может негативно сказаться на скорости выполнения данной операции. Значение preprocessedInput=true говорит, что пересчет значений уже выполнен. Данный параметр актуален лишь при наличии категориальных признаков.
Следует отметить, что метод CvDTree::predict возвращает указатель на структуру, описывающую узел дерева. Само предсказанное моделью значение целевого признака хранится в поле value данной структуры.
Сохранение модели дерева решений в файл и загрузка из него выполняется с помощью методов save и load, использование которых полностью совпадает с аналогичными методами класса CvSVM, описанными выше.
Следует отметить, что класс CvDTree также содержит дополнительный функционал, например, для определения значимости различных переменных для восстановления зависимости от , что, однако, выходит за рамки данной лабораторной работы и может быть предоставлено для самостоятельного изучения [7].
Приведем пример использования класса CvDTree для решения задачи бинарной классификации аналогичной, решаемой в примере для CvSVM.
#include <stdlib.h> #include <stdio.h> #include <opencv2/core/core.hpp> #include <opencv2/ml/ml.hpp> using namespace cv; // размерность пространства признаков const int d = 2; // функция истинной зависимости целевого признака // от остальных int f(Mat sample) { return (int)((sample.at<float>(0) < 0.5f && sample.at<float>(1) < 0.5f) || (sample.at<float>(0) > 0.5f && sample.at<float>(1) > 0.5f)); } int main(int argc, char* argv[]) { // объем генерируемой выборки int n = 2000; // объем обучающей части выборки int n1 = 1000; // матрица признаковых описаний объектов Mat samples(n, d, CV_32F); // номера классов (матрица значений целевой переменной) Mat labels(n, 1, CV_32S); // генерируем случайным образом точки // в пространстве признаков randu(samples, 0.0f, 1.0f); // вычисляем истинные значения целевой переменной for (int i = 0; i < n; ++i) { labels.at<int>(i) = f(samples.row(i)); } // создаем маску прецедентов, которые будут // использоваться для обучения: используем n1 // первых прецедентов Mat trainSampleMask(1, n1, CV_32S); for (int i = 0; i < n1; ++i) { trainSampleMask.at<int>(i) = i; } // будем обучать дерево решений высоты не больше 10, // после построения которого выполним отсечения // с помощью пятикратного перекресного контроля CvDTreeParams params; params.max_depth = 10; params.min_sample_count = 1; params.cv_folds = 5; CvDTree dtree; Mat varIdx(1, d, CV_8U, Scalar(1)); Mat varTypes(1, d + 1, CV_8U, Scalar(CV_VAR_ORDERED)); varTypes.at<uchar>(d) = CV_VAR_CATEGORICAL; dtree.train(samples, CV_ROW_SAMPLE, labels, varIdx, trainSampleMask, varTypes, Mat(), params); dtree.save("model-dtree.yml", "simpleDTreeModel"); // вычисляем ошибку на обучающей выборке float trainError = 0.0f; for (int i = 0; i < n1; ++i) { int prediction = (int)(dtree.predict(samples.row(i))->value); trainError += (labels.at<int>(i) != prediction); } trainError /= float(n1); // вычисляем ошибку на тестовой выборке float testError = 0.0f; for (int i = 0; i < n - n1; ++i) { int prediction = (int)(dtree.predict(samples.row(n1 + i))->value); testError += (labels.at<int>(n1 + i) != prediction); } testError /= float(n - n1); printf("train error = %.4f\ntest error = %.4f\n", trainError, testError); return 0; }
Иллюстрацией разбиения пространства признаков обученным с помощью данного кода деревом решений служит рис. 10.2.