WedX - журнал о программировании и компьютерных науках

Почему в данном случае оптимизация L_BFGS_B переходит к крайнему диапазону жизнеспособных решений?

Я понимаю, что это очень конкретный вопрос!

Чтобы помочь в объяснении: я изучаю использование линейного оптимизатора, чтобы продемонстрировать, как острые «утесы» в функциональной поверхности могут привести к неоптимальным решениям. Воспроизводимый код в R выглядит следующим образом:

library(glmnet)
library(mice)


# Load data
df <- read.csv(paste0('https://raw.githubusercontent.com/jbrownlee/Datasets',
                      '/master/pima-indians-diabetes.data.csv'), header = F)

colnames(df) <- c('Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness',
                  'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age', 'Outcome')


set.seed(40)

# Impute 0 (missing) values for columns 2 through 8 (Glucose - Age)
df[2:8] <- lapply(df[2:8], function(x) replace(x, x %in% 0, NA))
micedf <- mice(df)
df <- complete(micedf)

# Create train/test split
sample_size <- floor(0.75 * nrow(df))
train_index <- sample(seq_len(nrow(df)), size = sample_size)
train <- df[train_index,]
test  <- df[-train_index,]

# Generate model matrix format for glmnet
x <- as.matrix(train[,1:8])
y <- train$Outcome

# Fitting function
GLM_tune <- function(alpha) {
    set.seed(40)
    cvglmnet <- glmnet::cv.glmnet(x, y, nfolds = 5, family = "binomial",
                                  alpha = alpha, type.measure = "auc",
                                  parallel = F)

    return (cvglmnet$cvm[cvglmnet$lambda == cvglmnet$lambda.1se])    }

Теперь, если я введу значение где-то между 0 и 1 следующим образом:

optim(par = 0.9, fn = GLM_tune, lower = 0, upper = 1, 
      control = list(fnscale = -1, trace=3), method = c("L-BFGS-B"))

# >> $par = 0.86

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

surf <- data.frame(alpha = 0, auc = 0)   
for (a in seq(from=0, to=1000)) {
    surf[a+1,1] <- a/1000
    surf[a+1,2] <- GLM_tune(a/1000)
}

library(ggplot2)
ggplot() +
    geom_point(data=surf, size = 1.2, color = "black", aes(alpha, auc))

Однако когда я устанавливаю альфа = 1 в качестве начальной точки, алгоритм переходит к альфа = 0 во время второй итерации, а затем завершается как «окончательное» решение:

optim(par = 1, fn = GLM_tune, lower = 0, upper = 1, 
      control = list(fnscale = -1, trace=3), method = c("L-BFGS-B"))

# >> $par = 0

Почему это так? Ясно, что я не полностью понимаю алгоритм, но я предположил, что шаг по умолчанию равен 0,001 в функции optim (см. ndeps) — так почему же он должен перейти к противоположной крайности в качестве следующего шага? Я пропустил важный параметр, который должен быть установлен для этих проблем?


  • (Я не использую R и проигнорировал ваш код :) L-BGS-B не имеет фиксированного размера шага. Он использует строковый поиск. 08.02.2019
  • Я понимаю; но мне все еще неясно, почему размер шага, который он определяет, составляет (фактически) весь диапазон, когда альфа начинается с 1, но не делает этого, когда альфа начинается с 0,9. Я верю тому, что вы говорите, я просто не понимаю, почему это происходит. 13.02.2019

Ответы:


1

Из пути целевой функции видно, что она имеет много локальных максимумов, и, следовательно, алгоритм оптимизации на основе градиента, такой как "L-BFGS-B", не подходит для поиска глобального максимума. введите здесь описание изображения

Кроме того, с моим R (3.6),

optim(par = 1, fn = GLM_tune, lower = 0, upper = 1, 
      control = list(fnscale = -1, trace=3), method = c("L-BFGS-B"))$par
## [1] 1

возвращает 1, а не 0, как вы указываете.

Чтобы понять, почему он сходится к 1, мы можем взглянуть на путь оптимизации алгоритма «L-BFGS-B». Я предпочитаю использовать пакет R optimParallel https://CRAN.R-project.org/package=optimParallel для этой цели. Я автор пакета:

library("optimParallel")
cl <- makeCluster(2); setDefaultCluster(cl=cl)
clusterExport(cl, c("x", "y")) # export implicitly used values
optimParallel(par = 1, fn = GLM_tune,
              lower = 0, upper = 1, 
              control = list(fnscale = -1),
              parallel = list(optimParallel.loginfo=TRUE))$loginfo 
##      step       par1         fn        gr1 
## 1.0000000  1.0000000 -0.8215854  0.0000000 

Мы видим, что градиент в точке 1 равен 0. Следовательно, неудивительно, что алгоритм останавливается на 1.

Мы можем проверить вычисление приблизительного градиента с помощью

ndeps <- 0.001  # the default value
(GLM_tune(1) - GLM_tune(1-ndeps))/ndeps
## [1] 0

Обратите внимание, что если 1 не будет верхней границей, optim() будет использовать аппроксимацию градиента центральной разности. Что-то вроде

(GLM_tune(1+ndeps) - GLM_tune(1-ndeps))/(2*ndeps)
07.05.2019
  • Большое спасибо, Флориан; Я, очевидно, собираюсь попробовать ваш пакет сейчас, учитывая вилку! Я повторно тестировал как 3.6, так и 3.5.2; Я получаю номинал = 1 в версии 3.6, но я получаю номинал = 0 в версии 3.5.2. Интересный. ^ Я понимаю, что этот метод не оптимален для глобальных максимумов, этот вопрос возник из сообщения в блоге, в котором генетические алгоритмы сравниваются как решатель. Однако стоило уточнить, так что спасибо, на случай, если кто-то еще споткнется об этом в будущем. 08.05.2019
  • Новые материалы

    Объяснение документов 02: BERT
    BERT представил двухступенчатую структуру обучения: предварительное обучение и тонкая настройка. Во время предварительного обучения модель обучается на неразмеченных данных с помощью..

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

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..


    Для любых предложений по сайту: [email protected]