Calcolo dell'algebra lineare passo-passo della regressione dei minimi quadrati


22

Come prequel di una domanda sui modelli lineari misti in R e di condividere come riferimento per gli appassionati di statistiche per principianti / intermedi, ho deciso di pubblicare come "domande e risposte" indipendenti i passaggi coinvolti nel calcolo "manuale" del coefficienti e valori previsti di una semplice regressione lineare.

L'esempio è con il set di dati incorporato R mtcars, e verrebbe impostato come miglia per gallone consumate da un veicolo che agisce come variabile indipendente, regredito rispetto al peso dell'auto (variabile continua) e il numero di cilindri come fattore con tre livelli (4, 6 o 8) senza interazioni.

EDIT: Se sei interessato a questa domanda, troverai sicuramente una risposta dettagliata e soddisfacente in questo post di Matthew Drury fuori CV .


Quando dici "calcolo manuale", che cosa cerchi? È relativamente semplice mostrare una serie di passaggi relativamente semplici per ottenere stime dei parametri e così via (tramite l'ortogonalizzazione di Gram-Schmidt, ad esempio, o tramite operatori SWEEP), ma non è così che R esegue i calcoli internamente; esso (e la maggior parte degli altri pacchetti di statistiche) utilizza la decomposizione QR (discussa in numerosi post sul sito - una ricerca sulla decomposizione QR rivela una serie di post, alcuni dei quali potresti ottenere un valore diretto da)
Glen_b -Reinstate Monica

Sì. Credo che questo sia stato molto ben indirizzato nella risposta di MD. Probabilmente dovrei modificare il mio post, forse enfatizzando l'approccio geometrico dietro la mia risposta - spazio della colonna, matrice di proiezione ...
Antoni Parellada,

Sì! @Matthew Drury Vuoi che cancelli quella riga nell'OP o aggiorni il link?
Antoni Parellada,

1
Non sono sicuro che tu abbia questo link, ma questo è strettamente correlato e adoro la risposta di JM. stats.stackexchange.com/questions/1829/…
Haitao Du

Risposte:


51

Nota : ho pubblicato una versione estesa di questa risposta sul mio sito Web .

Considereresti gentilmente di pubblicare una risposta simile con l'attuale motore R esposto?

Sicuro! Scendiamo nella tana del coniglio.

Il primo livello è lm, l'interfaccia esposta al programmatore R. Puoi vedere la fonte per questo semplicemente digitando lmsulla console R. La maggior parte (come la maggior parte della maggior parte del codice del livello di produzione) è impegnata nel controllo degli input, nell'impostazione degli attributi degli oggetti e nel lancio di errori; ma questa linea sporge

lm.fit(x, y, offset = offset, singular.ok = singular.ok, 
                ...)

lm.fitè un'altra funzione R, puoi chiamarla tu. Mentre lmfunziona comodamente con formule e frame di dati, lm.fitdesidera matrici, quindi questo è un livello di astrazione rimosso. Verifica della fonte per lm.fit, più lavoro occupato e la seguente riga davvero interessante

z <- .Call(C_Cdqrls, x, y, tol, FALSE)

Adesso stiamo andando da qualche parte. .Callè il modo di R di chiamare nel codice C. C'è una funzione C, C_Cdqrls nell'origine R da qualche parte, e dobbiamo trovarla. Eccolo .

Osservando la funzione C, troviamo ancora il controllo dei limiti, la pulizia degli errori e il lavoro occupato. Ma questa linea è diversa

F77_CALL(dqrls)(REAL(qr), &n, &p, REAL(y), &ny, &rtol,
        REAL(coefficients), REAL(residuals), REAL(effects),
        &rank, INTEGER(pivot), REAL(qraux), work);

Quindi ora siamo nella nostra terza lingua, R ha chiamato C che sta chiamando fortran. Ecco il codice fortran .

Il primo commento dice tutto

c     dqrfit is a subroutine to compute least squares solutions
c     to the system
c
c     (1)               x * b = y

(interessante, sembra che il nome di questa routine sia stato cambiato ad un certo punto, ma qualcuno ha dimenticato di aggiornare il commento). Quindi siamo finalmente nel punto in cui possiamo fare un po 'di algebra lineare e risolvere effettivamente il sistema di equazioni. Questo è il genere di cose in cui Fortran è davvero bravo, il che spiega perché siamo passati attraverso così tanti livelli per arrivare qui.

Il commento spiega anche cosa farà il codice

c     on return
c
c        x      contains the output array from dqrdc2.
c               namely the qr decomposition of x stored in
c               compact form.

Così fortran sta per risolvere il sistema trovando il decomposizione.QR

La prima cosa che succede, e di gran lunga la più importante, è

call dqrdc2(x,n,n,p,tol,k,qraux,jpvt,work)

Questo chiama la funzione fortran dqrdc2sulla nostra matrice di input x. Che cos'è questo?

 c     dqrfit uses the linpack routines dqrdc and dqrsl.

Quindi finalmente ce l'abbiamo fatta a Linpack . Linpack è una libreria di algebra lineare fortran che esiste dagli anni '70. L'algebra lineare più seria alla fine trova la sua strada per linpack. Nel nostro caso, stiamo usando la funzione dqrdc2

c     dqrdc2 uses householder transformations to compute the qr
c     factorization of an n by p matrix x.

Qui è dove viene svolto il lavoro effettivo. Ci vorrebbe un bel giorno intero per capire cosa sta facendo questo codice, è di livello basso come arrivano. Ma genericamente, abbiamo una matrice e vogliamo fattorizzarla in un prodotto X = Q R dove Q è una matrice ortogonale e R è una matrice triangolare superiore. Questa è una cosa intelligente da fare, perché una volta che hai Q e R puoi risolvere le equazioni lineari per la regressioneXX=QRQRQR

XtXβ=XtY

molto facilmente. Infatti

XtX=RtQtQR=RtR

così l'intero sistema diventa

RtRβ=RtQty

ma è triangolare superiore e ha lo stesso rango di X t X.RXtX , quindi fintanto che il nostro problema è ben posto, è di rango massimo e potremmo anche risolvere il sistema ridotto

Rβ=Qty

Ma ecco la cosa fantastica. è triangolare superiore, quindi l'ultima equazione lineare qui è giusta , quindi risolvere per β n è banale. Puoi quindi risalire le righe, una ad una, e sostituire le β che già conosci, ottenendo ogni volta una semplice equazione lineare a una variabile da risolvere. Quindi, una volta che hai Q e R , tutto crolla in quella che viene chiamata sostituzione all'indietro , il che è facile. Puoi leggere questo in modo più dettagliato qui , dove un piccolo esempio esplicito è completamente elaborato.Rconstant * beta_n = constantβnβQR


4
Questo è stato il più breve saggio matematico / di programmazione che si possa immaginare. Non so quasi nulla di programmazione, ma il tuo "tour" attraverso le viscere di una funzione R apparentemente innocua è stato davvero sorprendente. Ottima scrittura! Dato che "gentilmente" ha fatto il trucco ... Potresti gentilmente considerare questo come una sfida correlata? :-)
Antoni Parellada,

6
+1 Non l'avevo mai visto prima, bel riassunto. Solo per aggiungere un po 'di informazioni nel caso in cui @Antoni non abbia familiarità con le trasformazioni di Householder; è essenzialmente una trasformazione lineare che ti consente di azzerare una parte della matrice R che stai cercando di ottenere senza mettere a repentaglio le parti che hai già affrontato (purché lo attraversi nel giusto ordine), rendendolo ideale per trasformare le matrici nella forma triangolare superiore (le rotazioni di Givens fanno un lavoro simile e sono forse più facili da visualizzare, ma sono un po 'più lente). Mentre costruisci R, devi contemporaneamente costruire Q
Glen_b -Reinstate Monica

2
Matthew (+1), ti suggerisco di iniziare o terminare il tuo post con un link al tuo articolo molto più dettagliato madrury.github.io/jekyll/update/2016/07/20/lm-in-R.html .
ameba dice Ripristina Monica il

3
-1 per chickening out e non scendere al codice macchina.
S. Kolassa - Ripristina Monica il

3
(Scusate,
sto

8

I calcoli passo-passo effettivi in ​​R sono descritti magnificamente nella risposta di Matthew Drury in questo stesso thread. In questa risposta voglio passare attraverso il processo di dimostrazione che i risultati in R con un semplice esempio possono essere raggiunti seguendo l'algebra lineare delle proiezioni sullo spazio della colonna e il concetto di errori perpendicolari (prodotto punto), illustrati in diversi post e ben spiegato dal Dr. Strang in Linear Algebra e sue applicazioni , e facilmente accessibile qui .

β

mpg=iontercept(cyl=4)+β1*weioght+D1*iontercept(cyl=6)+D2*iontercept(cyl=8)[*]

D1D2X

attach(mtcars)    
x1 <- wt

    x2 <- cyl; x2[x2==4] <- 1; x2[!x2==1] <-0

    x3 <- cyl; x3[x3==6] <- 1; x3[!x3==1] <-0

    x4 <- cyl; x4[x4==8] <- 1; x4[!x4==1] <-0

    X <- cbind(x1, x2, x3, x4)
    colnames(X) <-c('wt','4cyl', '6cyl', '8cyl')

head(X)
        wt 4cyl 6cyl 8cyl
[1,] 2.620    0    1    0
[2,] 2.875    0    1    0
[3,] 2.320    1    0    0
[4,] 3.215    0    1    0
[5,] 3.440    0    0    1
[6,] 3.460    0    1    0

[*]lm

βProjMun'trioX=(XTX)-1XT[ProjMun'trioX][y]=[RegrCoef'S](XTX)-1XTy=β

X_tr_X_inv <- solve(t(X) %*% X)    
Proj_M <- X_tr_X_inv %*% t(X)
Proj_M %*% mpg

          [,1]
wt   -3.205613
4cyl 33.990794
6cyl 29.735212
8cyl 27.919934

Identico a: coef(lm(mpg ~ wt + as.factor(cyl)-1)).

Hun'tMun'trioX=X(XTX)-1XT

HAT <- X %*% X_tr_X_inv %*% t(X)

y^X(XTX)-1XTyy_hat <- HAT %*% mpg

cyl <- as.factor(cyl); OLS <- lm(mpg ~ wt + cyl); predict(OLS):

y_hat <- as.numeric(y_hat)
predicted <- as.numeric(predict(OLS))
all.equal(y_hat,predicted)
[1] TRUE

1
In generale, nel calcolo numerico, credo che sia meglio risolvere l'equazione lineare invece di calcolare la matrice inversa. Quindi, penso che beta = solve(t(X) %*% X, t(X) %*% y)in pratica sia più preciso di solve(t(X) %*% X) %*% t(X) %*% y.
Matthew Drury,

R non lo fa in questo modo: utilizza una decomposizione QR. Se hai intenzione di descrivere l' algoritmo utilizzato, su un computer dubito che qualcuno usi quello che mostri.
Ripristina Monica - G. Simpson,

Non dopo l'algoritmo, sto solo cercando di capire le basi dell'algebra lineare.
Antoni Parellada,

@AntoniParellada Anche in quel caso, trovo ancora il pensiero in termini di equazioni lineari più illuminante in molte situazioni.
Matthew Drury,

1
Vista la relazione periferica di questo thread con gli obiettivi del nostro sito, pur vedendo il valore nell'illustrare l'uso di Rper calcoli importanti, vorrei suggerire di prendere in considerazione l'idea di trasformarlo in un contributo al nostro blog.
whuber
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.