image


В этой статье речь пойдет о задачи бинарной классификации объектов и ее реализации в одном из наиболее производительных пакетов машинного обучения "R" — "XGboost" (Extreme Gradient Boosting).
В реальной жизни мы довольно часто сталкиваемся с классом задач, где объектом предсказания является номинативная переменная с двумя градациями, когда нам необходимо предсказать результат некого события или принять решения в бинарном выражении на основании модели данных. Например, если мы оцениваем ситуацию на рынке и нашей целью является принятие однозначного решения, имеет ли смысл инвестировать в определенный инструмент в данный момент времени, купит ли покупатель исследуемый продукт или нет, расплатится ли заемщик по кредиту или уволится ли сотрудник из компании в ближайшее время и.т.д.


В общем случае бинарная классификация применяется для предсказания вероятности возникновения некоторого события по значениям множества признаков. Для этого вводится так называемая зависимая переменная (исход события), принимающая лишь одно из двух значений (0 или 1), и множество независимых переменных (также называемых признаками, предикторами или регрессорами).


Сразу оговорюсь, что в "R" существует несколько линейных функций для решения подобных задач, таких как "glm" из стандартного пакета функций, но здесь мы рассмотрим более продвинутый вариант бинарной классификации, имплементированный в пакете "XGboost". Эта модель, многократный победитель соревнований Kaggle, основана на построении бинарных деревьев решений способна поддерживать многопоточную обработку данных. Об особенностях реализации семейства моделей "Gradient Boosting" можно прочитать здесь:


http://www.ncbi.nlm.nih.gov/pmc/articles/PMC3885826/
https://en.wikipedia.org/wiki/Gradient_boosting


Возьмем тестовый набор данных (Train) и построим модель для предсказания выживаемости пассажиров при катастрофе :


    data(agaricus.train, package='xgboost')    
    data(agaricus.test, package='xgboost')    
    train <- agaricus.train    
    test <- agaricus.test    

Если после преобразования матрица содержит много нулей, то такой массив данных нужно предварительно преобразовать в sparse matrix — в таком виде данные займут намного меньше места, а соответственно и время обработки данных намного сократится. Здесь нам поможет библиотека ‘Matrix’ на сегодняшний день последняя доступная версия 1.2-6 содержит в себе набор функция для преобразования в dgCMatrix на колоночной основе.
В случае, когда уже уплотненная матрица (sparse matrix) после всех преобразований не помещается в оперативной памяти, то в таких случаях используют специальную программу “Vowpal Wabbit”. Это внешняя программа, которая может обработать датасеты любых размеров, читая из многих файлов или баз данных. “Vowpal Wabbit” представляет собой оптимизированную платформу для параллельного машинного обучения, разработанную для распределенных вычислений компанией “Yahoo!” Про нее довольно подробно можно прочесть по этим ссылкам:


https://habrahabr.ru/company/mlclass/blog/248779/
https://en.wikipedia.org/wiki/Vowpal_Wabbit


Использование разреженных матриц дает нам возможность строить модель с использованием текстовых переменных с предварительным их преобразованием.
Итак, для построения матрицы предикторов сначала загружаем необходимые библиотеки:


    library(xgboost)    
    library(Matrix)     
    library(DiagrammeR)    

При конвертации в матрицу все категориальные переменные будут транспониваны, соответственно функция со стандартным бустером включит их значения в модель. Первое что нужно сделать, это удалить из набора данных переменные с уникальными значениями, такими как “Passenger ID”, “Name” и “Ticket Number”. Такие же действия проводим и с тестовым набором данных, по которым будут рассчитывается прогнозные исходы. Для наглядности я загрузил данные из локальных файлов, которые скачал в соответствующем датасете Kaggle. Для модели, ним понадобятся следующие колонки таблицы:


    input.train <- train[, c(3,5,6,7,8,10,11,12)]    
    input.test <-  test[, c(2,4,5,6,7,9,10,11)]    

image
отдельно формируем вектор известных исходов для обучения модели


    train.lable <- train$Survived    

Теперь необходимо выполнить преобразование данных, дабы при обучении модели в учет были приняты статистически значимые переменные. Выполним следующие преобразования:
Заменим переменные содержащие категориальные данные на числовые значения. При этом нужно учитывать что упорядоченные категории, такие как 'good', 'normal', 'bad' можно заменить на 0,1,2. Не упорядоченные данные с относительно небольшой селективностью, такие как ‘gender’ или ‘Country Name’ можно оставить факторными без изменения, после преобразование в матрицу они транспонируются в соответствующее количество столбцов с нулями и единицами. Для числовых переменных, необходимо обработать все неприсвоенные и пропущенные значения. Здесь есть как минимум три варианта: их можно подменить на 1, 0 либо более приемлемый вариант будет замена на среднее значение по колонке этой переменной.
При использовании пакета “XGboost” со стандартным бустером (gbtree), масштабирование переменных можно не выполнять, в отличии от других линейных методов, таких как “glm” или “xgboost” c линейным бустером (gblinear).


Основную информацию о пакете можно найти по следующим ссылкам:


https://github.com/dmlc/xgboost
https://cran.r-project.org/web/packages/xgboost/xgboost.pdf


Возвращаясь к нашему коду, в результате мы получили таблицу следующего формата:


image


далее, заменяем все пропущенные записи на среднее арифметическое значение по столбцу предиктора


    if (class(inp.column) %in% c('numeric', 'integer')) {
            inp.table[is.na(inp.column), i] <- mean(inp.column, na.rm=TRUE)     

после предварительной обработки делаем преобразование в "dgCMatrix":


    sparse.model.matrix(~., inp.table)    

Имеет смысл создать отдельную функцию для предварительной обработки предикторов и преобразования в sparse.model.matrix формат, например вариант с "for" циклом приведен ниже. C целью оптимизации производительности можно векторизовать выражение используя функцию "apply".


    spr.matrix.conversion <- function(inp.table) {    
      for (i in 1:ncol(inp.table)) {    
        inp.column <- inp.table [ ,i]       
        if (class(inp.column) == 'character') {    
          inp.table [is.na(inp.column), i] <- 'NA'     
          inp.table [, i] <- as.factor(inp.table [, i])    
        }     
        else    
          if (class(inp.column) %in% c('numeric', 'integer')) {    
            inp.table [is.na(inp.column), i] <- mean(inp.column, na.rm=TRUE)        
          }    
      }    
  return(sparse.model.matrix(~.,inp.table))
    }    

Тогда воспользуемся нашей функцией и преобразуем фактическую и тестовую таблицы в разреженные матрицы:


    sparse.train <- preprocess(train)    
    sparse.test <- preprocess(test)    

image


Для построения модели нам потребуется два набора данных: матрица данных, которую мы только что создали и вектор фактических исходов с бинарным значением (0,1).
Функция «xgboost» является наиболее удобной в использовании. В “XGBoost” имплементирован стандартный бустер основан на бинарных деревьях решений.
Для использования “XGboost”, мы должны выбрать один из трех параметров: общие параметры, параметры бустера и параметров назначения:
• Общие параметры – определяем, какой бустер будет использован, линейный или стандартный.
Остальные параметры бустера зависят от того, какой бустер мы выбрали на первом шаге:
• Параметры задач обучения – определяем назначение и сценарий обучения
• Параметры командной строки — используются для определения режима командной строки при использовании “xgboost.”.


Общий вид функции “xgboost” который мы используем:


     xgboost(data = NULL, label = NULL, missing = NULL, params = list(), nrounds, verbose = 1, print.every.n = 1L, early.stop.round = NULL, maximize = NULL, ...)   

"data" – данные матричном формате ( "matrix", "dgCMatrix", local data file or "xgb.DMatrix".)
"label" – вектор зависимой переменной. Если данное поле было составляющей исходной таблицы параметров, то перед обработкой и преобразованием в матрицу, его следует исключить, дабы избежать транзитивности связей.
"nrounds" –количество построенных деревьев решений в финальной модели.
"objective" – через данный параметр мы передаем задачи и назначения обучения модели. Для логистической регрессии существуют 2 варианта:
"reg:logistic" – логистическая регрессия с непрерывной величиной оценки от 0 до 1;
"binary:logistic" – логистическая регрессия с бинарной величиной предсказания. Для этого параметра можно задать специфическую пороговую величину перехода от 0 к 1. По умолчанию это значение 0.5.
Детально про параметризацию модели можно прочесть по этой ссылке


http://xgboost/parameter.md%20at%20master%20·%20dmlc/xgboost%20·%20GitHub
Теперь приступаем к созданию и обучению модели “XGBoost”:


    set.seed(1)    
    xgb.model <- xgboost(data=sparse.train, label=train$Survived, nrounds=100, objective='reg:logistic')     

при желании можно извлечь структуру деревьев с помощью функции xgb.model.dt.tree( model = xgb). Далее, используем стандартную функцию “predict” для формирования прогнозного вектора:


    prediction <- predict(xgb.model, sparse.test)     

и наконец, сохраним данные в приемлемом для чтения формате


    solution <- data.frame(prediction = round(prediction, digits = 0), test)    
    write.csv(solution, 'solution.csv', row.names=FALSE, quote=FALSE)    

добавляя вектор предсказанных исходов, получаем таблицу следующего вида:


image


Теперь немного вернемся и вкратце рассмотрим саму модель, которую мы только что создали. Для отображения деревьев решения, можно воспользоваться функциями "xgb.model.dt.tree" и "xgb.plot.tree". Так, последняя функция выдаст нам список выбранных деревьев с коэффициентом подгонки модели:


image


Используя функцию xgb.plot.tree мы также увидим графическое представление деревьев, хотя нужно отметить что в текущей версии, оно далеко не лучшим способом имплементировано в данной функции и является мало полезным. По этому, для наглядности, мне пришлось воспроизвести вручную элементарное дерево решений на базе стандартной модели данных Train.


image


Проверка статистической значимости переменных в модели подскажет нам как оптимизировать матрицу предикторов для обучения XGB-модели. Лучше всего использовать функцию xgb.plot.importance в которую мы передадим агрегированную таблицу важности параметров.


    importance_frame <- xgb.importance(sparse.train@Dimnames[[2]],  model = xgb)    
    xgb.plot.importance(importance_frame)       

image


Итак, мы рассмотрели одну из возможных реализаций логистической регрессии на базе пакета функции “xgboost” со стандартным бустером. На данный момент я рекомендую использовать пакет “XGboost” как наиболее продвинутую группу моделей машинного обучения. В настоящие время предиктивные модели на базе логики “XGboost” широко используются в финансовом и рыночном прогнозировании, маркетинге и многих других областях прикладной аналитики и машинного интеллекта.

Поделиться с друзьями
-->

Комментарии (12)


  1. djinninia
    05.08.2016 11:31

    Спасибо. Надо попробовать


  1. YuryFedin
    05.08.2016 21:18

    Возьмем тестовый набор данных (Train)

    Вообще-то это набор Titanic


    1. AndrewKarpovych
      05.08.2016 22:36

      Да это верно, но поскольку данные я загружал прямо из пакета как «agaricus.train» я написал «Train» а не «Titanic». На Kaggle этот набор, конечно, же известен как «Titanic» так что извиняюсь за некоторую путаницу.


  1. rushter
    06.08.2016 14:03
    +7

    Не увидел в статье логистической регрессии. Вы путайте понятия.


    1. AndrewKarpovych
      06.08.2016 15:26
      -5

      Эта статья описывает решение задачи логистической регрессии с помощью XGboost с нелинейным бустером. Если Вы обратите внимание на параметризацию модели, Вы увидите назначение модели. У данного метода есть ряд преимуществ, которые здесь рассматриваются.


      1. rushter
        06.08.2016 15:32
        +6

        Не встречал такого понятия, как задача логистической регрессии. Есть задача классификации, которая может решаться лог. регрессией.
        То что, вы увидели «binary:logistic», совсем не означает что это логистическая регрессия, просто авторы решили это так назвать.
        Если мне не изменяет память, то в теории машинного обучения это называется logitboost, где всеголишь используется logistic loss.
        Да, здесь есть общие моменты, но смысл совсем разный, никто не называет проблемы классификация с помощью бустинга на деревьях лог. регрессиями.


        1. AndrewKarpovych
          06.08.2016 15:46
          -4

          Выходит что называют. Специально зашел по ссылке и скопировал описание из github.com —
          «binary:logistic» --logistic regression for binary classification, output probability


          1. rushter
            06.08.2016 16:02
            +5

            То, что вы показывайте, это всего лишь функция потерь, которая будет минимизироваться. Да она названа неудачно, но поверьте, даже сам Тянки Чен не будет называть классификацию лог. регрессией. Не стоит придавать новый смысл термину, которому уже более 50 лет.

            С большой натяжкой его можно здесь употреблять, но так никто не делает, чтобы не вводить людей в заблуждение. Для эксперимента можете поискать статьи на arxiv или просто погуглить употребление logistic regression, а ещё лучше погуглить gbm и LR сразу вместе.


  1. AndrewKarpovych
    06.08.2016 16:22
    -1

    Нигде в статье не написано, что в gbm реализована логит-модель. В статье есть ссылки на gbm и детали ее реализации. Я не думаю, что если человек прочтет внимательно статью то будет введен в заблуждение. т.к. все материалы подкреплены практической реализацией, которую можно при желании проверить.


    1. Roman_Kh
      06.08.2016 16:26
      +2

      Нигде в статье не написано, что в gbm реализована логит-модель.

      Вы сами-то свою статью читали?! Вот цитаты:


      В этой статье речь пойдет о логистической регрессии и ее реализации в одном из наиболее производительных пакетов машинного обучения "R" — "XGboost"

      или


      Сразу оговорюсь, что в "R" существует несколько линейных функций для обучения логит-модели, таких как "glm" из стандартного пакета функций, но здесь мы рассмотрим более продвинутый вариант, имплементированный в пакете "XGboost".


      1. AndrewKarpovych
        06.08.2016 16:35
        +1

        Спасибо за комментарий. Я отредактирую, дабы больше не возникало путаницы.


  1. yorko
    06.08.2016 18:04
    +1

    Короче любой скрипт на Kaggle расскажет вам больше про Xgboost в R. И на более-менее реальных данных, а не избитом Титанике. Если реально полезную статью писать — это надо разобраться, как чудо-зверь работает (в офиц документации неплохо рассказывается), что стоит за всеми этими параметрами в Xgboost и как их настраивать (hyperopt, sacred).
    А так по первой же попавшейся ссылке больше узнаешь.