การลดขนาด (SVD หรือ PCA) บนเมทริกซ์ขนาดใหญ่ที่กระจัดกระจาย


31

/ แก้ไข: ติดตามเพิ่มเติมตอนนี้คุณสามารถใช้irlba :: prcomp_irlba


/ แก้ไข: ติดตามโพสต์ของฉันเอง irlbaขณะนี้มีอาร์กิวเมนต์ "กลาง" และ "สเกล" ซึ่งให้คุณใช้ในการคำนวณส่วนประกอบหลักเช่น:

pc <- M %*% irlba(M, nv=5, nu=0, center=colMeans(M), right_only=TRUE)$v


ฉันมีMatrixคุณสมบัติเบาบางขนาดใหญ่ที่ฉันต้องการใช้ในอัลกอริทึมการเรียนรู้ของเครื่อง:

library(Matrix)
set.seed(42)
rows <- 500000
cols <- 10000
i <- unlist(lapply(1:rows, function(i) rep(i, sample(1:5,1))))
j <- sample(1:cols, length(i), replace=TRUE)
M <- sparseMatrix(i, j)

เนื่องจากเมทริกซ์นี้มีหลายคอลัมน์ฉันต้องการลดขนาดของมันเป็นสิ่งที่จัดการได้มากกว่า ฉันสามารถใช้แพ็คเกจ irlba ที่ยอดเยี่ยมเพื่อทำ SVD และคืนองค์ประกอบหลักตัวแรก (5 ที่แสดงที่นี่ฉันอาจใช้ 100 หรือ 500 ในชุดข้อมูลจริงของฉัน):

library(irlba)
pc <- irlba(M, nu=5)$u

อย่างไรก็ตามฉันได้อ่านแล้วว่าก่อนที่จะทำการ PCA หนึ่งควรอยู่ตรงกลางเมทริกซ์ (ลบค่าเฉลี่ยคอลัมน์จากแต่ละคอลัมน์) นี่เป็นเรื่องยากมากที่จะทำบนชุดข้อมูลของฉันและนอกจากนี้จะทำลายขอบเขตของเมทริกซ์

"ไม่ดี" ในการดำเนินการ SVD กับข้อมูลที่ไม่ได้ปรับขนาดและป้อนเข้าสู่อัลกอริทึมการเรียนรู้ของเครื่องอย่างไร มีวิธีใดที่มีประสิทธิภาพที่ฉันสามารถขยายข้อมูลนี้ในขณะที่รักษาความกระจัดกระจายของเมทริกซ์ได้หรือไม่?


/ edit: B_miner ได้รับความสนใจจากฉัน "พีซี" ควรจะเป็น:

pc <- M %*% irlba(M, nv=5, nu=0)$v 

นอกจากนี้ฉันคิดว่าคำตอบของคนผิวเผินน่าจะนำไปใช้งานได้ง่ายผ่านcrossprodฟังก์ชั่นซึ่งเร็วมากในการฝึกอบรมแบบกระจัดกระจาย:

system.time(M_Mt <- crossprod(M)) # 0.463 seconds
system.time(means <- colMeans(M)) #0.003 seconds

ตอนนี้ฉันไม่แน่ใจว่าจะทำอย่างไรกับmeansเวกเตอร์ก่อนที่จะลบออกM_Mtแต่จะโพสต์ทันทีที่ฉันเข้าใจ


/ edit3: นี่คือรุ่นที่แก้ไขของรหัส whuber โดยใช้การดำเนินการเมทริกซ์กระจัดกระจายสำหรับแต่ละขั้นตอนของกระบวนการ หากคุณสามารถเก็บเมทริกซ์ sparse ทั้งหมดในหน่วยความจำมันจะทำงานได้อย่างรวดเร็ว:

library('Matrix')
library('irlba')
set.seed(42)
m <- 500000
n <- 100
i <- unlist(lapply(1:m, function(i) rep(i, sample(25:50,1))))
j <- sample(1:n, length(i), replace=TRUE)
x <- sparseMatrix(i, j, x=runif(length(i)))

n_comp <- 50
system.time({
  xt.x <- crossprod(x)
  x.means <- colMeans(x)
  xt.x <- (xt.x - m * tcrossprod(x.means)) / (m-1)
  svd.0 <- irlba(xt.x, nu=0, nv=n_comp, tol=1e-10)
})
#user  system elapsed 
#0.148   0.030   2.923 

system.time(pca <- prcomp(x, center=TRUE))
#user  system elapsed 
#32.178   2.702  12.322

max(abs(pca$center - x.means))
max(abs(xt.x - cov(as.matrix(x))))
max(abs(abs(svd.0$v / pca$rotation[,1:n_comp]) - 1))

หากคุณตั้งค่าจำนวนคอลัมน์เป็น 10,000 และจำนวนองค์ประกอบหลักเป็น 25 irlbaPCA ที่ใช้เวลาประมาณ 17 นาทีในการคำนวณส่วนประกอบหลักประมาณ 50 รายการและใช้ RAM ประมาณ 6GB ซึ่งไม่เลวร้ายเกินไป


Zach อยากรู้อยากเห็นถ้าคุณเคยแก้ไขปัญหานี้
B_Miner

@ B_Miner: โดยพื้นฐานแล้วฉันเคยทำ SVD โดยไม่ต้องไปที่จุดศูนย์กลางหรือสเกลก่อนเพราะฉันไม่เคยพบวิธีที่ดีในการทำสิ่งนี้โดยไม่ต้องแปลงเมทริกซ์กระจัดกระจายเป็นเมทริกซ์หนาแน่น เมทริกซ์ดั้งเดิม% *% องค์ประกอบ V ของ svd ให้ "ส่วนประกอบหลัก" บางครั้งฉันได้ผลลัพธ์ที่ดีกว่าถ้าฉัน "พับ" ค่า eigen เช่น v% *% diag (d) โดยที่ d คือเวกเตอร์ของค่าลักษณะเฉพาะจาก SVD
Zach

คุณปฏิบัติต่อ v% *% diag (d) ด้วยตัวเองหรือยังคูณด้วยเมทริกซ์ X เดิม (เช่น X% *% v% *% diag (d)) ดูเหมือนว่าคุณกำลังใช้เมทริกซ์ u เป็นคะแนนส่วนประกอบหลักใช่ไหม
B_Miner

X %*% v %*% diag(d, ncol=length(d))ฉันใช้ v matrix ใน svd เทียบเท่ากับองค์ประกอบ "การหมุน" ของprcompวัตถุและX %*% vหรือX %*% v %*% diag(d, ncol=length(d))แสดงถึงxองค์ประกอบของprcompวัตถุ stats:::prcomp.defaultใช้เวลาดู
Zach

ใช่ X% *% v เป็นองค์ประกอบ x จาก prcomp ดูเหมือนว่าเมื่อคุณใช้เมทริกซ์ u ในคำถามของคุณคุณกำลังใช้ X% *% v% *% diag (1 / d)
B_Miner

คำตอบ:


37

ก่อนอื่นคุณต้องการจัดกึ่งกลางข้อมูลจริงๆ ถ้าไม่การตีความทางเรขาคณิตของ PCAแสดงให้เห็นว่าองค์ประกอบหลักแรกจะอยู่ใกล้กับเวกเตอร์ของค่าเฉลี่ยและพีซีที่ตามมาทั้งหมดจะตั้งฉากกับมันซึ่งจะป้องกันพวกเขาจากการประมาณพีซีใด ๆ ที่ใกล้เคียงกับเวกเตอร์แรก เราหวังว่าพีซีส่วนใหญ่ในภายหลังจะมีความถูกต้องโดยประมาณ แต่คุณค่าของมันนั้นน่าสงสัยเมื่อมีแนวโน้มว่าพีซีหลายเครื่องแรกซึ่งเป็นพีซีที่สำคัญที่สุดจะผิดไป

XXX'1000010000

YZ500000nม.Yม.Z1n1

(Y-ม.Y1)(Z-ม.Z1)=YZ-ม.Z1Y-ม.Y1.Z+ม.Zม.Y11=YZ-n(ม.Yม.Z),

ม.Y=1Y/nม.Z=1Z/n

XX'YZ10000XX'


ตัวอย่าง

Rget.colXprcomp

m <- 500000 # Will be 500,000
n <- 100    # will be 10,000
library("Matrix")
x <- as(matrix(pmax(0,rnorm(m*n, mean=-2)), nrow=m), "sparseMatrix")
#
# Compute centered version of x'x by having at most two columns
# of x in memory at any time.
#
get.col <- function(i) x[,i] # Emulates reading a column
system.time({
  xt.x <- matrix(numeric(), n, n)
  x.means <- rep(numeric(), n)
  for (i in 1:n) {
    i.col <- get.col(i)
    x.means[i] <- mean(i.col)
    xt.x[i,i] <- sum(i.col * i.col)
    if (i < n) {
      for (j in (i+1):n) {
        j.col <- get.col(j)
        xt.x[i,j] <- xt.x[j,i] <- sum(j.col * i.col)
      }    
    }
  }
  xt.x <- (xt.x - m * outer(x.means, x.means, `*`)) / (m-1)
  svd.0 <- svd(xt.x / m)
}
)
system.time(pca <- prcomp(x, center=TRUE))
#
# Checks: all should be essentially zero.
#
max(abs(pca$center - x.means))
max(abs(xt.x - cov(x)))
max(abs(abs(svd.0$v / pca$rotation) - 1)) # (This is an unstable calculation.)

ขอบคุณสำหรับคำตอบโดยละเอียด ข้อดีอย่างหนึ่งของการirlbaที่คุณสามารถระบุnuเพื่อ จำกัด อัลกอริทึมให้กับองค์ประกอบหลักการแรกซึ่งเพิ่มประสิทธิภาพอย่างมากและ (ฉันคิดว่า) ข้ามการคำนวณเมทริกซ์ของ XX
ซัค

1
100005000005×1091000010000108irlba

ฉันคิดว่าหลัง =) ดังนั้นฉันจำเป็นต้องคำนวณผลิตภัณฑ์ดอทสำหรับแต่ละคอลัมน์ในเมทริกซ์กระจัดกระจายของฉันลบcolMeansเมทริกซ์เบาบางออกจากเมทริกซ์ผลิตภัณฑ์ดอทแล้วเรียกใช้ irlba กับผลลัพธ์หรือไม่
ซัค

XX'RX'

5
ฉันเพิ่มรหัสเพื่อแสดง
whuber
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.