Si tratta di una risposta lunga . Quindi, diamo una versione breve di questo qui.
- Non esiste una buona soluzione algebrica a questo problema di ricerca delle radici, quindi abbiamo bisogno di un algoritmo numerico.
- La funzione ha molte belle proprietà. Possiamo sfruttarli per creare una versione specializzata del metodo di Newton per questo problema con una convergenza monotonica garantita a ciascuna radice.df(λ)
- Perfino il
R
codice morto in assenza di qualsiasi tentativo di ottimizzazione può calcolare una griglia di dimensioni 100 con in pochi secondi. Un codice scritto con cura ridurrebbe questo di almeno 2-3 ordini di grandezza.p=100000C
Di seguito sono riportati due schemi per garantire la convergenza monotonica. Uno usa i limiti mostrati di seguito, che sembrano aiutare a salvare uno o due passi di Newton a volte.
Esempio : e una griglia uniforme per i gradi di libertà di dimensione 100. Gli autovalori sono distribuiti da Pareto, quindi fortemente inclinati. Di seguito sono riportate le tabelle del numero di passaggi di Newton per trovare ciascuna radice.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
Non ci sarà una soluzione in forma chiusa per questo , in generale, ma v'è un sacco di struttura attuale che può essere utilizzato per produrre soluzioni molto efficaci e sicure con metodi radice-finding standard.
Prima di scavare troppo a fondo nelle cose, raccogliamo alcune proprietà e conseguenze della funzione
df(λ)=∑i=1pd2id2i+λ.
La proprietà 0 : è una funzione razionale di . (Ciò è evidente dalla definizione.)
Conseguenza 0 : non esiste una soluzione algebrica generale per trovare la radice . Questo perché esiste un equivalente problema di individuazione delle radici polinomiali di grado e quindi se non è estremamente piccolo (cioè meno di cinque), non esisterà una soluzione generale. Quindi, avremo bisogno di un metodo numerico. λdfλ
df(λ)−y=0ppp
Proprietà 1 : la funzione è convessa e decrescente su . (Prendi i derivati.)
Conseguenza 1 (a) : l'algoritmo di ricerca delle radici di Newton si comporterà molto bene in questa situazione. Sia il grado desiderato di libertà e la radice corrispondente, cioè . In particolare, se iniziamo con qualsiasi valore iniziale (quindi, ), allora la sequenza di iterazioni di Newton convergeranno monotonicamente al soluzione unica λ ≥ 0dfλ≥0
yλ0y=df(λ0)λ1<λ0df(λ1)>yλ1,λ2,…λ0 .
Conseguenza 1 (b) : Inoltre, se dovessimo iniziare con , il primo passo produrrebbe e quindi il primo passo di Newton ci porterà da qualche parte a sinistra della radice. NB Poiché non è generalmente convesso per negativoλ1>λ0λ2≤λ0, da cui aumenterà monotonicamente alla soluzione dalla conseguenza precedente (vedi avvertenza di seguito). Intuitivamente, questo ultimo fatto segue perché se iniziamo a destra della radice, la derivata è "troppo" superficiale a causa della convessità di , questo fornisce un motivo forte per preferire iniziare a sinistra della radice desiderata. Altrimenti, dobbiamo ricontrollare che il passaggio di Newton non abbia comportato un valore negativo per la radice stimata, che potrebbe posizionarci da qualche parte in una porzione non convessa di .
Conseguenza 1 (c) : una volta trovata la radice per alcuni e quindi stiamo cercando la radice da alcunidfdfλdf
y1y2<y1 , usando tale che come la nostra ipotesi iniziale garantiamo che iniziamo a la sinistra della seconda radice. Quindi, la nostra convergenza è garantita per essere monotona da lì.λ1df(λ1)=y1
Proprietà 2 : esistono limiti ragionevoli per dare punti di partenza "sicuri". Utilizzando argomenti di convessità e disuguaglianza di Jensen, abbiamo i seguenti limiti
Conseguenza 2 : questo ci dice che la radice soddisfacente
p1+λp∑d−2i≤df(λ)≤p∑id2i∑id2i+pλ.
λ0df(λ0)=y obbedisce
Quindi, fino a una costante comune, abbiamo inserito la radice tra i mezzi armonici e aritmetici di .
11p∑id−2i(p−yy)≤λ0≤(1p∑id2i)(p−yy).(⋆)
d2i
Ciò presuppone che per tutti . In caso contrario, lo stesso limite vale considerando solo il positivo e sostituendo con il numero di positivo . NB : da alloradi>0idipdidf(0)=p assumendo tutto , quindi , da cui i limiti sono sempre non banali (ad esempio, il limite inferiore è sempre non negativo).di>0y∈(0,p]
Ecco un diagramma di un esempio "tipico" di condf(λ)p=400 . Abbiamo sovrapposto una griglia di dimensioni 10 per i gradi di libertà. Queste sono le linee orizzontali nella trama. Le linee verdi verticali corrispondono al limite inferiore in .(⋆)
Un algoritmo e qualche esempio di codice R.
Un algoritmo molto efficiente dato una griglia di gradi di libertà desideratiy1,…yn in è quello di ordinarli in ordine decrescente e quindi trovare sequenzialmente la radice di ciascuno, usando la radice precedente come punto di partenza per il seguente 1. Possiamo perfezionarlo ulteriormente controllando se ogni radice è maggiore del limite inferiore per la radice successiva e, in caso contrario, possiamo invece iniziare la successiva iterazione sul limite inferiore.(0,p]
Ecco alcuni esempi di codice R
, senza tentativi di ottimizzazione. Come visto di seguito, è ancora abbastanza veloce anche se R
è - per dirla educatamente - terrificante, terribile, terribilmente terribilmente lento agli anelli.
# 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 seguito è riportato l'algoritmo completo finale che accetta una griglia di punti e un vettore di ( nondi 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
}
Chiamata di funzione di esempio
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)