При прохождении теста 1 в нем оказались вопросы, который во-первых в 1 лекции не рассматривались, во-вторых, оказалось, что вопрос был рассмаотрен в самостоятельно работе №2. Это значит, что их нужно выполнить перед прохождением теста? или это ошибка? |
Машинное обучение
2.4. Случайный лес
Случайный лес является бэггинг алгоритмом, основная идея которого заключается в построении как можно менее зависимых друг от друга деревьев решений, с последующим принятием решения путем усреднения в случае регрессии и голосованием в случае классификации. Зависимость между деревьями снижается за счет использования случайной выборки с возвращением из исходных прецедентов и случайного подмножества признаков.
Алгоритм случайного леса реализован в библиотеке OpenCV в виде класса CvRTrees. Рассмотрим принципы работы с данным классом.
Для обучения случайного леса предназначен метод 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(), CvRTParams params=CvRTParams() );
Назначение и формат параметров данного метода целиком совпадает с методом CvDTree::train, реализующим обучение одиночного дерева решений, за исключением параметров алгоритма обучения. Параметры случайного леса представляются в виде структуры CvRTParams:
struct CvRTParams : public CvDTreeParams { bool calc_var_importance; int nactive_vars; CvTermCriteria term_crit; CvRTParams(); CvRTParams( int max_depth, int min_sample_count, float regression_accuracy, bool use_surrogates, int max_categories, const float* priors, bool calc_var_importance, int nactive_vars, int max_num_of_trees_in_the_forest, float forest_accuracy, int termcrit_type ); };
Так как алгоритм случайного леса строит деревья решений, большинство параметров определяют настройки алгоритма обучения одиночных деревьев (см. описание структуры CvDTreeParams). Следует отметить, что в рамках модели случайного леса обучаются большие (оригинальный подход [ 4 ] не подразумевал ограничение высоты дерева как критерий останова его построения) деревья решений без последующего применения процедуры отсечения. Теперь рассмотрим параметры, имеющие отношение непосредственно к модели случайного леса:
- nactive_vars – количество признаков, выбираемых случайным образом для обучения каждого дерева решений. Если nactive_vars=0, то при обучении дерева будет использоваться целая часть снизу от признаков, если же указано значение nactive_vars > 0, то min(nactive_vars, ) признаков.
- term_crit – критерий прекращения добавления деревьев в ансамбль (лес). Так как при обучении каждого дерева используются не все прецеденты обучающей выборки, то неиспользуемая часть выборки (out-of-bag samples) может быть использована в качестве тестовой для оценки качества модели (out-of-bag error, oob error). В связи с этим появляется возможность прекратить построение новых деревьев, при достижении достаточно малой oob ошибки. Структура CvTermCriteria была рассмотрена выше (см. описание CvSVMParams), здесь отметим лишь, что значение term_crit.max_iter задает максимальное количество обучаемых деревьев, term_crit.epsilon – максимальную допустимую oob ошибку, term_crit.type – тип используемого критерия останова.
- calc_var_importance – флаг, определяющий необходимость вычисления значимости переменных в ходе обучения модели случайного леса.
Для осуществления предсказаний в классе CvRTrees предназначен метод predict:
float predict( const Mat& sample, const Mat& missing=Mat() ) const;
Параметрами метода являются признаковое описание объекта и маска пропущенных значений в данном описании. Возвращаемое значение соответствует предсказанной величине целевого признака.
Методы save и load, осуществляющие сохранение и загрузку модели соответственно, применяются аналогично данным методам классов CvSVM и CvDTree.
Приведем пример использования класса CvRTrees для решения задачи бинарной классификации и иллюстрацию порождаемого обученной моделью разбиения пространства признаков (см. рис. 8.3).
#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; } // будем обучать случайный лес из 250 деревьев // высоты не больше 10 CvRTParams params; params.max_depth = 10; params.min_sample_count = 1; params.calc_var_importance = false; params.term_crit.type = CV_TERMCRIT_ITER; params.term_crit.max_iter = 250; CvRTrees rf; 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; rf.train(samples, CV_ROW_SAMPLE, labels, varIdx, trainSampleMask, varTypes, Mat(), params); rf.save("model-rf.yml", "simpleRTreesModel"); // вычисляем ошибку на обучающей выборке float trainError = 0.0f; for (int i = 0; i < n1; ++i) { int prediction = (int)(rf.predict(samples.row(i))); trainError += (labels.at<int>(i) != prediction); } trainError /= float(n1); // вычисляем ошибку на тестовой выборке float testError = 0.0f; for (int i = 0; i < n - n1; ++i) { int prediction = (int)(rf.predict(samples.row(n1 + i))); testError += (labels.at<int>(n1 + i) != prediction); } testError /= float(n - n1); printf("train error = %.4f\ntest error = %.4f\n", trainError, testError); return 0; }