Зменшення розмірності (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 та повернення перших n головних компонентів (5 показано тут; я, мабуть, використовуватиму 100 чи 500 на фактичному наборі даних):

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

Однак я читав, що перед виконанням PCA слід центровувати матрицю (віднімати середнє значення стовпця з кожного стовпця). Це дуже важко зробити на моєму наборі даних, і, крім того, знищиться рідкість матриці.

Наскільки "погано" це виконувати SVD на немасштабованих даних і подавати їх прямо в алгоритм машинного навчання? Чи є якісь ефективні способи я міг масштабувати ці дані, зберігаючи рідкість матриці?


/ редагувати: До мене звернув увагу 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, що використовує операції з розрідженою матрицею для кожного кроку процесу. Якщо ви можете зберегти всю розріджену матрицю в пам'яті, вона працює дуже швидко:

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 показує, що перший головний компонент буде близький до вектора засобів, а всі наступні ПК будуть ортогональними до нього, що не дозволить їм наблизити будь-які ПК, які, можливо, близькі до цього першого вектора. Ми можемо сподіватися, що більшість пізніших ПК будуть приблизно коректними, але значення цього питання сумнівне, коли, ймовірно, перші кілька ПК - найважливіші - будуть абсолютно неправильними.

ХХХ'1000010000

YZ500000нмYмZ1н1

(Y-мY1)(Z-мZ1)=YZ-мZ1Y-мY1.Z+мZмY11=YZ-н(мYмZ),

мY=1Y/нмZ=1Z/н

ХХ'YZ10000ХХ'


Приклад

Rget.colХprcomp

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
100005000005×1091000010000108irlba

Я припускаю останнє. =). Отже, мені потрібно обчислити точковий добуток для кожної пари стовпців моєї розрідженої матриці, відняти colMeansрозріджену матрицю від матриці крапкового продукту, а потім запустити irlba за результатом?
Зак

ХХ'RХ'

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