内存高效的中心稀疏SVD / PCA(朱莉娅)?

时间:2017-09-25 06:50:48

标签: julia sparse-matrix pca svd

我有一个300万x 900万稀疏矩阵,有数十亿个非零项。 R和Python不允许稀疏矩阵具有超过MAXINT非零的条目,因此我发现自己使用Julia。

虽然使用标准偏差来缩放这些数据是微不足道的,但是贬低当然是一种天真的方式,因为它会产生一个密集的,超过200 TB的矩阵。

可以在https://github.com/JuliaLang/julia/blob/343b7f56fcc84b20cd1a9566fd548130bb883505/base/linalg/arnoldi.jl#L398

找到执行svd的相关代码julia

从我的阅读中,这段代码的一个关键元素是AtA_or_AAt结构和围绕它们的几个函数,特别是A_mul_B!。为方便起见,下面复制

struct AtA_or_AAt{T,S} <: AbstractArray{T, 2}
    A::S
    buffer::Vector{T}
end

function AtA_or_AAt(A::AbstractMatrix{T}) where T
    Tnew = typeof(zero(T)/sqrt(one(T)))
    Anew = convert(AbstractMatrix{Tnew}, A)
    AtA_or_AAt{Tnew,typeof(Anew)}(Anew, Vector{Tnew}(max(size(A)...)))
end

function A_mul_B!(y::StridedVector{T}, A::AtA_or_AAt{T}, x::StridedVector{T}) where T
    if size(A.A, 1) >= size(A.A, 2)
        A_mul_B!(A.buffer, A.A, x)
        return Ac_mul_B!(y, A.A, A.buffer)
    else
        Ac_mul_B!(A.buffer, A.A, x)
        return A_mul_B!(y, A.A, A.buffer)
    end
end
size(A::AtA_or_AAt) = ntuple(i -> min(size(A.A)...), Val(2))
ishermitian(s::AtA_or_AAt) = true

这被传递到eigs函数中,在那里发生了一些魔法,然后输出被处理到SVD的相关组件。

我认为最好的方法是让这项工作以“飞行中心”为中心。类型设置是使用AtA_or_AAT_centered版本来执行子类AtA_or_AAT,或多或少地模仿行为,但也存储列均值,并重新定义A_mul_B!功能恰当。

但是,我并没有非常使用朱莉娅,并且已经遇到了一些难以修改的问题。在我再次尝试深入研究之前,我想知道如果这被认为是一个合适的攻击计划,或者如果在这样一个大矩阵上进行SVD​​的简单方法,我是否能得到反馈(我还没有#39)看到它,但我可能错过了一些东西。)

编辑:我没有修改基础Julia,而是尝试编写一个"Centered Sparse Matrix"包来保持输入稀疏矩阵的稀疏结构,但是在各种计算中适当地进入列意味着。它实施的内容有限,而且有效。不幸的是,它仍然太慢,尽管为了优化事物做了一些非常广泛的努力。

1 个答案:

答案 0 :(得分:3)

在对稀疏矩阵算法进行了大量研究之后,我意识到在减法上分配乘法效率要高得多:

如果我们的居中矩阵Ac由原始n x m矩阵A组成,其列向量意为M,其中n 1}} x 1向量,我将称之为1。我们乘以m x k矩阵X

Ac := (A - 1M')
AcX = X
    = AX - 1M'X

我们基本上完成了。实际上,这非常简单。

AX可以使用通常的稀疏矩阵乘法函数执行,M'X是一个密集的向量矩阵内积,而1的“广播”向量(使用Julia的术语)来执行AX中间结果的每一行。大多数语言都有一种方法可以进行广播,而无需实现额外的内存分配。

这是我在package为AcX和Ac'X实施的内容。然后可以将生成的对象传递给算法,例如svds函数,该函数仅依赖于矩阵乘法和转置乘法。