Досить антикліматична відповідь на " Хто-небудь знає, чому це? " Полягає в тому, що просто ніхто не піклується про те, щоб реалізувати не негативний режим регресії хребта. Однією з головних причин є те, що люди вже розпочали впроваджувати
ненегативні пружні сіткові процедури (наприклад, тут і тут ). Еластична сітка включає регресію хребта як особливий випадок (по суті, одна частина LASSO встановлює нульове зважування). Ці роботи відносно нові, тому вони ще не були включені в scikit-learn або подібний пакет загального використання. Можливо, ви захочете запитати авторів цих робіт для коду.
Редагувати:
Як @amoeba і я обговорювали коментарі, реальна реалізація цього питання відносно проста. Скажімо, у вас є така проблема регресії:
y=2x1−x2+ϵ,ϵ∼N(0,0.22)
де і - обидва стандартні нормалі, такі як: . Зверніть увагу: я використовую стандартизовані змінні прогнозів, тому мені не доведеться нормалізуватися після цього. Для простоти я також не включаю перехоплення. Ми можемо негайно вирішити цю проблему регресії за допомогою стандартної лінійної регресії. Тож у R має бути щось подібне:x1x2xp∼N(0,1)
rm(list = ls());
library(MASS);
set.seed(123);
N = 1e6;
x1 = rnorm(N)
x2 = rnorm(N)
y = 2 * x1 - 1 * x2 + rnorm(N,sd = 0.2)
simpleLR = lm(y ~ -1 + x1 + x2 )
matrixX = model.matrix(simpleLR); # This is close to standardised
vectorY = y
all.equal(coef(simpleLR), qr.solve(matrixX, vectorY), tolerance = 1e-7) # TRUE
Помітьте останній рядок. Майже всі лінійні регресійні процедури використовують розкладання QR для оцінки . Ми хотіли б використати те саме для нашої проблеми з регресією хребта. У цей момент прочитайте цю публікацію від @whuber; ми будемо проводити саме цю процедуру. Коротше кажучи, ми будемо доповнювати свою оригінальну матрицю проекту з діагональною матрицею та нашим вектором відповіді з нулями. Таким чином ми зможемо повторно висловити початкову проблему регресії хребта as деβXλ−−√Ipyp(ХТХ+ λ I)- 1ХТу(Х¯ТХ¯)- 1Х¯Ту¯¯символізує доповнений варіант. Перевірте слайди 18-19 з цих записок також на повноту, я знайшов їх досить просто. Тож у R нам би хотілося наступного:
myLambda = 100;
simpleRR = lm.ridge(y ~ -1 + x1 + x2, lambda = myLambda)
newVecY = c(vectorY, rep(0, 2))
newMatX = rbind(matrixX, sqrt(myLambda) * diag(2))
all.equal(coef(simpleRR), qr.solve(newMatX, newVecY), tolerance = 1e-7) # TRUE
і це працює. Добре, тому ми отримали регресійну частину хребта. Ми могли б вирішити іншим способом, але можемо сформулювати це як оптимізаційну задачу, де залишкова сума квадратів є функцією витрат, а потім оптимізувати її, тобто. . Впевнені, що ми можемо це зробити:хвβ| |у¯-Х¯β||22
myRSS <- function(X,y,b){ return( sum( (y - X%*%b)^2 ) ) }
bfgsOptim = optim(myRSS, par = c(1,1), X = newMatX, y= newVecY,
method = 'L-BFGS-B')
all.equal(coef(simpleRR), bfgsOptim$par, check.attributes = FALSE,
tolerance = 1e-7) # TRUE
яка, як очікувалося, знову працює. Тому зараз ми просто хочемо: де . Яка просто та сама проблема оптимізації, але обмежена, щоб рішення не було негативним.хвβ| |у¯-Х¯β||22β≥ 0
bfgsOptimConst = optim(myRSS, par = c(1,1), X=newMatX, y= newVecY,
method = 'L-BFGS-B', lower = c(0,0))
all(bfgsOptimConst$par >=0) # TRUE
(bfgsOptimConst$par) # 2.000504 0.000000
з якого видно, що оригінальну задачу регресії хребта можна вирішити шляхом переформулювання як простої обмеженої задачі оптимізації. Деякі застереження:
- Я використовував (практично) нормовані змінні предиктора. Вам доведеться самостійно враховувати нормалізацію.
- Те ж саме стосується і відсутності нормалізації перехоплення.
- Я використовував
optim
«з L-BFGS-B аргумент. Це самий розв'язувач ванілі R, який приймає межі. Я впевнений, що ви знайдете десятки кращих вирішувачів.
- Загалом задачі лінійних найменших квадратів обмеження поставлені як завдання квадратичної оптимізації . Це надмірна робота для цієї посади, але майте на увазі, що ви можете отримати кращу швидкість, якщо потрібно.
- Як згадувалося в коментарях, ви можете пропустити регрес хребта як доповнену лінійно-регресійну частину і безпосередньо кодувати функцію витрат на хребет як проблему оптимізації. Це було б набагато простіше, і ця посада значно менша. Заради аргументу я додаю і це друге рішення.
- Я не повністю розмовляю в Python, але, по суті, ви можете повторити цю роботу, використовуючи lumg.solve NumPy і оптимізувати функції SciPy .
- Щоб вибрати гіперпараметр тощо, ви просто зробите звичайний крок CV, який ви зробили б у будь-якому випадку; нічого не змінюється.λ
Код для пункту 5:
myRidgeRSS <- function(X,y,b, lambda){
return( sum( (y - X%*%b)^2 ) + lambda * sum(b^2) )
}
bfgsOptimConst2 = optim(myRidgeRSS, par = c(1,1), X = matrixX, y = vectorY,
method = 'L-BFGS-B', lower = c(0,0), lambda = myLambda)
all(bfgsOptimConst2$par >0) # TRUE
(bfgsOptimConst2$par) # 2.000504 0.000000