Опубликован: 20.08.2013 | Уровень: для всех | Доступ: платный | ВУЗ: Новосибирский Государственный Университет
Самостоятельная работа 3:

Машинное обучение

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, то при обучении дерева будет использоваться целая часть снизу от \sqrt{d} признаков, если же указано значение nactive_vars > 0, то min(nactive_vars, d) признаков.
  • 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; 
}   
    
Точки обучающей выборки и разбиение пространства признаков случайным лесом

Рис. 8.3. Точки обучающей выборки и разбиение пространства признаков случайным лесом
Александра Максимова
Александра Максимова

При прохождении теста 1 в нем оказались вопросы, который во-первых в 1 лекции не рассматривались, во-вторых, оказалось, что вопрос был рассмаотрен в самостоятельно работе №2. Это значит, что их нужно выполнить перед прохождением теста? или это ошибка?
 

Алена Борисова
Алена Борисова

В лекции по обработке полутоновых изображений (http://www.intuit.ru/studies/courses/10621/1105/lecture/17979?page=2) увидела следующий фильтр:


    \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 2 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array} - \frac{1}{9} \begin{array}{|c|c|c|}
    \hline \\
    0 & 0 & 0 \\
    \hline \\
    0 & 1 & 0 \\
    \hline \\
    0 & 0 & 0 \\
    \hline 
    \end{array}

В описании говорится, что он "делает изображение более чётким, потому что, как видно из конструкции фильтра, в однородных частях изображение не изменяется, а в местах изменения яркости это изменение усиливается".

Что вижу я в конструкции фильтра (скорее всего ошибочно): F(x, y) = 2 * I(x, y) - 1/9 I(x, y) = 17/9 * I(x, y), где F(x, y) - яркость отфильтрованного пикселя, а I(x, y) - яркость исходного пикселя с координатами (x, y). Что означает обычное повышение яркости изображения, при этом без учета соседних пикселей (так как их множители равны 0).

Объясните, пожалуйста, как данный фильтр может повышать четкость изображения?

Сергей Кротов
Сергей Кротов
Россия
Дмитрий Донсков
Дмитрий Донсков
Россия, Москва, Московский Авиационный Институт