Устойчивая красота неприличных моделей

    Титаника на КДПВ нет, он утонул
    — Не могли бы вы построить нам статистическую модель?
    — С удовольствием. Можно посмотреть на ваши исторические данные?
    — Данных у нас ещё нет. Но модель всё равно нужна.

    Знакомый диалог, не правда ли? Далее возможны два варианта развития событий:

    A. «Тогда приходите, когда появятся данные.» Вариант рассматриваться не будет как тривиальный.
    Б. «Расскажите, какие факторы по вашему мнению наиболее важны.» Остаток статьи про это.

    Под катом рассказ о том, что такое improper model, почему их красота устойчива и чего это стоит. Всё на примере многострадального набора данных о выживании пассажиров Титаника.

    Откуда такое странное название


    Из статьи The Robust Beauty of Improper Linear Models in Decision Making товарища Robyn M. Dawes, который в свою очередь ссылается на работы предшественников.

    Приличные линейные модели (proper linear models) — те, в которых веса назначаются таким образом, чтобы полученная линейная комбинация предсказывала интересующую величину оптимальным образом. Примером может служить обычная линейная регрессия, подогнанная методом наименьших квадратов. [...]

    Неприличные линейные модели (improper linear models) — те, в которых веса определяются неоптимально. Например, назначаются на основании интуиции, предыдущего опыта или приравниваются единице.

    Обычная линейная модель выглядит так:

    ŷ = β0 + β1x1 + β2x2 +… + βmxm

    Здесь ŷ — оценка зависимой переменной, β0 — свободный член, β1...βm — коэффициенты регрессии, x1...xm — независимые переменные.

    По причине отсутствия данных для определения значений β1...βm мы поступим волюнтаристски: назначим им значения +1 (влияет положительно), -1 (влияет отрицательно) и 0 (не влияет).

    Притянутый за уши пример


    Характерными примерами статистических моделей, о которых шёл разговор в начале статьи, являются скоркарты для потребительского кредитования (они предсказывает вероятность глубокой просрочки или невозврата клиентом долга), модели отклика (они предсказывают вероятность того, что клиент ответит на предложение что-то купить или подписаться на продукт), модели оттока (предсказывают вероятность того, что клиент уйдёт к конкуренту) и т.п.

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


    Участники состязаний


    • ImproperModel — регрессия с единичными весами (тема этой статьи)
    • ProperModel — логистическая регрессия с теми же независимыми переменными
    • LogisticRegression — логистическая регрессия со всеми независимыми переменными
    • RandomForest — один из широко распространённых современных алгоритмов

    Как будем сравнивать


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

    Чудес не бывает и совсем без каких-то знаний толковую модель не построить. В данном случае таким знанием будет галантность — женщин и детей при посадке в шлюпки пропускали вперёд. На основании этого априорного знания для регрессии с единичными весами назначаем переменной female вес +1, а переменной age вес -1. Кроме того, пассажирам с билетами второго и третьего класса, у которых каюты в трюме, дальше бежать до шлюпок, поэтому переменной pclass назначаем вес -1.

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

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

    Повторим эксперимент тысячу раз и посмотрим на результаты. Качество модели будем измерять с помощью коэффициента Gini (G1).

    Код на R с пошаговыми комментариями
    install.packages("pROC")
    install.packages("randomForest")
    library(pROC)
    library(randomForest)
    

    Читаем исходные данные, также доступны тут: titanic3.csv
    data <- read.csv('~/habr/unit_weights/titanic3.csv')
    

    Зависимая переменная survived
    data$survived <- as.factor(data$survived)
    

    Исключаем переменные, у которых слишком много разных значений для нормального анализа
    data$name <- NULL
    data$ticket <- NULL
    data$cabin <- NULL
    data$home.dest <- NULL
    data$embarked <- NULL
    

    Не используем переменные из будущего
    data$boat <- NULL
    data$body <- NULL
    

    Заменяем пропущенные значения средним
    data$age[is.na(data$age)] <- mean(data$age, na.rm=TRUE)
    data$fare[is.na(data$fare)] <- mean(data$fare, na.rm=TRUE)
    

    Преобразуем пол в индикаторную переменную
    data$female <- 0
    data$female[which(data$sex == 'female')] <- 1
    data$sex <- NULL
    

    Смотрим на то, что осталось:
     survived     pclass           age              sibsp       
     0:809    Min.   :1.000   Min.   : 0.1667   Min.   :0.0000  
     1:500    1st Qu.:2.000   1st Qu.:22.0000   1st Qu.:0.0000  
              Median :3.000   Median :29.8811   Median :0.0000  
              Mean   :2.295   Mean   :29.8811   Mean   :0.4989  
              3rd Qu.:3.000   3rd Qu.:35.0000   3rd Qu.:1.0000  
              Max.   :3.000   Max.   :80.0000   Max.   :8.0000  
         parch            fare             female     
     Min.   :0.000   Min.   :  0.000   Min.   :0.000  
     1st Qu.:0.000   1st Qu.:  7.896   1st Qu.:0.000  
     Median :0.000   Median : 14.454   Median :0.000  
     Mean   :0.385   Mean   : 33.295   Mean   :0.356  
     3rd Qu.:0.000   3rd Qu.: 31.275   3rd Qu.:1.000  
     Max.   :9.000   Max.   :512.329   Max.   :1.000
    

    Повторяем эксперимент тысячу раз
    im.gini = NULL
    pm.gini = NULL
    lr.gini = NULL
    rf.gini = NULL
    set.seed(42)
    for (i in 1:1000) {
    

    Разбиваем пассажиров на две выборки — 70% отправляем в обучающую выборку, на остальных 30% будем мерять качество модели.
      data$random_number <- runif(nrow(data),0,1)
      development <- data[ which(data$random_number > 0.3), ]
      holdout     <- data[ which(data$random_number <= 0.3), ]
      development$random_number <- NULL
      holdout$random_number <- NULL
    

    Модель с единичными весами
      beta_pclass <- -1/sd(development$pclass)
      beta_age    <- -1/sd(development$age   )
      beta_female <-  1/sd(development$female)
      im.score <- beta_pclass*holdout$pclass + beta_age*holdout$age + beta_female*holdout$female 
      im.roc <- roc(holdout$survived, im.score)
      im.gini[i] <- 2*im.roc$auc-1
    

    Обычная модель — логистическая регрессия с теми же независимыми переменными
      pm.model = glm(survived~pclass+age+female, family=binomial(logit), data=development)
      pm.score <- predict(pm.model, holdout, type="response")
      pm.roc <- roc(holdout$survived, pm.score)
      pm.gini[i] <- 2*pm.roc$auc-1
    

    Логистическая регрессия со всеми переменными
      lr.model = glm(survived~., family=binomial(logit), data=development)
      lr.score <- predict(lr.model, holdout, type="response")
      lr.roc <- roc(holdout$survived, lr.score)
      lr.gini[i] <- 2*lr.roc$auc-1
    

    Всеми любимый (и не без оснований) RandomForest
      rf.model <- randomForest(survived~., development)
      rf.score <- predict(rf.model, holdout, type = "prob")
      rf.roc <- roc(holdout$survived, rf.score[,1])
      rf.gini[i] <- 2*rf.roc$auc-1
    }
    

    Выводим результаты
    bpd<-data.frame(ImproperModel=im.gini, ProperModel=pm.gini, LogisticRegression=lr.gini, RandomForest=rf.gini)
    png('~/habr/unit_weights/auc_comparison.png', height=700, width=400, res=120, units='px')
    boxplot(bpd, las=2, ylab="Gini", ylim=c(0,1), par(mar=c(9,5,1,1)+ 0.1), col=c("red","green","royalblue2","brown"))
    dev.off()
    mean(im.gini)
    mean(pm.gini)
    mean(lr.gini)
    mean(rf.gini)
    mean(im.gini)/mean(rf.gini)
    mean(pm.gini)/mean(rf.gini)
    mean(lr.gini)/mean(rf.gini)
    mean(rf.gini)/mean(rf.gini)
    


    Результаты


    Модель Gini В процентах от лучшего
    ImproperModel 0.639 90.4%
    ProperModel 0.667 94.3%
    LogisticRegression 0.679 96.0%
    RandomForest 0.707 100%


    Далекоидущие выводы


    Пессимист скажет, что модель с единичными весами в этом сравнении заняла последнее место, и это верно. Но так же верно, что она набрала 90% от лучшего результата без использования исторических данных, и отстала от обычной логистической регрессии с теми же независимыми переменными всего на 4%.

    Почему так происходит


    В статье с необычным названием Estimating Coefficients in Linear Models: It Don't Make No Nevermind её автор (Ховард Вайнер) приводит такую теорему равных весов:

    Если k линейно независимых переменных xi (i = 1, ..., k) с нулевым средним значением и единичной дисперсией используются для предсказания переменной y, которая также отмасштабирована до нулевого среднего и единичной дисперсии, и стандартизованные значения коэффициентов регрессии по методу наименьших квадратов βi (i = 1, ..., k) равномерно распределены на интервале [0.25, 0.75], то при переходе к равным весам (0.5) матожидание уменьшения доли дисперсии зависимой переменной, объясняемой моделью, будет меньше k/96. Потери ещё меньше, если xi коррелированы между собой.

    В приведённом выше примере регрессия логистическая, но эффект всё равно виден.

    Далее в той же статье автор отмечает, что модели с единичными весами устойчивы в частности потому, что они не подвержены влиянию выбросов в обучающей выборке и их невозможно перетренировать.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 11

      0
      То есть, имея вектор признаков длиной К, можно перебрать 3^K вариантов (-1 0 1 на каждый из признаков) и выбрать наилучшую модель? Хорошо, а дальше что?
        +1
        Нет, идея в том, чтобы вообще ничего не перебирать. Не обучать модель, а выбрать значения коэффициентов исключительно на основании априорных знаний.
        +2
        Почему для proper и improper выбрали именно такой вариант, как приличные и неприличные, соответственно? :)
          0
          А как надо было перевести? translate.ru предлагает «надлежащий, приличный, присущий, самый» и «неподходящий, неуместный, неправильный, непристойный, незаконный».

          А «improper model» переводит как «неподходящая модель», что совсем неправильно — модель-то как раз подходящая.
            +2
            У proper много вариантов перевода.
            В данном случае наверно больше подходит совершенный или точный, и их антонимы для improper.
            А то напомнило известный случай — «парень клеит модель в клубе».
              0
              Варианты «совершенный» и «точный» не подходят — модели по определению являются грубым приближением к реальности, к тому же коэффициенты подгоняем по случайной выборке, то есть неизбежны ошибки. На приведённом графике видно, что improper model зачастую показывает лучше результат, чем proper (распределения Gini перекрываются).

              Кроме того, в оригинале тогда было бы perfect или exact, а судя по совсем не статистическому слову beauty там та же игра слов про «клеить модель в клубе».
                0
                Нет проблем :)
                Ваше вИдение имеет право на существование!
          0
          Извините, не в ту ветку.
            0
            Да лаадно! Мне понравилась эта неприличная штучка. Следует ли ожидать, что, например, в соревновании на Kaggle, заведя единичные веса можно ожидать с первого же коммита оказаться в районе медианы среди других соревнующихся?
              +1
              В общем случае не следует. Если связи в данных сложные, например, задача про распознование картинок, то точно нет. Если связи более-менее простые, например, кредитный скоринг, тогда может быть.

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

            Only users with full accounts can post comments. Log in, please.