Это длинный ответ . Итак, давайте приведем краткую версию этого здесь.
- Нет хорошего алгебраического решения этой проблемы поиска корней, поэтому нам нужен численный алгоритм.
- Функция df(λ) имеет много приятных свойств. Мы можем использовать их для создания специализированной версии метода Ньютона для этой задачи с гарантированной монотонной сходимостью к каждому корню.
- Даже мертвый
R
код без каких-либо попыток оптимизации может вычислить сетку размером 100 с за несколько секунд. Тщательно написанный код уменьшит это как минимум на 2–3 порядка.p=100000C
Ниже приведены две схемы, гарантирующие монотонную сходимость. Каждый использует границы, показанные ниже, которые, кажется, помогают сохранить шаг Ньютона или два в некоторых случаях.
Пример : и равномерная сетка для степеней свободы размера 100. Собственные значения распределены по Парето, поэтому сильно искажены. Ниже приведены таблицы количества шагов Ньютона для поиска каждого корня.p=100000
# Table of Newton iterations per root.
# Without using lower-bound check.
1 3 4 5 6
1 28 65 5 1
# Table with lower-bound check.
1 2 3
1 14 85
Там не будет замкнутая форма решения для этого , в общем случае , но это много структуры настоящее время, которые могут быть использованы для создания очень эффективные и безопасных решений с использованием стандартных корневыми ознакомительными методов.
Прежде чем , давайте соберем некоторые свойства и последствия функции
df(λ)=∑i=1pd2id2i+λ.
Свойство 0 : является рациональной функцией . (Это видно из определения.)
Следствие 0 : Не будет общего алгебраического решения для нахождения корня . Это связано с тем, что существует эквивалентная задача поиска корней полинома степени и поэтому, если не очень мало (т. Е. Меньше пяти), никакого общего решения не будет. Итак, нам понадобится численный метод.dfλ
df(λ)−y=0pp
Свойство 1 : функция выпуклая и убывает на . (Возьмем производные.)
Следствие 1 (а) : алгоритм поиска корня Ньютона будет вести себя очень хорошо в этой ситуации. Пусть - требуемые степени свободы, а - соответствующий корень, т. . В частности, если мы начнем с любого начального значения (то есть, ), то последовательность итераций шага Ньютона будет монотонно сходиться к уникальное решениеdfλ≥0
yλ0y=df(λ0)λ1<λ0df(λ1)>yλ1,λ2,…λ0 .
Следствие 1 (b) : Более того, если мы начнем с , то первый шаг приведет к , откуда он будет монотонно увеличиваться до решения по предыдущему следствию (см. Предостережение). ниже). Интуитивно, этот последний факт следует из того, что, если мы начнем справа от корня, производная слишком мала из-за выпуклости и поэтому первый шаг Ньютона приведет нас куда-то слева от корня. NB, поскольку не является вообще выпуклым для отрицательныхλ1>λ0λ2≤λ0dfdfλэто дает веские основания предпочитать начинать слева от нужного корня. В противном случае нам нужно дважды проверить, что шаг Ньютона не привел к отрицательному значению для предполагаемого корня, что может поместить нас где-то в невыпуклую часть .
Следствие 1 (c) : Как только мы нашли корень для некоторого и затем ищем корень для некоторого , используя такой, что качестве нашего начального предположения гарантирует, что мы начинаем слева от второго корня. Таким образом, наша конвергенция гарантированно будет монотонной оттуда.df
y1y2<y1λ1df(λ1)=y1
Свойство 2 : Разумные границы существуют, чтобы дать «безопасные» отправные точки. Используя аргументы выпуклости и неравенство Дженсена, мы получаем следующие оценки
Следствие 2. Это говорит нам о том, что корень удовлетворяющий подчиняется
Итак, с точностью до общей константы мы корень между гармоническим и арифметическим средними .
p1+λp∑d−2i≤df(λ)≤p∑id2i∑id2i+pλ.
λ0df(λ0)=y11p∑id−2i(p−yy)≤λ0≤(1p∑id2i)(p−yy).(⋆)
d2i
Это предполагает, что для всех . Если это не так, то та же самая оценка имеет место, рассматривая только положительное и заменяя числом положительных . NB : Поскольку при условии, что все , то , поэтому границы всегда нетривиальны (например, нижняя граница всегда неотрицательна).di>0idipdidf(0)=pdi>0y∈(0,p]
Вот график «типичного» примера с . Мы наложили сетку размером 10 для степеней свободы. Это горизонтальные линии на графике. Вертикальные зеленые линии соответствуют нижней границе в .df(λ)p=400(⋆)
Алгоритм и пример кода R
Очень эффективный алгоритм, заданный сеткой желаемых степеней свободы в состоит в том, чтобы отсортировать их по убыванию, а затем последовательно найти корень каждого из них, используя предыдущий корень в качестве отправной точки для следующего 1. Мы можем уточнить это далее, проверив, больше ли каждый корень, чем нижняя граница для следующего корня, и, если нет, мы можем вместо этого начать следующую итерацию с нижней границы.y1,…yn(0,p]
Вот пример кода R
, без попыток его оптимизации. Как видно ниже, оно все еще довольно быстрое, хотя, если R
говорить вежливо, ужасно, ужасно, ужасно медленно на петлях.
# Newton's step for finding solutions to regularization dof.
dof <- function(lambda, d) { sum(1/(1+lambda / (d[d>0])^2)) }
dof.prime <- function(lambda, d) { -sum(1/(d[d>0]+lambda / d[d>0])^2) }
newton.step <- function(lambda, y, d)
{ lambda - (dof(lambda,d)-y)/dof.prime(lambda,d) }
# Full Newton step; Finds the root of y = dof(lambda, d).
newton <- function(y, d, lambda = NA, tol=1e-10, smart.start=T)
{
if( is.na(lambda) || smart.start )
lambda <- max(ifelse(is.na(lambda),0,lambda), (sum(d>0)/y-1)/mean(1/(d[d>0])^2))
iter <- 0
yn <- Inf
while( abs(y-yn) > tol )
{
lambda <- max(0, newton.step(lambda, y, d)) # max = pedantically safe
yn <- dof(lambda,d)
iter = iter + 1
}
return(list(lambda=lambda, dof=y, iter=iter, err=abs(y-yn)))
}
Ниже приведен окончательный полный алгоритм, который принимает сетку точек и вектор ( не !).di d2i
newton.grid <- function(ygrid, d, lambda=NA, tol=1e-10, smart.start=TRUE)
{
p <- sum(d>0)
if( any(d < 0) || all(d==0) || any(ygrid > p)
|| any(ygrid <= 0) || (!is.na(lambda) && lambda < 0) )
stop("Don't try to fool me. That's not nice. Give me valid inputs, please.")
ygrid <- sort(ygrid, decreasing=TRUE)
out <- data.frame()
lambda <- NA
for(y in ygrid)
{
out <- rbind(out, newton(y,d,lambda, smart.start=smart.start))
lambda <- out$lambda[nrow(out)]
}
out
}
Пример вызова функции
set.seed(17)
p <- 100000
d <- sqrt(sort(exp(rexp(p, 10)),decr=T))
ygrid <- p*(1:100)/100
# Should take ten seconds or so.
out <- newton.grid(ygrid,d)