Бинарные классификаторы. Практика. Часть 1.

==**Подготовка данных**==

В данной задаче используется набор данных Sonar. Это задача, которая решалась в статье [[https://papers.cnl.salk.edu/PDFs/Analysis%20of%20Hidden%20Units%20in%20a%20Layered%20Network%20Trained%20to%20Classify%20Sonar%20Targets%201988-2996.pdf|”Analysis of Hidden Units in a Layered Network Trained to Classify Sonar Targets”]] и представляют собой набор значений звуковых сигналов отражённых от металлических(M) и каменных(R) объектов. Каждая запись представляет собой строку из 60 чисел и значением “M” или “R”. Данные подготавливаются следующим кодом

{{{ lang=rsplus
#——- Packages ————————-
require(caret)
require(mlbench)

#——- Data preparation —————–
data(Sonar)
df <- Sonar names(df)[names(df) == "Class"] <- 'target' set.seed(1234) idx <- createDataPartition(df$target, p = 0.75, list = F) train <- df[idx, ] test <- df[-idx, ] }}} Здесь для удобства данные сохраняются в новую переменную и удобства ради, название целевого признака заменяется на 'target'. После чего фиксируется рандомизация и создаётся разбиение исходных данных на две части, обучающую выборку train и проверочную test. Индексы при этом формируются при помощи команды createDataPartition, в которой в данном случае размер обучающей выборки задаётся параметром p в 75% от исходного размера. Команда createDataPartition помимо всего прочего старается создать сбалансированные выборки, то есть доля положительных/отрицательных ответов в обучающей выборке должна быть приблизительно равна доле положительных/отрицательных ответов в исходной выборке. Это можно проверить следующим образом {{{ lang=rsplus #--- Test sample balance ------------------ mean(df$target == "R") # доля ответов 'R' в исходной выборке [1] 0.4663462 mean(train$target == "R") # доля ответов 'R' в обучающей выборке [1] 0.4649682 mean(test$target == "R") # доля ответов 'R' в проверочной выборке [1] 0.4705882 nrow(train)/nrow(df) # относительный объём обучающей выборки [1] 0.7548077 }}} Как можно видеть, данные получились корректными. ==**Построение бинарного классификатора (AdaBoost)**== Первый бинарный классификатор, который мы построим, будет использовать алгоритм [[https://ru.wikipedia.org/wiki/AdaBoost|AdaBoost]]. Наверное после такой длинной подготовки сам код будет разочаровывающе коротким, но стоит отметить, что такая краткость возможно лишь благодаря пакету caret. {{{ lang=rsplus #--- Default Modelling ---------------------------- ctrl <- trainControl( method = "repeatedcv", repeats = 10, number = 10, classProbs = T, verboseIter = T, summaryFunction = twoClassSummary ) model_default <- train( target ~ ., data = train, method = "ada", trControl = ctrl, metric = 'ROC' ) }}} Само построение модели производится командой train. В данном примере использованы далеко не все её возможности, позже будут использоваться дополнительные параметры. Имеющиеся же означают следующее # **target ~ .** - это объект R-формула, который означает, что мы строим зависимость параметра 'target' от всех остальных параметров, которые присутствуют в данных, '.' в данном случае это wildcard, который означает "в правой части формулы стоят все названия столбцов, которые встречаются в данных" . При этом R исключает названия, которые стоят в левой части формулы. Подобная форма записи удобнее, чем указывать в явном виде название каждого отдельного параметра, так как к примеру в данном случае их 60 штук, а вообще их могут быть тысячи. # **data = train** означает, что в качестве данных на основе которых будет строиться модель должна быть использована обучающая выборка. Если бы было написано data = df, то использовалась бы вся выборка. # **method = 'ada'** означает, что необходимо использовать метод AdaBoost, который имеет ярлык 'ada' в рамках пакета 'caret'. Полный список готовых для использовония методов можно посмотреть командой "?train_model_list". Так же 'caret' позволяет добавлять свои собственные алгоритмы. # **metric = 'ROC'** означает, что нужно использовать AUC метрику ROC-кривых. Если опустить этот параметр, то по умолчанию будет использоваться accuracy. Параметр 'trControl' позволяет установить дополнительные настройки работы обучения, не связанные непосредственно с алгоритмами обучения. Контроль задается командой 'trainControl' и параметры имеют следующий смысл # **method = 'repeatedcv'** означает, что будет использоваться повторная кросс-валидация. Так же возможна обычная кросс-валидация, leave-one-out и так далее. # **repeats = 10** означает, что повторная кросс-валидация должна быть запущена 10 раз. # **number = 10** означает, что в процессе кросс-валидации выборку надо разбивать на 10 равных частей. # **classProbs = T** означает, что в процессе вычислений алгоритм будет сохранять данные о вероятностях попадания объекта в каждый класс, а не только конечные метки класса. Этот параметр необходим для использования метрики ROC-кривой. # **verboseIter = T** означает, что в ходе вычислений caret будет показывать на каком этапе он находится. Это удобно для оценки оставшегося времени вычислений, которое зачастую бывает очень большим. # **summaryFunction = twoClassSummary** означает, что для оценки эффективности алгоритма необходимо использовать встроенную функцию twoClassSummary, это необходимое требование при использовании метрики ROC. Процесс вычисления выглядит следующим образом {{{ lang=rsplus + Fold01.Rep01: iter= 50, maxdepth=1, nu=0.1 - Fold01.Rep01: iter= 50, maxdepth=1, nu=0.1 + Fold01.Rep01: iter=100, maxdepth=1, nu=0.1 - Fold01.Rep01: iter=100, maxdepth=1, nu=0.1 ... + Fold10.Rep10: iter=150, maxdepth=3, nu=0.1 - Fold10.Rep10: iter=150, maxdepth=3, nu=0.1 Aggregating results Selecting tuning parameters Fitting iter = 150, maxdepth = 3, nu = 0.1 on full training set }}} Знаки + и - означают начало и окончание соответствующего этапа вычислений. Значения iter, maxdepth и nu означают набор гиперпараметров AdaBoost, соответственно количество деревьев, максимальная глубина дерева и параметр сжатия. Их точный смысл можно найти в интернете, однако в рамках данной статьи он не важен, а важно лишь то, что caret по умолчанию создал семейство алгоритмов с различными значениями гиперпараметров и автоматически выбирает наилучший при помощи повторной кросс-валидации. В данном случае обучающий алгоритм выбрал модель с гиперпараметрами iter = 150, maxdepth = 3, nu = 0.1 и произвёл обучение этой модели на всей обучающей выборке. Почему было принято именно такое решение легко понять, если показать результат вычислений {{{ lang=rsplus > model_default

Boosted Classification Trees
157 samples
60 predictors
2 classes: ‘M’, ‘R’
No pre-processing
Resampling: Cross-Validated (10 fold, repeated 10 times)
Summary of sample sizes: 141, 141, 142, 142, 140, 142, …
Resampling results across tuning parameters:
iter maxdepth ROC Sens Spec ROC SD Sens SD Spec SD
50 1 0.846 0.832 0.685 0.117 0.124 0.179
50 2 0.889 0.846 0.725 0.0851 0.135 0.179
50 3 0.904 0.853 0.751 0.0693 0.124 0.162
100 1 0.889 0.852 0.744 0.0829 0.127 0.164
100 2 0.918 0.867 0.761 0.0645 0.12 0.174
100 3 0.918 0.877 0.769 0.0652 0.116 0.178
150 1 0.907 0.872 0.776 0.0691 0.121 0.165
150 2 0.923 0.867 0.791 0.0614 0.126 0.167
150 3 0.927 0.881 0.782 0.0583 0.117 0.161
Tuning parameter ‘nu’ was held constant at a value of 0.1
ROC was used to select the optimal model using the largest value.
The final values used for the model were iter = 150, maxdepth = 3 and nu = 0.1.
}}}

Как видно, параметр nu не менялся, поэтому он не показан в виде отдельного столбца. Значение ROC для iter = 150, maxdepth = 3 равно 0.927 и является лучшим результатом. На втором месте стоит iter = 150, maxdepth = 2 со значением ROC 0.923. Остальные столбцы означают Sens=Sensitivity, Spec=Specificity, то есть точность определения классов ‘M’ и ‘R’ отдельно, а следующие три столбца показывают стандартные отклонения для соответствующих величин (ROC SD = standard deviation of ROC и так далее). Как видно, кстати, ROC с наилучшим значением имеет к тому же и наименьшее отклонение 0.0583, что дополнительно говорит в пользу этого выбора.

Эти данные удобнее показывать в виде графика при помощи команды ‘ggplot(model_default)’

[[image:ada_default.jpeg|link=source]]

Как видно увеличение количества деревьев улучшает предсказательную силу всех алгоритмов, но явным фаворитом является алгоритм с глубиной 3.

Теперь можно оценить качество работы алгоритма на проверочной выборке
{{{ lang=rsplus
> testClasses <- predict(model_default, newdata = test) > confusionMatrix(data = testClasses, test$target)

Confusion Matrix and Statistics
Reference
Prediction M R
M 25 3
R 2 21

Accuracy : 0.902
95% CI : (0.7859, 0.9674)
No Information Rate : 0.5294
P-Value [Acc > NIR] : 1.209e-08

Kappa : 0.8028
Mcnemar’s Test P-Value : 1

Sensitivity : 0.9259
Specificity : 0.8750
Pos Pred Value : 0.8929
Neg Pred Value : 0.9130
Prevalence : 0.5294
Detection Rate : 0.4902
Detection Prevalence : 0.5490
Balanced Accuracy : 0.9005

‘Positive’ Class : M
}}}

Применение построенной модели к тестовым данным производится при помощи команды ‘predict’. Оценка эффективности алгоритма на проверочной выборке в читабельном виде показывается командой ‘confusionMatrix’. Как видно, на тестовой выборке алгоритм ошибся всего лишь 3 + 2 = 5 раз, что дало accuracy 90%. Смысл остальных параметров читателю предлагается выяснить самостоятельно.

Как было уже сказано, настройка модели производилась по ROC-кривой, и её вид и параметры выглядят следующим образом
{{{ lang=rsplus
> testProbs_default <- predict(model_default, newdata = test, type = 'prob') > head(testProbs_default)
M R
1 0.42626815 0.5737319
2 0.49835861 0.5016414
3 0.42028321 0.5797168
4 0.63866396 0.3613360
5 0.01186840 0.9881316
6 0.09051109 0.9094889

> (roc_default <- roc(test$target ~ test_probs_default$M)) Call: roc.formula(formula = test$target ~ test_probs_default$M) Data: test_probs_default$M in 27 controls (test$target M) > 24 cases (test$target R).
Area under the curve: 0.9444

> plot(roc_default) # построение графика ROC-кривой
}}}

Как видно площадь под кривой равна 0.9444, что является достаточно хорошим значением, но при этом выше чем значение 0.927 полученное в результате кросс-валидации. Это связано как с относительно малым объёмом проверочной выборки, так и с тем фактом, что в обучающую выборку по всей видимости попало достаточно много шумовых данных. Также test_probs_default содержит теперь не метки классов, а вероятности принадлежности к тому или иному классу. К примеру 5 объект практически 100% является камнем, в то время как принадлежность объекта 4 к металлу в принципе может вызывать сомнения. График ROC-кривой выглядит следующим образом

[[image:ada_default_roc.jpeg|link=source]]

Tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *