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

==**Улучшение классификатора**==

При рассмотрении графика изменения значений ROC-кривой в предыдущем пункте, можно заметить, что количество деревьев равное 150 недостаточно для того, чтобы полностью использовать все возможности алгоритма и потому есть смысл нарастить этот параметр и тем самым попытаться улучшить классификатор. Для этого необходимо сделать две вещи

# создать сетку желаемых параметров
# передать эту сетку в обучающий алгоритм.

Делается это следующим образом

{{{ lang=rsplus
modelGrid <- expand.grid(iter = c(100, 200, 300, 400, 500), maxdepth = c(1, 2, 3, 4), nu = 0.1) model_tuned <- train( target ~ ., data = train, method = "ada", trControl = ctrl, tuneGrid = modelGrid, metric = 'ROC' ) }}} Передача сетки производится при помощи параметра 'tuneGrid' в команде 'train'. Сама же сетка формируется при помощи команды 'expand.grid', которая составляет все возможные комбинации входных параметров {{{ lang=rsplus > head(modelGrid, n = 10)
iter maxdepth nu
1 100 1 0.1
2 200 1 0.1
3 300 1 0.1
4 400 1 0.1
5 500 1 0.1
6 100 2 0.1
7 200 2 0.1
8 300 2 0.1
9 400 2 0.1
10 500 2 0.1
}}}

Результат вычислений выглядит следующим образом

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

Как видно из графика, после того, как количество используемых деревьев переходит 300 единиц, алгоритм выходит на плато и по всей видимости уже не может улучшать свою работу. Самым выигрышным набором гиперпараметров становится комбинация с iter = 400, maxdepth = 3, nu = 0.1 и значением ROC = 0.944. Соответствующий график ROC-кривой на проверочной выборке выглядит следующим образом

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

В данной модели обнаруживается следующий интересный факт. Если попытаться посчитать предсказание модели на проверочной выборке, то результат получится хуже, чем в случае, когда модель строилась без каких-либо дополнительных настроек

{{{ lang=rsplus
> testClasses <- predict(model_tuned, newdata = test) > confusionMatrix(data = testClasses, test$target)

Confusion Matrix and Statistics
Reference
Prediction M R
M 25 4
R 2 20

Accuracy : 0.8824
95% CI : (0.7613, 0.9556)
No Information Rate : 0.5294
P-Value [Acc > NIR] : 8.488e-08

Kappa : 0.7628
Mcnemar’s Test P-Value : 0.6831

Sensitivity : 0.9259
Specificity : 0.8333
Pos Pred Value : 0.8621
Neg Pred Value : 0.9091
Prevalence : 0.5294
Detection Rate : 0.4902
Detection Prevalence : 0.5686
Balanced Accuracy : 0.8796

‘Positive’ Class : M
}}}

Тем не менее, это не противоречит тому факту, что ROC для такой модели выше. Из результатов видно, что алгоритм чаще ошибается занося ‘R’ в класс ‘M’. Таким образом, если перекалибровать модель, есть возможность понизить количество ошибок такого типа.

{{{ lang=rsplus
> tc_calibrated <- factor(ifelse(testProbs_tuned$M >= 0.7, ‘M’, ‘R’))
> confusionMatrix(data = tc_calibrated, test$target)

Confusion Matrix and Statistics
Reference
Prediction M R
M 25 2
R 2 22

Accuracy : 0.9216
95% CI : (0.8112, 0.9782)
No Information Rate : 0.5294
P-Value [Acc > NIR] : 1.407e-09

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

Sensitivity : 0.9259
Specificity : 0.9167
Pos Pred Value : 0.9259
Neg Pred Value : 0.9167
Prevalence : 0.5294
Detection Rate : 0.4902
Detection Prevalence : 0.5294
Balanced Accuracy : 0.9213

‘Positive’ Class : M
}}}

Сдвиг порога обнаружения металла до 70% позволяет повысить //accuracy// до 92%!

==**Сравнение с другим классификатором (Random Forest)**==

Для демонстрации удобства работы с пакетом ‘caret’, построим решение той же задачи при помощи классификатора [[https://ru.wikipedia.org/wiki/Random_forest|RandomForest]].

{{{ lang=rsplus
modelGrid <- expand.grid(mtry = seq(2, 60, 1)) ctrl <- trainControl( method = "repeatedcv", repeats = 10, number = 10, classProbs = T, verboseIter = T, summaryFunction = twoClassSummary ) model_rf <- train( target ~ ., data = train, method = "rf", tuneGrid = modelGrid, trControl = ctrl, metric = 'ROC' ) }}} Настройка данного алгоритма выглядит следующим образом [[image:rf.jpeg|link=source]] И из графика видно, что увеличение числа предикторов ухудшает точность работы алгоритма, и наилучший результат получается при числе предикторов равном 4 с ROC=0.935, что немного хуже, чем в случае AdaBoost. Более низкое качество показывается и на проверочной выборке {{{ lang=rsplus > testClasses <- predict(model_rf, newdata = test) > confusionMatrix(data = testClasses, test$target)

Confusion Matrix and Statistics
Reference
Prediction M R
M 26 9
R 1 15

Accuracy : 0.8039
95% CI : (0.6688, 0.9018)
No Information Rate : 0.5294
P-Value [Acc > NIR] : 4.341e-05

Kappa : 0.5991
Mcnemar’s Test P-Value : 0.02686

Sensitivity : 0.9630
Specificity : 0.6250
Pos Pred Value : 0.7429
Neg Pred Value : 0.9375
Prevalence : 0.5294
Detection Rate : 0.5098
Detection Prevalence : 0.6863
Balanced Accuracy : 0.7940

‘Positive’ Class : M
}}}

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

{{{ lang=rsplus
> summary(resamps <- resamples(list(ada = model_tuned, rf = model_rf))) Call: summary.resamples(object = resamps <- resamples(list(ada = model_tuned, rf = model_rf))) Models: ada, rf Number of resamples: 100 ROC Min. 1st Qu. Median Mean 3rd Qu. Max. NA's ada 0.7500 0.9092 0.9531 0.9435 0.9883 1 0 rf 0.7143 0.9009 0.9524 0.9353 0.9842 1 0 Sens ... > summary(diffs <- diff(resamps)) Call: summary.diff.resamples(object = diffs <- diff(resamps)) p-value adjustment: bonferroni Upper diagonal: estimates of the difference Lower diagonal: p-value for H0: difference = 0 ROC ada rf ada 0.008202 rf 0.3594 ... }}} Первая команда сравнивает набранные статистики по ROC метрике между разными методами. Как можно видеть, RandomForest в целом смещён влево по сравнению с AdaBoost. Вторая команда производит сравнение распределений по теории гипотез. Из этого сравнения видно, что AdaBoost немного лучше RandomForest (на 0.008), но значимость нулевой гипотезы (что модели равносильны) равна 36%, то есть разница есть, но она может быть статистической ошибкой.

Бинарные классификаторы. Практика. Часть 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]]

Построение бинарных классификаторов. Теория.

==**ROC-кривая**==

Перед введением понятия ROC-кривой необходимо предварительно объяснить из какой задачи оно появляется. Вообще, при решении проблемы классификации объекты имеют маркировку либо “Да”, либо “Нет”, и предсказаний также возможно только два, что приводит к четырём возможностям: “Да” для самого объекта и “Да” в предсказании, “Да” для самого объекта и “Нет” в предсказании и так далее. В целом, истинные значения и предсказания складываются в матрицу 2×2 (ниже пример такой матрицы для ста объектов)

|=|=”Да” объекта |=”Нет” объекта|
|=”Да” предсказания| 60| 10|
|=”Нет” предсказания| 5| 25|

Естественно, что классификатор тем лучше, чем меньше у него внедиагональных элементов. И потому достаточно часто используемой характеристикой является так называемая //accuracy// (более подробно можно прочитать [[http://en.wikipedia.org/wiki/Accuracy_and_precision|здесь]])

//accuracy// = {количество правильно распознанных объектов} / {количество всех объектов}

В вышеприведённом примере, //accuracy// = (60 + 25)/(60 + 10 + 5 + 25) = 85%

Однако, несмотря на интуитивную понятность этой характеристики, существуют задачи, которые с её помощью решить нельзя. Допустим, что в задаче фрода нам важно не столько правильно распознать ситуации нарушения/не нарушения пользователем правил, сколько с высокой надёжностью выявить всех возможных нарушителей. Возможно, что выявленное множество будет достаточно малым, чтобы позволить дополнительную ручную проверку. В этом случае мы готовы пожертвовать точностью “Нет” предсказания ради того, чтобы точно идентифицировать все “Да” объекта, или другими словами, чтобы в вышеприведённом примере во второй строке первого столбца стоял 0 или число как можно меньшее. Но так как при этом мы всё же не хотим все объекты записывать в положительно определённые, необходимо найти какой-то баланс и эту задачу и решает ROC-кривая.

ROC расшифровывается как **R**eceiver **O**perating **C**haracteristic, т.е. рабочая характеристика приёмника и более-менее полное введение в это понятие можно прочитать на [[https://ru.wikipedia.org/wiki/ROC-%D0%BA%D1%80%D0%B8%D0%B2%D0%B0%D1%8F|вики]]. Для целей данной статьи достаточно понимать, что практически все классификаторы возвращают не метку объекта, а вероятность принадлежности объекта к тому или иному классу, то есть типичный ответ выглядит так

|=Объекты|=Вероятность “Да”|=Вероятность “Нет”|
|объект 1| 80%| 20%|
|объект 2| 49%| 51%|
|объект 3| 30%| 70%|

В этом случае для того, чтобы отнести объект к тому или иному классу, нужно выбрать какой-то порог вероятности, по которому будет происходить конечная классификация. В простейшем случае такой порог выбирается равным 50% и в этом примере объекты 2 и 3 будут промаркированы как “Нет”. Однако, с точки зрения фрода объект 2 является подозрительным и поэтому имеет смысл сдвинуть порог до 48%. Естественно, что выбирая различные пороги, будем получать разное количество правильно и неправильно определённых объектов и если нанести все точки на график, то как раз и получится ROC-кривая

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

Здесь по оси Y отложена //sensitivity//(чувствительность) – доля правильно идентифицированных объектов класса “Да” (в первом примере эта величина равна 60/(60 + 5) ~ 0.92), а по оси X доля правильно идентифицированных объектов класса “Нет” (в первом примере эта величина равна 25/(25 + 10) ~ 0.71). Как видно, чем больше правильно идентифицированных объектов класса “Да”, тем хуже определение объектов класса “Нет” и наоборот. При этом всегда есть две предельные точки, с //sensitivity// 0 и 1, соответственно когда все объекты маркируются “Нет” или все объекты маркируются “Да”. Классификатор получается тем лучше, чем сильнее ROC-кривая прижимается к верхнему левому углу, и идеальная кривая выглядит следующим образом

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

Таким образом задачей будет построение не просто классификаторов обладающих высокой //accuracy//, а классификаторов с наилучшей ROC-кривой, так как это позволит убить сразу двух зайцев: построить хороший классификатор и при этом дать возможность оптимизации классификатора под конкретную задачу. Более того, как будет показано ниже, перекалибровка результатов классификатора позволяет повысить конечную точность вычислений.

Для сравнения различных ROC-кривых используется метрика AUC – **A**rea **U**nder **C**urve (площадь под кривой), чем она больше, тем лучше ROC-кривая. Допуская некоторую вольность речи в ходе вычислений вместо AUC будет использовать слово ROC. Никакой несогласованности при этом не возникает, так как ROC-кривая означает график ROC-кривой, а число, называемое ROC обозначает площадь под этой кривой.

==**Тестирование и кросс-валидация**==

Одной из основных проблем различных методов классификации является переобучение, то есть ситуация, когда алгоритм настолько хорошо обучается на имеющихся данных, что производит правильную идентификацию в 100% случаев. И это было бы хорошо, но к сожалению практика показывает, что столь хорошо обученные методы начинают сильно ошибаться, когда их пытаются применить к новым данным. Это связано обычно с тем, что в исходных данных содержатся не имеющие значения шумы и попросту неправильные значения. Поэтому первый шаг, который имеет смысл сделать – это разбить исходные данные на две выборки, обучающую и проверочную. После обучения модели на обучающей выборке необходимо проверить её на проверочной выборке и в качестве меры качества обучения оценивать результаты полученные на проверочной выборке.

Однако, это не единственная операция которую нужно сделать. Допустим, что будет сравниваться несколько алгоритмов между собой, каждый из которых будет обучаться на обучающей выборке и проверяться на проверочной. При этом возникает опасность того, что эффективность работы алгоритмов будет зависеть от разбиения выборки. Может так оказаться (и оказывается), что на одних и тех же данных при одном разбиении выигрывает один алгоритм, а при другом разбиении – другой алгоритм. Чтобы уменьшить зависимость от разбиения производят операцию кросс-валидации. Она состоит в том, что выборка разбивается на несколько частей (стандартно берётся 10) и затем последовательно каждая из частей фиксируется как проверочная, а на оставшихся производится обучение. В результате получается набор характеристик, на каждой из проверочных выборок, который усредняется и уже это число используется в качестве меры эффективности алгоритма. Подобный подход позволяет снизить влияние разбиения на оценку качества алгоритма и к тому же даёт оценку дисперсии точности алгоритма.

Чтобы ещё больше снизить зависимость от разбиения иногда проводят процедуру повторной кросс-валидации. При этом процедура кросс-валидации повторяется несколько раз, с разными вариантами разбиения и в результате набирается достаточно богатая статистика, чтобы можно было достаточно корректно судить об эффективности того или иного метода, правда подобный подход используется не всегда, так как обычно он очень ресурсоёмкий. Однако, надо всё же обратить внимание на то, что кросс-валидация сама по себе не даёт точное значение эффективности наилучшего алгоритма, на практике значения полученные в результате кросс-валидации и на реальных данных могут заметно отличаться. Процесс кросс-валидации лишь позволяет сравнить разные алгоритмы между собой, показывая, какой алгоритм даёт хорошие результаты при этом мало завися от степени зашумлённости исходных данных, то есть в каком-то смысле оценивает алгоритм сам по себе, а не в зависимости от качества и вида входных данных.

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

# Выборка разибвается на обучающую и проверочную.
# Выбирается семейство алгоритмов и на обучающей выборке производится процедура повторной кросс-валидации. По результатам проверки выбирается наилучший алгоритм.
# Лучший алгоритм из пункта 2 проверяется на проверочной выборке и производится сравнение данных кросс-валидации с данными полученными на тестовой выборки. Если результат не устраивает, то выбирается новое семейство алгоритмов и повторяются пункты 2-3.

Построение бинарных классификаторов. Введение

В данной серии заметок будет показан пример построения бинарных классификаторов в R с использованием пакета caret и ROC-кривой. Основная цель состоит в понимании шагов, которые нужно совершить, чтобы получить правильный конечный результат, а также методов оценки качества построенных классификаторов и способов их улучшения. При этом предполагается базовое понимание работы с R. Все вычисления можно скачать здесь.

В самом простейшем понимании бинарный классификатор – это конструкция, которая каждому объекту по совокупности его признаков ставит в соответствие один из двух ответов: “Да” или “Нет”. Необходимость в построении бинарных классификаторов появляется достаточно часто, например в задаче определения фрода (является ли совокупность действий пользователя фродом), в задаче определения ухода пользователя из проекта (ушел или нет пользователь в зависимости от его активности на проекте) и многих других.

Бинарные классификаторы умеют строить все классификационные алгоритмы, но их количество огромно и в R существует ещё большее количество их реализаций. При этом каждый пакет имеет свои особенности построения моделей, из-за чего заблудитьсяв этом многообразии очень легко и исследователь, к сожалению, зачастую склоняется к использованию одного-двух алгоритмов, которые для него привычнее и проще использовать. С целью решения этой проблемы, Max Kuhn разработал пакет [[http://topepo.github.io/caret/index.html|caret]], который унифицирует использование самых различных классификаторов, а также даёт достаточно мощные и универсальные методы для оценки и сравнения работы разных моделей.

Однако, прежде чем начать построение моделей, необходимо немного теории о том, что такое ROC-кривая и кросс-валидация и почему именно эти конструкции будут использованы при построении классификаторов. Те, кто знает, что это такое или кого интересуют конкретные примеры работы с R, могут пропустить [[http://blog.arkoniak.com/?p=270|следующую]] заметку и перейти сразу к [[http://blog.arkoniak.com/?p=280|построению классификатора]].