Уменьшение размерности (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 <- 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, irlbaPCA на основе вычислений займет около 17 минут, чтобы рассчитать 50 приблизительных основных компонентов, и потребует около 6 ГБ ОЗУ, что не так уж и плохо.


Зак, любопытно, если ты когда-нибудь решил это.
B_Miner

@B_Miner: По сути, я делал SVD, не заботясь о том, чтобы сначала центрировать или масштабировать, потому что я никогда не находил хороший способ сделать это без преобразования моей разреженной матрицы в плотную матрицу. Исходная матрица% *% V-компонента svd дает «основные компоненты». Иногда я получаю лучшие результаты, если «складываю» собственные значения, например, v% *% diag (d), где d - вектор собственных значений из SVD.
Зак

Вы рассматриваете v% *% diag (d) отдельно или все еще умножаете на исходную матрицу X (то есть X% *% v% *% diag (d)). Выше, кажется, вы используете матрицу u в качестве оценки основных компонентов?
B_Miner

Я использую X %*% v %*% diag(d, ncol=length(d)). Матрица v в svd эквивалентна элементу «вращения» prcompобъекта и X %*% vили X %*% v %*% diag(d, ncol=length(d))представляет xэлемент prcompобъекта. Посмотри stats:::prcomp.default.
Зак

Да, X% *% v - это элемент x из prcomp. Похоже, что когда вы используете матрицу u, как в вашем вопросе, вы на самом деле используете X% *% v% *% diag (1 / d).
B_Miner

Ответы:


37

Прежде всего, вы действительно хотите центрировать данные . Если нет, то геометрическая интерпретация PCA показывает, что первый главный компонент будет близок к вектору средних значений, и все последующие ПК будут ортогональны ему, что не позволит им приблизиться к любым ПК, оказавшимся близко к этому первому вектору. Мы можем надеяться, что большинство более поздних ПК будут примерно правильными, но ценность этого сомнительна, когда, вероятно, первые несколько ПК - самые важные - будут совершенно неверными.

XXX1000010000 матрицу : это может быть управляемым. Его вычисление включает в себя около 50 миллионов вычислений точечных произведений одного столбца со следующим.

YZ500000nmYmZ1n1

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

Спасибо за подробный ответ. Одним из преимуществ irlbaявляется то, что вы можете указать nuограничение алгоритма первыми n основными компонентами, что значительно повышает его эффективность и (я думаю) обходит вычисление матрицы XX '.
Зак

1
Но с чем вы хотите работать? Разреженный от 500000 матрицы с 5 × 10 9100005000005×1091000010000108irlba

Я полагаю, последний. знак равно Поэтому мне нужно вычислить скалярное произведение для каждой пары столбцов в моей разреженной матрице, вычесть colMeansразреженную матрицу из матрицы точечных произведений, а затем запустить irlba для результата?
Зак

XXRX

5
Я добавил код для иллюстрации.
whuber
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.