岭回归需要多少space?

How much space does ridge regression require?

在Haskell中,ridge regression可以表示为:

import Numeric.LinearAlgebra 

createReadout :: Matrix Double → Matrix Double → Matrix Double
createReadout a b = oA <\> oB
  where
   μ = 1e-4

   oA = (a <> (tr a)) + (μ * (ident $ rows a))
   oB = a <> (tr b)

但是,这个操作非常耗费内存。这是一个简约的例子,在我的机器上需要超过 2GB 的内存,并且需要 3 分钟才能执行。

import Numeric.LinearAlgebra
import System.Random

createReadout :: Matrix Double -> Matrix Double -> Matrix Double
createReadout a b = oA <\> oB
  where
    mu = 1e-4
    oA = (a <> (tr a)) + (mu * (ident $ rows a))
    oB = a <> (tr b)

teacher :: [Int] -> Int -> Int -> Matrix Double
teacher labelsList cols' correctRow = fromBlocks $ f <$> labelsList
  where ones = konst 1.0 (1, cols')
        zeros = konst 0.0 (1, cols')
        rows' = length labelsList
        f i | i == correctRow = [ones]
            | otherwise = [zeros]

glue :: Element t => [Matrix t] -> Matrix t
glue xs = fromBlocks [xs]

main :: IO ()
main = do

  let n = 1500  -- <- The constant to be increased
      m = 10000
      cols' = 12

  g <- newStdGen

  -- Stub data
  let labels = take m . map (`mod` 10) . randoms $ g :: [Int]
      a = (n >< (cols' * m)) $ take (cols' * m * n) $ randoms g :: Matrix Double
      teachers = zipWith (teacher [0..9]) (repeat cols') labels
      b = glue teachers

  print $ maxElement $ createReadout a b
  return ()

$ cabal exec ghc -- -O2 Test.hs

$ time ./Test
./Test 190.16s user 5.22s system 106% cpu 3:03.93 total

问题是增加常量n,至少增加到n = 4000,而内存限制在5GB。理论上矩阵求逆运算需要的最小space是多少?如何根据 space 优化此操作?可以用更便宜的方法有效地替代岭回归吗?

简单的Gauss-Jordan消元只需要space来存储输入输出矩阵加上常量辅助space。如果我没看错的话,你需要反转的矩阵 oAn x n 所以这不是问题。

您的内存使用量完全由存储输入矩阵 a 支配,它至少使用 1500 * 120000 * 8 = 1.34 GB。 n = 4000 将是 4000 * 120000 * 8 = 3.58 GB,这超过了您 space 预算的一半。我不知道你使用的是什么矩阵库,也不知道它是如何存储矩阵的,但如果它们在 Haskell 堆上,那么 GC 效应可以很容易地解释 space 使用中的另一个因素 2。

好吧,你可以用 3*m + nxn space,但我不确定这在数值上有多稳定。

基础是身份

inv( inv(Q) + A'*A)) = Q - Q*A'*R*A*Q
where R = inv( I + A*Q*A')

如果 A 是你的 A 矩阵并且

Q = inv( mu*I*mu*I) = I/(mu*mu)

那么你的岭回归的解是

inv( inv(Q) + A'*A)) * A'*b

多一点代数显示

inv( inv(Q) + A'*A)) = (I - A'*inv( (mu2 + A*A'))*A)/mu2
where mu2 = mu*m

请注意,由于 A 是 n x m,所以 A*A' 是 n x n。

所以一种算法是

计算 C = A*A' + mu2

对C进行cholesky分解,即求上三角U使得U'*U = C

计算向量 y = A'*b

计算向量 z = A*y

求解 U'*u = z for u in z

求解 U*v = z for v in z

计算 w = A'*z

计算 x = (y - w)/mu2。