Hồi quy bình phương nhỏ nhất Tính toán đại số tuyến tính từng bước


22

Với tư cách là một câu hỏi trước về các mô hình hỗn hợp tuyến tính trong R và để chia sẻ làm tài liệu tham khảo cho người hâm mộ thống kê người mới bắt đầu / trung gian, tôi đã quyết định đăng bài dưới dạng "Hỏi & Đáp" độc lập các bước liên quan đến tính toán "thủ công" của hệ số và giá trị dự đoán của hồi quy tuyến tính đơn giản.

Các ví dụ là với R trong xây dựng bộ dữ liệu, mtcarsvà sẽ được thiết lập như dặm cho mỗi gallon tiêu thụ bởi một chiếc xe đóng vai trò là các biến độc lập, thụt lùi so với trọng lượng của xe (biến liên tục), và số lượng xi lanh như một yếu tố với ba cấp độ (4, 6 hoặc 8) không có tương tác.

EDIT: Nếu bạn quan tâm đến câu hỏi này, bạn chắc chắn sẽ tìm thấy một câu trả lời chi tiết và thỏa đáng trong bài đăng này của Matthew Drury bên ngoài CV .


Khi bạn nói "tính toán thủ công", bạn đang tìm kiếm cái gì? Điều này tương đối đơn giản để hiển thị một loạt các bước tương đối đơn giản để có được ước tính tham số, v.v. (ví dụ, thông qua trực giao Gram-Schmidt, hoặc bởi các toán tử SWEEP), nhưng đó không phải là cách R thực hiện các phép tính bên trong; nó (và hầu hết các gói thống kê khác) sử dụng phân tách QR (được thảo luận trong một số bài đăng trên trang web - một tìm kiếm về phân tách QR cho thấy một số bài đăng, một vài trong số đó bạn có thể nhận được giá trị trực tiếp từ)
Glen_b -Reinstate Monica

Vâng. Tôi tin rằng đây là địa chỉ rất hay trong câu trả lời của MD Tôi có lẽ nên chỉnh sửa bài đăng của mình, có lẽ nhấn mạnh cách tiếp cận hình học đằng sau câu trả lời của tôi - không gian cột, ma trận chiếu ...
Antoni Parellada

Vâng! @Matthew Drury Bạn có muốn tôi xóa dòng đó trong OP hoặc cập nhật liên kết không?
Antoni Parellada

1
Không chắc chắn nếu bạn có liên kết này, nhưng điều này có liên quan chặt chẽ và tôi thực sự thích câu trả lời của JM. stats.stackexchange.com/questions/1829/ Mạnh
Haitao Du

Câu trả lời:


51

Lưu ý : Tôi đã đăng phiên bản mở rộng của câu trả lời này trên trang web của mình .

Bạn có vui lòng xem xét việc đăng một câu trả lời tương tự với công cụ R thực tế được đưa ra không?

Chắc chắn rồi! Xuống hố thỏ chúng tôi đi.

Lớp đầu tiên là lm, giao diện tiếp xúc với lập trình viên R. Bạn có thể xem nguồn cho việc này bằng cách chỉ cần gõ lmvào bảng điều khiển R. Phần lớn của nó (giống như phần lớn mã phần lớn mức sản xuất) đang bận kiểm tra các đầu vào, thiết lập các thuộc tính đối tượng và ném lỗi; nhưng dòng này dính ra

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

lm.fitlà một hàm R khác, bạn có thể tự gọi nó. Mặc dù lmhoạt động thuận tiện với các công thức và khung dữ liệu, lm.fitmuốn có ma trận, do đó, một mức độ trừu tượng được loại bỏ. Kiểm tra nguồn cho lm.fit, công việc bận rộn hơn và dòng thực sự thú vị sau đây

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

Bây giờ chúng tôi đang nhận được ở đâu đó. .Calllà cách gọi của R vào mã C. Có một hàm C, C_Cdqrls trong nguồn R ở đâu đó và chúng ta cần tìm nó. Ở đây nó là .

Nhìn vào chức năng C, một lần nữa, chúng tôi thấy chủ yếu là kiểm tra giới hạn, dọn dẹp lỗi và công việc bận rộn. Nhưng dòng này thì khác

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

Vì vậy, bây giờ chúng tôi đang sử dụng ngôn ngữ thứ ba của mình, R đã gọi C đang gọi vào fortran. Đây là mã fortran .

Bình luận đầu tiên nói lên tất cả

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

(thật thú vị, có vẻ như tên của thói quen này đã được thay đổi tại một số điểm, nhưng ai đó đã quên cập nhật nhận xét). Vì vậy, cuối cùng chúng ta đã ở điểm chúng ta có thể thực hiện một số đại số tuyến tính và thực sự giải được hệ phương trình. Đây là thứ mà fortran thực sự giỏi, điều này giải thích tại sao chúng tôi đã đi qua rất nhiều lớp để đến đây.

Nhận xét cũng giải thích những gì mã sẽ làm

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

Vì vậy fortran sẽ giải quyết hệ thống bằng cách tìm phân tách QR

Điều đầu tiên xảy ra, và cho đến nay, điều quan trọng nhất là

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

Điều này gọi hàm fortran dqrdc2trên ma trận đầu vào của chúng tôi x. Đây là gì?

 c     dqrfit uses the linpack routines dqrdc and dqrsl.

Vì vậy, cuối cùng chúng tôi đã làm cho nó để linpack . Linpack là một thư viện đại số tuyến tính fortran đã có từ những năm 70. Hầu hết các đại số tuyến tính nghiêm trọng cuối cùng tìm thấy đường đến linpack. Trong trường hợp của chúng tôi, chúng tôi đang sử dụng chức năng dqrdc2

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

Đây là nơi công việc thực tế được thực hiện. Sẽ mất cả ngày để tôi tìm ra mã này đang làm gì, nó ở mức độ thấp như chúng đến. Nhưng nói chung, chúng ta có một ma trận và chúng ta muốn đưa nó vào một sản phẩm X = Q R trong đó Q là một ma trận trực giao và R là một ma trận tam giác trên. Đây là một điều thông minh để làm, bởi vì một khi bạn có QR, bạn có thể giải các phương trình tuyến tính cho hồi quyXX=QRQRQR

XtXβ=XtY

rất dễ dàng Thật

XtX=RtQtQR=RtR

vì vậy toàn bộ hệ thống trở thành

RtRβ=RtQty

nhưng là tam giác trên và có cùng thứ hạng với X t X , miễn là vấn đề của chúng ta được đặt ra, đó là thứ hạng đầy đủ, và chúng ta cũng có thể giải quyết hệ thống rút gọnRXtX

Rβ=Qty

Nhưng đây là điều tuyệt vời. là thượng tam giác, vì vậy phương trình tuyến tính trước đây chỉ là , vì vậy giải quyết cho β n là tầm thường. Sau đó bạn có thể đi lên các hàng, từng người một, và thay thế trong β s bạn đã biết, mỗi lần nhận được một biến đơn giản tuyến tính phương trình để giải quyết. Vì vậy, một khi bạn có QR , toàn bộ điều này sẽ sụp đổ với cái được gọi là sự thay thế ngược , rất dễ dàng. Bạn có thể đọc về điều này chi tiết hơn ở đây , nơi một ví dụ nhỏ rõ ràng được thực hiện đầy đủ.Rconstant * beta_n = constantβnβQR


4
Đây là bài tiểu luận ngắn về toán học / mã hóa thú vị nhất mà người ta có thể tưởng tượng. Tôi không biết gì về mã hóa, nhưng "chuyến tham quan" của bạn thông qua các chức năng R dường như vô hại đã thực sự mở mắt. Bài viết xuất sắc! Vì "vui lòng" đã thực hiện mánh khóe ... Bạn có thể vui lòng coi đây là một thử thách liên quan không? :-)
Antoni Parellada

6
+1 Tôi đã không nhìn thấy điều này trước đây, tóm tắt tốt đẹp. Chỉ cần thêm một chút thông tin trong trường hợp @Antoni không quen thuộc với các biến đổi của Householder; về cơ bản, nó là một phép biến đổi tuyến tính cho phép bạn loại bỏ một phần của ma trận R mà bạn đang cố gắng đạt được mà không cần xử lý các phần bạn đã xử lý (miễn là bạn thực hiện theo đúng thứ tự), làm cho nó trở nên lý tưởng để chuyển đổi ma trận sang dạng tam giác trên (phép quay Givens thực hiện một công việc tương tự và có lẽ dễ hình dung hơn, nhưng chậm hơn một chút). Khi bạn xây dựng R, bạn phải đồng thời xây dựng Q
Glen_b -Reinstate Monica

2
Matthew (+1), tôi khuyên bạn nên bắt đầu hoặc kết thúc bài đăng của mình bằng một liên kết đến madrury.github.io/jekyll/update/2016/07/20/lm-in-R.html .
amip nói rằng Phục hồi Monica

3
-1 cho ra và không đi xuống mã máy.
S. Kolassa - Tái lập Monica

3
(Xin lỗi, chỉ đùa thôi ;-)
S. Kolassa - Tái lập lại

8

Các tính toán từng bước thực tế trong R được mô tả rất đẹp trong câu trả lời của Matthew Drury trong cùng chủ đề này. Trong câu trả lời này, tôi muốn thực hiện quá trình chứng minh với bản thân rằng kết quả trong R với một ví dụ đơn giản có thể đạt được theo đại số tuyến tính của các phép chiếu lên không gian cột và khái niệm lỗi vuông góc (sản phẩm chấm), được minh họa trong các bài viết khác nhau và được giải thích độc đáo bởi Tiến sĩ Strang trong Đại số tuyến tính và các ứng dụng của nó , và có thể truy cập dễ dàng ở đây .

β

mpg=intercept(cyl=4)+β1weight+D1intercept(cyl=6)+D2intercept(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

βProjMatrix=(XTX)1XT[ProjMatrix][y]=[RegrCoefs](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

Đồng nhất với : coef(lm(mpg ~ wt + as.factor(cyl)-1)).

HatMatrix=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
Nói chung, trong điện toán số, tôi tin rằng tốt nhất là giải phương trình tuyến tính thay vì tính ma trận nghịch đảo. Vì vậy, tôi nghĩ beta = solve(t(X) %*% X, t(X) %*% y)là trong thực tế chính xác hơn solve(t(X) %*% X) %*% t(X) %*% y.
Matthew Drury

R không làm theo cách đó - nó sử dụng phân tách QR. Nếu bạn định mô tả thuật toán được sử dụng, trên máy tính tôi nghi ngờ bất cứ ai sử dụng thuật toán bạn hiển thị.
Phục hồi Monica - G. Simpson

Không phải sau thuật toán, chỉ cần cố gắng để hiểu nền tảng đại số tuyến tính.
Antoni Parellada

@AntoniParellada Ngay cả trong trường hợp đó, tôi vẫn thấy suy nghĩ về các phương trình tuyến tính được chiếu sáng nhiều hơn trong nhiều tình huống.
Matthew Drury

1
Do mối quan hệ ngoại vi của chủ đề này với các mục tiêu của trang web của chúng tôi, trong khi thấy giá trị trong việc minh họa việc sử dụng Rcho các tính toán quan trọng, tôi muốn đề nghị bạn xem xét biến nó thành một đóng góp cho blog của chúng tôi.
whuber
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.