Настройка гиперпараметров для оптимизации производительности.

Мне лично нравится использовать mlr для выполнения задач машинного обучения, но вы также можете использовать любую другую библиотеку по своему вкусу.

Сначала загрузим соответствующие библиотеки:

  • mlr для алгоритмов машинного обучения
  • FSelector для выбора функций. (Опять же, вы можете использовать любую библиотеку выбора функций, которую пожелаете)
  • rpart.plot потому что я хочу визуализировать дерево и буду использовать rpart алгоритм дерева решений.
library(mlr) 
library(FSelector) 
library(rpart.plot)

glimpse(Diabetes)
Observations: 768 Variables: 6 
$ Pregnancies <fct> Yes, Yes, Yes, Yes, No, Yes, Yes... 
$ Glucose <fct> Hyperglycemia, Normal, Hyperglyc... 
$ BMI <fct> Obese, Overweight, Normal, Overw... 
$ DiabetesPedigreeFunction <dbl> 0.627, 0.351, 0.672, 0.167, 2.28... $ Age <int> 50, 31, 32, 21, 33, 30, 26, 29, ... $ Outcome <fct> Positive, Negative, Positive, Ne...

Взгляд на набор данных, над которым я работал в моем предыдущем посте, показывает переменные, с которыми мы будем работать.

Набор для обучения и тестирования

Я собираюсь работать с набором данных для обучения / тестирования 80/20.

set.seed(1000) 
train_index <- sample(1:nrow(Diabetes), 0.8 * nrow(Diabetes)) 
test_index <- setdiff(1:nrow(Diabetes), train_index) 
train <- Diabetes[train_index,] test <- Diabetes[test_index,]
list( train = summary(train), test = summary(test) )
  • Обучающий набор показывает, что наша целевая переменная имеет 212 положительных результатов и 402 отрицательных результата.
  • Набор тестов показывает, что у нас есть 56 положительных результатов и 98 отрицательных результатов.

Здесь наблюдается очевидный дисбаланс классов с нашей целевой переменной, и поскольку он смещен в сторону «отрицательного» (без диабета), нам будет труднее построить прогностическую модель для «положительного» результата.

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

Древо решений

(dt_task <- makeClassifTask(data=train, target="Outcome"))
Supervised task: train 
Type: classif 
Target: Outcome 
Observations: 614 
Features: numerics factors ordered functionals 
            2        3        0       0 
Missings: FALSE 
Has weights: FALSE 
Has blocking: FALSE 
Has coordinates: FALSE 
Classes: 2 Positive Negative 
             212      402 
Positive class: Positive

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

Как мы видим, уровень Positive из Outcome по умолчанию соответствует классу Positive в задаче машинного обучения. Это не всегда так. Вы можете изменить это, указав Positive=x (где «x» - целевой уровень переменной, которую вы хотите предсказать). В этом случае мы хотим спрогнозировать людей, у которых есть диабет (а именно, уровень Positive переменной Outcome).

Ученик

(dt_prob <- makeLearner('classif.rpart', predict.type="prob"))
Learner classif.rpart from package rpart 
Type: classif 
Name: Decision Tree; Short name: rpart 
Class: classif.rpart 
Properties: twoclass,multiclass,missings,numerics,factors,ordered,prob,weights,featimp 
Predict-Type: prob 
Hyperparameters: xval=0

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

Выбор функции

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

generateFilterValuesData(dt_task, method = c("information.gain","chi.squared", "gain.ratio")) %>% plotFilterValues()

Функция generateFeatureImportanceData также работает аналогичным образом. Кроме того, он покажет нам важность каждой функции в соответствии с заданными критериями производительности. Я выбрал Истинно положительный процент и Площадь под кривой.

generateFeatureImportanceData(task=dt_task, learner = dt_prob,measure = tpr, interaction = FALSE)
FeatureImportance: Task: train Interaction: FALSE Learner: classif.rpart Measure: tpr Contrast: function (x, y) x - y Aggregation: function (x, ...) UseMethod("mean") Replace: TRUE Number of Monte-Carlo iterations: 50 Local: FALSE 
tpr
Pregnancies 0
Glucose -0.1869811
BMI -0.1443396
DiabetesPedigreeFunction -0.06339623
Age -0.06896226
generateFeatureImportanceData(task=dt_task, learner = dt_prob,measure = auc, interaction = FALSE)
FeatureImportance: Task: train 
Interaction: FALSE 
Learner: classif.rpart 
Measure: auc 
Contrast: function (x, y) x - y 
Aggregation: function (x, ...) 
UseMethod("mean") 
Replace: TRUE 
Number of Monte-Carlo iterations: 50 
Local: FALSE 
auc
Pregnancies 0
Glucose -0.1336535
BMI -0.07317023
DiabetesPedigreeFunction -0.01907362
Age -0.08251478

Как видно из приведенного выше вывода:

  • Соотношение получения и получения информации показывает нулевой балл или низкий балл для беременностей.
  • generateFeatureImportanceData показывает нулевой балл по беременности, если рассматривать TPR и AUC как показатель эффективности.

Глядя на все свидетельства, я не стану отбрасывать только 32_ переменной. Другие переменные по-прежнему показывают возможности прогнозирования по определенным критериям.

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

Глядя ниже, я взяла беременность из наших наборов для обучения и тестов и сделала новую задачу классификации с нашим новым набором для обучения.

set.seed(1000) 
train <- select(train, -Pregnancies) 
test <- select(test, -Pregnancies)
list( train = summary(train), test = summary(test) )

Другая проблема заключается в том, что в категории глюкозы «гипогликемия» имеет только 5 представлений во всем наборе данных. Когда мы пойдем на перекрестную проверку, это будет проблемой, потому что почти наверняка этот уровень будет отсутствовать ни в одной из складок. Это не позволит правильно обучить модель позже. Поэтому нам нужно удалить гипогликемию из обоих наборов данных:

train <- filter(train, Glucose!='Hypoglycemia') %>% droplevels() 
test <- filter(test, Glucose!='Hypoglycemia') %>% droplevels()
list( train = summary(train), test = summary(test) )

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

(dt_task <- makeClassifTask(data=train, target="Outcome"))
Supervised task: train 
Type: classif 
Target: Outcome 
Observations: 609 
Features: numerics factors ordered functionals 
            2        2       0        0 
Missings: FALSE 
Has 
weights: FALSE 
Has blocking: FALSE 
Has coordinates: FALSE 
Classes: 2 
Positive Negative 
 210        399 
Positive class: Positive

Настройка гиперпараметров

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

Настройка алгоритма машинного обучения в mlr включает следующие процедуры:

  • Определите пространство поиска.
  • Определите алгоритм оптимизации (также известный как метод настройки).
  • Определите метод оценки (т. Е. Стратегию повторной выборки и показатель эффективности).

Пространство поиска

Таким образом, определение пространства поиска - это когда указываются параметры для данной функции / переменной. Поскольку в этом посте мы фокусируемся на деревьях решений, используя функцию getParamSet(learner), мы можем получить гиперпараметры для желаемого алгоритма.

Нам нужны параметры для rpart учащегося.

getParamSet("classif.rpart")

Мы видим, что есть 10 гиперпараметров для rpart, и только xval не настраивается (т.е. его нельзя изменить).

Вот объяснение вышеуказанных параметров:

  • minsplit

Минимальное количество наблюдений в узле, для которого процедура даже попытается вычислить разбиение. Значение по умолчанию - 20. Настройка этого параметра может сэкономить время вычислений, поскольку узлы меньшего размера почти всегда удаляются путем перекрестной проверки.

  • minbucket

Минимальное количество наблюдений в конечном узле. По умолчанию используется minspit/3 (хотя я не знаю, является ли это оптимальным выбором).

  • maxcompete

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

  • maxdepth

Это определяет, насколько глубоко может быть построено дерево.

  • cp

Это параметр сложности. Чем ниже он, тем крупнее вырастет дерево. cp=1 не приведет к никакому дереву. Это также помогает при обрезке дерева. Каким бы ни было значение cp, мы должны быть осторожны при чрезмерной обрезке дерева. Параметры более высокой сложности могут привести к переполнению дерева. Я лично считаю, что очень высокое значение параметра сложности (в моем случае выше 0,3) приводит к недостаточной подгонке дерева из-за чрезмерной обрезки, но это также зависит от количества имеющихся у вас функций.

Раньше я не использовал суррогатные переменные, поэтому в данном случае я их опущу. Я просто не хочу переходить к ним на данном этапе без адекватного понимания, чтобы объяснить себя.

Итак, теперь нам нужно установить гиперпараметры на то, что мы хотим. Помните, что нет однозначно правильного ответа. Нам нужно определить пространство и запустить поиск, чтобы автоматически найти, какие значения гиперпараметров дадут нам лучший результат В СООТВЕТСТВИИ С ОПРЕДЕЛЕННЫМ МЫ ПРОСТРАНСТВОМ. Это означает, что на производительность может или не может повлиять изменение гиперпараметров (большое или малое).

Так что либо действуйте интуитивно, если у вас мало времени, либо определите большое пространство для гиперпараметров, и если у вас есть мощная машина и выдающееся терпение, позвольте mlr сделать тяжелую работу.

dt_param <- makeParamSet( 
makeDiscreteParam("minsplit", values=seq(5,10,1)), makeDiscreteParam("minbucket", values=seq(round(5/3,0), round(10/3,0), 1)), 
makeNumericParam("cp", lower = 0.01, upper = 0.05), makeDiscreteParam("maxcompete", values=6), makeDiscreteParam("usesurrogate", values=0), makeDiscreteParam("maxdepth", values=10) )

Для модели, которую я использую, я установлю:

  • minsplit = [5,6,7,8,9,10]
  • minbucket = [5/3, 10/3]
  • cp = [0.01, 0.05]
  • maxcompete = 6
  • usesurrogate = 0
  • maxdepth = 10

Основная причина, по которой я не определяю огромные пространства, заключается в том, что я делал это раньше, и для его запуска потребовалось около 4 часов и было 100 000 комбинаций гиперпараметров. Лично для меня это слишком много времени, если я не занимаюсь проектом, который принесет огромную пользу.

Алгоритм оптимизации

Один из доступных нам стандартных, но медленных алгоритмов - Grid Search для выбора подходящего набора параметров.

ctrl = makeTuneControlGrid()

Таким образом мы указываем, что хотим запустить поиск по сетке. С пробелом, который мы указали выше, мы получаем 120 возможных комбинаций в случае dt_param.

Оценка настройки с передискретизацией

После указания вышеуказанного мы можем теперь провести процесс настройки. Мы определяем стратегию передискретизации и отмечаем производительность.

Мы установили стратегию повторной выборки на трехкратную перекрестную проверку со стратифицированной выборкой. Стратифицированная выборка полезна, если у вас есть несбалансированность классов в целевой переменной. Он будет пытаться иметь одинаковое количество классов в каждой группе. Обычно для хорошего результата k-кратной перекрестной проверки хорошо работает 3–5-кратная перекрестная проверка. Сколько складок также будет зависеть от объема имеющихся данных (т. е. количества уровней для факторов / категорий, количества строк).

rdesc = makeResampleDesc("CV", iters = 3L, stratify=TRUE)

Тюнинг

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

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

set.seed(1000) 
(dt_tuneparam <- tuneParams(learner=dt_prob, 
                 resampling=rdesc, 
                 measures=list(tpr,auc, fnr, mmce, tnr, setAggregation(tpr, test.sd)), 
                 par.set=dt_param, 
                 control=ctrl, 
                 task=dt_task, 
                 show.info = TRUE) )
Tune result: Op. pars: minsplit=9; minbucket=2; cp=0.0144; maxcompete=6; usesurrogate=0; maxdepth=10 tpr.test.mean=0.6095238,auc.test.mean=0.7807376,fnr.test.mean=0.3904762,mmce.test.mean=0.2725780,tnr.test.mean=0.7894737,tpr.test.sd=0.0704698

Запустив тюнер, мы видим 120 возможных комбинаций заданных нами гиппараметров. Окончательный результат в нижней части вывода (т.е. [Tune] Result:...) дает нам оптимальную комбинацию. Это будет меняться каждый раз, когда вы его запускаете. Пока вы можете видеть аналогичные результаты производительности, не должно быть никакой опасности в продолжении работы с текущим набором данных. Если результаты производительности начинают слишком сильно расходиться, данные могут быть неадекватными.

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

ПРИМЕЧАНИЕ tuneParams знает, какие показатели производительности следует минимизировать, а какие - максимизировать. Так, например, он знает, как добиться максимальной точности и минимизировать частоту ошибок (mmce).

С другой стороны, я определил большое пространство поиска, на его завершение ушло около 4 часов, и в итоге получилось 100 000 комбинаций. Это был результат:

[Настроить] Результат: minsplit = 17; minbucket = 7; cp = 0,0433; maxcompete = 4; usesurrogate = 0; maxdepth = 7: tpr.test.mean = 0,6904762, auc.test.mean = 0,7277720, f1.test.mean = 0,6156823, acc.test.mean = 0,7283265, mmce.test.mean = 0,2716735, timepredict.test.mean = 0,0000000, tnr.test.mean = 0,7460928

Хотя TPR выше, я собираюсь использовать свои предыдущие гиперпараметры, потому что это менее затратно с точки зрения вычислений.

Оптимальные гиперпараметры

list( `Optimal HyperParameters` = dt_tuneparam$x, 
      `Optimal Metrics` = dt_tuneparam$y )
$`Optimal HyperParameters` 
$`Optimal HyperParameters`$minsplit [1] 9 
$`Optimal HyperParameters`$minbucket [1] 2 
$`Optimal HyperParameters`$cp [1] 0.01444444 
$`Optimal HyperParameters`$maxcompete [1] 6 
$`Optimal HyperParameters`$usesurrogate [1] 0 
$`Optimal HyperParameters`$maxdepth [1] 10 
$`Optimal Metrics` 
tpr.test.mean auc.test.mean fnr.test.mean mmce.test.mean 
0.60952381    0.78073756    0.39047619      0.27257800 
tnr.test.mean tpr.test.sd 
0.78947368     0.07046976

Используя dt_tuneparam$x, мы можем извлечь оптимальные значения, а dt_tuneparam$y дает нам соответствующие показатели производительности.

setHyperPars настроит учащегося на свои оптимальные значения.

dtree <- setHyperPars(dt_prob, par.vals = dt_tuneparam$x)

Модельное обучение

set.seed(1000) 
dtree_train <- train(learner=dtree, task=dt_task) 
getLearnerModel(dtree_train)
n= 609 node), split, n, loss, yval, (yprob) * denotes terminal node 1) root 609 210 Negative (0.34482759 0.65517241) 2) Glucose=Hyperglycemia 149 46 Positive (0.69127517 0.30872483) 4) BMI=Obese 117 28 Positive (0.76068376 0.23931624) * 5) BMI=Normal,Overweight 32 14 Negative (0.43750000 0.56250000) * 3) Glucose=Normal 460 107 Negative (0.23260870 0.76739130) 6) Age>=28.5 215 78 Negative (0.36279070 0.63720930) 12) BMI=Underweight,Overweight,Obese 184 77 Negative (0.41847826 0.58152174) 24) DiabetesPedigreeFunction>=0.5275 61 23 Positive (0.62295082 0.37704918) * 25) DiabetesPedigreeFunction< 0.5275 123 39 Negative (0.31707317 0.68292683) * 13) BMI=Normal 31 1 Negative (0.03225806 0.96774194) * 7) Age< 28.5 245 29 Negative (0.11836735 0.88163265) *
rpart.plot(dtree_train$learner.model, roundint=FALSE, varlen=3, type = 3, clip.right.labs = FALSE, yesno = 2)

rpart.rules(dtree_train$learner.model, roundint = FALSE)
Outcome 
0.24 when Glucose is Hyperglycemia & BMI is Obese 
0.38 when Glucose is Normal & BMI is Underweight or Overweight or Obese & Age >= 29 & DiabetesPedigreeFunction >= 0.53 
0.56 when Glucose is Hyperglycemia & BMI is Normal or Overweight
 
0.68 when Glucose is Normal & BMI is Underweight or Overweight or Obese & Age >= 29 & DiabetesPedigreeFunction < 0.53 
0.88 when Glucose is Normal & Age < 29 0.97 when Glucose is Normal & BMI is Normal & Age >= 29

После обучения дерева решений я смог построить его с помощью функции rpart.plot, и я легко могу увидеть правила дерева с помощью rpart.rules. Поскольку mlr - это оболочка для алгоритмов машинного обучения, я могу настроить ее по своему вкусу, и это только один пример.

Прогнозирование модели (тестирование)

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

set.seed(1000) 
(dtree_predict <- predict(dtree_train, newdata = test))
Prediction: 154 observations predict.type: prob threshold: Positive=0.50,Negative=0.50 time: 0.00 truth prob.Positive prob.Negative response 1 Negative 0.3170732 0.6829268 Negative 2 Positive 0.6229508 0.3770492 Positive 3 Negative 0.4375000 0.5625000 Negative 4 Negative 0.3170732 0.6829268 Negative 5 Positive 0.7606838 0.2393162 Positive 6 Negative 0.1183673 0.8816327 Negative ... (#rows: 154, #cols: 4)

Порог для классификации каждой строки составляет 50/50. Это по умолчанию, но позже его можно будет изменить (что я и сделаю).

dtree_predict %>% calculateROCMeasures()

Итак, теперь у нас есть матрица путаницы для нашей модели. Мы видим, что он отлично справляется с прогнозированием Negative результата, но плохо справляется с Positive результатом. Это по своей сути связано с дисбалансом классов в нашем наборе данных. Вот почему пороговая обработка - это более простая тактика для получения нашей предпочтительной модели. Конечно, было бы лучше, если бы у нас были сбалансированные классы и больше строк наблюдений в наших данных, но это не всегда выбор или реальность.

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

Performance <- performance(dtree_predict, measures = list(tpr,auc,mmce, acc,tnr)) %>% 
as.data.frame(row.names = c("True Positive","Area Under Curve", "Mean Misclassification Error","Accuracy","True Negative")) Performance %>% kable(caption="Performance of Decision Tree",digits = 2, format = 'html', col.names = "Result")

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

Порог

(dtree_threshold <- generateThreshVsPerfData(dtree_predict, measures = list(tpr,auc, mmce,tnr)) %>% plotThreshVsPerf() + geom_point() )

Моей личной целью для этой модели будет получение приемлемых и удовлетворительных True Positive Rate и True Negative Rate. Поскольку AUC остается неизменной для всех пороговых значений, нам не нужно беспокоиться об этом. Изменяя порог, я намеренно создаю предвзятую модель, но это нормальная проблема машинного обучения. Компромисс смещения-дисперсии настолько распространен, что нам нужно научиться ориентироваться в нем. Это совершенно другая тема, и любому, кто занимается машинным обучением, потребуются определенные знания.

Ниже вы увидите 3 разных порога:

  • Максимальный порог, при котором наш TPR ниже 100%.
  • Минимальный порог, при котором наш TPR превышает 80%.
  • Среднее значение двух порогов.
  • Максимальный порог, при котором наше TNR превышает 70%.
list( 
`TPR Threshold for 100%` = tpr_threshold100 <- dtree_threshold$data$threshold[ which.max(dtree_threshold$data$performance[ dtree_threshold$data$measure=="True positive rate"]<1)], 
`TPR Threshold for 80%` = tpr_threshold80 <- dtree_threshold$data$threshold[ which.min(dtree_threshold$data$performance[ dtree_threshold$data$measure=="True positive rate"]>0.80)], 
`Average Threshold` = avg_threshold <- mean(c(tpr_threshold100,tpr_threshold80)), 
`TNR Threshold for 80%` = tnr_threshold80 <- dtree_threshold$data$threshold[ which.max(dtree_threshold$data$performance[ dtree_threshold$data$measure=="True negative rate"]>0.70)] )

$TPR Threshold for 100% [1] 0.1212121

$TPR Threshold for 80% [1] 0.3232323

$Average Threshold [1] 0.2222222

$TNR Threshold for 80% [1] 0.3232323

Используя avg_threhsold для прогнозирования нашей модели еще раз, мы можем получить следующие показатели производительности. Однако, глядя на пороговые значения из графика и те, которые я определил выше, наша истинная отрицательная скорость сильно пострадает.

DecisionTree <- dtree_predict %>% setThreshold(avg_threshold) (dt_performance <- DecisionTree %>% performance(measures = list(tpr,auc, mmce,tnr)) )
tpr           auc      mmce     tnr 
0.8214286 0.7693149 0.3376623 0.5714286

Наш TPR сейчас составляет 82,14%. Это огромная разница по сравнению с порогом 50/50. Наш TNR снизился до 57,14%, но это следствие смещения нашей модели в сторону TPR. Ниже я объясню, глядя на новую матрицу путаницы.

(dt_cm <- DecisionTree %>% calculateROCMeasures() )

Мы замечаем и другие следующие изменения:

  • TNR снизилось до 57% из-за разницы пороговых значений.
  • FPR увеличился до 43%. Это означает, что модель имеет повышенную вероятность ошибки 1-го типа, при которой она обнаружит диабет, когда он фактически отсутствует. Это жертва, на которую мы должны были пойти, чтобы получить более высокий TPR.
  • Точность снизилась до 66%. Это еще одно следствие изменения порога. Это не повод для беспокойства, потому что наша модель выполняет то, что я задумал, - имеет высокий показатель истинных положительных результатов. Точность не является адекватным показателем производительности модели, если мы заботимся только о точном прогнозе определенного результата. Так бывает в большинстве случаев, но даже в этом случае вам все равно нужно углубиться в характеристики модели.
Performance_threshold <- performance(DecisionTree, measures = list(tpr,auc, mmce, acc, tnr)) %>% 
as.data.frame(row.names = c("True Positive","Area Under Curve", "Mean Misclassification Error","Accuracy","True Negative")) Performance_threshold %>% kable(caption=paste("Performance of Decision Tree\n\nAfter Thresholding to",(avg_threshold*100) %>% round(0),'%'), digits = 2, format = 'html', col.names = 'RESULT')

Мы можем увидеть наши новые показатели производительности после изменения порога классификации. Установление пороговых значений очень субъективно для пользователя и предмета проблемы. Иногда бывает необходимо отклонить характеристики модели в одну сторону, в зависимости от того, чего вам дорого обойдется в неправильной классификации. В этом сценарии будет много ложных срабатываний для пациентов, но если стоимость неправильной классификации этих пациентов не слишком высока, то стоит получить более высокую вероятность правильного диагноза (т. Е. Истинных положительных результатов).

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

Если у вас есть какие-либо вопросы или проблемы, пожалуйста, прокомментируйте обсуждение, и я приглашаю всех, кому это интересно, присоединиться. Спасибо

Первоначально опубликовано в Easy Data Science with R and Python.