Büyük, seyrek bir matris üzerinde boyutsallık azaltma (SVD veya PCA)


31

/ edit: Şimdi daha fazla takip irlba :: prcomp_irlba kullanabilirsiniz


/ edit: kendi gönderimde takip etmek. irlbaŞimdi, temel bileşenleri hesaplamak için kullanmanıza izin veren "merkez" ve "ölçek" argümanlarına sahiptir, örneğin:

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


Büyük bir seyrek var Matrixben öğrenme algoritması bir makinede kullanmak istiyorum özelliklerinden:

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)

Bu matrisin birçok sütunu olduğu için, boyutluluğunu daha yönetilebilir bir şeye düşürmek istiyorum. Mükemmel irlba paketini SVD yapmak ve ilk n ana bileşenlerini döndürmek için kullanabilirim (burada 5; gerçek veri setimde muhtemelen 100 veya 500 kullanacağım):

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

Ancak, PCA'yı gerçekleştirmeden önce birinin matrisi ortalaması gerektiğini okudum (sütun ortalamasını her bir sütundan çıkarın). Bunu veri setimde yapmak çok zor ve ayrıca matrisin azlığını da mahveder.

Ölçeklendirilmemiş verilerde SVD gerçekleştirmek ve doğrudan bir makine öğrenme algoritmasına beslemek ne kadar "kötü"? Matrisin azlığını koruyarak bu verileri ölçeklendirmenin etkili yolları var mı?


/ edit: B_miner tarafından dikkatimi çekti, "PC'ler" gerçekten olmalı:

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

Ayrıca, whuber'in cevabının crossprod, seyrek matrislerde son derece hızlı olan fonksiyonu aracılığıyla uygulanması oldukça kolay olmalı :

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

Şimdi meansçıkarmadan önce vektöre ne yapacağımdan emin değilim M_Mt, ancak çözdüğümde yayınlayacağım.


/ edit3: İşte işlemin her aşaması için seyrek matris işlemleri kullanan, whuber kodunun değiştirilmiş hali. Eğer siz bellekte tüm seyrek matris saklayabilir, çok hızlı bir şekilde çalışır:

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))

Sütun sayısını 10.000'e, asıl bileşenlerin sayısını 25'e ayarlarsanız, irlbatabanlı PCA'nın yaklaşık 50 ana bileşeni hesaplaması yaklaşık 17 dakika sürer ve yaklaşık 6 GB RAM kullanır, bu da çok da kötü değildir.


Zach, bunu çözüp çözmediğini merak ettin.
B_Miner

@B_Miner: Temel olarak, önce ortalamak veya ölçeklendirmek için zahmete girmeden SVD yapıyorum, çünkü seyrek matrisimi yoğun bir matrise dönüştürmeden bunu yapmanın iyi bir yolunu bulamadım. Orijinal matris% *% svd'nin V bileşeni "temel bileşenler" i verir. Bazen, özdeğerleri "katlarsam" (ör. V% *% diag (d)) daha iyi sonuçlar alırım, burada d, SVD'den gelen özdeğerlerin vektörüdür.
Zach,

V% *% diag (d) 'i tek başına mı yoksa X orijinal matrisiyle hala çarpıyor musunuz (yani, X% *% v% *% diag (d)). Yukarıda göründüğü gibi u matrisini ana bileşen puanları olarak mı kullanıyorsunuz?
B_Miner

Kullandığım X %*% v %*% diag(d, ncol=length(d)). Svd'deki v matrisi, bir prcompnesnenin "döndürme" öğesine eşdeğerdir ve X %*% vveya bir nesnenin öğesini X %*% v %*% diag(d, ncol=length(d))temsil eder . Bir göz atın a . xprcompstats:::prcomp.default
Zach,

Evet, X% *% v, prcomp öğesinden x öğesidir. U matrisini sorunuzda olduğu gibi kullanıyorsanız, aslında% X% *% v% *% diag (1 / d) kullanıyorsunuz.
B_Miner

Yanıtlar:


37

Her şeyden önce, verileri gerçekten merkezlemek istersiniz . Değilse, PCA geometrik yorumu gösterileri birinci temel bileşen yakın araçlarının vektöre olacağını ve sonraki tüm PC'ler yakın ilk vektöre olmak olur herhangi PC'ler yaklaşan engel olan, buna dik olacaktır. Daha sonraki bilgisayarların çoğunun yaklaşık olarak doğru olacağını ümit edebiliriz, ancak ilk birkaç PC'nin - en önemlisi - oldukça yanlış olacağı durumlarda, bunun değeri sorgulanabilir.

Peki ne yapmalı? PCA, matrisinin tekil değer ayrışması yoluyla ilerler . Esansiyel bilgiler içerdiği edilecektir X X 'XXX bu durumda bir olduğunu, ile 10000 matris: o yönetilebilir olabilir. Hesaplaması, bir diğeriyle bir sütunun yaklaşık 50 milyon nokta ürününün hesaplanmasını içerir.1000010000

İki sütunu düşünün, o zaman, ve Z (her biri bir 500000 -vector; bu boyut n olsun ). Ortalamaları sırasıyla m Y ve m Z olsun . Ne istediğiniz bilgi işlem yazma, olduğu 1YZ500000nmYmZ1 için ait -vector 1 's,n1

(YmY1)(ZmZ1)=YZmZ1YmY1.Z+mZmY11=YZn(mYmZ),

çünkü ve m Z = 1Z / n .mY=1Y/nmZ=1Z/n

Bu , girişleri Y Z değerlerini sağlayan comp ' ı hesaplamak için seyrek matris tekniklerini kullanmanıza ve ardından 10000 sütun aracını temel alarak katsayılarını ayarlamanıza olanak tanır . Ayarlama zarar vermemelidir, çünkü X X çok seyrek olacak gibi görünmüyor .XXYZ10000XX


Örnek

Aşağıdaki Rkod bu yaklaşımı göstermektedir. get.colPratikte harici bir veri kaynağından bir seferde bir sütunu okuyabilen ve böylece gerekli RAM miktarını azaltan (tabii ki hesaplama hızında bir miktar maliyet) bir saplama kullanır . PCA'yı iki şekilde hesaplar: önceki yapıya uygulanan SVD yoluyla ve doğrudan kullanma . Daha sonra iki yaklaşımın çıktısını karşılaştırır. Hesaplama süresi 100 sütun için yaklaşık 50 saniyedir ve yaklaşık dörtlü ölçeklenir: 10K'lık bir 10K matrisinde SVD yaparken beklemeye hazır olun!Xprcomp

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.)

Detaylı cevap için teşekkür ederim. Avantajlarından biri irlba, nualgoritmayı ilk n temel bileşenlerle sınırlandırmayı belirleyebilmenizdir , bu da etkinliğini büyük ölçüde arttırır ve (bence) XX 'matrisinin hesaplanmasını atlar.
Zach,

1
100005000005×1091000010000108irlba

Sanırım ikincisi. =). Bu yüzden, seyrek matrisimdeki her bir sütun çifti için nokta ürününü hesaplamalıyım, colMeansseyrek matrisin noktasını nokta ürün matrisinden çıkarın, sonra sonuçta irlba çalıştırmalıyım?
Zach,

Neredeyse: sütun araçlarını çıkardığınıza dikkat edin, sütun kendileri değil demektir. Algoritma formülasyonunuz aksi halde mükemmeldir, çünkü soyut olarak hesaplasanız bileXX', gerçekten Roluşturmak istemiyorsunX'matris çarpımını yapmak için. Bunun yerine, eğer RAM gerçekten sınırlıysa, bir anda sütunların alt kümelerini okuyarak sütun nokta ürünlerini gruplar halinde gruplar halinde gerçekleştirebilirsiniz. İlk başta çok daha küçük matrislerle deneme yapmak akıllıca olur :-).
whuber

5
Göstermek için kod ekledim.
whuber
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.