Новосибирский Государственный Университет
Опубликован: 20.08.2013 | Доступ: свободный | Студентов: 861 / 36 | Длительность: 14:11:00
Самостоятельная работа 3:

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

2.5. Градиентный бустинг деревьев решений

Градиентный бустинг, как и любой бустинг алгоритм, последовательно строит базовые модели так, что каждая следующая улучшает качество всего ансамбля. Градиентный бустинг деревьев решений строит модель в виде суммы деревьев f(x)=h_{0} + v \sum_{j=1}^{M}{h_{j}(x)}, где h_{0} – некоторая константная модель (начальное приближение), v \in (0,1] – параметр, регулирующий скорость обучения и влияние отдельных деревьев на всю модель, h_{j}(x) – регрессионные деревья решений. Новые слагаемые-деревья добавляются в сумму путем жадной минимизации эмпирического риска, заданного некоторой функцией потерь L(y,y')=L(y,f(x)). Данный метод без серьезной модификации может применяться для любой дифференцируемой функции потерь.

Метод градиентного бустинга деревьев решений в библиотеке OpenCV реализован в виде класса CvGBTrees. Для запуска обучения модели реализован метод 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(), 
      CvGBTreesParams params=CvGBTreesParams(), 
      bool update=false );       
    

Использование данного метода аналогично рассмотренным ранее алгоритмам за исключением следующих моментов: параметр update на настоящий момент является фиктивным, не поддерживается задание используемых прецедентов с помощью маски, по умолчанию обучается регрессионная модель (для обучения классификатора необходимо явно задать категориальный тип целевого признака и выбрать соответствующую функцию потерь). Параметры алгоритма обучения модели градиентного бустинга деревьев решений представляются структурой CvGBTreesParams:

struct CvGBTreesParams : public CvDTreeParams 
{ 
  int weak_count; 
  int loss_function_type; 
  float subsample_portion; 
  float shrinkage; 
 
  CvGBTreesParams(); 
  CvGBTreesParams( int loss_function_type, 
      int weak_count, 
      float shrinkage, 
      float subsample_portion, 
      int max_depth, 
      bool use_surrogates ); 
}; 
    

Рассмотрим значения ее полей:

  • loss_function_type – тип используемой функции потерь. Список реализованных штрафных функций представлен перечислением в классе CvGBTrees:

    enum {SQUARED_LOSS=0, ABSOLUTE_LOSS, HUBER_LOSS=3, 
    DEVIANCE_LOSS}; 
            

    где для задачи восстановления регрессии предназначены функции: L(y,y')=(y-y')^2 (SQUARED_LOSS), L(y,y')=\mid y-y'\mid (ABSOLUTE_LOSS), L(y,y')=\frac{1}{2}(y-y')^2 1(\mid y-y'\mid \leq \delta) + \delta \left(\mid y-y'\mid - \frac{\delta}{2} \right)1(\mid y-y'\mid > \delta (HUBER_LOSS). Функция L(y,y_{1}',y_{2}',...,,y_{K}')=-\sum_{k=1}^{K}{1(y=k)}ln \left( \frac{exp(y_{k}')}{\sum{i=1}^{K}{exp(y_{i}')}}\right)(DEVIANCE_LOSS) предназначена для решения задач классификации на K классов. При использовании данного штрафа строится K моделей в виде суммы деревьев, для оценки вероятностей принадлежности объекта каждому из классов.

  • weak_count – количество обучаемых деревьев. В случае loss_function_type=DEVIANCE_LOSS фактическое количество деревьев, которое будет построено в K раз больше.
  • shrinkage – скорость обучения, уменьшение которой позволяет бороться с переобучением модели.
  • subsample_portion – доля выборки, используемая для обучения каждого дерева. Для построения дерева решений случайным образом (без возвращения) выбирается subsample_portion \cdot 100% прецедентов обучающей выборки. Случайные подвыбороки могут улучшить качество модели, однако, неприменимы в случае небольшого числа имеющихся прецедентов.

Предсказания с помощью модели градиентного бустинга деревьев решений выполняет метод predict:

float predict( const Mat& sample, 
      const Mat& missing=Mat(), 
      const Range& slice=Range::all(), 
      int k=-1 ) const; 
    

Рассмотрим параметры данного метода:

  • sample – матрица-вектор признакового описания объекта.
  • missing – матрица-вектор, содержащая маску пропущенных значений.
  • slice – используемые для предсказания деревья (по умолчанию используются все). Может применяться для подбора оптимального количества деревьев без необходимости производить обучение заново.
  • k – для loss_function_type=DEVIANCE_LOSS номер функции, значение которой следует вернуть. Если k=-1, возвращается предсказанное значение целевой переменной.

Сохранение и загрузка модели выполняется так же, как и в рассмотренных ранее алгоритмах.

Ниже приведен пример использования класса CvGBTrees для решения задачи бинарной классификации и иллюстрация полученного классификатора (см. рис. 8.4)

#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 деревьев высоты 3 и 
  // скоростью обучения 0.5,без использования подвыборок. 
  // Т.к. решается задача классификации используем 
  // функцию потерь DEVIANCE_LOSS 
  CvGBTreesParams params; 
  params.max_depth = 3; 
  params.min_sample_count = 1; 
  params.weak_count = 250; 
  params.shrinkage = 0.5f; 
  params.subsample_portion = 1.0f; 
  params.loss_function_type = CvGBTrees::DEVIANCE_LOSS; 
 
  CvGBTrees gbt; 
  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; 
  gbt.train(samples, CV_ROW_SAMPLE, 
    labels, varIdx, 
    trainSampleMask, varTypes, 
    Mat(), params); 
  gbt.save("model-gbt.yml", "simpleGBTreesModel"); 
 
  // вычисляем ошибку на обучающей выборке 
  float trainError = 0.0f; 
  for (int i = 0; i < n1; ++i) 
  { 
    int prediction = 
      (int)(gbt.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)(gbt.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.4. Точки обучающей выборки и разбиение пространства признаков с помощью модели градиентного бустинга
Александра Максимова
Александра Максимова

При прохождении теста 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).

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