Ceci est une longue réponse . Alors, donnons-en une version courte ici.
- Il n'y a pas de bonne solution algébrique à ce problème de recherche de racine, nous avons donc besoin d'un algorithme numérique.
- La fonction a beaucoup de belles propriétés. Nous pouvons les exploiter pour créer une version spécialisée de la méthode de Newton pour ce problème avec une convergence monotone garantie à chaque racine.df(λ)
- Même un
R
code mort cérébral en l' absence de toute tentative d'optimisation peut calculer une grille de taille 100 avec en quelques secondes. Un code soigneusement rédigé réduirait cela d'au moins 2 à 3 ordres de grandeur.p=100000C
Deux schémas sont donnés ci-dessous pour garantir la convergence monotone. On utilise les limites indiquées ci-dessous, qui semblent aider à enregistrer une étape de Newton ou deux à l'occasion.
Exemple : et une grille uniforme pour les degrés de liberté de taille 100. Les valeurs propres sont Pareto-distribuées, donc fortement asymétriques. Voici les tableaux du nombre d'étapes de Newton pour trouver chaque racine.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
Il n'y aura pas de solution de forme fermée pour cela , en général, mais il y a beaucoup de structure présente qui peut être utilisée pour produire des solutions très efficaces et sûres en utilisant des méthodes de recherche de racines standard.
Avant de creuser trop profondément dans les choses, collectons quelques propriétés et conséquences de la fonction
df(λ)=∑i=1pd2id2i+λ.
Propriété 0 : est une fonction rationnelle de . (Cela ressort de la définition.)
Conséquence 0 : aucune solution algébrique générale n'existera pour trouver la racine . En effet, il existe un problème de recherche de racines polynomial équivalent de degré et donc si n'est pas extrêmement petit (c'est-à-dire moins de cinq), aucune solution générale n'existera. Nous aurons donc besoin d'une méthode numérique. λdfλ
df(λ)−y=0ppp
Propriété 1 : La fonction est convexe et décroissante sur . (Prenez les dérivés.)
Conséquence 1 (a) : l'algorithme de recherche de racines de Newton se comportera très bien dans cette situation. Soit les degrés de liberté souhaités et la racine correspondante, c'est-à-dire . En particulier, si nous commençons avec une valeur initiale (donc, ), alors la séquence d'itérations de Newton convergera de façon monotone vers le solution unique λ ≥ 0dfλ≥0
λ 0 y = d f ( λ 0 )yλ0y=df(λ0)λ1<λ0df(λ1)>yλ1,λ2,…λ0 .
Conséquence 1 (b) : En outre, si nous , la première étape donnerait , d'où elle augmentera de façon monotone jusqu'à la solution par la conséquence précédente (voir la mise en garde au dessous de). Intuitivement, ce dernier fait suit parce que si nous commençons à droite de la racine, la dérivée est "trop" superficielle en raison de la convexité de et donc la première étape de Newton nous mènera quelque part à gauche de la racine. NB Puisque n'est pas en général convexe pour négatifλ1>λ0λ2≤λ0dfdfλ, cela fournit une bonne raison de préférer commencer à gauche de la racine souhaitée. Sinon, nous devons vérifier que l'étape de Newton n'a pas donné lieu à une valeur négative pour la racine estimée, ce qui peut nous placer quelque part dans une partie non convexe de .
Conséquence 1 (c) : Une fois que nous avons trouvé la racine pour certains et que nous recherchons ensuite la racine à partir de certains , en utilisant telle sorte que comme garantie initiale, nous commençons à la gauche de la deuxième racine. Ainsi, notre convergence est garantie d'être monotone à partir de là.df
y1y2<y1λ1df(λ1)=y1
Propriété 2 : Des limites raisonnables existent pour donner des points de départ "sûrs". En utilisant des arguments de convexité et l'inégalité de Jensen, nous avons les limites suivantes
Conséquence 2 : cela nous indique que la racine satisfaisant obéit
Donc, jusqu'à une constante commune, nous avons pris en sandwich la racine entre les moyennes harmonique et arithmétique du .
p1+λp∑d−2i≤df(λ)≤p∑id2i∑id2i+pλ.
λ0df(λ0)=y11p∑id−2i(p−yy)≤λ0≤(1p∑id2i)(p−yy).(⋆)
d2i
Cela suppose que pour tout . Si ce n'est pas le cas, alors la même borne tient en considérant uniquement le positif et en remplaçant par le nombre de positifs . NB : Puisque supposant que tous les , alors , d'où les bornes sont toujours non triviales (par exemple, la borne inférieure est toujours non négative).di>0idipdidf(0)=pdi>0y∈(0,p]
Voici un tracé d'un exemple "typique" de avec . Nous avons superposé une grille de taille 10 pour les degrés de liberté. Ce sont les lignes horizontales de l'intrigue. Les lignes vertes verticales correspondent à la borne inférieure de .df(λ)p=400(⋆)
Un algorithme et un exemple de code R
Un algorithme très efficace étant donné une grille de degrés de liberté in consiste à les trier par ordre décroissant puis à trouver séquentiellement la racine de chacun, en utilisant la racine précédente comme point de départ pour les éléments suivants 1. Nous pouvons affiner cela en vérifiant si chaque racine est supérieure à la borne inférieure de la racine suivante, et sinon, nous pouvons commencer l'itération suivante à la borne inférieure à la place.y1,…yn(0,p]
Voici un exemple de code R
, sans aucune tentative d'optimisation. Comme on le voit ci-dessous, il est encore assez rapide même s'il R
est - pour le dire poliment - horriblement, terriblement, terriblement lent aux boucles.
# 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)))
}
Ci-dessous se trouve l'algorithme complet final qui prend une grille de points, et un vecteur du ( pas !).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
}
Exemple d'appel de fonction
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)