大規模なスパース行列の次元削減(SVDまたはPCA)


31

/ edit:irlba :: prcomp_irlbaを使用できるようになりました。


/ edit:自分の投稿のフォローアップ。 irlba現在、「center」および「scale」引数があり、これを使用して主成分を計算できます。例:

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を実行し、最初のn個の主要コンポーネントを返すことができます(ここに示す5つ。実際のデータセットではおそらく100または500を使用します)。

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

ただし、PCAを実行する前に、行列を中央に配置する必要があることを読みました(各列から列の平均値を減算します)。これは私のデータセットで行うことは非常に難しく、さらにマトリックスのスパース性を破壊します。

スケーリングされていないデータに対してSVDを実行し、それを機械学習アルゴリズムに直接入力するのはどの程度「悪い」のでしょうか?マトリックスのスパース性を維持しながら、このデータをスケーリングできる効率的な方法はありますか?


/ edit:AはB_minerによって注目されました。「PC」は次のようになります。

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

また、whuberの答えはcrossprod、スパース行列では非常に高速な関数を介して実装するのが非常に簡単だと思います。

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

今、私はmeansから減算する前にベクトルに何をすべきかよく分からないM_Mtが、それがわかり次第投稿する。


/ edit3:プロセスの各ステップにスパース行列演算を使用した、修正されたバージョンのwhuberのコードです。 スパース行列全体をメモリに保存できる場合、非常に高速に動作します。

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に設定すると、- irlbaベースのPCAはおよそ50の主成分を計算するのに約17分かかり、約6GBのRAMを消費しますが、それほど悪くはありません。


ザック、これを解決したことがあるのか​​興味があります。
B_Miner

@B_Miner:基本的に、スパースマトリックスをデンスマトリックスに変換せずにこれを行う良い方法を見つけられなかったため、最初に中心またはスケールに煩わされることなくSVDを実行しました。元の行列%*%svdのVコンポーネントは、「主要コンポーネント」を提供します。v%*%diag(d)のように固有値を「フォールドイン」すると、時々、より良い結果が得られます。dはSVDからの固有値のベクトルです。
ザック

v%*%diag(d)を単独で処理しますか、それとも元の行列Xで乗算しますか(つまり、X%*%v%*%diag(d))。上記のように、u行列を主成分スコアとして使用していますか?
B_Miner

を使用しますX %*% v %*% diag(d, ncol=length(d))。svdのv行列は、prcompオブジェクトの「回転」要素に相当し、X %*% vまたはオブジェクトX %*% v %*% diag(d, ncol=length(d))x要素を表しprcompます。を見てくださいstats:::prcomp.default
ザック

はい、X%*%vはprcompのx要素です。質問のようにu行列を使用すると、実際にはX%*%v%*%diag(1 / d)を使用しているように見えます。
B_Miner

回答:


37

まず第一に、あなたは本当にデータを集中させたいのです。そうでない場合、PCA幾何学的な解釈は、最初の主成分が平均のベクトルに近く、後続のすべてのPCがそれに直交することを示します。後のPCのほとんどがほぼ正しいことを期待できますが、最初のいくつかのPC(最も重要なPC)である可能性が高い場合、その価値は疑わしいでしょう。

じゃあ何をすればいいの?PCAは、行列特異値分解によって進行します。必須情報はX X に含まれます。この場合は10000ですXXX10000によって管理することができる:マトリックス。その計算には、ある列と次の列の内積の約5,000万の計算が含まれます。10000

次に、Zの 2つの列を検討します(それぞれが500000のベクトルです。この次元をnとします)。それらの平均をそれぞれm Yおよびm Zとします。計算したいのは、nに1を書き込むことですYZ500000nmYmZ1nベクトルに1

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

mY=1Y/nmZ=1Z/n

XXYZ10000XX


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

詳細な回答ありがとうございます。利点の1つは、アルゴリズムを最初のn個の主成分に制限irlbaするnuように指定できることです。これにより、有効性が大幅に向上し、XX 'マトリックスの計算がバイパスされます。
ザック

1
100005000005×1091000010000108irlba

後者だと思います。=)。だから、スパース行列の列の各ペアのドット積を計算し、ドット積colMeans行列からスパース行列を減算し、結果に対してirlbaを実行する必要がありますか?
ザック

XXRX

5
説明のためにコードを追加しました。
whuber
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.